using Amphora; using System; using System.Collections.Generic; using UnitTestSharp; namespace Glass.UnitTests { public class InMemoryFileSystemTests : TestFixture { private static readonly DateTime Stamp = new DateTime(2026, 10, 5, 12, 30, 0); /// /// A root holding docs/readme.txt, docs/notes/todo.txt, an empty music directory and top.txt. /// public static InMemoryFileSystem Sample() { var returnMe = new InMemoryFileSystem(); returnMe.AddFile("/docs/readme.txt", size: 10, modified: Stamp); returnMe.AddFile("/docs/notes/todo.txt", size: 20); returnMe.AddDirectory("/music"); returnMe.AddFile("/top.txt", size: 30); return returnMe; } public class RootsTests : TestFixture { public void Always_IsJustTheSlash() { CheckEqual(new List { "/" }, new List(new InMemoryFileSystem().Roots)); } } public class GetParentTests : TestFixture { public void Root_ReturnsNull() { CheckNull(Sample().GetParent("/")); } public void TopLevel_ReturnsTheRoot() { CheckEqual("/", Sample().GetParent("/docs")); } public void Nested_ReturnsTheEnclosingDirectory() { CheckEqual("/docs/notes", Sample().GetParent("/docs/notes/todo.txt")); } public void NothingThere_StillReturnsTheEnclosingDirectory() { CheckEqual("/nowhere", Sample().GetParent("/nowhere/at")); } public void RelativePath_Throws() { InMemoryFileSystem fileSystem = Sample(); CheckThrow(typeof(ArgumentException)); fileSystem.GetParent("docs"); } } public class CombineTests : TestFixture { public void InTheRoot_JoinsWithOneSlash() { CheckEqual("/docs", Sample().Combine("/", "docs")); } public void InADirectory_JoinsWithOneSlash() { CheckEqual("/docs/notes", Sample().Combine("/docs", "notes")); } public void NothingThere_StillJoins() { CheckEqual("/nowhere/file.txt", Sample().Combine("/nowhere", "file.txt")); } public void NameWithASlash_Throws() { InMemoryFileSystem fileSystem = Sample(); CheckThrow(typeof(ArgumentException)); fileSystem.Combine("/docs", "a/b"); } public void EmptyName_Throws() { InMemoryFileSystem fileSystem = Sample(); CheckThrow(typeof(ArgumentException)); fileSystem.Combine("/docs", ""); } public void RelativeDirectory_Throws() { InMemoryFileSystem fileSystem = Sample(); CheckThrow(typeof(ArgumentException)); fileSystem.Combine("docs", "readme.txt"); } } public class GetNameTests : TestFixture { public void Root_IsEmpty() { CheckEqual("", Sample().GetName("/")); } public void TopLevel_IsItsOnlyPart() { CheckEqual("docs", Sample().GetName("/docs")); } public void Nested_IsTheLastPart() { CheckEqual("todo.txt", Sample().GetName("/docs/notes/todo.txt")); } public void RelativePath_Throws() { InMemoryFileSystem fileSystem = Sample(); CheckThrow(typeof(ArgumentException)); fileSystem.GetName("docs"); } } public class ValidateFileNameTests : TestFixture { [IgnoreTest] private InvalidFileName ProblemWith(string name) { return FileSystemFailures.InvalidName.Read(new InMemoryFileSystem().ValidateFileName(name)); } public void OrdinaryName_IsOk() { CheckEqual(Status.Ok, new InMemoryFileSystem().ValidateFileName("readme.txt")); } public void NameWithSpacesInside_IsOk() { CheckEqual(Status.Ok, new InMemoryFileSystem().ValidateFileName("my file.txt")); } public void NameStartingWithADot_IsOk() { CheckEqual(Status.Ok, new InMemoryFileSystem().ValidateFileName(".hidden")); } public void Null_Throws() { var fileSystem = new InMemoryFileSystem(); CheckThrow(typeof(ArgumentNullException)); fileSystem.ValidateFileName(null); } public void Empty_IsBlank() { InvalidFileName problem = ProblemWith(""); CheckEqual(FileNameProblem.Blank, problem.Problem); CheckEqual("", problem.Name); } public void OnlySpaces_IsBlank() { InvalidFileName problem = ProblemWith(" "); CheckEqual(FileNameProblem.Blank, problem.Problem); CheckEqual(" ", problem.Name); } public void SingleDot_IsADotName() { CheckEqual(FileNameProblem.DotName, ProblemWith(".").Problem); } public void DoubleDot_IsADotName() { InvalidFileName problem = ProblemWith(".."); CheckEqual(FileNameProblem.DotName, problem.Problem); CheckEqual("..", problem.Name); } public void Slash_IsAForbiddenCharacter() { InvalidFileName problem = ProblemWith("a/b"); CheckEqual(FileNameProblem.ForbiddenCharacter, problem.Problem); CheckEqual("a/b", problem.Name); CheckEqual('/', problem.Character); } public void Tab_IsAControlCharacter() { InvalidFileName problem = ProblemWith("a\tb"); CheckEqual(FileNameProblem.ControlCharacter, problem.Problem); CheckEqual('\t', problem.Character); } } public class GetKindTests : TestFixture { public void Root_IsADirectory() { CheckEqual(FileKind.Directory, Sample().GetKind("/")); } public void ExistingDirectory_IsADirectory() { CheckEqual(FileKind.Directory, Sample().GetKind("/docs/notes")); } public void ExistingFile_IsAFile() { CheckEqual(FileKind.File, Sample().GetKind("/docs/readme.txt")); } public void NothingThere_IsNone() { CheckEqual(FileKind.None, Sample().GetKind("/docs/missing.txt")); } public void BelowAFile_IsNone() { CheckEqual(FileKind.None, Sample().GetKind("/top.txt/inside")); } public void Null_Throws() { InMemoryFileSystem fileSystem = Sample(); CheckThrow(typeof(ArgumentNullException)); fileSystem.GetKind(null); } public void Empty_Throws() { InMemoryFileSystem fileSystem = Sample(); CheckThrow(typeof(ArgumentException)); fileSystem.GetKind(""); } public void Relative_Throws() { InMemoryFileSystem fileSystem = Sample(); CheckThrow(typeof(ArgumentException)); fileSystem.GetKind("docs"); } public void TrailingSlash_Throws() { InMemoryFileSystem fileSystem = Sample(); CheckThrow(typeof(ArgumentException)); fileSystem.GetKind("/docs/"); } public void DotDotPart_Throws() { InMemoryFileSystem fileSystem = Sample(); CheckThrow(typeof(ArgumentException)); fileSystem.GetKind("/docs/../notes"); } } public class ListTests : TestFixture { public void Root_ListsWhatIsDirectlyInIt() { IReadOnlyList entries = Sample().List("/").Value; CheckEqual(3, entries.Count); CheckEqual(new FileEntry("docs", IsDirectory: true), entries[0]); CheckEqual(new FileEntry("music", IsDirectory: true), entries[1]); CheckEqual(new FileEntry("top.txt", IsDirectory: false, Size: 30), entries[2]); } public void File_ReportsItsSizeAndTime() { IReadOnlyList entries = Sample().List("/docs").Value; CheckEqual(new FileEntry("notes", IsDirectory: true), entries[0]); CheckEqual(new FileEntry("readme.txt", IsDirectory: false, Size: 10, Modified: Stamp), entries[1]); } public void EmptyDirectory_ListsNothing() { CheckEqual(0, Sample().List("/music").Value.Count); } public void Names_ComeOutInOrdinalOrder() { var fileSystem = new InMemoryFileSystem(); fileSystem.AddFile("/b"); fileSystem.AddFile("/a"); fileSystem.AddFile("/B"); IReadOnlyList entries = fileSystem.List("/").Value; CheckEqual("B", entries[0].Name); CheckEqual("a", entries[1].Name); CheckEqual("b", entries[2].Name); } public void NothingThere_FailsAsNotFound() { CheckEqual(FileSystemFailures.NotFound.Fail("/missing"), Sample().List("/missing").Status); } public void AFile_FailsAsNotADirectory() { CheckEqual(FileSystemFailures.NotADirectory.Fail("/top.txt"), Sample().List("/top.txt").Status); } public void RelativePath_Throws() { InMemoryFileSystem fileSystem = Sample(); CheckThrow(typeof(ArgumentException)); fileSystem.List("docs"); } } public class CreateDirectoryTests : TestFixture { public void InAnExistingDirectory_MakesItAndIsOk() { InMemoryFileSystem fileSystem = Sample(); CheckEqual(Status.Ok, fileSystem.CreateDirectory("/docs/drafts")); CheckEqual(FileKind.Directory, fileSystem.GetKind("/docs/drafts")); CheckEqual(0, fileSystem.List("/docs/drafts").Value.Count); } public void InTheRoot_MakesIt() { InMemoryFileSystem fileSystem = Sample(); fileSystem.CreateDirectory("/videos"); CheckEqual(FileKind.Directory, fileSystem.GetKind("/videos")); } public void ParentMissing_FailsAsNotFoundForTheParent() { CheckEqual(FileSystemFailures.NotFound.Fail("/missing"), Sample().CreateDirectory("/missing/drafts")); } public void ParentIsAFile_FailsAsNotADirectoryForTheParent() { CheckEqual(FileSystemFailures.NotADirectory.Fail("/top.txt"), Sample().CreateDirectory("/top.txt/drafts")); } public void DirectoryAlreadyThere_FailsAndKeepsIt() { InMemoryFileSystem fileSystem = Sample(); CheckEqual(FileSystemFailures.AlreadyExists.Fail("/docs"), fileSystem.CreateDirectory("/docs")); CheckEqual(2, fileSystem.List("/docs").Value.Count); } public void FileAlreadyThere_FailsAsAlreadyExists() { CheckEqual(FileSystemFailures.AlreadyExists.Fail("/top.txt"), Sample().CreateDirectory("/top.txt")); } public void Root_FailsAsAlreadyExists() { CheckEqual(FileSystemFailures.AlreadyExists.Fail("/"), Sample().CreateDirectory("/")); } public void RelativePath_Throws() { InMemoryFileSystem fileSystem = Sample(); CheckThrow(typeof(ArgumentException)); fileSystem.CreateDirectory("drafts"); } } public class AddDirectoryTests : TestFixture { public void MissingParents_AreMadeToo() { var fileSystem = new InMemoryFileSystem(); fileSystem.AddDirectory("/a/b/c"); CheckEqual(FileKind.Directory, fileSystem.GetKind("/a")); CheckEqual(FileKind.Directory, fileSystem.GetKind("/a/b")); CheckEqual(FileKind.Directory, fileSystem.GetKind("/a/b/c")); } public void AlreadyThere_KeepsWhatIsInside() { InMemoryFileSystem fileSystem = Sample(); fileSystem.AddDirectory("/docs"); CheckEqual(2, fileSystem.List("/docs").Value.Count); } public void Root_DoesNothing() { InMemoryFileSystem fileSystem = Sample(); fileSystem.AddDirectory("/"); CheckEqual(3, fileSystem.List("/").Value.Count); } public void ThroughAFile_Throws() { InMemoryFileSystem fileSystem = Sample(); CheckThrow(typeof(ArgumentException)); fileSystem.AddDirectory("/top.txt/inside"); } public void RelativePath_Throws() { var fileSystem = new InMemoryFileSystem(); CheckThrow(typeof(ArgumentException)); fileSystem.AddDirectory("a/b"); } } public class AddFileTests : TestFixture { public void Always_MakesTheFileWithItsSizeAndTime() { var fileSystem = new InMemoryFileSystem(); fileSystem.AddFile("/a.txt", size: 5, modified: Stamp); CheckEqual(new FileEntry("a.txt", IsDirectory: false, Size: 5, Modified: Stamp), fileSystem.List("/").Value[0]); } public void WithNoSizeOrTime_IsEmptyAndUndated() { var fileSystem = new InMemoryFileSystem(); fileSystem.AddFile("/a.txt"); CheckEqual(new FileEntry("a.txt", IsDirectory: false, Size: 0), fileSystem.List("/").Value[0]); } public void MissingParents_AreMadeToo() { var fileSystem = new InMemoryFileSystem(); fileSystem.AddFile("/a/b/c.txt"); CheckEqual(FileKind.Directory, fileSystem.GetKind("/a/b")); CheckEqual(FileKind.File, fileSystem.GetKind("/a/b/c.txt")); } public void FileAlreadyThere_IsReplaced() { InMemoryFileSystem fileSystem = Sample(); fileSystem.AddFile("/top.txt", size: 99); CheckEqual(99L, fileSystem.List("/").Value[2].Size); CheckEqual(3, fileSystem.List("/").Value.Count); } public void DirectoryAlreadyThere_Throws() { InMemoryFileSystem fileSystem = Sample(); CheckThrow(typeof(ArgumentException)); fileSystem.AddFile("/docs"); } public void ThroughAFile_Throws() { InMemoryFileSystem fileSystem = Sample(); CheckThrow(typeof(ArgumentException)); fileSystem.AddFile("/top.txt/inside.txt"); } public void Root_Throws() { var fileSystem = new InMemoryFileSystem(); CheckThrow(typeof(ArgumentException)); fileSystem.AddFile("/"); } public void RelativePath_Throws() { var fileSystem = new InMemoryFileSystem(); CheckThrow(typeof(ArgumentException)); fileSystem.AddFile("a.txt"); } } } }