using System; using LibHac.Common; using LibHac.Fs; using LibHac.Fs.Fsa; using LibHac.FsSystem; using LibHac.Util; using Xunit; namespace LibHac.Tests.Fs { public class LayeredFileSystemTests { private IFileSystem CreateFileSystem() { var lowerLayerFs = new InMemoryFileSystem(); var upperLayerFs = new InMemoryFileSystem(); var layeredFs = new LayeredFileSystem(lowerLayerFs, upperLayerFs); lowerLayerFs.CreateDirectory("/dir".ToU8Span()).ThrowIfFailure(); upperLayerFs.CreateDirectory("/dir".ToU8Span()).ThrowIfFailure(); lowerLayerFs.CreateDirectory("/dir2".ToU8Span()).ThrowIfFailure(); upperLayerFs.CreateDirectory("/dir2".ToU8Span()).ThrowIfFailure(); lowerLayerFs.CreateDirectory("/dir3".ToU8Span()).ThrowIfFailure(); upperLayerFs.CreateDirectory("/dir3".ToU8Span()).ThrowIfFailure(); lowerLayerFs.CreateDirectory("/lowerDir".ToU8Span()).ThrowIfFailure(); upperLayerFs.CreateDirectory("/upperDir".ToU8Span()).ThrowIfFailure(); lowerLayerFs.CreateFile("/dir/replacedFile".ToU8Span(), 1, CreateFileOptions.None).ThrowIfFailure(); upperLayerFs.CreateFile("/dir/replacedFile".ToU8Span(), 2, CreateFileOptions.None).ThrowIfFailure(); lowerLayerFs.CreateFile("/dir2/lowerFile".ToU8Span(), 0, CreateFileOptions.None).ThrowIfFailure(); upperLayerFs.CreateFile("/dir2/upperFile".ToU8Span(), 0, CreateFileOptions.None).ThrowIfFailure(); lowerLayerFs.CreateFile("/dir3/lowerFile".ToU8Span(), 0, CreateFileOptions.None).ThrowIfFailure(); upperLayerFs.CreateFile("/dir3/upperFile".ToU8Span(), 2, CreateFileOptions.None).ThrowIfFailure(); lowerLayerFs.CreateFile("/dir3/replacedFile".ToU8Span(), 1, CreateFileOptions.None).ThrowIfFailure(); upperLayerFs.CreateFile("/dir3/replacedFile".ToU8Span(), 2, CreateFileOptions.None).ThrowIfFailure(); lowerLayerFs.CreateFile("/replacedWithDir".ToU8Span(), 0, CreateFileOptions.None).ThrowIfFailure(); upperLayerFs.CreateDirectory("/replacedWithDir".ToU8Span()).ThrowIfFailure(); upperLayerFs.CreateFile("/replacedWithDir/subFile".ToU8Span(), 0, CreateFileOptions.None).ThrowIfFailure(); return layeredFs; } private IFileSystem CreateEmptyFileSystem() { var baseLayerFs = new InMemoryFileSystem(); var topLayerFs = new InMemoryFileSystem(); return new LayeredFileSystem(baseLayerFs, topLayerFs); } [Fact] public void OpenFile_FileDoesNotExist_ReturnsPathNotFound() { IFileSystem fs = CreateFileSystem(); Assert.Result(ResultFs.PathNotFound, fs.OpenFile(out _, "/fakefile".ToU8Span(), OpenMode.All)); } [Fact] public void OpenFile_FileIsInBothSources_ReturnsFileFromTopSource() { IFileSystem fs = CreateFileSystem(); Assert.Success(fs.OpenFile(out IFile file, "/dir/replacedFile".ToU8Span(), OpenMode.All)); Assert.Success(file.GetSize(out long fileSize)); Assert.Equal(2, fileSize); } [Fact] public void OpenFile_InsideMergedDirectory_CanOpenFilesFromBothSources() { IFileSystem fs = CreateFileSystem(); Assert.Success(fs.OpenFile(out _, "/dir2/lowerFile".ToU8Span(), OpenMode.All)); Assert.Success(fs.OpenFile(out _, "/dir2/upperFile".ToU8Span(), OpenMode.All)); } [Fact] public void OpenDirectory_DirDoesNotExist_ReturnsPathNotFound() { IFileSystem fs = CreateFileSystem(); Assert.Result(ResultFs.PathNotFound, fs.OpenDirectory(out _, "/fakedir".ToU8Span(), OpenDirectoryMode.All)); } [Fact] public void OpenDirectory_ExistsInSingleLayer_ReturnsNonMergedDirectory() { IFileSystem fs = CreateFileSystem(); Assert.Success(fs.OpenDirectory(out IDirectory dir, "/lowerDir".ToU8Span(), OpenDirectoryMode.All)); Assert.Equal(typeof(InMemoryFileSystem), dir.GetType().DeclaringType); } [Fact] public void OpenDirectory_ExistsInMultipleLayers_ReturnsMergedDirectory() { IFileSystem fs = CreateFileSystem(); Assert.Success(fs.OpenDirectory(out IDirectory dir, "/dir".ToU8Span(), OpenDirectoryMode.All)); Assert.Equal(typeof(LayeredFileSystem), dir.GetType().DeclaringType); } [Fact] public void GetEntryType_InsideMergedDirectory_CanGetEntryTypesFromBothSources() { IFileSystem fs = CreateFileSystem(); Assert.Success(fs.GetEntryType(out _, "/dir2/lowerFile".ToU8Span())); Assert.Success(fs.GetEntryType(out _, "/dir2/upperFile".ToU8Span())); } [Fact] public void IDirectoryRead_DuplicatedEntriesAreReturnedOnlyOnce() { IFileSystem fs = CreateFileSystem(); Span entries = stackalloc DirectoryEntry[4]; Assert.Success(fs.OpenDirectory(out IDirectory directory, "/dir3".ToU8Span(), OpenDirectoryMode.All)); Assert.Success(directory.Read(out long entriesRead, entries)); Assert.Equal(3, entriesRead); } [Fact] public void IDirectoryRead_DuplicatedEntryReturnsFromTopLayer() { IFileSystem fs = CreateFileSystem(); var entry = new DirectoryEntry(); Assert.Success(fs.OpenDirectory(out IDirectory directory, "/dir".ToU8Span(), OpenDirectoryMode.All)); Assert.Success(directory.Read(out _, SpanHelpers.AsSpan(ref entry))); Assert.Equal("replacedFile", StringUtils.Utf8ZToString(entry.Name)); Assert.Equal(2, entry.Size); } [Fact] public void IDirectoryRead_EmptyFs_NoEntriesAreRead() { IFileSystem fs = CreateEmptyFileSystem(); var entry = new DirectoryEntry(); Assert.Success(fs.OpenDirectory(out IDirectory directory, "/".ToU8Span(), OpenDirectoryMode.All)); Assert.Success(directory.Read(out long entriesRead, SpanHelpers.AsSpan(ref entry))); Assert.Equal(0, entriesRead); } [Fact] public void IDirectoryGetEntryCount_DuplicatedEntriesAreCountedOnlyOnce() { IFileSystem fs = CreateFileSystem(); Assert.Success(fs.OpenDirectory(out IDirectory directory, "/dir3".ToU8Span(), OpenDirectoryMode.All)); Assert.Success(directory.GetEntryCount(out long entryCount)); Assert.Equal(3, entryCount); } [Fact] public void IDirectoryGetEntryCount_MergedDirectoryAfterRead_AllEntriesAreCounted() { IFileSystem fs = CreateFileSystem(); var entry = new DirectoryEntry(); Assert.Success(fs.OpenDirectory(out IDirectory directory, "/dir3".ToU8Span(), OpenDirectoryMode.All)); // Read all entries long entriesRead; do { Assert.Success(directory.Read(out entriesRead, SpanHelpers.AsSpan(ref entry))); } while (entriesRead != 0); Assert.Success(directory.GetEntryCount(out long entryCount)); Assert.Equal(3, entryCount); } [Fact] public void IDirectoryGetEntryCount_EmptyFs_EntryCountIsZero() { IFileSystem fs = CreateEmptyFileSystem(); Assert.Success(fs.OpenDirectory(out IDirectory directory, "/".ToU8Span(), OpenDirectoryMode.All)); Assert.Success(directory.GetEntryCount(out long entryCount)); Assert.Equal(0, entryCount); } } }