Add ArchiveTest class and update Structs for archive testing

This commit is contained in:
2026-06-19 15:00:08 +01:00
parent 8a78e21416
commit 6edd8d5cf2
3 changed files with 222 additions and 24 deletions

View File

@@ -28,6 +28,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Aaru.Archives\Aaru.Archives.csproj"/>
<ProjectReference Include="..\Aaru.Checksums\Aaru.Checksums.csproj"/>
<ProjectReference Include="..\Aaru.CommonTypes\Aaru.CommonTypes.csproj"/>
<ProjectReference Include="..\Aaru.Core\Aaru.Core.csproj"/>

View File

@@ -0,0 +1,179 @@
using System;
using System.IO;
using System.Text;
using System.Text.Json;
using Aaru.CommonTypes;
using Aaru.CommonTypes.Enums;
using Aaru.CommonTypes.Interfaces;
using Aaru.CommonTypes.Structs;
using Aaru.Core;
using FluentAssertions;
using FluentAssertions.Execution;
using NUnit.Framework;
using FileAttributes = Aaru.CommonTypes.Structs.FileAttributes;
namespace Aaru.Tests.Archives;
public abstract class ArchiveTest
{
public abstract string DataFolder { get; }
public abstract IArchive Plugin { get; }
public abstract ArchiveTestExpected[] Tests { get; }
[OneTimeSetUp]
public void InitTest() => PluginBase.Init();
[Test]
public void Identify()
{
Environment.CurrentDirectory = DataFolder;
using(new AssertionScope())
{
foreach(ArchiveTestExpected test in Tests)
{
string testFile = test.TestFile;
bool exists = File.Exists(testFile);
exists.Should().BeTrue(Localization._0_not_found, testFile);
if(!exists) continue;
IFilter filter = PluginRegister.Singleton.GetFilter(testFile);
filter.Should().NotBeNull(Localization.Filter_0, testFile);
ErrorNumber openedFilter = filter.Open(testFile);
openedFilter.Should().Be(ErrorNumber.NoError, string.Format(Localization.Open_0, testFile));
if(openedFilter != ErrorNumber.NoError) continue;
var archive = Activator.CreateInstance(Plugin.GetType()) as IArchive;
archive.Should().NotBeNull(Localization.Could_not_instantiate_filesystem_for_0, testFile);
archive.Identify(filter).Should().BeTrue(Localization.Not_identified_for_0, testFile);
filter.Close();
}
}
}
[Test]
public void Contents()
{
Environment.CurrentDirectory = DataFolder;
using(new AssertionScope())
{
foreach(ArchiveTestExpected test in Tests)
{
string testFile = test.TestFile;
bool exists = File.Exists(testFile);
exists.Should().BeTrue(Localization._0_not_found, testFile);
if(!exists) continue;
IFilter filter = PluginRegister.Singleton.GetFilter(testFile);
filter.Should().NotBeNull(Localization.Filter_0, testFile);
ErrorNumber openedFilter = filter.Open(testFile);
openedFilter.Should().Be(ErrorNumber.NoError, string.Format(Localization.Open_0, testFile));
if(openedFilter != ErrorNumber.NoError) continue;
var archive = Activator.CreateInstance(Plugin.GetType()) as IArchive;
archive.Should().NotBeNull(Localization.Could_not_instantiate_filesystem_for_0, testFile);
ErrorNumber openedArchive = archive.Open(filter, Encoding.ASCII);
openedArchive.Should().Be(ErrorNumber.NoError, string.Format(Localization.Open_0, testFile));
if(openedArchive != ErrorNumber.NoError)
{
filter.Close();
continue;
}
archive.NumberOfEntries.Should()
.Be(test.EntryCount,
string.Format(Localization.Expected_0_partitions_in_1_but_found_2,
test.EntryCount,
testFile,
archive.NumberOfEntries));
ArchiveEntryData[] expectedEntries = test.Contents;
if(expectedEntries is null && File.Exists($"{testFile}.contents.json"))
{
JsonSerializerOptions serializerOptions = new()
{
PropertyNameCaseInsensitive = true
};
using FileStream stream = new($"{testFile}.contents.json", FileMode.Open, FileAccess.Read);
expectedEntries = JsonSerializer.Deserialize<ArchiveEntryData[]>(stream, serializerOptions);
}
expectedEntries.Should().NotBeNull();
if(expectedEntries is null)
{
filter.Close();
archive.Close();
continue;
}
expectedEntries.Length.Should()
.Be(archive.NumberOfEntries,
string.Format(Localization.Expected_0_partitions_in_1_but_found_2,
expectedEntries.Length,
testFile,
archive.NumberOfEntries));
using(new AssertionScope())
{
for(var i = 0; i < archive.NumberOfEntries; i++)
{
ErrorNumber filenameErrno = archive.GetFilename(i, out string fileName);
filenameErrno.Should()
.Be(ErrorNumber.NoError,
string.Format(Localization.Cannot_open_image_for_0, testFile));
ErrorNumber statErrno = archive.Stat(i, out FileEntryInfo stat);
statErrno.Should()
.Be(ErrorNumber.NoError,
string.Format(Localization.Cannot_open_image_for_0, testFile));
string entryType = GetEntryType(stat.Attributes);
long entrySize = stat.Length;
fileName.Should().Be(expectedEntries[i].Path, testFile);
entryType.Should().Be(expectedEntries[i].Type, testFile);
entrySize.Should().Be(expectedEntries[i].Size, testFile);
}
}
archive.Close();
filter.Close();
}
}
}
static string GetEntryType(FileAttributes attributes)
{
if(attributes.HasFlag(FileAttributes.Directory)) return "directory";
if(attributes.HasFlag(FileAttributes.Symlink)) return "symlink";
if(attributes.HasFlag(FileAttributes.CharDevice)) return "chardevice";
if(attributes.HasFlag(FileAttributes.BlockDevice)) return "blockdevice";
if(attributes.HasFlag(FileAttributes.FIFO)) return "fifo";
return "file";
}
}

