Files
Aaru/Aaru.Tests/Images/OpticalMediaImageTest.cs

473 lines
22 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using Aaru.Checksums;
using Aaru.CommonTypes;
using Aaru.CommonTypes.Enums;
using Aaru.CommonTypes.Interfaces;
using Aaru.CommonTypes.Structs;
using Aaru.Core;
using Aaru.Tests.Filesystems;
using FluentAssertions;
using FluentAssertions.Execution;
using NUnit.Framework;
namespace Aaru.Tests.Images;
2022-03-06 13:29:38 +00:00
public abstract class OpticalMediaImageTest : BaseMediaImageTest
{
2022-03-06 13:29:38 +00:00
const uint SECTORS_TO_READ = 256;
public abstract OpticalImageTestExpected[] Tests { get; }
[Test]
public void Info()
{
2022-03-06 13:29:38 +00:00
Environment.CurrentDirectory = DataFolder;
2022-03-06 13:29:38 +00:00
Assert.Multiple(() =>
{
2022-03-06 13:29:38 +00:00
foreach(OpticalImageTestExpected test in Tests)
{
2022-03-06 13:29:38 +00:00
string testFile = test.TestFile;
2022-03-06 13:29:38 +00:00
bool exists = File.Exists(testFile);
Assert.True(exists, string.Format(Localization._0_not_found, testFile));
2022-03-06 13:29:38 +00:00
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
// It arrives here...
if(!exists)
continue;
2022-03-06 13:29:38 +00:00
var filtersList = new FiltersList();
IFilter filter = filtersList.GetFilter(testFile);
filter.Open(testFile);
2022-03-15 01:37:37 +00:00
var image = Activator.CreateInstance(Plugin.GetType()) as IOpticalMediaImage;
Assert.NotNull(image, string.Format(Localization.Could_not_instantiate_filesystem_for_0, testFile));
2022-03-06 13:29:38 +00:00
ErrorNumber opened = image.Open(filter);
Assert.AreEqual(ErrorNumber.NoError, opened, string.Format(Localization.Open_0, testFile));
2022-03-06 13:29:38 +00:00
if(opened != ErrorNumber.NoError)
continue;
2022-03-06 13:29:38 +00:00
using(new AssertionScope())
Assert.Multiple(() =>
{
Assert.AreEqual(test.Sectors, image.Info.Sectors,
string.Format(Localization.Sectors_0, testFile));
2022-03-06 13:29:38 +00:00
if(test.SectorSize > 0)
Assert.AreEqual(test.SectorSize, image.Info.SectorSize,
string.Format(Localization.Sector_size_0, testFile));
Assert.AreEqual(test.MediaType, image.Info.MediaType,
string.Format(Localization.Media_type_0, testFile));
2022-03-06 13:29:38 +00:00
if(image.Info.XmlMediaType != XmlMediaType.OpticalDisc)
return;
Assert.AreEqual(test.Tracks.Length, image.Tracks.Count,
string.Format(Localization.Tracks_0, testFile));
2022-03-06 13:29:38 +00:00
image.Tracks.Select(t => t.Session).Should().
BeEquivalentTo(test.Tracks.Select(s => s.Session),
string.Format(Localization.Track_session_0, testFile));
2022-03-06 13:29:38 +00:00
image.Tracks.Select(t => t.StartSector).Should().
BeEquivalentTo(test.Tracks.Select(s => s.Start),
string.Format(Localization.Track_start_0, testFile));
2022-03-06 13:29:38 +00:00
image.Tracks.Select(t => t.EndSector).Should().
BeEquivalentTo(test.Tracks.Select(s => s.End),
string.Format(Localization.Track_end_0, testFile));
2022-03-06 13:29:38 +00:00
image.Tracks.Select(t => t.Pregap).Should().
BeEquivalentTo(test.Tracks.Select(s => s.Pregap),
string.Format(Localization.Track_pregap_0, testFile));
int trackNo = 0;
byte?[] flags = new byte?[image.Tracks.Count];
ulong latestEndSector = 0;
2022-03-06 13:29:38 +00:00
foreach(Track currentTrack in image.Tracks)
{
if(currentTrack.EndSector > latestEndSector)
latestEndSector = currentTrack.EndSector;
2022-03-06 13:29:38 +00:00
if(image.Info.ReadableSectorTags.Contains(SectorTagType.CdTrackFlags))
{
ErrorNumber errno =
image.ReadSectorTag(currentTrack.Sequence, SectorTagType.CdTrackFlags,
out byte[] tmp);
2022-03-06 13:29:38 +00:00
if(errno != ErrorNumber.NoError)
continue;
2022-03-06 13:29:38 +00:00
flags[trackNo] = tmp[0];
}
2022-03-06 13:29:38 +00:00
trackNo++;
}
flags.Should().BeEquivalentTo(test.Tracks.Select(s => s.Flags),
string.Format(Localization.Track_flags_0, testFile));
2022-03-06 13:29:38 +00:00
Assert.AreEqual(latestEndSector, image.Info.Sectors - 1,
string.Format(Localization.Last_sector_for_tracks_is_0_but_it_is_1_for_image,
latestEndSector, image.Info.Sectors));
2022-03-06 13:29:38 +00:00
});
}
});
}
2022-03-06 13:29:38 +00:00
[Test]
public void Contents()
{
Environment.CurrentDirectory = DataFolder;
2022-03-06 13:29:38 +00:00
Assert.Multiple(() =>
{
foreach(OpticalImageTestExpected test in Tests)
{
2022-03-06 13:29:38 +00:00
string testFile = test.TestFile;
2022-03-06 13:29:38 +00:00
bool exists = File.Exists(testFile);
Assert.True(exists, string.Format(Localization._0_not_found, testFile));
2022-03-06 13:29:38 +00:00
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
// It arrives here...
if(!exists)
continue;
2022-03-06 13:29:38 +00:00
var filtersList = new FiltersList();
IFilter filter = filtersList.GetFilter(testFile);
filter.Open(testFile);
2022-03-15 01:37:37 +00:00
var image = Activator.CreateInstance(Plugin.GetType()) as IOpticalMediaImage;
Assert.NotNull(image, string.Format(Localization.Could_not_instantiate_filesystem_for_0, testFile));
2022-03-06 13:29:38 +00:00
ErrorNumber opened = image.Open(filter);
Assert.AreEqual(ErrorNumber.NoError, opened, string.Format(Localization.Open_0, testFile));
2022-03-06 13:29:38 +00:00
if(opened != ErrorNumber.NoError)
continue;
2022-03-06 13:29:38 +00:00
using(new AssertionScope())
Assert.Multiple(() =>
{
2022-03-06 13:29:38 +00:00
foreach(TrackInfoTestExpected track in test.Tracks)
{
2022-03-06 13:29:38 +00:00
if(track.FileSystems is null)
continue;
2022-03-06 13:29:38 +00:00
ulong trackStart = track.Start + track.Pregap;
if(track.Number <= 1 &&
2022-03-06 13:29:38 +00:00
track.Pregap >= 150)
trackStart -= 150;
2022-03-06 13:29:38 +00:00
var partition = new Partition
{
Length = track.End - trackStart + 1,
Start = trackStart
};
Core.Filesystems.Identify(image, out List<string> idPlugins, partition);
2022-03-06 13:29:38 +00:00
Assert.AreEqual(track.FileSystems.Length, idPlugins.Count,
string.Format(Localization.Expected_0_filesystems_in_1_but_found_2,
track.FileSystems.Length, testFile, idPlugins.Count));
for(int i = 0; i < track.FileSystems.Length; i++)
2022-03-06 13:29:38 +00:00
{
PluginBase plugins = GetPluginBase.Instance;
bool found = plugins.PluginsList.TryGetValue(idPlugins[i], out IFilesystem plugin);
2022-03-06 13:29:38 +00:00
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
// It is not the case, it changes
if(!found)
continue;
2022-03-06 13:29:38 +00:00
var fs = Activator.CreateInstance(plugin.GetType()) as IFilesystem;
Assert.NotNull(fs,
string.Format(Localization.Could_not_instantiate_filesystem_for_0,
testFile));
2022-03-06 13:29:38 +00:00
fs.GetInformation(image, partition, out _, null);
2022-03-06 13:29:38 +00:00
if(track.FileSystems[i].ApplicationId != null)
Assert.AreEqual(track.FileSystems[i].ApplicationId,
fs.XmlFsType.ApplicationIdentifier,
string.Format(Localization.Application_ID_0, testFile));
2022-03-06 13:29:38 +00:00
Assert.AreEqual(track.FileSystems[i].Bootable, fs.XmlFsType.Bootable,
string.Format(Localization.Bootable_0, testFile));
2022-03-06 13:29:38 +00:00
Assert.AreEqual(track.FileSystems[i].Clusters, fs.XmlFsType.Clusters,
string.Format(Localization.Clusters_0, testFile));
2022-03-06 13:29:38 +00:00
Assert.AreEqual(track.FileSystems[i].ClusterSize, fs.XmlFsType.ClusterSize,
string.Format(Localization.Cluster_size_0, testFile));
2022-03-06 13:29:38 +00:00
if(track.FileSystems[i].SystemId != null)
Assert.AreEqual(track.FileSystems[i].SystemId, fs.XmlFsType.SystemIdentifier,
string.Format(Localization.System_ID_0, testFile));
2022-03-06 13:29:38 +00:00
Assert.AreEqual(track.FileSystems[i].Type, fs.XmlFsType.Type,
string.Format(Localization.Filesystem_type_0, testFile));
2022-03-06 13:29:38 +00:00
Assert.AreEqual(track.FileSystems[i].VolumeName, fs.XmlFsType.VolumeName,
string.Format(Localization.Volume_name_0, testFile));
2022-03-06 13:29:38 +00:00
Assert.AreEqual(track.FileSystems[i].VolumeSerial, fs.XmlFsType.VolumeSerial,
string.Format(Localization.Volume_serial_0, testFile));
2022-11-14 01:49:10 +00:00
if(Activator.CreateInstance(plugin.GetType()) is not IReadOnlyFilesystem rofs)
2022-03-06 13:29:38 +00:00
{
if(track.FileSystems[i].Contents != null ||
track.FileSystems[i].ContentsJson != null ||
File.Exists($"{testFile}.track{track.Number}.filesystem{i}.contents.json"))
2022-03-17 23:54:41 +00:00
Assert.NotNull(null,
string.
Format(Localization.Could_not_instantiate_filesystem_for_0_track_1_filesystem_2,
testFile, track.Number, i));
2022-03-06 13:29:38 +00:00
continue;
}
2022-03-06 13:29:38 +00:00
track.FileSystems[i].Encoding ??= Encoding.ASCII;
2022-03-06 13:29:38 +00:00
ErrorNumber ret = rofs.Mount(image, partition, track.FileSystems[i].Encoding, null,
track.FileSystems[i].Namespace);
Assert.AreEqual(ErrorNumber.NoError, ret,
string.Format(Localization.Unmountable_0, testFile));
var serializerOptions = new JsonSerializerOptions
2022-03-06 13:29:38 +00:00
{
Converters =
{
new JsonStringEnumConverter()
},
MaxDepth = 1536, // More than this an we get a StackOverflowException
WriteIndented = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
PropertyNameCaseInsensitive = true
2022-03-06 13:29:38 +00:00
};
2022-03-06 13:29:38 +00:00
if(track.FileSystems[i].ContentsJson != null)
track.FileSystems[i].Contents =
JsonSerializer.
Deserialize<Dictionary<string, FileData>>(track.FileSystems[i].ContentsJson,
serializerOptions);
2022-03-06 13:29:38 +00:00
else if(File.Exists($"{testFile}.track{track.Number}.filesystem{i}.contents.json"))
{
var sr =
new FileStream($"{testFile}.track{track.Number}.filesystem{i}.contents.json",
FileMode.Open);
2022-03-06 13:29:38 +00:00
track.FileSystems[i].Contents =
JsonSerializer.Deserialize<Dictionary<string, FileData>>(sr, serializerOptions);
2022-03-06 13:29:38 +00:00
}
2022-03-06 13:29:38 +00:00
if(track.FileSystems[i].Contents is null)
continue;
int currentDepth = 0;
2022-03-07 07:36:44 +00:00
ReadOnlyFilesystemTest.TestDirectory(rofs, "/", track.FileSystems[i].Contents, testFile,
true,
out List<ReadOnlyFilesystemTest.NextLevel>
currentLevel, currentDepth);
while(currentLevel.Count > 0)
{
currentDepth++;
List<ReadOnlyFilesystemTest.NextLevel> nextLevels = new();
foreach(ReadOnlyFilesystemTest.NextLevel subLevel in currentLevel)
{
ReadOnlyFilesystemTest.TestDirectory(rofs, subLevel.Path, subLevel.Children,
testFile, true,
out List<ReadOnlyFilesystemTest.NextLevel>
nextLevel, currentDepth);
nextLevels.AddRange(nextLevel);
}
currentLevel = nextLevels;
}
2022-03-06 13:29:38 +00:00
// Uncomment to generate JSON file
/* var contents = ReadOnlyFilesystemTest.BuildDirectory(rofs, "/", 0);
2021-09-14 21:18:28 +01:00
var sw = new FileStream($"{testFile}.track{track.Number}.filesystem{i}.contents.json", FileMode.Create);
JsonSerializer.Serialize(sw, contents, serializerOptions);
2022-03-06 13:29:38 +00:00
sw.Close();*/
}
2022-03-06 13:29:38 +00:00
}
});
}
});
}
2022-03-06 13:29:38 +00:00
[Test]
public void Hashes()
{
Environment.CurrentDirectory = Environment.CurrentDirectory = DataFolder;
ErrorNumber errno;
2022-03-06 13:29:38 +00:00
Assert.Multiple(() =>
{
2022-03-16 00:31:33 +00:00
Parallel.For(0L, Tests.Length, (i, _) =>
{
2022-03-06 13:29:38 +00:00
string testFile = Tests[i].TestFile;
2022-03-06 13:29:38 +00:00
bool exists = File.Exists(testFile);
Assert.True(exists, string.Format(Localization._0_not_found, testFile));
2022-03-06 13:29:38 +00:00
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
// It arrives here...
if(!exists)
return;
2022-03-06 13:29:38 +00:00
var filtersList = new FiltersList();
IFilter filter = filtersList.GetFilter(testFile);
filter.Open(testFile);
2022-03-15 01:37:37 +00:00
var image = Activator.CreateInstance(Plugin.GetType()) as IOpticalMediaImage;
Assert.NotNull(image, string.Format(Localization.Could_not_instantiate_filesystem_for_0, testFile));
2022-03-06 13:29:38 +00:00
ErrorNumber opened = image.Open(filter);
Assert.AreEqual(ErrorNumber.NoError, opened, string.Format(Localization.Open_0, testFile));
2022-03-06 13:29:38 +00:00
if(opened != ErrorNumber.NoError)
return;
2022-03-06 13:29:38 +00:00
Md5Context ctx;
2022-03-06 13:29:38 +00:00
if(image.Info.XmlMediaType == XmlMediaType.OpticalDisc)
{
foreach(bool @long in new[]
{
2022-03-06 13:29:38 +00:00
false, true
})
{
ctx = new Md5Context();
foreach(Track currentTrack in image.Tracks)
{
2021-09-14 21:18:28 +01:00
ulong sectors = currentTrack.EndSector - currentTrack.StartSector + 1;
ulong doneSectors = 0;
while(doneSectors < sectors)
{
byte[] sector;
if(sectors - doneSectors >= SECTORS_TO_READ)
{
2022-03-06 13:29:38 +00:00
errno = @long ? image.ReadSectorsLong(doneSectors, SECTORS_TO_READ,
currentTrack.Sequence, out sector)
2022-03-07 07:36:44 +00:00
: image.ReadSectors(doneSectors, SECTORS_TO_READ, currentTrack.Sequence,
2022-03-06 13:29:38 +00:00
out sector);
doneSectors += SECTORS_TO_READ;
}
else
{
2022-03-07 07:36:44 +00:00
errno = @long ? image.ReadSectorsLong(doneSectors, (uint)(sectors - doneSectors),
2022-03-06 13:29:38 +00:00
currentTrack.Sequence, out sector)
: image.ReadSectors(doneSectors, (uint)(sectors - doneSectors),
currentTrack.Sequence, out sector);
doneSectors += sectors - doneSectors;
}
Assert.AreEqual(ErrorNumber.NoError, errno);
2022-03-06 13:29:38 +00:00
ctx.Update(sector);
}
}
2022-03-15 01:37:37 +00:00
Assert.AreEqual(@long ? Tests[i].LongMd5 : Tests[i].Md5, ctx.End(),
2022-03-06 13:29:38 +00:00
$"{(@long ? "Long hash" : "Hash")}: {testFile}");
}
2022-03-06 13:29:38 +00:00
if(!image.Info.ReadableSectorTags.Contains(SectorTagType.CdSectorSubchannel))
return;
ctx = new Md5Context();
foreach(Track currentTrack in image.Tracks)
{
2022-03-06 13:29:38 +00:00
ulong sectors = currentTrack.EndSector - currentTrack.StartSector + 1;
ulong doneSectors = 0;
2022-03-06 13:29:38 +00:00
while(doneSectors < sectors)
{
byte[] sector;
2022-03-06 13:29:38 +00:00
if(sectors - doneSectors >= SECTORS_TO_READ)
{
2022-03-06 13:29:38 +00:00
errno = image.ReadSectorsTag(doneSectors, SECTORS_TO_READ, currentTrack.Sequence,
SectorTagType.CdSectorSubchannel, out sector);
doneSectors += SECTORS_TO_READ;
}
else
{
2022-03-06 13:29:38 +00:00
errno = image.ReadSectorsTag(doneSectors, (uint)(sectors - doneSectors),
2022-03-07 07:36:44 +00:00
currentTrack.Sequence, SectorTagType.CdSectorSubchannel,
out sector);
2022-03-06 13:29:38 +00:00
doneSectors += sectors - doneSectors;
}
Assert.AreEqual(ErrorNumber.NoError, errno);
ctx.Update(sector);
}
2022-03-06 13:29:38 +00:00
}
Assert.AreEqual(Tests[i].SubchannelMd5, ctx.End(),
string.Format(Localization.Subchannel_hash_0, testFile));
2022-03-06 13:29:38 +00:00
}
else
{
ctx = new Md5Context();
ulong doneSectors = 0;
while(doneSectors < image.Info.Sectors)
{
byte[] sector;
if(image.Info.Sectors - doneSectors >= SECTORS_TO_READ)
{
errno = image.ReadSectors(doneSectors, SECTORS_TO_READ, out sector);
doneSectors += SECTORS_TO_READ;
}
else
{
errno = image.ReadSectors(doneSectors, (uint)(image.Info.Sectors - doneSectors),
out sector);
doneSectors += image.Info.Sectors - doneSectors;
}
2022-03-06 13:29:38 +00:00
Assert.AreEqual(ErrorNumber.NoError, errno);
ctx.Update(sector);
}
2022-03-06 13:29:38 +00:00
Assert.AreEqual(Tests[i].Md5, ctx.End(), string.Format(Localization.Hash_0, testFile));
2022-03-06 13:29:38 +00:00
}
});
2022-03-06 13:29:38 +00:00
});
}
}