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");
}
}
}
}