View File

@@ -10,12 +10,12 @@ public class MediaInfoTest
{
/// <summary>Expected media type</summary>
public MediaType MediaType;
/// <summary>Expected number of sectors in media</summary>
public ulong Sectors;
/// <summary>Expected media sector size</summary>
public uint SectorSize;
public uint SectorSize;
/// <summary>Expected number of sectors in media</summary>
public ulong Sectors;
/// <summary>File that contains the image to test</summary>
public string TestFile;
public string TestFile;
public override string ToString() => TestFile;
}
@@ -25,25 +25,25 @@ public class MediaInfoTest
public class FileSystemTest : MediaInfoTest
{
/// <summary>Application ID</summary>
public string ApplicationId;
public string ApplicationId;
/// <summary>Can the volume boot?</summary>
public bool Bootable;
/// <summary>Clusters in volume</summary>
public long Clusters;
public bool Bootable;
/// <summary>Bytes per cluster</summary>
public uint ClusterSize;
public uint ClusterSize;
/// <summary>Clusters in volume</summary>
public long Clusters;
public Dictionary<string, FileData> Contents;
public string ContentsJson;
public Encoding Encoding;
public string Namespace;
/// <summary>System or OEM ID</summary>
public string SystemId;
public string SystemId;
/// <summary>Filesystem type. null if always the same, as defined in test class</summary>
public string Type;
public string Type;
/// <summary>Volume name</summary>
public string VolumeName;
public string VolumeName;
/// <summary>Volume serial number or set identifier</summary>
public string VolumeSerial;
public string VolumeSerial;
}
public class BlockImageTestExpected : MediaInfoTest
@@ -52,6 +52,24 @@ public class BlockImageTestExpected : MediaInfoTest
public BlockPartitionVolumes[] Partitions;
}
public class ArchiveTestExpected
{
public ArchiveEntryData[] Contents;
public string ContentsJson;
public int EntryCount;
public string TestFile;
public override string ToString() => TestFile;
}
public class ArchiveEntryData
{
public string Path { get; set; }
public long Size { get; set; }
public string Type { get; set; }
public string LinkTarget { get; set; }
}
public class TrackInfoTestExpected
{
public ulong End;
@@ -78,24 +96,24 @@ public class TapeImageTestExpected : BlockImageTestExpected
public class FluxCaptureTestExpected
{
/// <summary>Capture index for this head/track/subTrack combination</summary>
public uint CaptureIndex;
/// <summary>Expected data resolution in picoseconds</summary>
public ulong DataResolution;
/// <summary>Physical head (0-based)</summary>
public uint Head;
public uint Head;
/// <summary>Expected index resolution in picoseconds</summary>
public ulong IndexResolution;
/// <summary>Physical sub-track (0-based, e.g. half-track)</summary>
public byte SubTrack;
/// <summary>Physical track (0-based)</summary>
public ushort Track;
/// <summary>Physical sub-track (0-based, e.g. half-track)</summary>
public byte SubTrack;
/// <summary>Capture index for this head/track/subTrack combination</summary>
public uint CaptureIndex;
/// <summary>Expected index resolution in picoseconds</summary>
public ulong IndexResolution;
/// <summary>Expected data resolution in picoseconds</summary>
public ulong DataResolution;
}
public class FluxImageTestExpected : BlockImageTestExpected
{
/// <summary>Expected number of flux captures in the image</summary>
public uint FluxCaptureCount;
public uint FluxCaptureCount;
/// <summary>Expected flux captures to validate</summary>
public FluxCaptureTestExpected[] FluxCaptures;
}
@@ -104,7 +122,7 @@ public class PartitionTest
{
public Partition[] Partitions;
/// <summary>File that contains the partition scheme to test</summary>
public string TestFile;
public string TestFile;
}
public class FsExtractHashData