diff --git a/.editorconfig b/.editorconfig index bfb9d50..b20343b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -110,7 +110,7 @@ dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggest #Style - Modifier preferences #when this rule is set to a list of modifiers, prefer the specified ordering. -csharp_preferred_modifier_order = public,protected,private,internal,static,async,abstract,virtual,readonly,sealed,override:suggestion +csharp_preferred_modifier_order = public, protected, private, internal, static, async, abstract, virtual, readonly, sealed, override:suggestion #Style - Pattern matching @@ -167,31 +167,31 @@ dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case dotnet_naming_symbols.interface.applicable_kinds = interface dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.interface.required_modifiers = +dotnet_naming_symbols.interface.required_modifiers = dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.types.required_modifiers = +dotnet_naming_symbols.types.required_modifiers = dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.non_field_members.required_modifiers = +dotnet_naming_symbols.non_field_members.required_modifiers = # Naming styles dotnet_naming_style.begins_with_i.required_prefix = I -dotnet_naming_style.begins_with_i.required_suffix = -dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = dotnet_naming_style.begins_with_i.capitalization = pascal_case -dotnet_naming_style.pascal_case.required_prefix = -dotnet_naming_style.pascal_case.required_suffix = -dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = dotnet_naming_style.pascal_case.capitalization = pascal_case -dotnet_naming_style.pascal_case.required_prefix = -dotnet_naming_style.pascal_case.required_suffix = -dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = dotnet_naming_style.pascal_case.capitalization = pascal_case dotnet_style_operator_placement_when_wrapping = beginning_of_line tab_width = 4 diff --git a/README.md b/README.md index 2b31fd7..0476db9 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,11 @@ A virtual file system implementation in modern C#. -When writing applications in .NET, you often need to write or read the contents of a file. .NET provides `System.IO` namespace dedicated to this purpose. But how do we deal with the filesystem when testing our code? - -"Virtual File System" is an attempt to solve this problem. Currently, this library is at an early stage of development. If you need additional functionality, I invite you to open an issue to discuss it. +When writing applications in .NET, you often need to write or read the contents of a file. .NET provides `System.IO` +namespace dedicated to this purpose. But how do we deal with the filesystem when testing our code? +"Virtual File System" is an attempt to solve this problem. Currently, this library is at an early stage of development. +If you need additional functionality, I invite you to open an issue to discuss it. ## Badges @@ -16,7 +17,6 @@ _Social buttons_ [![stars - VirtualFileSystem](https://img.shields.io/github/stars/Atypical-Consulting/VirtualFileSystem?style=social)](https://github.com/Atypical-Consulting/VirtualFileSystem) [![forks - VirtualFileSystem](https://img.shields.io/github/forks/Atypical-Consulting/VirtualFileSystem?style=social)](https://github.com/Atypical-Consulting/VirtualFileSystem) - _Repo metadata_ [![GitHub tag](https://img.shields.io/github/tag/Atypical-Consulting/VirtualFileSystem?include_prereleases=&sort=semver&color=blue)](https://github.com/Atypical-Consulting/VirtualFileSystem/releases/) @@ -30,21 +30,24 @@ _Call-to-Action buttons_ [![View site - GH Pages](https://img.shields.io/badge/View_site-GH_Pages-2ea44f?style=for-the-badge)](https://atypical-consulting.github.io/VirtualFileSystem/) [![view - Documentation](https://img.shields.io/badge/view-Documentation-blue?style=for-the-badge)](/docs/ "Go to project documentation") - ## What is a virtual file system and why should I use it? -A virtual file system is a data structure that represents a file system in memory. It is used to simulate a file system on a computer. It is useful for testing purposes, for example, when you want to test a file system without actually creating files on the hard drive. +A virtual file system is a data structure that represents a file system in memory. It is used to simulate a file system +on a computer. It is useful for testing purposes, for example, when you want to test a file system without actually +creating files on the hard drive. ## We use the lastest C# features -This library targets .NET 7.0 and uses the latest C# features. It is written in C# 11.0 and uses the new `init` properties, `record` types, `switch` expressions, `using` declarations, and more. - -I invite you to read the [C# 11.0 documentation](https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-11) to learn more about these features. +This library targets .NET 7.0 and uses the latest C# features. It is written in C# 11.0 and uses the new `init` +properties, `record` types, `switch` expressions, `using` declarations, and more. +I invite you to read the [C# 11.0 documentation](https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-11) to +learn more about these features. ## License This project is licensed under the terms of the BSD-3-Clause license. If you use this library in your project, please consider adding a link to this repository in your project's README. -This project is maintained by [Atypical Consulting](https://www.atypical.consulting/). If you need help with this project, please contact us from this repository by opening an issue. +This project is maintained by [Atypical Consulting](https://www.atypical.consulting/). If you need help with this +project, please contact us from this repository by opening an issue. diff --git a/src/Atypical.VirtualFileSystem.Core/Abstractions/VFSNode.cs b/src/Atypical.VirtualFileSystem.Core/Abstractions/VFSNode.cs index c202669..de7877c 100644 --- a/src/Atypical.VirtualFileSystem.Core/Abstractions/VFSNode.cs +++ b/src/Atypical.VirtualFileSystem.Core/Abstractions/VFSNode.cs @@ -1,49 +1,49 @@ -// Copyright (c) 2022, Atypical Consulting SRL -// All rights reserved. -// -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. - -namespace Atypical.VirtualFileSystem.Core.Abstractions; - -/// -/// Represents a node in a virtual file system. -/// A node can be a file or a directory. -/// -public abstract record VFSNode - : IVirtualFileSystemNode -{ - /// - /// Initializes a new instance of the class. - /// This constructor is used by derived classes. - /// - /// The path of the node. - protected VFSNode(VFSPath path) - { - ArgumentNullException.ThrowIfNull(path); - - // set timestamps - var now = DateTime.UtcNow; - CreationTime = now; - LastAccessTime = now; - LastWriteTime = now; - } - - /// - public abstract VFSPath Path { get; } - - /// - public DateTimeOffset CreationTime { get; } - - /// - public DateTimeOffset LastAccessTime { get; } - - /// - public DateTimeOffset LastWriteTime { get; } - - /// - public abstract bool IsDirectory { get; } - - /// - public abstract bool IsFile { get; } +// Copyright (c) 2022, Atypical Consulting SRL +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +namespace Atypical.VirtualFileSystem.Core.Abstractions; + +/// +/// Represents a node in a virtual file system. +/// A node can be a file or a directory. +/// +public abstract record VFSNode + : IVirtualFileSystemNode +{ + /// + /// Initializes a new instance of the class. + /// This constructor is used by derived classes. + /// + /// The path of the node. + protected VFSNode(VFSPath path) + { + ArgumentNullException.ThrowIfNull(path); + + // set timestamps + var now = DateTime.UtcNow; + CreationTime = now; + LastAccessTime = now; + LastWriteTime = now; + } + + /// + public abstract VFSPath Path { get; } + + /// + public DateTimeOffset CreationTime { get; } + + /// + public DateTimeOffset LastAccessTime { get; } + + /// + public DateTimeOffset LastWriteTime { get; } + + /// + public abstract bool IsDirectory { get; } + + /// + public abstract bool IsFile { get; } } \ No newline at end of file diff --git a/src/Atypical.VirtualFileSystem.Core/Abstractions/VFSPath.cs b/src/Atypical.VirtualFileSystem.Core/Abstractions/VFSPath.cs index 223988f..ceddade 100644 --- a/src/Atypical.VirtualFileSystem.Core/Abstractions/VFSPath.cs +++ b/src/Atypical.VirtualFileSystem.Core/Abstractions/VFSPath.cs @@ -1,194 +1,187 @@ -// Copyright (c) 2022, Atypical Consulting SRL -// All rights reserved. -// -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. - -namespace Atypical.VirtualFileSystem.Core.Abstractions; - -/// -/// Represents a file system entry (file or directory) in the virtual file system. -/// -public abstract record VFSPath -{ - /// - /// Regex pattern for matching a valid file system path. - /// - private const string VFSPathRegexPattern = - @$"^{ROOT_PATH}(?([a-zA-Z0-9_\-\.]+{DIRECTORY_SEPARATOR})*[a-zA-Z0-9_\-\.]+)$"; - - /// - /// Regex for matching a valid file system path. - /// - public static readonly Regex VFSPathRegex = new(VFSPathRegexPattern, RegexOptions.Compiled); - - /// - /// Creates a new instance of . - /// - /// The path to the file system entry. - /// Thrown when the path is null. - /// Thrown when the path is invalid. - public VFSPath(string path) - { - ArgumentNullException.ThrowIfNull(path); - - var vfsPath = CleanVFSPathInput(path); - - if (vfsPath == ROOT_PATH) - { - Value = vfsPath; - Parent = null; - return; - } - - if (!VFSPathRegex.IsMatch(vfsPath)) - throw new ArgumentException($"The path '{path}' is invalid.", nameof(path)); - - if (vfsPath.Contains($".{DIRECTORY_SEPARATOR}") || vfsPath.Contains($"{DIRECTORY_SEPARATOR}.")) - throw new ArgumentException($"The path '{path}' contains relative path segments.", nameof(path)); - - Value = vfsPath; - - // set parent path - var lastIndexOfParentPath = vfsPath.LastIndexOf(DIRECTORY_SEPARATOR, StringComparison.Ordinal); - var parentPath = vfsPath[..lastIndexOfParentPath] + DIRECTORY_SEPARATOR; - Parent = new VFSDirectoryPath(parentPath); - } - - /// - /// Gets the path of the file system entry with the VFS prefix. - /// - public string Value { get; } - - /// - /// Gets the path of the parent directory. - /// - public VFSDirectoryPath? Parent { get; } - - /// - /// Indicates whether the path has a parent directory. - /// - public bool HasParent - => Parent != null; - - /// - /// Gets a value indicating whether the directory is the root directory. - /// - /// - /// if the directory is the root directory; otherwise, . - /// - public bool IsRoot - => Value == ROOT_PATH; - - /// - /// Gets the depth of the file system entry. - /// The root directory has a depth of 0. - /// The depth of a file is the depth of its parent directory plus one. - /// The depth of a directory is the depth of its parent directory plus one. - /// - public int Depth { - get - { - if (IsRoot) - return 0; - - var depth = 0; - var path = this; - while (path!.Value != ROOT_PATH) - { - path = path.Parent; - depth++; - } - - return depth; - } - } - - /// - /// Gets the name of the file system entry. - /// The name of the root directory is . - /// The name of a file is the name of the file with its extension. - /// - public string Name { - get - { - if (IsRoot) - return ROOT_PATH; - - var lastIndexOfName = Value.LastIndexOf(DIRECTORY_SEPARATOR, StringComparison.Ordinal); - return Value[(lastIndexOfName + 1)..]; - } - } - - /// - /// Cleans the input path. - /// - /// The path to clean. - /// The cleaned path. - private string CleanVFSPathInput(string path) - { - // clean up the path - var cleanPath = path.Trim(); - - // if is root path, return it - if (cleanPath is ROOT_PATH or "") - return cleanPath; - - // clean up the path - remove leading and trailing slashes - cleanPath = cleanPath.TrimStart('/'); - cleanPath = cleanPath.TrimEnd('/'); - - // if path does not start with the root path, add it - if (!cleanPath.StartsWith(ROOT_PATH)) - cleanPath = $"{ROOT_PATH}{cleanPath}"; - - return cleanPath; - } - - /// - /// Gets the absolute path of the parent directory with depth . - /// The root directory has a depth of 0. - /// The depth of a file is the depth of its parent directory plus one. - /// The depth of a directory is the depth of its parent directory plus one. - /// - /// The depth of the parent directory from the root directory. - /// The absolute path of the parent directory with depth . - /// Thrown when the depth is negative. - public VFSPath GetAbsoluteParentPath(int depthFromRoot) - { - if (depthFromRoot < 0) - throw new ArgumentOutOfRangeException(nameof(depthFromRoot), "The depth from root must be greater than or equal to 0."); - - if (IsRoot) - return this; - - var path = this; - while (path!.Depth > depthFromRoot) - { - path = path.Parent; - } - - return path; - } - - /// - /// Indicates whether the current object is equal to another object of the same type. - /// - /// An object to compare with this object. - /// A value that indicates whether the current object is equal to the parameter. - public virtual bool Equals(VFSPath? other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return Value == other.Value; - } - - /// - /// Serves as the default hash function. - /// - /// A hash code for the current object. - public override int GetHashCode() - { - return Value.GetHashCode(); - } +// Copyright (c) 2022, Atypical Consulting SRL +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +namespace Atypical.VirtualFileSystem.Core.Abstractions; + +/// +/// Represents a file system entry (file or directory) in the virtual file system. +/// +public abstract record VFSPath +{ + /// + /// Regex pattern for matching a valid file system path. + /// + private const string VFSPathRegexPattern = + @$"^{ROOT_PATH}(?([a-zA-Z0-9_\-\.]+{DIRECTORY_SEPARATOR})*[a-zA-Z0-9_\-\.]+)$"; + + /// + /// Regex for matching a valid file system path. + /// + public static readonly Regex VFSPathRegex = new(VFSPathRegexPattern, RegexOptions.Compiled); + + /// + /// Creates a new instance of . + /// + /// The path to the file system entry. + /// Thrown when the path is null. + /// Thrown when the path is invalid. + public VFSPath(string path) + { + ArgumentNullException.ThrowIfNull(path); + + var vfsPath = CleanVFSPathInput(path); + + if (vfsPath == ROOT_PATH) + { + Value = vfsPath; + Parent = null; + return; + } + + if (!VFSPathRegex.IsMatch(vfsPath)) + throw new ArgumentException($"The path '{path}' is invalid.", nameof(path)); + + if (vfsPath.Contains($".{DIRECTORY_SEPARATOR}") || vfsPath.Contains($"{DIRECTORY_SEPARATOR}.")) + throw new ArgumentException($"The path '{path}' contains relative path segments.", nameof(path)); + + Value = vfsPath; + + // set parent path + var lastIndexOfParentPath = vfsPath.LastIndexOf(DIRECTORY_SEPARATOR, StringComparison.Ordinal); + var parentPath = vfsPath[..lastIndexOfParentPath] + DIRECTORY_SEPARATOR; + Parent = new VFSDirectoryPath(parentPath); + } + + /// + /// Gets the path of the file system entry with the VFS prefix. + /// + public string Value { get; } + + /// + /// Gets the path of the parent directory. + /// + public VFSDirectoryPath? Parent { get; } + + /// + /// Indicates whether the path has a parent directory. + /// + public bool HasParent + => Parent != null; + + /// + /// Gets a value indicating whether the directory is the root directory. + /// + /// + /// if the directory is the root directory; otherwise, . + /// + public bool IsRoot + => Value == ROOT_PATH; + + /// + /// Gets the depth of the file system entry. + /// The root directory has a depth of 0. + /// The depth of a file is the depth of its parent directory plus one. + /// The depth of a directory is the depth of its parent directory plus one. + /// + public int Depth { + get { + if (IsRoot) + return 0; + + var depth = 0; + var path = this; + while (path!.Value != ROOT_PATH) + { + path = path.Parent; + depth++; + } + + return depth; + } + } + + /// + /// Gets the name of the file system entry. + /// The name of the root directory is . + /// The name of a file is the name of the file with its extension. + /// + public string Name { + get { + if (IsRoot) + return ROOT_PATH; + + var lastIndexOfName = Value.LastIndexOf(DIRECTORY_SEPARATOR, StringComparison.Ordinal); + return Value[(lastIndexOfName + 1)..]; + } + } + + /// + /// Indicates whether the current object is equal to another object of the same type. + /// + /// An object to compare with this object. + /// A value that indicates whether the current object is equal to the parameter. + public virtual bool Equals(VFSPath? other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return Value == other.Value; + } + + /// + /// Cleans the input path. + /// + /// The path to clean. + /// The cleaned path. + private string CleanVFSPathInput(string path) + { + // clean up the path + var cleanPath = path.Trim(); + + // if is root path, return it + if (cleanPath is ROOT_PATH or "") + return cleanPath; + + // clean up the path - remove leading and trailing slashes + cleanPath = cleanPath.TrimStart('/'); + cleanPath = cleanPath.TrimEnd('/'); + + // if path does not start with the root path, add it + if (!cleanPath.StartsWith(ROOT_PATH)) + cleanPath = $"{ROOT_PATH}{cleanPath}"; + + return cleanPath; + } + + /// + /// Gets the absolute path of the parent directory with depth . + /// The root directory has a depth of 0. + /// The depth of a file is the depth of its parent directory plus one. + /// The depth of a directory is the depth of its parent directory plus one. + /// + /// The depth of the parent directory from the root directory. + /// The absolute path of the parent directory with depth . + /// Thrown when the depth is negative. + public VFSPath GetAbsoluteParentPath(int depthFromRoot) + { + if (depthFromRoot < 0) + throw new ArgumentOutOfRangeException(nameof(depthFromRoot), + "The depth from root must be greater than or equal to 0."); + + if (IsRoot) + return this; + + var path = this; + while (path!.Depth > depthFromRoot) path = path.Parent; + + return path; + } + + /// + /// Serves as the default hash function. + /// + /// A hash code for the current object. + public override int GetHashCode() => Value.GetHashCode(); } \ No newline at end of file diff --git a/src/Atypical.VirtualFileSystem.Core/Atypical.VirtualFileSystem.Core.csproj b/src/Atypical.VirtualFileSystem.Core/Atypical.VirtualFileSystem.Core.csproj index 9486bf8..f75d44d 100644 --- a/src/Atypical.VirtualFileSystem.Core/Atypical.VirtualFileSystem.Core.csproj +++ b/src/Atypical.VirtualFileSystem.Core/Atypical.VirtualFileSystem.Core.csproj @@ -12,11 +12,11 @@ - + - + Atypical.VirtualFileSystem @@ -39,7 +39,7 @@ - Initial release - + diff --git a/src/Atypical.VirtualFileSystem.Core/Contracts/IVirtualFileSystem.cs b/src/Atypical.VirtualFileSystem.Core/Contracts/IVirtualFileSystem.cs index 904ecd6..e7f3a0e 100644 --- a/src/Atypical.VirtualFileSystem.Core/Contracts/IVirtualFileSystem.cs +++ b/src/Atypical.VirtualFileSystem.Core/Contracts/IVirtualFileSystem.cs @@ -1,220 +1,220 @@ -// Copyright (c) 2022, Atypical Consulting SRL -// All rights reserved. -// -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. - -namespace Atypical.VirtualFileSystem.Core.Contracts; - -/// -/// Represents a virtual file system. -/// This is the main entry point for all operations on the file system. -/// You can get an instance of this interface by calling . -/// -public interface IVirtualFileSystem -{ - /// - /// Gets the root directory of the file system. - /// This is the entry point for all operations on the file system. - /// - IRootNode Root { get; } - - /// - /// Gets the file index of the file system. - /// Basically, this is a dictionary that maps file paths to file nodes. - /// This is useful for quickly finding a file node by its path. - /// - VFSIndex Index { get; } - - /// - /// Indicates whether the file system is empty. - /// This is the case if the root directory is empty. - /// - bool IsEmpty { get; } - - /// - /// Gets a directory node by its path. - /// The path must be absolute. - /// - /// The path of the directory node. - /// The directory node. - IDirectoryNode GetDirectory(VFSDirectoryPath directoryPath); - - /// - /// Gets a file node by its path. - /// The path must be absolute. - /// - /// The path of the file node. - /// The file node. - IDirectoryNode GetDirectory(string directoryPath); - - /// - /// Try to get a directory node by its path. - /// The path must be absolute. - /// If the directory node does not exist, this method returns false - /// and is set to null. - /// - /// The path of the directory node. - /// The directory node. - /// true if the directory node exists; otherwise, false. - bool TryGetDirectory(VFSDirectoryPath directoryPath, out IDirectoryNode? directory); - - /// - /// Try to get a directory node by its path. - /// The path must be absolute. - /// If the directory node does not exist, this method returns false - /// and is set to null. - /// - /// The path of the directory node. - /// The directory node. - /// true if the directory node exists; otherwise, false. - bool TryGetDirectory(string directoryPath, out IDirectoryNode? directory); - - /// - /// Creates a directory node at the specified path. - /// The path must be absolute. - /// - /// The path of the directory node. - /// The file system. - IVirtualFileSystem CreateDirectory(VFSDirectoryPath directoryPath); - - /// - /// Creates a directory node at the specified path. - /// The path must be absolute. - /// - /// The path of the directory node. - /// The file system. - IVirtualFileSystem CreateDirectory(string directoryPath); - - /// - /// Deletes a directory node at the specified path. - /// The path must be absolute. - /// - /// The path of the directory node. - /// The file system. - IVirtualFileSystem DeleteDirectory(VFSDirectoryPath directoryPath); - - /// - /// Deletes a directory node at the specified path. - /// The path must be absolute. - /// - /// The path of the directory node. - /// The file system. - IVirtualFileSystem DeleteDirectory(string directoryPath); - - /// - /// Finds all directory nodes. - /// - /// The directory nodes. - IEnumerable FindDirectories(); - - /// - /// Finds all directory nodes that match the specified regular expression. - /// The regular expression must be relative to the root directory. - /// - /// The regular expression pattern. - /// The directory nodes. - IEnumerable FindDirectories(Regex regexPattern); - - /// - /// Gets a file node by its path. - /// The path must be absolute. - /// - /// The path of the file node. - /// The file node. - IFileNode GetFile(VFSFilePath filePath); - - /// - /// Gets a file node by its path. - /// The path must be absolute. - /// - /// The path of the file node. - /// The file node. - IFileNode GetFile(string filePath); - - /// - /// Try to get a file node by its path. - /// The path must be absolute. - /// - /// The path of the file node. - /// The file node. - /// true if the file node exists; otherwise, false. - bool TryGetFile(VFSFilePath filePath, out IFileNode? file); - - /// - /// Try to get a file node by its path. - /// The path must be absolute. - /// - /// The path of the file node. - /// The file node. - /// true if the file node exists; otherwise, false. - bool TryGetFile(string filePath, out IFileNode? file); - - /// - /// Creates a file node at the specified path. - /// The path must be absolute. - /// - /// The path of the file node. - /// The content of the file node. - /// The file system. - IVirtualFileSystem CreateFile(VFSFilePath filePath, string? content = null); - - /// - /// Creates a file node at the specified path. - /// The path must be absolute. - /// - /// The path of the file node. - /// The content of the file node. - /// The file system. - IVirtualFileSystem CreateFile(string filePath, string? content = null); - - /// - /// Deletes a file node at the specified path. - /// The path must be absolute. - /// - /// The path of the file node. - /// The file system. - IVirtualFileSystem DeleteFile(VFSFilePath filePath); - - /// - /// Deletes a file node at the specified path. - /// The path must be absolute. - /// - /// The path of the file node. - /// The file system. - IVirtualFileSystem DeleteFile(string filePath); - - /// - /// Finds all file nodes. - /// - /// The file nodes. - IEnumerable FindFiles(); - - /// - /// Finds all file nodes that match the specified regular expression. - /// - /// The regular expression pattern. - /// The file nodes. - IEnumerable FindFiles(Regex regexPattern); - - // TODO: Add methods for copying, moving, renaming, etc. - // /// - // /// Reads the content of a file. - // /// The file must exist. - // /// - // /// The path to the file. - // /// The content of the file. - // /// Thrown if the file does not exist. - // /// Thrown if an error occurs while reading the file. - // string ReadContentFromFile(VFSFilePath path); - // - // /// - // /// Writes the content of a file. - // /// The file must exist. - // /// - // /// The path to the file. - // /// The content to write to the file. - // /// Thrown if the file does not exist. - // /// Thrown if an error occurs while writing the file. - // void WriteContentToFile(VFSFilePath path, string content); +// Copyright (c) 2022, Atypical Consulting SRL +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +namespace Atypical.VirtualFileSystem.Core.Contracts; + +/// +/// Represents a virtual file system. +/// This is the main entry point for all operations on the file system. +/// You can get an instance of this interface by calling . +/// +public interface IVirtualFileSystem +{ + /// + /// Gets the root directory of the file system. + /// This is the entry point for all operations on the file system. + /// + IRootNode Root { get; } + + /// + /// Gets the file index of the file system. + /// Basically, this is a dictionary that maps file paths to file nodes. + /// This is useful for quickly finding a file node by its path. + /// + VFSIndex Index { get; } + + /// + /// Indicates whether the file system is empty. + /// This is the case if the root directory is empty. + /// + bool IsEmpty { get; } + + /// + /// Gets a directory node by its path. + /// The path must be absolute. + /// + /// The path of the directory node. + /// The directory node. + IDirectoryNode GetDirectory(VFSDirectoryPath directoryPath); + + /// + /// Gets a file node by its path. + /// The path must be absolute. + /// + /// The path of the file node. + /// The file node. + IDirectoryNode GetDirectory(string directoryPath); + + /// + /// Try to get a directory node by its path. + /// The path must be absolute. + /// If the directory node does not exist, this method returns false + /// and is set to null. + /// + /// The path of the directory node. + /// The directory node. + /// true if the directory node exists; otherwise, false. + bool TryGetDirectory(VFSDirectoryPath directoryPath, out IDirectoryNode? directory); + + /// + /// Try to get a directory node by its path. + /// The path must be absolute. + /// If the directory node does not exist, this method returns false + /// and is set to null. + /// + /// The path of the directory node. + /// The directory node. + /// true if the directory node exists; otherwise, false. + bool TryGetDirectory(string directoryPath, out IDirectoryNode? directory); + + /// + /// Creates a directory node at the specified path. + /// The path must be absolute. + /// + /// The path of the directory node. + /// The file system. + IVirtualFileSystem CreateDirectory(VFSDirectoryPath directoryPath); + + /// + /// Creates a directory node at the specified path. + /// The path must be absolute. + /// + /// The path of the directory node. + /// The file system. + IVirtualFileSystem CreateDirectory(string directoryPath); + + /// + /// Deletes a directory node at the specified path. + /// The path must be absolute. + /// + /// The path of the directory node. + /// The file system. + IVirtualFileSystem DeleteDirectory(VFSDirectoryPath directoryPath); + + /// + /// Deletes a directory node at the specified path. + /// The path must be absolute. + /// + /// The path of the directory node. + /// The file system. + IVirtualFileSystem DeleteDirectory(string directoryPath); + + /// + /// Finds all directory nodes. + /// + /// The directory nodes. + IEnumerable FindDirectories(); + + /// + /// Finds all directory nodes that match the specified regular expression. + /// The regular expression must be relative to the root directory. + /// + /// The regular expression pattern. + /// The directory nodes. + IEnumerable FindDirectories(Regex regexPattern); + + /// + /// Gets a file node by its path. + /// The path must be absolute. + /// + /// The path of the file node. + /// The file node. + IFileNode GetFile(VFSFilePath filePath); + + /// + /// Gets a file node by its path. + /// The path must be absolute. + /// + /// The path of the file node. + /// The file node. + IFileNode GetFile(string filePath); + + /// + /// Try to get a file node by its path. + /// The path must be absolute. + /// + /// The path of the file node. + /// The file node. + /// true if the file node exists; otherwise, false. + bool TryGetFile(VFSFilePath filePath, out IFileNode? file); + + /// + /// Try to get a file node by its path. + /// The path must be absolute. + /// + /// The path of the file node. + /// The file node. + /// true if the file node exists; otherwise, false. + bool TryGetFile(string filePath, out IFileNode? file); + + /// + /// Creates a file node at the specified path. + /// The path must be absolute. + /// + /// The path of the file node. + /// The content of the file node. + /// The file system. + IVirtualFileSystem CreateFile(VFSFilePath filePath, string? content = null); + + /// + /// Creates a file node at the specified path. + /// The path must be absolute. + /// + /// The path of the file node. + /// The content of the file node. + /// The file system. + IVirtualFileSystem CreateFile(string filePath, string? content = null); + + /// + /// Deletes a file node at the specified path. + /// The path must be absolute. + /// + /// The path of the file node. + /// The file system. + IVirtualFileSystem DeleteFile(VFSFilePath filePath); + + /// + /// Deletes a file node at the specified path. + /// The path must be absolute. + /// + /// The path of the file node. + /// The file system. + IVirtualFileSystem DeleteFile(string filePath); + + /// + /// Finds all file nodes. + /// + /// The file nodes. + IEnumerable FindFiles(); + + /// + /// Finds all file nodes that match the specified regular expression. + /// + /// The regular expression pattern. + /// The file nodes. + IEnumerable FindFiles(Regex regexPattern); + + // TODO: Add methods for copying, moving, renaming, etc. + // /// + // /// Reads the content of a file. + // /// The file must exist. + // /// + // /// The path to the file. + // /// The content of the file. + // /// Thrown if the file does not exist. + // /// Thrown if an error occurs while reading the file. + // string ReadContentFromFile(VFSFilePath path); + // + // /// + // /// Writes the content of a file. + // /// The file must exist. + // /// + // /// The path to the file. + // /// The content to write to the file. + // /// Thrown if the file does not exist. + // /// Thrown if an error occurs while writing the file. + // void WriteContentToFile(VFSFilePath path, string content); } \ No newline at end of file diff --git a/src/Atypical.VirtualFileSystem.Core/Contracts/IVirtualFileSystemNode.cs b/src/Atypical.VirtualFileSystem.Core/Contracts/IVirtualFileSystemNode.cs index f988bec..6ef4e48 100644 --- a/src/Atypical.VirtualFileSystem.Core/Contracts/IVirtualFileSystemNode.cs +++ b/src/Atypical.VirtualFileSystem.Core/Contracts/IVirtualFileSystemNode.cs @@ -1,59 +1,59 @@ -// Copyright (c) 2022, Atypical Consulting SRL -// All rights reserved. -// -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. - -namespace Atypical.VirtualFileSystem.Core.Contracts; - -/// -/// Represents a node in a virtual file system. -/// A node can be a file or a directory. -/// -public interface IVirtualFileSystemNode -{ - /// - /// Gets the full path of the node. - /// The path is the path from the root of the file system to the node. - /// For example, the path of the node with the path "./temp/file.txt" is "./temp/file.txt". - /// The path of the node with the path "./temp/" is "./temp/". - /// - /// - /// The full path of the node. - /// - VFSPath Path { get; } - - /// - /// Gets the name of the virtual file system node. - /// The name is the last part of the path. - /// For example, the name of the file "vfs://temp/file.txt" is "file.txt". - /// The name of the directory "vfs://temp" is "temp". - /// - public string Name - => Path.Name; - - /// - /// Gets the creation time of the node. - /// - DateTimeOffset CreationTime { get; } - - /// - /// Gets the last access time of the node. - /// - DateTimeOffset LastAccessTime { get; } - - /// - /// Gets the last write time of the node. - /// - DateTimeOffset LastWriteTime { get; } - - /// - /// Indicates whether the node is a directory. - /// - bool IsDirectory { get; } - - /// - /// Indicates whether the node is a file. - /// - bool IsFile { get; } +// Copyright (c) 2022, Atypical Consulting SRL +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +namespace Atypical.VirtualFileSystem.Core.Contracts; + +/// +/// Represents a node in a virtual file system. +/// A node can be a file or a directory. +/// +public interface IVirtualFileSystemNode +{ + /// + /// Gets the full path of the node. + /// The path is the path from the root of the file system to the node. + /// For example, the path of the node with the path "./temp/file.txt" is "./temp/file.txt". + /// The path of the node with the path "./temp/" is "./temp/". + /// + /// + /// The full path of the node. + /// + VFSPath Path { get; } + + /// + /// Gets the name of the virtual file system node. + /// The name is the last part of the path. + /// For example, the name of the file "vfs://temp/file.txt" is "file.txt". + /// The name of the directory "vfs://temp" is "temp". + /// + public string Name + => Path.Name; + + /// + /// Gets the creation time of the node. + /// + DateTimeOffset CreationTime { get; } + + /// + /// Gets the last access time of the node. + /// + DateTimeOffset LastAccessTime { get; } + + /// + /// Gets the last write time of the node. + /// + DateTimeOffset LastWriteTime { get; } + + /// + /// Indicates whether the node is a directory. + /// + bool IsDirectory { get; } + + /// + /// Indicates whether the node is a file. + /// + bool IsFile { get; } } \ No newline at end of file diff --git a/src/Atypical.VirtualFileSystem.Core/Models/DirectoryNode.cs b/src/Atypical.VirtualFileSystem.Core/Models/DirectoryNode.cs index 8bb29d6..248ed54 100644 --- a/src/Atypical.VirtualFileSystem.Core/Models/DirectoryNode.cs +++ b/src/Atypical.VirtualFileSystem.Core/Models/DirectoryNode.cs @@ -1,58 +1,55 @@ -// Copyright (c) 2022, Atypical Consulting SRL -// All rights reserved. -// -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. - -namespace Atypical.VirtualFileSystem.Core.Models; - -/// -/// Represents a directory in the virtual file system. -/// -public record DirectoryNode - : VFSNode, IDirectoryNode -{ - private readonly List _directories = new(); - private readonly List _files = new(); - - /// - /// Initializes a new instance of the class. - /// Creates a new directory node. - /// The directory is created with the current date and time as creation and last modification date. - /// - /// The path of the directory. - [SuppressMessage("ReSharper", "SuggestBaseTypeForParameterInConstructor")] - public DirectoryNode(VFSDirectoryPath directoryPath) - : base(directoryPath) - { - Path = directoryPath; - } - - /// - public override VFSDirectoryPath Path { get; } - - /// - public ReadOnlyCollection Directories - => _directories.AsReadOnly(); - - /// - public override bool IsDirectory - => true; - - /// - public ReadOnlyCollection Files - => _files.AsReadOnly(); - - /// - public override bool IsFile - => false; - - /// - /// Returns a string that represents the path of the directory. - /// - /// A string that represents the path of the directory. - public override string ToString() - { - return Path.ToString(); - } +// Copyright (c) 2022, Atypical Consulting SRL +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +namespace Atypical.VirtualFileSystem.Core.Models; + +/// +/// Represents a directory in the virtual file system. +/// +public record DirectoryNode + : VFSNode, IDirectoryNode +{ + private readonly List _directories = new(); + private readonly List _files = new(); + + /// + /// Initializes a new instance of the class. + /// Creates a new directory node. + /// The directory is created with the current date and time as creation and last modification date. + /// + /// The path of the directory. + [SuppressMessage("ReSharper", "SuggestBaseTypeForParameterInConstructor")] + public DirectoryNode(VFSDirectoryPath directoryPath) + : base(directoryPath) + { + Path = directoryPath; + } + + /// + public override VFSDirectoryPath Path { get; } + + /// + public ReadOnlyCollection Directories + => _directories.AsReadOnly(); + + /// + public ReadOnlyCollection Files + => _files.AsReadOnly(); + + /// + public override bool IsDirectory + => true; + + /// + public override bool IsFile + => false; + + /// + /// Returns a string that represents the path of the directory. + /// + /// A string that represents the path of the directory. + public override string ToString() => Path.ToString(); } \ No newline at end of file diff --git a/src/Atypical.VirtualFileSystem.Core/Models/FileNode.cs b/src/Atypical.VirtualFileSystem.Core/Models/FileNode.cs index 3f05ad5..3269b4e 100644 --- a/src/Atypical.VirtualFileSystem.Core/Models/FileNode.cs +++ b/src/Atypical.VirtualFileSystem.Core/Models/FileNode.cs @@ -1,52 +1,49 @@ -// Copyright (c) 2022, Atypical Consulting SRL -// All rights reserved. -// -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. - -namespace Atypical.VirtualFileSystem.Core.Models; - -/// -/// Represents a file in the virtual file system. -/// -public record FileNode - : VFSNode, IFileNode -{ - /// - /// Initializes a new instance of the class. - /// Creates a new file node. - /// The file is created with the current date and time as creation and last modification date. - /// - /// The path of the file. - /// The content of the file. - [SuppressMessage("ReSharper", "SuggestBaseTypeForParameterInConstructor")] - public FileNode(VFSFilePath filePath, string? content = null) - : base(filePath) - { - Path = filePath; - Content = content ?? string.Empty; - } - - /// - public string Content { get; set; } - - /// - public override VFSFilePath Path { get; } - - /// - public override bool IsDirectory - => false; - - /// - public override bool IsFile - => true; - - /// - /// Returns a string that represents the path of the file. - /// - /// A string that represents the path of the file. - public override string ToString() - { - return Path.ToString(); - } +// Copyright (c) 2022, Atypical Consulting SRL +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +namespace Atypical.VirtualFileSystem.Core.Models; + +/// +/// Represents a file in the virtual file system. +/// +public record FileNode + : VFSNode, IFileNode +{ + /// + /// Initializes a new instance of the class. + /// Creates a new file node. + /// The file is created with the current date and time as creation and last modification date. + /// + /// The path of the file. + /// The content of the file. + [SuppressMessage("ReSharper", "SuggestBaseTypeForParameterInConstructor")] + public FileNode(VFSFilePath filePath, string? content = null) + : base(filePath) + { + Path = filePath; + Content = content ?? string.Empty; + } + + /// + public override VFSFilePath Path { get; } + + /// + public string Content { get; set; } + + /// + public override bool IsDirectory + => false; + + /// + public override bool IsFile + => true; + + /// + /// Returns a string that represents the path of the file. + /// + /// A string that represents the path of the file. + public override string ToString() => Path.ToString(); } \ No newline at end of file diff --git a/src/Atypical.VirtualFileSystem.Core/Models/RootNode.cs b/src/Atypical.VirtualFileSystem.Core/Models/RootNode.cs index 3ca3331..73d4e7d 100644 --- a/src/Atypical.VirtualFileSystem.Core/Models/RootNode.cs +++ b/src/Atypical.VirtualFileSystem.Core/Models/RootNode.cs @@ -1,32 +1,29 @@ -// Copyright (c) 2022, Atypical Consulting SRL -// All rights reserved. -// -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. - -namespace Atypical.VirtualFileSystem.Core.Models; - -/// -/// Represents the root directory of the virtual file system. -/// -public record RootNode - : DirectoryNode, IRootNode -{ - /// - /// Initializes a new instance of the class. - /// - public RootNode() - : base(new VFSRootPath()) - { - } - - /// - /// Returns a string that represents the current object. - /// For this is always the constant string . - /// - /// - public override string ToString() - { - return Path.ToString(); - } +// Copyright (c) 2022, Atypical Consulting SRL +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +namespace Atypical.VirtualFileSystem.Core.Models; + +/// +/// Represents the root directory of the virtual file system. +/// +public record RootNode + : DirectoryNode, IRootNode +{ + /// + /// Initializes a new instance of the class. + /// + public RootNode() + : base(new VFSRootPath()) + { + } + + /// + /// Returns a string that represents the current object. + /// For this is always the constant string . + /// + /// + public override string ToString() => Path.ToString(); } \ No newline at end of file diff --git a/src/Atypical.VirtualFileSystem.Core/VFS.cs b/src/Atypical.VirtualFileSystem.Core/VFS.cs index 19a5112..d4a1ab7 100644 --- a/src/Atypical.VirtualFileSystem.Core/VFS.cs +++ b/src/Atypical.VirtualFileSystem.Core/VFS.cs @@ -1,255 +1,255 @@ -// Copyright (c) 2022, Atypical Consulting SRL -// All rights reserved. -// -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. - -namespace Atypical.VirtualFileSystem.Core; - -/// -/// Represents a virtual file system. -/// This class cannot be inherited. -/// -public record VFS - : IVirtualFileSystem -{ - /// - /// Initializes a new instance of the class. - /// - public VFS() - { - Root = new RootNode(); - Index = new VFSIndex(Root); - } - - /// - public IRootNode Root { get; } - - /// - public VFSIndex Index { get; } - - /// - public bool IsEmpty - => Index.Count == 1; - - #region Indexing - - private void AddToIndex(IVirtualFileSystemNode node) - { - var added = Index.TryAdd(node.Path.Value, node); - - if (!added) - throw new InvalidOperationException($"The node '{node.Path}' already exists in the index."); - - if (node.Path.Parent is not null && !Index.ContainsKey(node.Path.Parent.Value)) - CreateDirectory(node.Path.Parent); - } - - #endregion - - #region Directory - - /// - public IDirectoryNode GetDirectory(VFSDirectoryPath directoryPath) - => (IDirectoryNode)Index[directoryPath.Value]; - - /// - public IDirectoryNode GetDirectory(string directoryPath) - => GetDirectory(new VFSDirectoryPath(directoryPath)); - - /// - public bool TryGetDirectory(VFSDirectoryPath directoryPath, out IDirectoryNode? directory) - { - try - { - directory = GetDirectory(directoryPath); - return true; - } - catch (KeyNotFoundException) - { - directory = null; - return false; - } - } - - /// - public bool TryGetDirectory(string path, out IDirectoryNode? directory) - => TryGetDirectory(new VFSDirectoryPath(path), out directory); - - /// - public IVirtualFileSystem CreateDirectory(VFSDirectoryPath directoryPath) - { - if (directoryPath.IsRoot) - throw new ArgumentException("Cannot create root directory.", nameof(directoryPath)); - - var directory = new DirectoryNode(directoryPath); - AddToIndex(directory); - return this; - } - - /// - public IVirtualFileSystem CreateDirectory(string path) - => CreateDirectory(new VFSDirectoryPath(path)); - - /// - public IVirtualFileSystem DeleteDirectory(VFSDirectoryPath directoryPath) - { - if (directoryPath.IsRoot) - throw new ArgumentException("Cannot delete root directory.", nameof(directoryPath)); - - // try to get the directory - var found = TryGetDirectory(directoryPath, out _); - if (!found) - throw new KeyNotFoundException($"The directory '{directoryPath}' does not exist."); - - // find the path and its children in the index - var paths = Index.Keys - .Where(p => p.StartsWith(directoryPath.Value)) - .OrderByDescending(p => p.Length) - .ToList(); - - // remove the paths from the index - foreach (var p in paths) - Index.Remove(p); - - return this; - } - - /// - public IVirtualFileSystem DeleteDirectory(string directoryPath) - => DeleteDirectory(new VFSDirectoryPath(directoryPath)); - - /// - public IEnumerable FindDirectories() - => Index.Values.OfType(); - - /// - public IEnumerable FindDirectories(Regex regexPattern) - => FindDirectories().Where(f => regexPattern.IsMatch(f.Path.Value)); - - #endregion - - #region File - - /// - public IFileNode GetFile(VFSFilePath filePath) - => (IFileNode)Index[filePath.Value]; - - /// - public IFileNode GetFile(string filePath) - => GetFile(new VFSFilePath(filePath)); - - /// - public bool TryGetFile(VFSFilePath filePath, out IFileNode? file) - { - try - { - file = GetFile(filePath); - return true; - } - catch (KeyNotFoundException) - { - file = null; - return false; - } - } - - /// - public bool TryGetFile(string filePath, out IFileNode? file) - => TryGetFile(new VFSFilePath(filePath), out file); - - /// - public IVirtualFileSystem CreateFile(VFSFilePath filePath, string? content = null) - { - var file = new FileNode(filePath, content); - AddToIndex(file); - return this; - } - - /// - public IVirtualFileSystem CreateFile(string filePath, string? content = null) - => CreateFile(new VFSFilePath(filePath), content); - - /// - public IVirtualFileSystem DeleteFile(VFSFilePath filePath) - { - // try to get the file - var found = TryGetFile(filePath, out _); - if (!found) - throw new KeyNotFoundException($"The file '{filePath}' does not exist."); - - // remove the file from the index - Index.Remove(filePath.Value); - - return this; - } - - /// - public IVirtualFileSystem DeleteFile(string filePath) - => DeleteFile(new VFSFilePath(filePath)); - - /// - public IEnumerable FindFiles() - => Index.Values.OfType(); - - /// - public IEnumerable FindFiles(Regex regexPattern) - => FindFiles().Where(f => regexPattern.IsMatch(f.Path.Value)); - - #endregion - - /// - /// Returns the index as an ASCII tree. - /// - /// - /// The index as an ASCII tree. - /// - /// The root directory is always the first line. - /// - public override string ToString() - { - var sb = new StringBuilder(); - - sb.AppendLine(Root.Name); - - foreach (IVirtualFileSystemNode node in Index.Values.Skip(1)) - { - int depth = node.Path.Depth; - - // get all brothers of the node - var brothers = GetBrothers(node); - - // check if the node is the last node on its level - bool isLastNodeOfLevel = brothers.Last() == node; - - while (depth > 1) - { - string parent = node.Path.GetAbsoluteParentPath(1).Value; - bool isLastNodeOfLevelParent = GetBrothers(GetDirectory(parent)).Last() == GetDirectory(parent); - - sb.Append(isLastNodeOfLevelParent ? STR_INDENT_CLEAR : STR_INDENT_FILL); - depth--; - } - - sb.Append(isLastNodeOfLevel ? STR_INDENT_ENTRY_LAST : STR_INDENT_ENTRY_MIDDLE); - sb.AppendLine(node.Path.Name); - } - - return sb.ToString().Trim(); - } - - /// - /// Get all brothers of the node - /// (all nodes on the same level including the node itself) - /// - /// The node - /// - private List GetBrothers(IVirtualFileSystemNode node) - { - var brothers = Index.Values - .Where(n => n.Path.Parent == node.Path.Parent) - .ToList(); - - return brothers; - } -} +// Copyright (c) 2022, Atypical Consulting SRL +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +namespace Atypical.VirtualFileSystem.Core; + +/// +/// Represents a virtual file system. +/// This class cannot be inherited. +/// +public record VFS + : IVirtualFileSystem +{ + /// + /// Initializes a new instance of the class. + /// + public VFS() + { + Root = new RootNode(); + Index = new VFSIndex(Root); + } + + /// + public IRootNode Root { get; } + + /// + public VFSIndex Index { get; } + + /// + public bool IsEmpty + => Index.Count == 1; + + #region Indexing + + private void AddToIndex(IVirtualFileSystemNode node) + { + var added = Index.TryAdd(node.Path.Value, node); + + if (!added) + throw new InvalidOperationException($"The node '{node.Path}' already exists in the index."); + + if (node.Path.Parent is not null && !Index.ContainsKey(node.Path.Parent.Value)) + CreateDirectory(node.Path.Parent); + } + + #endregion + + /// + /// Returns the index as an ASCII tree. + /// + /// + /// The index as an ASCII tree. + /// + /// The root directory is always the first line. + /// + public override string ToString() + { + var sb = new StringBuilder(); + + sb.AppendLine(Root.Name); + + foreach (var node in Index.Values.Skip(1)) + { + var depth = node.Path.Depth; + + // get all brothers of the node + var brothers = GetBrothers(node); + + // check if the node is the last node on its level + var isLastNodeOfLevel = brothers.Last() == node; + + while (depth > 1) + { + var parent = node.Path.GetAbsoluteParentPath(1).Value; + var isLastNodeOfLevelParent = GetBrothers(GetDirectory(parent)).Last() == GetDirectory(parent); + + sb.Append(isLastNodeOfLevelParent ? STR_INDENT_CLEAR : STR_INDENT_FILL); + depth--; + } + + sb.Append(isLastNodeOfLevel ? STR_INDENT_ENTRY_LAST : STR_INDENT_ENTRY_MIDDLE); + sb.AppendLine(node.Path.Name); + } + + return sb.ToString().Trim(); + } + + /// + /// Get all brothers of the node + /// (all nodes on the same level including the node itself) + /// + /// The node + /// + private List GetBrothers(IVirtualFileSystemNode node) + { + var brothers = Index.Values + .Where(n => n.Path.Parent == node.Path.Parent) + .ToList(); + + return brothers; + } + + #region Directory + + /// + public IDirectoryNode GetDirectory(VFSDirectoryPath directoryPath) + => (IDirectoryNode)Index[directoryPath.Value]; + + /// + public IDirectoryNode GetDirectory(string directoryPath) + => GetDirectory(new VFSDirectoryPath(directoryPath)); + + /// + public bool TryGetDirectory(VFSDirectoryPath directoryPath, out IDirectoryNode? directory) + { + try + { + directory = GetDirectory(directoryPath); + return true; + } + catch (KeyNotFoundException) + { + directory = null; + return false; + } + } + + /// + public bool TryGetDirectory(string path, out IDirectoryNode? directory) + => TryGetDirectory(new VFSDirectoryPath(path), out directory); + + /// + public IVirtualFileSystem CreateDirectory(VFSDirectoryPath directoryPath) + { + if (directoryPath.IsRoot) + throw new ArgumentException("Cannot create root directory.", nameof(directoryPath)); + + var directory = new DirectoryNode(directoryPath); + AddToIndex(directory); + return this; + } + + /// + public IVirtualFileSystem CreateDirectory(string path) + => CreateDirectory(new VFSDirectoryPath(path)); + + /// + public IVirtualFileSystem DeleteDirectory(VFSDirectoryPath directoryPath) + { + if (directoryPath.IsRoot) + throw new ArgumentException("Cannot delete root directory.", nameof(directoryPath)); + + // try to get the directory + var found = TryGetDirectory(directoryPath, out _); + if (!found) + throw new KeyNotFoundException($"The directory '{directoryPath}' does not exist."); + + // find the path and its children in the index + var paths = Index.Keys + .Where(p => p.StartsWith(directoryPath.Value)) + .OrderByDescending(p => p.Length) + .ToList(); + + // remove the paths from the index + foreach (var p in paths) + Index.Remove(p); + + return this; + } + + /// + public IVirtualFileSystem DeleteDirectory(string directoryPath) + => DeleteDirectory(new VFSDirectoryPath(directoryPath)); + + /// + public IEnumerable FindDirectories() + => Index.Values.OfType(); + + /// + public IEnumerable FindDirectories(Regex regexPattern) + => FindDirectories().Where(f => regexPattern.IsMatch(f.Path.Value)); + + #endregion + + #region File + + /// + public IFileNode GetFile(VFSFilePath filePath) + => (IFileNode)Index[filePath.Value]; + + /// + public IFileNode GetFile(string filePath) + => GetFile(new VFSFilePath(filePath)); + + /// + public bool TryGetFile(VFSFilePath filePath, out IFileNode? file) + { + try + { + file = GetFile(filePath); + return true; + } + catch (KeyNotFoundException) + { + file = null; + return false; + } + } + + /// + public bool TryGetFile(string filePath, out IFileNode? file) + => TryGetFile(new VFSFilePath(filePath), out file); + + /// + public IVirtualFileSystem CreateFile(VFSFilePath filePath, string? content = null) + { + var file = new FileNode(filePath, content); + AddToIndex(file); + return this; + } + + /// + public IVirtualFileSystem CreateFile(string filePath, string? content = null) + => CreateFile(new VFSFilePath(filePath), content); + + /// + public IVirtualFileSystem DeleteFile(VFSFilePath filePath) + { + // try to get the file + var found = TryGetFile(filePath, out _); + if (!found) + throw new KeyNotFoundException($"The file '{filePath}' does not exist."); + + // remove the file from the index + Index.Remove(filePath.Value); + + return this; + } + + /// + public IVirtualFileSystem DeleteFile(string filePath) + => DeleteFile(new VFSFilePath(filePath)); + + /// + public IEnumerable FindFiles() + => Index.Values.OfType(); + + /// + public IEnumerable FindFiles(Regex regexPattern) + => FindFiles().Where(f => regexPattern.IsMatch(f.Path.Value)); + + #endregion +} diff --git a/src/Atypical.VirtualFileSystem.Core/VFSConstants.cs b/src/Atypical.VirtualFileSystem.Core/VFSConstants.cs index 84940eb..7fc5af6 100644 --- a/src/Atypical.VirtualFileSystem.Core/VFSConstants.cs +++ b/src/Atypical.VirtualFileSystem.Core/VFSConstants.cs @@ -1,49 +1,49 @@ -// Copyright (c) 2022, Atypical Consulting SRL -// All rights reserved. -// -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. - -namespace Atypical.VirtualFileSystem.Core; - -/// -/// Constants used by the Virtual File System. -/// -public static class VFSConstants -{ - /// - /// The root path. - /// This is the path used to identify the root directory. - /// - public const string ROOT_PATH = "vfs://"; - - /// - /// The directory separator. - /// This is the character used to separate directory names. - /// - public const string DIRECTORY_SEPARATOR = "/"; - - /// - /// The string indent clear. - /// A 4 characters string used by a string builder to indent a line. - /// - public const string STR_INDENT_CLEAR = " "; - - /// - /// The string indent entry middle. - /// A 4 characters string used by a string builder to indent a line which is not the last one. - /// - public const string STR_INDENT_ENTRY_MIDDLE = "├── "; - - /// - /// The string indent entry last. - /// A 4 characters string used by a string builder to indent a line which is the last one. - /// - public const string STR_INDENT_ENTRY_LAST = "└── "; - - /// - /// The string indent entry fill. - /// A 4 characters string used by a string builder to indent a line which is not the last one. - /// - public const string STR_INDENT_FILL = "│ "; +// Copyright (c) 2022, Atypical Consulting SRL +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +namespace Atypical.VirtualFileSystem.Core; + +/// +/// Constants used by the Virtual File System. +/// +public static class VFSConstants +{ + /// + /// The root path. + /// This is the path used to identify the root directory. + /// + public const string ROOT_PATH = "vfs://"; + + /// + /// The directory separator. + /// This is the character used to separate directory names. + /// + public const string DIRECTORY_SEPARATOR = "/"; + + /// + /// The string indent clear. + /// A 4 characters string used by a string builder to indent a line. + /// + public const string STR_INDENT_CLEAR = " "; + + /// + /// The string indent entry middle. + /// A 4 characters string used by a string builder to indent a line which is not the last one. + /// + public const string STR_INDENT_ENTRY_MIDDLE = "├── "; + + /// + /// The string indent entry last. + /// A 4 characters string used by a string builder to indent a line which is the last one. + /// + public const string STR_INDENT_ENTRY_LAST = "└── "; + + /// + /// The string indent entry fill. + /// A 4 characters string used by a string builder to indent a line which is not the last one. + /// + public const string STR_INDENT_FILL = "│ "; } \ No newline at end of file diff --git a/src/Atypical.VirtualFileSystem.Core/VFSIndex.cs b/src/Atypical.VirtualFileSystem.Core/VFSIndex.cs index f24488b..630deca 100644 --- a/src/Atypical.VirtualFileSystem.Core/VFSIndex.cs +++ b/src/Atypical.VirtualFileSystem.Core/VFSIndex.cs @@ -1,30 +1,30 @@ -// Copyright (c) 2022, Atypical Consulting SRL -// All rights reserved. -// -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. - -namespace Atypical.VirtualFileSystem.Core; - -/// -/// Represents the index of the virtual file system. -/// - a vfs index is a dictionary of vfs paths and vfs nodes -/// - the vfs index is used to store the nodes of the virtual file system -/// This class cannot be inherited. -/// -public sealed class VFSIndex - : SortedDictionary -{ - /// - /// Initializes a new instance of the class. - /// - the vfs index is a dictionary of vfs paths and vfs nodes - /// - the vfs index is used to store the nodes of the virtual file system - /// - the vfs index is sorted by the vfs paths - /// - the vfs index is case insensitive - /// - /// The root node of the virtual file system. - public VFSIndex(IRootNode rootNode) - { - Add(rootNode.Path.Value, rootNode); - } +// Copyright (c) 2022, Atypical Consulting SRL +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +namespace Atypical.VirtualFileSystem.Core; + +/// +/// Represents the index of the virtual file system. +/// - a vfs index is a dictionary of vfs paths and vfs nodes +/// - the vfs index is used to store the nodes of the virtual file system +/// This class cannot be inherited. +/// +public sealed class VFSIndex + : SortedDictionary +{ + /// + /// Initializes a new instance of the class. + /// - the vfs index is a dictionary of vfs paths and vfs nodes + /// - the vfs index is used to store the nodes of the virtual file system + /// - the vfs index is sorted by the vfs paths + /// - the vfs index is case insensitive + /// + /// The root node of the virtual file system. + public VFSIndex(IRootNode rootNode) + { + Add(rootNode.Path.Value, rootNode); + } } \ No newline at end of file diff --git a/src/Atypical.VirtualFileSystem.Core/ValueObjects/VFSDirectoryPath.cs b/src/Atypical.VirtualFileSystem.Core/ValueObjects/VFSDirectoryPath.cs index e0cc6ef..7ea7790 100644 --- a/src/Atypical.VirtualFileSystem.Core/ValueObjects/VFSDirectoryPath.cs +++ b/src/Atypical.VirtualFileSystem.Core/ValueObjects/VFSDirectoryPath.cs @@ -1,52 +1,44 @@ -// Copyright (c) 2022, Atypical Consulting SRL -// All rights reserved. -// -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. - -namespace Atypical.VirtualFileSystem.Core.ValueObjects; - -/// -/// Represents a directory in the virtual file system. -/// A directory is a first-class citizen in the virtual file system. -/// It can contain files and other directories. -/// -public record VFSDirectoryPath : VFSPath -{ - /// - /// Initializes a new instance of the class. - /// The file path is relative to the root of the virtual file system. - /// - /// The path of the directory. - public VFSDirectoryPath(string path) - : base(path) - { - // cannot ends with a file extension - var lastSegment = Value.Split('/').Last(); - if (lastSegment.Contains('.')) - { - throw new ArgumentException("The path must not contain a file extension.", nameof(path)); - } - } - - /// - /// Returns a string that represents the current object. - /// The string representation of the directory path is the path itself. - /// - /// A string that represents the current object. - public override string ToString() - { - return Value; - } - - /// - /// Implicit conversion to string - /// This allows you to use a as a string. - /// - /// The path to convert. - /// The string representation of the path. - public static implicit operator string(VFSDirectoryPath path) - { - return path.Value; - } +// Copyright (c) 2022, Atypical Consulting SRL +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +namespace Atypical.VirtualFileSystem.Core.ValueObjects; + +/// +/// Represents a directory in the virtual file system. +/// A directory is a first-class citizen in the virtual file system. +/// It can contain files and other directories. +/// +public record VFSDirectoryPath : VFSPath +{ + /// + /// Initializes a new instance of the class. + /// The file path is relative to the root of the virtual file system. + /// + /// The path of the directory. + public VFSDirectoryPath(string path) + : base(path) + { + // cannot ends with a file extension + var lastSegment = Value.Split('/').Last(); + if (lastSegment.Contains('.')) + throw new ArgumentException("The path must not contain a file extension.", nameof(path)); + } + + /// + /// Returns a string that represents the current object. + /// The string representation of the directory path is the path itself. + /// + /// A string that represents the current object. + public override string ToString() => Value; + + /// + /// Implicit conversion to string + /// This allows you to use a as a string. + /// + /// The path to convert. + /// The string representation of the path. + public static implicit operator string(VFSDirectoryPath path) => path.Value; } \ No newline at end of file diff --git a/src/Atypical.VirtualFileSystem.Core/ValueObjects/VFSFilePath.cs b/src/Atypical.VirtualFileSystem.Core/ValueObjects/VFSFilePath.cs index cc66751..851806f 100644 --- a/src/Atypical.VirtualFileSystem.Core/ValueObjects/VFSFilePath.cs +++ b/src/Atypical.VirtualFileSystem.Core/ValueObjects/VFSFilePath.cs @@ -1,45 +1,39 @@ -// Copyright (c) 2022, Atypical Consulting SRL -// All rights reserved. -// -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. - -namespace Atypical.VirtualFileSystem.Core.ValueObjects; - -/// -/// Represents a file system entry in the virtual file system. -/// A file is a first-class citizen in the virtual file system. -/// -public record VFSFilePath : VFSPath -{ - /// - /// Initializes a new instance of the class. - /// The file path is relative to the root of the virtual file system. - /// - /// The path of the file. - public VFSFilePath(string path) - : base(path) - { - } - - /// - /// Returns a string that represents the current object. - /// The file path is relative to the root of the virtual file system. - /// - /// A string that represents the current object. - public override string ToString() - { - return Value; - } - - /// - /// Implicit conversion to string - /// This allows you to use a as a string. - /// - /// The path to convert. - /// The string representation of the path. - public static implicit operator string(VFSFilePath path) - { - return path.Value; - } +// Copyright (c) 2022, Atypical Consulting SRL +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +namespace Atypical.VirtualFileSystem.Core.ValueObjects; + +/// +/// Represents a file system entry in the virtual file system. +/// A file is a first-class citizen in the virtual file system. +/// +public record VFSFilePath : VFSPath +{ + /// + /// Initializes a new instance of the class. + /// The file path is relative to the root of the virtual file system. + /// + /// The path of the file. + public VFSFilePath(string path) + : base(path) + { + } + + /// + /// Returns a string that represents the current object. + /// The file path is relative to the root of the virtual file system. + /// + /// A string that represents the current object. + public override string ToString() => Value; + + /// + /// Implicit conversion to string + /// This allows you to use a as a string. + /// + /// The path to convert. + /// The string representation of the path. + public static implicit operator string(VFSFilePath path) => path.Value; } \ No newline at end of file diff --git a/src/Atypical.VirtualFileSystem.Core/ValueObjects/VFSRootPath.cs b/src/Atypical.VirtualFileSystem.Core/ValueObjects/VFSRootPath.cs index b1c31c1..3d6cda8 100644 --- a/src/Atypical.VirtualFileSystem.Core/ValueObjects/VFSRootPath.cs +++ b/src/Atypical.VirtualFileSystem.Core/ValueObjects/VFSRootPath.cs @@ -1,35 +1,29 @@ -// Copyright (c) 2022, Atypical Consulting SRL -// All rights reserved. -// -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. - -namespace Atypical.VirtualFileSystem.Core.ValueObjects; - -/// -/// Represents the root directory of the virtual file system. -/// -public record VFSRootPath() - : VFSDirectoryPath(ROOT_PATH) -{ - /// - /// Returns a string that represents the current object. - /// The string representation of the root directory is the constant . - /// - /// A string that represents the current object. - public override string ToString() - { - return Value; - } - - /// - /// Implicit conversion to string - /// This allows you to use a as a string. - /// - /// The path to convert. - /// The string representation of the path. - public static implicit operator string(VFSRootPath path) - { - return path.Value; - } +// Copyright (c) 2022, Atypical Consulting SRL +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +namespace Atypical.VirtualFileSystem.Core.ValueObjects; + +/// +/// Represents the root directory of the virtual file system. +/// +public record VFSRootPath() + : VFSDirectoryPath(ROOT_PATH) +{ + /// + /// Returns a string that represents the current object. + /// The string representation of the root directory is the constant . + /// + /// A string that represents the current object. + public override string ToString() => Value; + + /// + /// Implicit conversion to string + /// This allows you to use a as a string. + /// + /// The path to convert. + /// The string representation of the path. + public static implicit operator string(VFSRootPath path) => path.Value; } \ No newline at end of file diff --git a/src/Atypical.VirtualFileSystem.Core/VirtualFileSystemFactory.cs b/src/Atypical.VirtualFileSystem.Core/VirtualFileSystemFactory.cs index 9bec380..09058d1 100644 --- a/src/Atypical.VirtualFileSystem.Core/VirtualFileSystemFactory.cs +++ b/src/Atypical.VirtualFileSystem.Core/VirtualFileSystemFactory.cs @@ -1,26 +1,23 @@ -// Copyright (c) 2022, Atypical Consulting SRL -// All rights reserved. -// -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. - -namespace Atypical.VirtualFileSystem.Core; - -/// -/// Represents a factory for creating instances. -/// -public class VirtualFileSystemFactory : IVirtualFileSystemFactory -{ - /// - /// Initializes a new instance of the class. - /// - public VirtualFileSystemFactory() - { - } - - /// - public IVirtualFileSystem CreateFileSystem() - { - return new VFS(); - } +// Copyright (c) 2022, Atypical Consulting SRL +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +namespace Atypical.VirtualFileSystem.Core; + +/// +/// Represents a factory for creating instances. +/// +public class VirtualFileSystemFactory : IVirtualFileSystemFactory +{ + /// + /// Initializes a new instance of the class. + /// + public VirtualFileSystemFactory() + { + } + + /// + public IVirtualFileSystem CreateFileSystem() => new VFS(); } \ No newline at end of file diff --git a/tests/Atypical.VirtualFileSystem.UnitTests/Atypical.VirtualFileSystem.UnitTests.csproj b/tests/Atypical.VirtualFileSystem.UnitTests/Atypical.VirtualFileSystem.UnitTests.csproj index bc2283c..3236c9f 100644 --- a/tests/Atypical.VirtualFileSystem.UnitTests/Atypical.VirtualFileSystem.UnitTests.csproj +++ b/tests/Atypical.VirtualFileSystem.UnitTests/Atypical.VirtualFileSystem.UnitTests.csproj @@ -13,9 +13,9 @@ - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all @@ -27,7 +27,7 @@ - + diff --git a/tests/Atypical.VirtualFileSystem.UnitTests/Models/DirectoryNodeTests.cs b/tests/Atypical.VirtualFileSystem.UnitTests/Models/DirectoryNodeTests.cs index 9b970ab..934acc6 100644 --- a/tests/Atypical.VirtualFileSystem.UnitTests/Models/DirectoryNodeTests.cs +++ b/tests/Atypical.VirtualFileSystem.UnitTests/Models/DirectoryNodeTests.cs @@ -1,65 +1,65 @@ -// Copyright (c) 2022, Atypical Consulting SRL -// All rights reserved. -// -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. - -namespace VirtualFileSystem.UnitTests.Models; - -public class DirectoryNodeTests -{ - public class Constructor - { - [Fact] - public void Constructor_create_a_directory_node() - { - // Arrange - var directoryPath = new VFSDirectoryPath("test"); - const string expectedPath = @"vfs://test"; - const string expectedParentPath = @"vfs://"; - - // Act - var directoryNode = new DirectoryNode(directoryPath); - - // Assert - directoryNode.Should().NotBeNull(); - directoryNode.Directories.Should().BeEmpty(); - directoryNode.Files.Should().BeEmpty(); - directoryNode.Path.Value.Should().Be(expectedPath); - directoryNode.Path.Parent.Should().NotBeNull(); - directoryNode.Path.Parent!.Value.Should().Be(expectedParentPath); - } - - [Fact] - public void Constructor_create_a_directory_node_with_parent() - { - var directoryPath = new VFSDirectoryPath("parent/child"); - var directory = new DirectoryNode(directoryPath); - - directory.Path.Value.Should().Be("vfs://parent/child"); - directory.Path.Parent.Should().NotBeNull(); - directory.Path.Parent!.Value.Should().Be("vfs://parent"); - } - } - - public class MethodToString - { - [Fact] - public void ToString_return_path() - { - var directoryPath = new VFSDirectoryPath("test"); - var directoryNode = new DirectoryNode(directoryPath); - - directoryNode.ToString().Should().Be("vfs://test"); - } - - [Fact] - public void ToString_return_path_with_parent() - { - var directoryPath = new VFSDirectoryPath("parent/child"); - var directory = new DirectoryNode(directoryPath); - - directory.ToString().Should().Be("vfs://parent/child"); - } - } +// Copyright (c) 2022, Atypical Consulting SRL +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +namespace VirtualFileSystem.UnitTests.Models; + +public class DirectoryNodeTests +{ + public class Constructor + { + [Fact] + public void Constructor_create_a_directory_node() + { + // Arrange + var directoryPath = new VFSDirectoryPath("test"); + const string expectedPath = @"vfs://test"; + const string expectedParentPath = @"vfs://"; + + // Act + var directoryNode = new DirectoryNode(directoryPath); + + // Assert + directoryNode.Should().NotBeNull(); + directoryNode.Directories.Should().BeEmpty(); + directoryNode.Files.Should().BeEmpty(); + directoryNode.Path.Value.Should().Be(expectedPath); + directoryNode.Path.Parent.Should().NotBeNull(); + directoryNode.Path.Parent!.Value.Should().Be(expectedParentPath); + } + + [Fact] + public void Constructor_create_a_directory_node_with_parent() + { + var directoryPath = new VFSDirectoryPath("parent/child"); + var directory = new DirectoryNode(directoryPath); + + directory.Path.Value.Should().Be("vfs://parent/child"); + directory.Path.Parent.Should().NotBeNull(); + directory.Path.Parent!.Value.Should().Be("vfs://parent"); + } + } + + public class MethodToString + { + [Fact] + public void ToString_return_path() + { + var directoryPath = new VFSDirectoryPath("test"); + var directoryNode = new DirectoryNode(directoryPath); + + directoryNode.ToString().Should().Be("vfs://test"); + } + + [Fact] + public void ToString_return_path_with_parent() + { + var directoryPath = new VFSDirectoryPath("parent/child"); + var directory = new DirectoryNode(directoryPath); + + directory.ToString().Should().Be("vfs://parent/child"); + } + } } \ No newline at end of file diff --git a/tests/Atypical.VirtualFileSystem.UnitTests/Models/FileNodeTests.cs b/tests/Atypical.VirtualFileSystem.UnitTests/Models/FileNodeTests.cs index 24fbf24..aff4482 100644 --- a/tests/Atypical.VirtualFileSystem.UnitTests/Models/FileNodeTests.cs +++ b/tests/Atypical.VirtualFileSystem.UnitTests/Models/FileNodeTests.cs @@ -1,46 +1,46 @@ -// Copyright (c) 2022, Atypical Consulting SRL -// All rights reserved. -// -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. - -namespace VirtualFileSystem.UnitTests.Models; - -public class FileNodeTests -{ - public class Constructor - { - [Fact] - public void Constructor_creates_a_file_with_the_specified_name() - { - // Arrange - VFSFilePath path = new("file.txt"); - - // Act - var fileNode = new FileNode(path); - - // Assert - fileNode.Path.Should().Be(path); - fileNode.Content.Should().BeEmpty(); - fileNode.IsDirectory.Should().BeFalse(); - fileNode.IsFile.Should().BeTrue(); - } - } - - public class MethodToString - { - [Fact] - public void ToString_returns_the_path_of_the_file() - { - // Arrange - VFSFilePath path = new("file.txt"); - var fileNode = new FileNode(path); - - // Act - string result = fileNode.ToString(); - - // Assert - result.Should().Be(path.ToString()); - } - } +// Copyright (c) 2022, Atypical Consulting SRL +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +namespace VirtualFileSystem.UnitTests.Models; + +public class FileNodeTests +{ + public class Constructor + { + [Fact] + public void Constructor_creates_a_file_with_the_specified_name() + { + // Arrange + VFSFilePath path = new("file.txt"); + + // Act + var fileNode = new FileNode(path); + + // Assert + fileNode.Path.Should().Be(path); + fileNode.Content.Should().BeEmpty(); + fileNode.IsDirectory.Should().BeFalse(); + fileNode.IsFile.Should().BeTrue(); + } + } + + public class MethodToString + { + [Fact] + public void ToString_returns_the_path_of_the_file() + { + // Arrange + VFSFilePath path = new("file.txt"); + var fileNode = new FileNode(path); + + // Act + var result = fileNode.ToString(); + + // Assert + result.Should().Be(path.ToString()); + } + } } \ No newline at end of file diff --git a/tests/Atypical.VirtualFileSystem.UnitTests/Models/RootNodeTests.cs b/tests/Atypical.VirtualFileSystem.UnitTests/Models/RootNodeTests.cs index 3fc36ef..a932d8d 100644 --- a/tests/Atypical.VirtualFileSystem.UnitTests/Models/RootNodeTests.cs +++ b/tests/Atypical.VirtualFileSystem.UnitTests/Models/RootNodeTests.cs @@ -1,41 +1,41 @@ -// Copyright (c) 2022, Atypical Consulting SRL -// All rights reserved. -// -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. - -namespace VirtualFileSystem.UnitTests.Models; - -public class RootNodeTests -{ - public class Constructor - { - [Fact] - public void Constructor_create_a_root_node() - { - // Arrange - const string expectedPath = @"vfs://"; - - // Act - var rootNode = new RootNode(); - - // Assert - rootNode.Should().NotBeNull(); - rootNode.Directories.Should().BeEmpty(); - rootNode.Files.Should().BeEmpty(); - rootNode.Path.Value.Should().Be(expectedPath); - rootNode.Path.Parent.Should().BeNull(); - } - } - - public class MethodToString - { - [Fact] - public void ToString_return_path() - { - var rootNode = new RootNode(); - - rootNode.ToString().Should().Be("vfs://"); - } - } +// Copyright (c) 2022, Atypical Consulting SRL +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +namespace VirtualFileSystem.UnitTests.Models; + +public class RootNodeTests +{ + public class Constructor + { + [Fact] + public void Constructor_create_a_root_node() + { + // Arrange + const string expectedPath = @"vfs://"; + + // Act + var rootNode = new RootNode(); + + // Assert + rootNode.Should().NotBeNull(); + rootNode.Directories.Should().BeEmpty(); + rootNode.Files.Should().BeEmpty(); + rootNode.Path.Value.Should().Be(expectedPath); + rootNode.Path.Parent.Should().BeNull(); + } + } + + public class MethodToString + { + [Fact] + public void ToString_return_path() + { + var rootNode = new RootNode(); + + rootNode.ToString().Should().Be("vfs://"); + } + } } \ No newline at end of file diff --git a/tests/Atypical.VirtualFileSystem.UnitTests/Models/VirtualFileSystemFactoryTests.cs b/tests/Atypical.VirtualFileSystem.UnitTests/Models/VirtualFileSystemFactoryTests.cs index ccae2b1..dfeb45b 100644 --- a/tests/Atypical.VirtualFileSystem.UnitTests/Models/VirtualFileSystemFactoryTests.cs +++ b/tests/Atypical.VirtualFileSystem.UnitTests/Models/VirtualFileSystemFactoryTests.cs @@ -1,41 +1,41 @@ -// Copyright (c) 2022, Atypical Consulting SRL -// All rights reserved. -// -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. - -namespace VirtualFileSystem.UnitTests.Models; - -public class VirtualFileSystemFactoryTests -{ - public class Constructor - { - [Fact] - public void Constructor_create_a_virtual_file_system_factory() - { - // Act - var virtualFileSystemFactory = new VirtualFileSystemFactory(); - - // Assert - virtualFileSystemFactory.Should().NotBeNull(); - } - } - - public class MethodCreateFileSystem - { - [Fact] - public void CreateFileSystem_create_a_virtual_file_system() - { - // Arrange - var virtualFileSystemFactory = new VirtualFileSystemFactory(); - - // Act - var virtualFileSystem = virtualFileSystemFactory.CreateFileSystem(); - - // Assert - virtualFileSystem.Should().NotBeNull(); - virtualFileSystem.Root.Should().NotBeNull(); - virtualFileSystem.Root.Path.Value.Should().Be("vfs://"); - } - } +// Copyright (c) 2022, Atypical Consulting SRL +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +namespace VirtualFileSystem.UnitTests.Models; + +public class VirtualFileSystemFactoryTests +{ + public class Constructor + { + [Fact] + public void Constructor_create_a_virtual_file_system_factory() + { + // Act + var virtualFileSystemFactory = new VirtualFileSystemFactory(); + + // Assert + virtualFileSystemFactory.Should().NotBeNull(); + } + } + + public class MethodCreateFileSystem + { + [Fact] + public void CreateFileSystem_create_a_virtual_file_system() + { + // Arrange + var virtualFileSystemFactory = new VirtualFileSystemFactory(); + + // Act + var virtualFileSystem = virtualFileSystemFactory.CreateFileSystem(); + + // Assert + virtualFileSystem.Should().NotBeNull(); + virtualFileSystem.Root.Should().NotBeNull(); + virtualFileSystem.Root.Path.Value.Should().Be("vfs://"); + } + } } \ No newline at end of file diff --git a/tests/Atypical.VirtualFileSystem.UnitTests/Models/VirtualFileSystemTests.cs b/tests/Atypical.VirtualFileSystem.UnitTests/Models/VirtualFileSystemTests.cs index 72f8e47..1217c2e 100644 --- a/tests/Atypical.VirtualFileSystem.UnitTests/Models/VirtualFileSystemTests.cs +++ b/tests/Atypical.VirtualFileSystem.UnitTests/Models/VirtualFileSystemTests.cs @@ -1,621 +1,621 @@ -// Copyright (c) 2022, Atypical Consulting SRL -// All rights reserved. -// -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. - -using System.Text.RegularExpressions; -using Atypical.VirtualFileSystem.Core.Contracts; - -namespace VirtualFileSystem.UnitTests.Models; - -public class VirtualFileSystemTests -{ - public class Constructor - { - [Fact] - public void Constructor_creates_a_new_file_system() - { - // Act - IVirtualFileSystem vfs = new VFS(); - - // Assert - vfs.Should().NotBeNull(); - vfs.IsEmpty.Should().BeTrue(); - vfs.Root.IsDirectory.Should().BeTrue(); - vfs.Root.IsFile.Should().BeFalse(); - vfs.Root.Path.Value.Should().Be("vfs://"); - vfs.Root.CreationTime.Should().BeCloseTo(DateTime.Now, TimeSpan.FromHours(1)); - } - } - - public class MethodGetDirectory - { - [Fact] - public void GetDirectory_returns_the_root_directory() - { - // Arrange - IVirtualFileSystem vfs = new VFS(); - var rootPath = new VFSRootPath(); - - // Act - var root = vfs.GetDirectory(rootPath); - - // Assert - root.Should().NotBeNull(); - root.Path.Value.Should().Be("vfs://"); - } - - [Fact] - public void GetDirectory_returns_a_directory() - { - // Arrange - IVirtualFileSystem vfs = new VFS(); - const string directoryPath = "dir1/dir2/dir3"; - vfs.CreateDirectory(directoryPath); - - // Act - var directory = vfs.GetDirectory(directoryPath); - - // Assert - directory.Should().NotBeNull(); - directory.Path.Value.Should().Be("vfs://dir1/dir2/dir3"); - } - - [Fact] - public void GetDirectory_throws_an_exception_if_the_directory_does_not_exist() - { - // Arrange - IVirtualFileSystem vfs = new VFS(); - const string directoryPath = "dir1/dir2/dir3"; - - // Act - Action action = () => vfs.GetDirectory(directoryPath); - - // Assert - action.Should().Throw(); - } - } - - public class MethodTryGetDirectory - { - [Fact] - public void TryGetDirectory_returns_true_if_the_directory_exists() - { - // Arrange - IVirtualFileSystem vfs = new VFS(); - const string directoryPath = "dir1/dir2/dir3"; - vfs.CreateDirectory(directoryPath); - - // Act - var result = vfs.TryGetDirectory(directoryPath, out var directory); - - // Assert - result.Should().BeTrue(); - directory.Should().NotBeNull(); - directory!.Path.Value.Should().Be("vfs://dir1/dir2/dir3"); - } - - [Fact] - public void TryGetDirectory_returns_false_if_the_directory_does_not_exist() - { - // Arrange - IVirtualFileSystem vfs = new VFS(); - const string directoryPath = "dir1/dir2/dir3"; - - // Act - var result = vfs.TryGetDirectory(directoryPath, out var directory); - - // Assert - result.Should().BeFalse(); - directory.Should().BeNull(); - } - } - - public class MethodCreateDirectory - { - [Fact] - public void CreateDirectory_creates_a_directory() - { - // Arrange - IVirtualFileSystem vfs = new VFS(); - const string directoryPath = "dir1"; - - // Act - vfs.CreateDirectory(directoryPath); - - // Assert - vfs.IsEmpty.Should().BeFalse(); - vfs.Root.IsDirectory.Should().BeTrue(); - vfs.Root.IsFile.Should().BeFalse(); - vfs.Root.Path.Value.Should().Be("vfs://"); - vfs.Root.CreationTime.Should().BeCloseTo(DateTime.Now, TimeSpan.FromHours(1)); - } - - [Fact] - public void CreateDirectory_creates_a_directory_and_its_parents() - { - // Arrange - IVirtualFileSystem vfs = new VFS(); - const string directoryPath = "dir1/dir2/dir3"; - var path = new VFSDirectoryPath(directoryPath); - - // Act - vfs.CreateDirectory(directoryPath); - - // Assert - vfs.Index.Should().NotBeEmpty(); - vfs.Index.Should().HaveCount(4); // Root + dir1 + dir2 + dir3 - vfs.Index.Should().ContainKey(path.Value); - vfs.Index.Should().ContainKey("vfs://dir1"); - vfs.Index.Should().ContainKey("vfs://dir1/dir2"); - vfs.Index.Should().ContainKey("vfs://dir1/dir2/dir3"); - } - - [Fact] - public void CreateDirectory_throws_an_exception_if_the_directory_already_exists() - { - // Arrange - IVirtualFileSystem vfs = new VFS(); - var directoryPath = new VFSDirectoryPath("dir1"); - vfs.CreateDirectory(directoryPath); - - // Act - Action action = () => vfs.CreateDirectory(directoryPath); - - // Assert - action.Should().Throw(); - } - - [Fact] - public void CreateDirectory_throws_an_exception_if_the_path_is_not_a_directory() - { - // Arrange - var filePath = new VFSFilePath("dir1/dir2/dir3/file.txt"); - IVirtualFileSystem vfs = new VFS(); - vfs.CreateFile(filePath); - - // Act - Action action = () => vfs.CreateDirectory(filePath); - - // Assert - action.Should().Throw(); - } - } - - public class MethodDeleteDirectory - { - [Fact] - public void DeleteDirectory_deletes_a_directory() - { - // Arrange - IVirtualFileSystem vfs = new VFS(); - const string directoryPath = "dir1"; - vfs.CreateDirectory(directoryPath); - - // Act - vfs.DeleteDirectory(directoryPath); - - // Assert - vfs.Index.Count.Should().Be(1); // Root - } - - [Fact] - public void DeleteDirectory_deletes_a_directory_and_its_children() - { - // Arrange - IVirtualFileSystem vfs = new VFS(); - const string directoryPath = "dir1/dir2/dir3"; - vfs.CreateDirectory(directoryPath); - - // Act - vfs.DeleteDirectory("dir1"); - - // Assert - vfs.Index.Count.Should().Be(1); // Root - } - - [Fact] - public void DeleteDirectory_throws_an_exception_if_the_directory_does_not_exist() - { - // Arrange - IVirtualFileSystem vfs = new VFS(); - const string directoryPath = "dir1"; - - // Act - Action action = () => vfs.DeleteDirectory(directoryPath); - - // Assert - action.Should().Throw(); - } - } - - public class MethodFindDirectories - { - [Fact] - public void FindDirectories_returns_all_directories() - { - // Arrange - IVirtualFileSystem vfs = new VFS(); - vfs.CreateDirectory("dir1"); - vfs.CreateDirectory("dir2"); - vfs.CreateDirectory("dir3"); - - // Act - var directories = vfs.FindDirectories().ToList(); - - // Assert - directories.Should().NotBeEmpty(); - directories.Should().HaveCount(4); // Root + dir1 + dir2 + dir3 - directories.Should().Contain(d => d.Path.Value == "vfs://dir1"); - directories.Should().Contain(d => d.Path.Value == "vfs://dir2"); - directories.Should().Contain(d => d.Path.Value == "vfs://dir3"); - } - - [Fact] - public void FindDirectories_returns_all_directories_matching_a_pattern() - { - // Arrange - IVirtualFileSystem vfs = new VFS(); - vfs.CreateDirectory("dir1"); - vfs.CreateDirectory("dir2"); - vfs.CreateDirectory("dir3"); - - // Act - var regexPattern = new Regex("dir1"); - var directories = vfs.FindDirectories(regexPattern).ToList(); - - // Assert - directories.Should().NotBeEmpty(); - directories.Should().HaveCount(1); - directories.Should().Contain(d => d.Path.Value == "vfs://dir1"); - } - } - - public class MethodGetFile - { - [Fact] - public void GetFile_returns_the_file() - { - // Arrange - IVirtualFileSystem vfs = new VFS(); - const string filePath = "dir1/dir2/dir3/file.txt"; - vfs.CreateFile(filePath); - - // Act - var file = vfs.GetFile(filePath); - - // Assert - file.Should().NotBeNull(); - file.Path.Value.Should().Be("vfs://dir1/dir2/dir3/file.txt"); - } - - [Fact] - public void GetFile_throws_an_exception_if_the_file_does_not_exist() - { - // Arrange - IVirtualFileSystem vfs = new VFS(); - const string filePath = "dir1/dir2/dir3/file.txt"; - - // Act - Action action = () => vfs.GetFile(filePath); - - // Assert - action.Should().Throw(); - } - } - - public class MethodTryGetFile - { - [Fact] - public void TryGetFile_returns_the_file() - { - // Arrange - IVirtualFileSystem vfs = new VFS(); - const string filePath = "dir1/dir2/dir3/file.txt"; - vfs.CreateFile(filePath); - - // Act - var result = vfs.TryGetFile(filePath, out IFileNode? file); - - // Assert - result.Should().BeTrue(); - file.Should().NotBeNull(); - file!.Path.Value.Should().Be("vfs://dir1/dir2/dir3/file.txt"); - } - - [Fact] - public void TryGetFile_returns_false_if_the_file_does_not_exist() - { - // Arrange - IVirtualFileSystem vfs = new VFS(); - const string filePath = "dir1/dir2/dir3/file.txt"; - - // Act - var result = vfs.TryGetFile(filePath, out var file); - - // Assert - result.Should().BeFalse(); - file.Should().BeNull(); - } - } - - public class MethodCreateFile - { - [Fact] - public void CreateFile_creates_a_file() - { - // Arrange - IVirtualFileSystem vfs = new VFS(); - - // Act - vfs.CreateFile(new VFSFilePath("dir1/dir2/dir3/file.txt")); - - // Assert - vfs.Index.Count.Should().Be(5); // Root + dir1 + dir2 + dir3 + file.txt - vfs.Index.Should().ContainKey(new VFSFilePath("dir1/dir2/dir3/file.txt")); - } - - [Fact] - public void CreateFile_throws_an_exception_if_the_file_already_exists() - { - // Arrange - IVirtualFileSystem vfs = new VFS(); - var filePath = new VFSFilePath("dir1/dir2/dir3/file.txt"); - vfs.CreateFile(filePath); - - // Act - Action action = () => vfs.CreateFile(filePath); - - // Assert - action.Should().Throw(); - } - } - - public class MethodDeleteFile - { - [Fact] - public void DeleteFile_deletes_a_file() - { - // Arrange - IVirtualFileSystem vfs = new VFS(); - var filePath = new VFSFilePath("dir1/dir2/dir3/file.txt"); - vfs.CreateFile(filePath); - - // Act - vfs.DeleteFile(filePath); - - // Assert - vfs.Index.Count.Should().Be(4); // Root, dir1, dir2, dir3 - } - - [Fact] - public void DeleteFile_throws_an_exception_if_the_file_does_not_exist() - { - // Arrange - IVirtualFileSystem vfs = new VFS(); - - // Act - Action action = () => vfs.DeleteFile("dir1/dir2/dir3/file.txt"); - - // Assert - action.Should().Throw(); - } - } - - public class MethodFindFiles - { - [Fact] - public void FindFiles_returns_all_files() - { - // Arrange - IVirtualFileSystem vfs = new VFS(); - vfs.CreateDirectory("dir1"); - vfs.CreateDirectory("dir2"); - vfs.CreateDirectory("dir3"); - vfs.CreateFile("dir1/file1.txt"); - vfs.CreateFile("dir2/file2.txt"); - vfs.CreateFile("dir3/file3.txt"); - - // Act - var files = vfs.FindFiles().ToList(); - - // Assert - files.Should().NotBeEmpty(); - files.Should().HaveCount(3); - files.Should().Contain(f => f.Path.Value == "vfs://dir1/file1.txt"); - files.Should().Contain(f => f.Path.Value == "vfs://dir2/file2.txt"); - files.Should().Contain(f => f.Path.Value == "vfs://dir3/file3.txt"); - } - - [Fact] - public void FindFiles_with_valid_data_returns_a_list_of_files_with_content_and_name() - { - // Arrange - IVirtualFileSystem vfs = new VFS(); - vfs.CreateFile("file1.txt", "content1"); - vfs.CreateFile("file2.txt", "content2"); - vfs.CreateFile("file3.txt", "content3"); - - Regex regex = new Regex(@"file\d.txt"); - - // Act - var files = vfs.FindFiles(regex).ToList(); - - // Assert - files.Should().NotBeNull(); - files.Count.Should().Be(3); - files[0].Name.Should().Be("file1.txt"); - files[0].Content.Should().Be("content1"); - files[1].Name.Should().Be("file2.txt"); - files[1].Content.Should().Be("content2"); - files[2].Name.Should().Be("file3.txt"); - files[2].Content.Should().Be("content3"); - // Assert Index - vfs.Index.Count.Should().Be(4); // Root, file1, file2, file3 - } - } - - public class MethodToString - { - [Fact] - public void ToString_returns_root_directory() - { - // Arrange - IVirtualFileSystem vfs = new VFS(); - - // Act - var result = vfs.ToString(); - - // Assert - result.Should().Be("vfs://"); - } - - [Fact] - public void ToString_returns_3_files_as_ASCII_tree() - { - // Arrange - string expected = """ - vfs:// - ├── file1.txt - ├── file2.txt - └── file3.txt - """ - .Replace("\r", ""); - - IVirtualFileSystem vfs = new VFS() - .CreateFile("file1.txt") - .CreateFile("file2.txt") - .CreateFile("file3.txt"); - - // Act - var result = vfs.ToString(); - - // Assert - result.Should().Be(expected); - } - - [Fact] - public void ToString_returns_3_directories_as_ASCII_tree() - { - // Arrange - string expected = """ - vfs:// - ├── dir1 - ├── dir2 - └── dir3 - """ - .Replace("\r", ""); - - IVirtualFileSystem vfs = new VFS() - .CreateDirectory("dir1") - .CreateDirectory("dir2") - .CreateDirectory("dir3"); - - // Act - var result = vfs.ToString(); - - // Assert - result.Should().Be(expected); - } - - [Fact] - public void ToString_returns_3_files_and_3_directories_as_ASCII_tree() - { - // Arrange - string expected = """ - vfs:// - ├── dir1 - │ ├── file1.txt - │ ├── file2.txt - │ └── file3.txt - ├── dir2 - │ ├── file1.txt - │ ├── file2.txt - │ └── file3.txt - └── dir3 - ├── file1.txt - ├── file2.txt - └── file3.txt - """ - .Replace("\r", ""); - - IVirtualFileSystem vfs = new VFS() - .CreateFile("dir1/file1.txt") - .CreateFile("dir1/file2.txt") - .CreateFile("dir1/file3.txt") - .CreateFile("dir2/file1.txt") - .CreateFile("dir2/file2.txt") - .CreateFile("dir2/file3.txt") - .CreateFile("dir3/file1.txt") - .CreateFile("dir3/file2.txt") - .CreateFile("dir3/file3.txt"); - - // Act - var result = vfs.ToString(); - - // Assert - result.Should().Be(expected); - } - - [Fact] - public void ToString_returns_a_complex_tree() - { - // Arrange - string expected = """ - vfs:// - ├── dir1 - │ ├── dir2 - │ │ ├── dir3 - │ │ │ ├── file1.txt - │ │ │ ├── file2.txt - │ │ │ └── file3.txt - │ │ ├── file1.txt - │ │ ├── file2.txt - │ │ └── file3.txt - │ ├── file1.txt - │ ├── file2.txt - │ └── file3.txt - ├── dir2 - │ ├── dir3 - │ │ ├── file1.txt - │ │ ├── file2.txt - │ │ └── file3.txt - │ ├── file1.txt - │ ├── file2.txt - │ └── file3.txt - └── dir3 - ├── file1.txt - ├── file2.txt - └── file3.txt - """ - .Replace("\r", ""); - - IVirtualFileSystem vfs = new VFS() - .CreateFile("dir1/dir2/dir3/file1.txt") - .CreateFile("dir1/dir2/dir3/file2.txt") - .CreateFile("dir1/dir2/dir3/file3.txt") - .CreateFile("dir1/dir2/file1.txt") - .CreateFile("dir1/dir2/file2.txt") - .CreateFile("dir1/dir2/file3.txt") - .CreateFile("dir1/file1.txt") - .CreateFile("dir1/file2.txt") - .CreateFile("dir1/file3.txt") - .CreateFile("dir2/dir3/file1.txt") - .CreateFile("dir2/dir3/file2.txt") - .CreateFile("dir2/dir3/file3.txt") - .CreateFile("dir2/file1.txt") - .CreateFile("dir2/file2.txt") - .CreateFile("dir2/file3.txt") - .CreateFile("dir3/file1.txt") - .CreateFile("dir3/file2.txt") - .CreateFile("dir3/file3.txt"); - - // Act - var result = vfs.ToString(); - - // Assert - result.Should().Be(expected); - } - } -} +// Copyright (c) 2022, Atypical Consulting SRL +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +using Atypical.VirtualFileSystem.Core.Contracts; +using System.Text.RegularExpressions; + +namespace VirtualFileSystem.UnitTests.Models; + +public class VirtualFileSystemTests +{ + public class Constructor + { + [Fact] + public void Constructor_creates_a_new_file_system() + { + // Act + IVirtualFileSystem vfs = new VFS(); + + // Assert + vfs.Should().NotBeNull(); + vfs.IsEmpty.Should().BeTrue(); + vfs.Root.IsDirectory.Should().BeTrue(); + vfs.Root.IsFile.Should().BeFalse(); + vfs.Root.Path.Value.Should().Be("vfs://"); + vfs.Root.CreationTime.Should().BeCloseTo(DateTime.Now, TimeSpan.FromHours(1)); + } + } + + public class MethodGetDirectory + { + [Fact] + public void GetDirectory_returns_the_root_directory() + { + // Arrange + IVirtualFileSystem vfs = new VFS(); + var rootPath = new VFSRootPath(); + + // Act + var root = vfs.GetDirectory(rootPath); + + // Assert + root.Should().NotBeNull(); + root.Path.Value.Should().Be("vfs://"); + } + + [Fact] + public void GetDirectory_returns_a_directory() + { + // Arrange + IVirtualFileSystem vfs = new VFS(); + const string directoryPath = "dir1/dir2/dir3"; + vfs.CreateDirectory(directoryPath); + + // Act + var directory = vfs.GetDirectory(directoryPath); + + // Assert + directory.Should().NotBeNull(); + directory.Path.Value.Should().Be("vfs://dir1/dir2/dir3"); + } + + [Fact] + public void GetDirectory_throws_an_exception_if_the_directory_does_not_exist() + { + // Arrange + IVirtualFileSystem vfs = new VFS(); + const string directoryPath = "dir1/dir2/dir3"; + + // Act + Action action = () => vfs.GetDirectory(directoryPath); + + // Assert + action.Should().Throw(); + } + } + + public class MethodTryGetDirectory + { + [Fact] + public void TryGetDirectory_returns_true_if_the_directory_exists() + { + // Arrange + IVirtualFileSystem vfs = new VFS(); + const string directoryPath = "dir1/dir2/dir3"; + vfs.CreateDirectory(directoryPath); + + // Act + var result = vfs.TryGetDirectory(directoryPath, out var directory); + + // Assert + result.Should().BeTrue(); + directory.Should().NotBeNull(); + directory!.Path.Value.Should().Be("vfs://dir1/dir2/dir3"); + } + + [Fact] + public void TryGetDirectory_returns_false_if_the_directory_does_not_exist() + { + // Arrange + IVirtualFileSystem vfs = new VFS(); + const string directoryPath = "dir1/dir2/dir3"; + + // Act + var result = vfs.TryGetDirectory(directoryPath, out var directory); + + // Assert + result.Should().BeFalse(); + directory.Should().BeNull(); + } + } + + public class MethodCreateDirectory + { + [Fact] + public void CreateDirectory_creates_a_directory() + { + // Arrange + IVirtualFileSystem vfs = new VFS(); + const string directoryPath = "dir1"; + + // Act + vfs.CreateDirectory(directoryPath); + + // Assert + vfs.IsEmpty.Should().BeFalse(); + vfs.Root.IsDirectory.Should().BeTrue(); + vfs.Root.IsFile.Should().BeFalse(); + vfs.Root.Path.Value.Should().Be("vfs://"); + vfs.Root.CreationTime.Should().BeCloseTo(DateTime.Now, TimeSpan.FromHours(1)); + } + + [Fact] + public void CreateDirectory_creates_a_directory_and_its_parents() + { + // Arrange + IVirtualFileSystem vfs = new VFS(); + const string directoryPath = "dir1/dir2/dir3"; + var path = new VFSDirectoryPath(directoryPath); + + // Act + vfs.CreateDirectory(directoryPath); + + // Assert + vfs.Index.Should().NotBeEmpty(); + vfs.Index.Should().HaveCount(4); // Root + dir1 + dir2 + dir3 + vfs.Index.Should().ContainKey(path.Value); + vfs.Index.Should().ContainKey("vfs://dir1"); + vfs.Index.Should().ContainKey("vfs://dir1/dir2"); + vfs.Index.Should().ContainKey("vfs://dir1/dir2/dir3"); + } + + [Fact] + public void CreateDirectory_throws_an_exception_if_the_directory_already_exists() + { + // Arrange + IVirtualFileSystem vfs = new VFS(); + var directoryPath = new VFSDirectoryPath("dir1"); + vfs.CreateDirectory(directoryPath); + + // Act + Action action = () => vfs.CreateDirectory(directoryPath); + + // Assert + action.Should().Throw(); + } + + [Fact] + public void CreateDirectory_throws_an_exception_if_the_path_is_not_a_directory() + { + // Arrange + var filePath = new VFSFilePath("dir1/dir2/dir3/file.txt"); + IVirtualFileSystem vfs = new VFS(); + vfs.CreateFile(filePath); + + // Act + Action action = () => vfs.CreateDirectory(filePath); + + // Assert + action.Should().Throw(); + } + } + + public class MethodDeleteDirectory + { + [Fact] + public void DeleteDirectory_deletes_a_directory() + { + // Arrange + IVirtualFileSystem vfs = new VFS(); + const string directoryPath = "dir1"; + vfs.CreateDirectory(directoryPath); + + // Act + vfs.DeleteDirectory(directoryPath); + + // Assert + vfs.Index.Count.Should().Be(1); // Root + } + + [Fact] + public void DeleteDirectory_deletes_a_directory_and_its_children() + { + // Arrange + IVirtualFileSystem vfs = new VFS(); + const string directoryPath = "dir1/dir2/dir3"; + vfs.CreateDirectory(directoryPath); + + // Act + vfs.DeleteDirectory("dir1"); + + // Assert + vfs.Index.Count.Should().Be(1); // Root + } + + [Fact] + public void DeleteDirectory_throws_an_exception_if_the_directory_does_not_exist() + { + // Arrange + IVirtualFileSystem vfs = new VFS(); + const string directoryPath = "dir1"; + + // Act + Action action = () => vfs.DeleteDirectory(directoryPath); + + // Assert + action.Should().Throw(); + } + } + + public class MethodFindDirectories + { + [Fact] + public void FindDirectories_returns_all_directories() + { + // Arrange + IVirtualFileSystem vfs = new VFS(); + vfs.CreateDirectory("dir1"); + vfs.CreateDirectory("dir2"); + vfs.CreateDirectory("dir3"); + + // Act + var directories = vfs.FindDirectories().ToList(); + + // Assert + directories.Should().NotBeEmpty(); + directories.Should().HaveCount(4); // Root + dir1 + dir2 + dir3 + directories.Should().Contain(d => d.Path.Value == "vfs://dir1"); + directories.Should().Contain(d => d.Path.Value == "vfs://dir2"); + directories.Should().Contain(d => d.Path.Value == "vfs://dir3"); + } + + [Fact] + public void FindDirectories_returns_all_directories_matching_a_pattern() + { + // Arrange + IVirtualFileSystem vfs = new VFS(); + vfs.CreateDirectory("dir1"); + vfs.CreateDirectory("dir2"); + vfs.CreateDirectory("dir3"); + + // Act + var regexPattern = new Regex("dir1"); + var directories = vfs.FindDirectories(regexPattern).ToList(); + + // Assert + directories.Should().NotBeEmpty(); + directories.Should().HaveCount(1); + directories.Should().Contain(d => d.Path.Value == "vfs://dir1"); + } + } + + public class MethodGetFile + { + [Fact] + public void GetFile_returns_the_file() + { + // Arrange + IVirtualFileSystem vfs = new VFS(); + const string filePath = "dir1/dir2/dir3/file.txt"; + vfs.CreateFile(filePath); + + // Act + var file = vfs.GetFile(filePath); + + // Assert + file.Should().NotBeNull(); + file.Path.Value.Should().Be("vfs://dir1/dir2/dir3/file.txt"); + } + + [Fact] + public void GetFile_throws_an_exception_if_the_file_does_not_exist() + { + // Arrange + IVirtualFileSystem vfs = new VFS(); + const string filePath = "dir1/dir2/dir3/file.txt"; + + // Act + Action action = () => vfs.GetFile(filePath); + + // Assert + action.Should().Throw(); + } + } + + public class MethodTryGetFile + { + [Fact] + public void TryGetFile_returns_the_file() + { + // Arrange + IVirtualFileSystem vfs = new VFS(); + const string filePath = "dir1/dir2/dir3/file.txt"; + vfs.CreateFile(filePath); + + // Act + var result = vfs.TryGetFile(filePath, out var file); + + // Assert + result.Should().BeTrue(); + file.Should().NotBeNull(); + file!.Path.Value.Should().Be("vfs://dir1/dir2/dir3/file.txt"); + } + + [Fact] + public void TryGetFile_returns_false_if_the_file_does_not_exist() + { + // Arrange + IVirtualFileSystem vfs = new VFS(); + const string filePath = "dir1/dir2/dir3/file.txt"; + + // Act + var result = vfs.TryGetFile(filePath, out var file); + + // Assert + result.Should().BeFalse(); + file.Should().BeNull(); + } + } + + public class MethodCreateFile + { + [Fact] + public void CreateFile_creates_a_file() + { + // Arrange + IVirtualFileSystem vfs = new VFS(); + + // Act + vfs.CreateFile(new VFSFilePath("dir1/dir2/dir3/file.txt")); + + // Assert + vfs.Index.Count.Should().Be(5); // Root + dir1 + dir2 + dir3 + file.txt + vfs.Index.Should().ContainKey(new VFSFilePath("dir1/dir2/dir3/file.txt")); + } + + [Fact] + public void CreateFile_throws_an_exception_if_the_file_already_exists() + { + // Arrange + IVirtualFileSystem vfs = new VFS(); + var filePath = new VFSFilePath("dir1/dir2/dir3/file.txt"); + vfs.CreateFile(filePath); + + // Act + Action action = () => vfs.CreateFile(filePath); + + // Assert + action.Should().Throw(); + } + } + + public class MethodDeleteFile + { + [Fact] + public void DeleteFile_deletes_a_file() + { + // Arrange + IVirtualFileSystem vfs = new VFS(); + var filePath = new VFSFilePath("dir1/dir2/dir3/file.txt"); + vfs.CreateFile(filePath); + + // Act + vfs.DeleteFile(filePath); + + // Assert + vfs.Index.Count.Should().Be(4); // Root, dir1, dir2, dir3 + } + + [Fact] + public void DeleteFile_throws_an_exception_if_the_file_does_not_exist() + { + // Arrange + IVirtualFileSystem vfs = new VFS(); + + // Act + Action action = () => vfs.DeleteFile("dir1/dir2/dir3/file.txt"); + + // Assert + action.Should().Throw(); + } + } + + public class MethodFindFiles + { + [Fact] + public void FindFiles_returns_all_files() + { + // Arrange + IVirtualFileSystem vfs = new VFS(); + vfs.CreateDirectory("dir1"); + vfs.CreateDirectory("dir2"); + vfs.CreateDirectory("dir3"); + vfs.CreateFile("dir1/file1.txt"); + vfs.CreateFile("dir2/file2.txt"); + vfs.CreateFile("dir3/file3.txt"); + + // Act + var files = vfs.FindFiles().ToList(); + + // Assert + files.Should().NotBeEmpty(); + files.Should().HaveCount(3); + files.Should().Contain(f => f.Path.Value == "vfs://dir1/file1.txt"); + files.Should().Contain(f => f.Path.Value == "vfs://dir2/file2.txt"); + files.Should().Contain(f => f.Path.Value == "vfs://dir3/file3.txt"); + } + + [Fact] + public void FindFiles_with_valid_data_returns_a_list_of_files_with_content_and_name() + { + // Arrange + IVirtualFileSystem vfs = new VFS(); + vfs.CreateFile("file1.txt", "content1"); + vfs.CreateFile("file2.txt", "content2"); + vfs.CreateFile("file3.txt", "content3"); + + var regex = new Regex(@"file\d.txt"); + + // Act + var files = vfs.FindFiles(regex).ToList(); + + // Assert + files.Should().NotBeNull(); + files.Count.Should().Be(3); + files[0].Name.Should().Be("file1.txt"); + files[0].Content.Should().Be("content1"); + files[1].Name.Should().Be("file2.txt"); + files[1].Content.Should().Be("content2"); + files[2].Name.Should().Be("file3.txt"); + files[2].Content.Should().Be("content3"); + // Assert Index + vfs.Index.Count.Should().Be(4); // Root, file1, file2, file3 + } + } + + public class MethodToString + { + [Fact] + public void ToString_returns_root_directory() + { + // Arrange + IVirtualFileSystem vfs = new VFS(); + + // Act + var result = vfs.ToString(); + + // Assert + result.Should().Be("vfs://"); + } + + [Fact] + public void ToString_returns_3_files_as_ASCII_tree() + { + // Arrange + var expected = """ + vfs:// + ├── file1.txt + ├── file2.txt + └── file3.txt + """ + .Replace("\r", ""); + + var vfs = new VFS() + .CreateFile("file1.txt") + .CreateFile("file2.txt") + .CreateFile("file3.txt"); + + // Act + var result = vfs.ToString(); + + // Assert + result.Should().Be(expected); + } + + [Fact] + public void ToString_returns_3_directories_as_ASCII_tree() + { + // Arrange + var expected = """ + vfs:// + ├── dir1 + ├── dir2 + └── dir3 + """ + .Replace("\r", ""); + + var vfs = new VFS() + .CreateDirectory("dir1") + .CreateDirectory("dir2") + .CreateDirectory("dir3"); + + // Act + var result = vfs.ToString(); + + // Assert + result.Should().Be(expected); + } + + [Fact] + public void ToString_returns_3_files_and_3_directories_as_ASCII_tree() + { + // Arrange + var expected = """ + vfs:// + ├── dir1 + │ ├── file1.txt + │ ├── file2.txt + │ └── file3.txt + ├── dir2 + │ ├── file1.txt + │ ├── file2.txt + │ └── file3.txt + └── dir3 + ├── file1.txt + ├── file2.txt + └── file3.txt + """ + .Replace("\r", ""); + + var vfs = new VFS() + .CreateFile("dir1/file1.txt") + .CreateFile("dir1/file2.txt") + .CreateFile("dir1/file3.txt") + .CreateFile("dir2/file1.txt") + .CreateFile("dir2/file2.txt") + .CreateFile("dir2/file3.txt") + .CreateFile("dir3/file1.txt") + .CreateFile("dir3/file2.txt") + .CreateFile("dir3/file3.txt"); + + // Act + var result = vfs.ToString(); + + // Assert + result.Should().Be(expected); + } + + [Fact] + public void ToString_returns_a_complex_tree() + { + // Arrange + var expected = """ + vfs:// + ├── dir1 + │ ├── dir2 + │ │ ├── dir3 + │ │ │ ├── file1.txt + │ │ │ ├── file2.txt + │ │ │ └── file3.txt + │ │ ├── file1.txt + │ │ ├── file2.txt + │ │ └── file3.txt + │ ├── file1.txt + │ ├── file2.txt + │ └── file3.txt + ├── dir2 + │ ├── dir3 + │ │ ├── file1.txt + │ │ ├── file2.txt + │ │ └── file3.txt + │ ├── file1.txt + │ ├── file2.txt + │ └── file3.txt + └── dir3 + ├── file1.txt + ├── file2.txt + └── file3.txt + """ + .Replace("\r", ""); + + var vfs = new VFS() + .CreateFile("dir1/dir2/dir3/file1.txt") + .CreateFile("dir1/dir2/dir3/file2.txt") + .CreateFile("dir1/dir2/dir3/file3.txt") + .CreateFile("dir1/dir2/file1.txt") + .CreateFile("dir1/dir2/file2.txt") + .CreateFile("dir1/dir2/file3.txt") + .CreateFile("dir1/file1.txt") + .CreateFile("dir1/file2.txt") + .CreateFile("dir1/file3.txt") + .CreateFile("dir2/dir3/file1.txt") + .CreateFile("dir2/dir3/file2.txt") + .CreateFile("dir2/dir3/file3.txt") + .CreateFile("dir2/file1.txt") + .CreateFile("dir2/file2.txt") + .CreateFile("dir2/file3.txt") + .CreateFile("dir3/file1.txt") + .CreateFile("dir3/file2.txt") + .CreateFile("dir3/file3.txt"); + + // Act + var result = vfs.ToString(); + + // Assert + result.Should().Be(expected); + } + } +} diff --git a/tests/Atypical.VirtualFileSystem.UnitTests/ValueObjects/VFSDirectoryPathTests.cs b/tests/Atypical.VirtualFileSystem.UnitTests/ValueObjects/VFSDirectoryPathTests.cs index a3d2c3c..039f913 100644 --- a/tests/Atypical.VirtualFileSystem.UnitTests/ValueObjects/VFSDirectoryPathTests.cs +++ b/tests/Atypical.VirtualFileSystem.UnitTests/ValueObjects/VFSDirectoryPathTests.cs @@ -1,294 +1,294 @@ -// Copyright (c) 2022, Atypical Consulting SRL -// All rights reserved. -// -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. - -namespace VirtualFileSystem.UnitTests.ValueObjects; - -public class VFSDirectoryPathTests -{ - public class Constructor - { - // Business rules: - // - The path must not be null or empty. - // - The path contains only valid characters (a-z, A-Z, 0-9, /, ., _). - // - The path must not contain any relative path segments (..). - // - The path must not contain any consecutive slashes (//). - - [Fact] - public void Constructor_throw_ArgumentNullException_when_path_is_null() - { - // Arrange - const string path = null!; - - // Act - Action action = () => - { - var _ = new VFSDirectoryPath(path!); - }; - - // Assert - action.Should().Throw(); - } - - [Fact] - public void Constructor_throw_ArgumentException_when_path_is_empty() - { - // Arrange - const string path = ""; - - // Act - Action action = () => - { - var _ = new VFSDirectoryPath(path); - }; - - // Assert - action.Should().Throw(); - } - - [Fact] - public void Constructor_throw_ArgumentException_when_path_contains_invalid_characters() - { - // Arrange - const string path = @"invalid\path"; - - // Act - Action action = () => - { - var _ = new VFSDirectoryPath(path); - }; - - // Assert - action.Should().Throw(); - } - - [Fact] - public void Constructor_throw_ArgumentException_when_path_contains_relative_path_segments() - { - // Arrange - const string path = @"invalid\..\path"; - - // Act - Action action = () => - { - var _ = new VFSDirectoryPath(path); - }; - - // Assert - action.Should().Throw(); - } - - [Fact] - public void Constructor_throw_ArgumentException_when_path_contains_consecutive_slashes() - { - // Arrange - const string path = @"invalid//path"; - - // Act - Action action = () => - { - var _ = new VFSDirectoryPath(path); - }; - - // Assert - action.Should().Throw(); - } - - [Fact] - public void Constructor_throw_ArgumentException_when_path_is_not_a_directory_path() - { - // Arrange - const string path = @"invalid/path.txt"; - - // Act - Action action = () => - { - var _ = new VFSDirectoryPath(path); - }; - - // Assert - action.Should().Throw(); - } - - [Fact] - public void Constructor_create_instance_when_path_is_valid() - { - // Arrange - const string path = @"valid/path"; - const string expectedPath = @"vfs://valid/path"; - - // Act - var directoryPath = new VFSDirectoryPath(path); - - // Assert - directoryPath.Should().NotBeNull(); - directoryPath.Value.Should().Be(expectedPath); - } - - [Fact] - public void Constructor_create_instance_when_path_is_valid_and_contains_uppercase_characters() - { - // Arrange - const string path = @"valid/PATH"; - const string expectedPath = @"vfs://valid/PATH"; - - // Act - var directoryPath = new VFSDirectoryPath(path); - - // Assert - directoryPath.Should().NotBeNull(); - directoryPath.Value.Should().Be(expectedPath); - } - - [Fact] - public void Constructor_create_instance_when_path_is_valid_and_contains_numbers() - { - // Arrange - const string path = @"valid/123"; - const string expectedPath = @"vfs://valid/123"; - - // Act - var directoryPath = new VFSDirectoryPath(path); - - // Assert - directoryPath.Should().NotBeNull(); - directoryPath.Value.Should().Be(expectedPath); - } - - [Fact] - public void Constructor_create_instance_when_path_has_leading_slash() - { - // Arrange - const string path = @"/valid/path"; - const string expectedPath = @"vfs://valid/path"; - - // Act - var directoryPath = new VFSDirectoryPath(path); - - // Assert - directoryPath.Should().NotBeNull(); - directoryPath.Value.Should().Be(expectedPath); - } - - [Fact] - public void Constructor_create_instance_for_root_directory() - { - // Arrange - const string path = @"/"; - const string expectedPath = @"vfs://"; - - // Act - var directoryPath = new VFSDirectoryPath(path); - - // Assert - directoryPath.Should().NotBeNull(); - directoryPath.Value.Should().Be(expectedPath); - directoryPath.IsRoot.Should().BeTrue(); - directoryPath.Parent.Should().BeNull(); - } - - [Fact] - public void Constructor_create_instance_for_simple_directory() - { - // Arrange - const string path = @"/valid"; - const string expectedPath = @"vfs://valid"; - - // Act - var directoryPath = new VFSDirectoryPath(path); - - // Assert - directoryPath.Should().NotBeNull(); - directoryPath.Value.Should().Be(expectedPath); - directoryPath.IsRoot.Should().BeFalse(); - directoryPath.Parent.Should().NotBeNull(); - directoryPath.Parent!.Value.Should().Be(@"vfs://"); - } - - [Fact] - public void Constructor_create_instance_for_nested_directory() - { - // Arrange - const string path = @"/valid/path"; - const string expectedPath = @"vfs://valid/path"; - - // Act - var directoryPath = new VFSDirectoryPath(path); - - // Assert - directoryPath.Should().NotBeNull(); - directoryPath.Value.Should().Be(expectedPath); - directoryPath.IsRoot.Should().BeFalse(); - directoryPath.Parent.Should().NotBeNull(); - directoryPath.Parent!.Value.Should().Be(@"vfs://valid"); - } - } - - public class MethodToString - { - [Fact] - public void ToString_return_path() - { - // Arrange - const string path = @"test"; - const string expectedPath = @"vfs://test"; - var directoryPath = new VFSDirectoryPath(path); - - // Act - var result = directoryPath.ToString(); - - // Assert - result.Should().Be(expectedPath); - } - - [Fact] - public void ToString_return_path_with_vfs_prefix() - { - // Arrange - const string path = @"valid/path"; - var directoryPath = new VFSDirectoryPath(path); - - // Act - var result = directoryPath.ToString(); - - // Assert - result.Should().Be($"vfs://{path}"); - } - } - - public class ImplicitOperator - { - [Fact] - public void ImplicitOperator_ToString_returns_value() - { - // Arrange - var directoryPath = new VFSDirectoryPath("valid/path"); - const string expectedPath = "vfs://valid/path"; - - // Act - string result = directoryPath; - - // Assert - result.Should().Be(expectedPath); - } - } - - public class Equality - { - [Fact] - public void Equals_returns_true_when_paths_are_equal() - { - // Arrange - var directoryPath1 = new VFSDirectoryPath("valid/path"); - var directoryPath2 = new VFSDirectoryPath("valid/path"); - - // Act - var result = directoryPath1.Equals(directoryPath2); - - // Assert - result.Should().BeTrue(); - } - } +// Copyright (c) 2022, Atypical Consulting SRL +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +namespace VirtualFileSystem.UnitTests.ValueObjects; + +public class VFSDirectoryPathTests +{ + public class Constructor + { + // Business rules: + // - The path must not be null or empty. + // - The path contains only valid characters (a-z, A-Z, 0-9, /, ., _). + // - The path must not contain any relative path segments (..). + // - The path must not contain any consecutive slashes (//). + + [Fact] + public void Constructor_throw_ArgumentNullException_when_path_is_null() + { + // Arrange + const string path = null!; + + // Act + var action = () => + { + var _ = new VFSDirectoryPath(path!); + }; + + // Assert + action.Should().Throw(); + } + + [Fact] + public void Constructor_throw_ArgumentException_when_path_is_empty() + { + // Arrange + const string path = ""; + + // Act + var action = () => + { + var _ = new VFSDirectoryPath(path); + }; + + // Assert + action.Should().Throw(); + } + + [Fact] + public void Constructor_throw_ArgumentException_when_path_contains_invalid_characters() + { + // Arrange + const string path = @"invalid\path"; + + // Act + var action = () => + { + var _ = new VFSDirectoryPath(path); + }; + + // Assert + action.Should().Throw(); + } + + [Fact] + public void Constructor_throw_ArgumentException_when_path_contains_relative_path_segments() + { + // Arrange + const string path = @"invalid\..\path"; + + // Act + var action = () => + { + var _ = new VFSDirectoryPath(path); + }; + + // Assert + action.Should().Throw(); + } + + [Fact] + public void Constructor_throw_ArgumentException_when_path_contains_consecutive_slashes() + { + // Arrange + const string path = @"invalid//path"; + + // Act + var action = () => + { + var _ = new VFSDirectoryPath(path); + }; + + // Assert + action.Should().Throw(); + } + + [Fact] + public void Constructor_throw_ArgumentException_when_path_is_not_a_directory_path() + { + // Arrange + const string path = @"invalid/path.txt"; + + // Act + var action = () => + { + var _ = new VFSDirectoryPath(path); + }; + + // Assert + action.Should().Throw(); + } + + [Fact] + public void Constructor_create_instance_when_path_is_valid() + { + // Arrange + const string path = @"valid/path"; + const string expectedPath = @"vfs://valid/path"; + + // Act + var directoryPath = new VFSDirectoryPath(path); + + // Assert + directoryPath.Should().NotBeNull(); + directoryPath.Value.Should().Be(expectedPath); + } + + [Fact] + public void Constructor_create_instance_when_path_is_valid_and_contains_uppercase_characters() + { + // Arrange + const string path = @"valid/PATH"; + const string expectedPath = @"vfs://valid/PATH"; + + // Act + var directoryPath = new VFSDirectoryPath(path); + + // Assert + directoryPath.Should().NotBeNull(); + directoryPath.Value.Should().Be(expectedPath); + } + + [Fact] + public void Constructor_create_instance_when_path_is_valid_and_contains_numbers() + { + // Arrange + const string path = @"valid/123"; + const string expectedPath = @"vfs://valid/123"; + + // Act + var directoryPath = new VFSDirectoryPath(path); + + // Assert + directoryPath.Should().NotBeNull(); + directoryPath.Value.Should().Be(expectedPath); + } + + [Fact] + public void Constructor_create_instance_when_path_has_leading_slash() + { + // Arrange + const string path = @"/valid/path"; + const string expectedPath = @"vfs://valid/path"; + + // Act + var directoryPath = new VFSDirectoryPath(path); + + // Assert + directoryPath.Should().NotBeNull(); + directoryPath.Value.Should().Be(expectedPath); + } + + [Fact] + public void Constructor_create_instance_for_root_directory() + { + // Arrange + const string path = @"/"; + const string expectedPath = @"vfs://"; + + // Act + var directoryPath = new VFSDirectoryPath(path); + + // Assert + directoryPath.Should().NotBeNull(); + directoryPath.Value.Should().Be(expectedPath); + directoryPath.IsRoot.Should().BeTrue(); + directoryPath.Parent.Should().BeNull(); + } + + [Fact] + public void Constructor_create_instance_for_simple_directory() + { + // Arrange + const string path = @"/valid"; + const string expectedPath = @"vfs://valid"; + + // Act + var directoryPath = new VFSDirectoryPath(path); + + // Assert + directoryPath.Should().NotBeNull(); + directoryPath.Value.Should().Be(expectedPath); + directoryPath.IsRoot.Should().BeFalse(); + directoryPath.Parent.Should().NotBeNull(); + directoryPath.Parent!.Value.Should().Be(@"vfs://"); + } + + [Fact] + public void Constructor_create_instance_for_nested_directory() + { + // Arrange + const string path = @"/valid/path"; + const string expectedPath = @"vfs://valid/path"; + + // Act + var directoryPath = new VFSDirectoryPath(path); + + // Assert + directoryPath.Should().NotBeNull(); + directoryPath.Value.Should().Be(expectedPath); + directoryPath.IsRoot.Should().BeFalse(); + directoryPath.Parent.Should().NotBeNull(); + directoryPath.Parent!.Value.Should().Be(@"vfs://valid"); + } + } + + public class MethodToString + { + [Fact] + public void ToString_return_path() + { + // Arrange + const string path = @"test"; + const string expectedPath = @"vfs://test"; + var directoryPath = new VFSDirectoryPath(path); + + // Act + var result = directoryPath.ToString(); + + // Assert + result.Should().Be(expectedPath); + } + + [Fact] + public void ToString_return_path_with_vfs_prefix() + { + // Arrange + const string path = @"valid/path"; + var directoryPath = new VFSDirectoryPath(path); + + // Act + var result = directoryPath.ToString(); + + // Assert + result.Should().Be($"vfs://{path}"); + } + } + + public class ImplicitOperator + { + [Fact] + public void ImplicitOperator_ToString_returns_value() + { + // Arrange + var directoryPath = new VFSDirectoryPath("valid/path"); + const string expectedPath = "vfs://valid/path"; + + // Act + string result = directoryPath; + + // Assert + result.Should().Be(expectedPath); + } + } + + public class Equality + { + [Fact] + public void Equals_returns_true_when_paths_are_equal() + { + // Arrange + var directoryPath1 = new VFSDirectoryPath("valid/path"); + var directoryPath2 = new VFSDirectoryPath("valid/path"); + + // Act + var result = directoryPath1.Equals(directoryPath2); + + // Assert + result.Should().BeTrue(); + } + } } \ No newline at end of file diff --git a/tests/Atypical.VirtualFileSystem.UnitTests/ValueObjects/VFSFilePathTests.cs b/tests/Atypical.VirtualFileSystem.UnitTests/ValueObjects/VFSFilePathTests.cs index c3e67dd..fbeefab 100644 --- a/tests/Atypical.VirtualFileSystem.UnitTests/ValueObjects/VFSFilePathTests.cs +++ b/tests/Atypical.VirtualFileSystem.UnitTests/ValueObjects/VFSFilePathTests.cs @@ -1,137 +1,137 @@ -// Copyright (c) 2022, Atypical Consulting SRL -// All rights reserved. -// -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. - -namespace VirtualFileSystem.UnitTests.ValueObjects; - -public class VFSFilePathTests -{ - public class Constructor - { - // Business rules: - // - The path must not be null or empty. - // - The path contains only valid characters (a-z, A-Z, 0-9, /, ., _). - // - The path must not contain any relative path segments (..). - // - The path must not contain any consecutive slashes (//). - // - The path must not end with a slash (/). - - [Fact] - public void Constructor_throw_ArgumentNullException_when_path_is_null() - { - // Arrange - const string path = null!; - - // Act - Action action = () => - { - var _ = new VFSFilePath(path!); - }; - - // Assert - action.Should().Throw(); - } - - [Fact] - public void Constructor_throw_ArgumentException_when_path_is_empty() - { - // Arrange - const string path = ""; - - // Act - Action action = () => - { - var _ = new VFSFilePath(path); - }; - - // Assert - action.Should().Throw(); - } - - [Fact] - public void Constructor_throw_ArgumentException_when_path_contains_invalid_characters() - { - // Arrange - const string path = @"invalid\path"; - - // Act - Action action = () => - { - var _ = new VFSFilePath(path); - }; - - // Assert - action.Should().Throw(); - } - } - - public class MethodToString - { - [Fact] - public void ToString_returns_value() - { - // Arrange - var filePath = new VFSFilePath("valid/path"); - const string expectedPath = "vfs://valid/path"; - - // Act - var result = filePath.ToString(); - - // Assert - result.Should().Be(expectedPath); - } - } - - public class MethodGetHashCode - { - [Fact] - public void GetHashCode_returns_same_value_for_same_path() - { - // Arrange - var filePath1 = new VFSFilePath("valid/path"); - var filePath2 = new VFSFilePath("valid/path"); - - // Act - var hashCode1 = filePath1.GetHashCode(); - var hashCode2 = filePath2.GetHashCode(); - - // Assert - hashCode1.Should().Be(hashCode2); - } - } - - public class ImplicitOperator - { - [Fact] - public void ImplicitOperator_ToString_returns_value() - { - // Arrange - var filePath = new VFSFilePath("valid/path/file.txt"); - const string expectedPath = "vfs://valid/path/file.txt"; - - // Act - string result = filePath; - - // Assert - result.Should().Be(expectedPath); - } - } - - public class Equality - { - [Fact] - public void Equals_returns_true_when_paths_are_equal() - { - // Arrange - var filePath1 = new VFSFilePath("valid/path"); - var filePath2 = new VFSFilePath("valid/path"); - - // Act - var result = filePath1.Equals(filePath2); - - // Assert - result.Should().BeTrue(); - } - } +// Copyright (c) 2022, Atypical Consulting SRL +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +namespace VirtualFileSystem.UnitTests.ValueObjects; + +public class VFSFilePathTests +{ + public class Constructor + { + // Business rules: + // - The path must not be null or empty. + // - The path contains only valid characters (a-z, A-Z, 0-9, /, ., _). + // - The path must not contain any relative path segments (..). + // - The path must not contain any consecutive slashes (//). + // - The path must not end with a slash (/). + + [Fact] + public void Constructor_throw_ArgumentNullException_when_path_is_null() + { + // Arrange + const string path = null!; + + // Act + var action = () => + { + var _ = new VFSFilePath(path!); + }; + + // Assert + action.Should().Throw(); + } + + [Fact] + public void Constructor_throw_ArgumentException_when_path_is_empty() + { + // Arrange + const string path = ""; + + // Act + var action = () => + { + var _ = new VFSFilePath(path); + }; + + // Assert + action.Should().Throw(); + } + + [Fact] + public void Constructor_throw_ArgumentException_when_path_contains_invalid_characters() + { + // Arrange + const string path = @"invalid\path"; + + // Act + var action = () => + { + var _ = new VFSFilePath(path); + }; + + // Assert + action.Should().Throw(); + } + } + + public class MethodToString + { + [Fact] + public void ToString_returns_value() + { + // Arrange + var filePath = new VFSFilePath("valid/path"); + const string expectedPath = "vfs://valid/path"; + + // Act + var result = filePath.ToString(); + + // Assert + result.Should().Be(expectedPath); + } + } + + public class MethodGetHashCode + { + [Fact] + public void GetHashCode_returns_same_value_for_same_path() + { + // Arrange + var filePath1 = new VFSFilePath("valid/path"); + var filePath2 = new VFSFilePath("valid/path"); + + // Act + var hashCode1 = filePath1.GetHashCode(); + var hashCode2 = filePath2.GetHashCode(); + + // Assert + hashCode1.Should().Be(hashCode2); + } + } + + public class ImplicitOperator + { + [Fact] + public void ImplicitOperator_ToString_returns_value() + { + // Arrange + var filePath = new VFSFilePath("valid/path/file.txt"); + const string expectedPath = "vfs://valid/path/file.txt"; + + // Act + string result = filePath; + + // Assert + result.Should().Be(expectedPath); + } + } + + public class Equality + { + [Fact] + public void Equals_returns_true_when_paths_are_equal() + { + // Arrange + var filePath1 = new VFSFilePath("valid/path"); + var filePath2 = new VFSFilePath("valid/path"); + + // Act + var result = filePath1.Equals(filePath2); + + // Assert + result.Should().BeTrue(); + } + } } \ No newline at end of file diff --git a/tests/Atypical.VirtualFileSystem.UnitTests/ValueObjects/VFSPathTest.cs b/tests/Atypical.VirtualFileSystem.UnitTests/ValueObjects/VFSPathTest.cs index 7a565a4..5ba751a 100644 --- a/tests/Atypical.VirtualFileSystem.UnitTests/ValueObjects/VFSPathTest.cs +++ b/tests/Atypical.VirtualFileSystem.UnitTests/ValueObjects/VFSPathTest.cs @@ -1,478 +1,478 @@ -// Copyright (c) 2022, Atypical Consulting SRL -// All rights reserved. -// -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. - -namespace VirtualFileSystem.UnitTests.ValueObjects; - -public class VFSPathTest -{ - public class Constructor - { - // Business rules: - // - The path must not be null or empty. - // - The path contains only valid characters (a-z, A-Z, 0-9, /, ., _). - // - The path must not contain any relative path segments (..). - // - The path must not contain any consecutive slashes (//). - // - The path must not end with a slash (/). - - [Fact] - public void Constructor_throw_ArgumentNullException_when_path_is_null() - { - // Arrange - const string path = null!; - - // Act - Action action = () => - { - var _ = new VFSDirectoryPath(path!); - }; - - // Assert - action.Should().Throw(); - } - - [Fact] - public void Constructor_throw_ArgumentException_when_path_is_empty() - { - // Arrange - const string path = ""; - - // Act - Action action = () => - { - var _ = new VFSDirectoryPath(path); - }; - - // Assert - action.Should().Throw(); - } - - [Fact] - public void Constructor_throw_ArgumentException_when_path_contains_invalid_characters() - { - // Arrange - const string path = @"invalid\path"; - - // Act - Action action = () => - { - var _ = new VFSDirectoryPath(path); - }; - - // Assert - action.Should().Throw(); - } - - [Fact] - public void Constructor_throw_ArgumentException_when_path_contains_relative_path_segments() - { - // Arrange - const string path = @"invalid/../path"; - - // Act - Action action = () => - { - var _ = new VFSDirectoryPath(path); - }; - - // Assert - action.Should().Throw(); - } - - [Fact] - public void Constructor_throw_ArgumentException_when_path_contains_consecutive_slashes() - { - // Arrange - const string path = @"invalid//path"; - - // Act - Action action = () => - { - var _ = new VFSDirectoryPath(path); - }; - - // Assert - action.Should().Throw(); - } - - [Fact] - public void Constructor_create_instance_when_path_is_valid() - { - // Arrange - const string path = @"valid/path"; - const string expectedPath = @"vfs://valid/path"; - - // Act - var vfsPath = new VFSDirectoryPath(path); - - // Assert - vfsPath.Value.Should().NotBeNull(); - vfsPath.Value.Should().Be(expectedPath); - } - - [Fact] - public void Constructor_create_instance_when_path_is_valid_and_ends_with_slash() - { - // Arrange - const string path = @"valid/path/"; - const string expectedPath = @"vfs://valid/path"; - - // Act - var vfsPath = new VFSDirectoryPath(path); - - // Assert - vfsPath.Value.Should().NotBeNull(); - vfsPath.Value.Should().Be(expectedPath); - } - - [Fact] - public void Constructor_create_instance_when_path_is_valid_and_starts_with_slash() - { - // Arrange - const string path = @"/valid/path"; - const string expectedPath = @"vfs://valid/path"; - - // Act - var vfsPath = new VFSDirectoryPath(path); - - // Assert - vfsPath.Value.Should().NotBeNull(); - vfsPath.Value.Should().Be(expectedPath); - } - - [Fact] - public void Constructor_create_instance_when_path_is_valid_and_starts_and_ends_with_slash() - { - // Arrange - const string path = @"/valid/path/"; - const string expectedPath = @"vfs://valid/path"; - - // Act - var vfsPath = new VFSDirectoryPath(path); - - // Assert - vfsPath.Value.Should().NotBeNull(); - vfsPath.Value.Should().Be(expectedPath); - } - - [Fact] - public void PropertyParentPath_return_null_when_path_is_root() - { - // Arrange - const string path = @"/"; - - // Act - var vfsPath = new VFSDirectoryPath(path); - - // Assert - vfsPath.Parent.Should().BeNull(); - } - - [Fact] - public void Constructor_create_instance_when_path_is_valid_and_has_parent() - { - // Arrange - const string path = @"valid/path"; - const string expectedPath = @"vfs://valid/path"; - const string expectedParentPath = @"vfs://valid"; - - // Act - var vfsPath = new VFSDirectoryPath(path); - - // Assert - vfsPath.Value.Should().NotBeNull(); - vfsPath.Value.Should().Be(expectedPath); - vfsPath.Parent.Should().NotBeNull(); - vfsPath.Parent!.Value.Should().Be(expectedParentPath); - } - } - - public class PropertyName - { - [Fact] - public void PropertyName_return_name_of_directory() - { - // Arrange - const string path = @"valid/path"; - const string expectedName = @"path"; - - // Act - var vfsPath = new VFSDirectoryPath(path); - - // Assert - vfsPath.Name.Should().Be(expectedName); - } - - [Fact] - public void PropertyName_return_name_of_file() - { - // Arrange - const string path = @"valid/path/file.txt"; - const string expectedName = @"file.txt"; - - // Act - var vfsPath = new VFSFilePath(path); - - // Assert - vfsPath.Name.Should().Be(expectedName); - } - - [Fact] - public void PropertyName_return_name_of_root_directory() - { - // Arrange - const string path = @"/"; - const string expectedName = @"vfs://"; - - // Act - var vfsPath = new VFSFilePath(path); - - // Assert - vfsPath.Name.Should().Be(expectedName); - } - } - - public class PropertyDepth - { - [Fact] - public void PropertyDepth_return_0_when_path_is_root() - { - // Arrange - const string path = @"/"; - - // Act - var vfsPath = new VFSDirectoryPath(path); - - // Assert - vfsPath.Depth.Should().Be(0); - } - - [Fact] - public void PropertyDepth_return_1_with_one_directory() - { - // Arrange - const string path = @"directory"; - - // Act - var vfsPath = new VFSDirectoryPath(path); - - // Assert - vfsPath.Depth.Should().Be(1); - } - - [Fact] - public void PropertyDepth_return_2_with_two_directories() - { - // Arrange - const string path = @"directory/subdirectory"; - - // Act - var vfsPath = new VFSDirectoryPath(path); - - // Assert - vfsPath.Depth.Should().Be(2); - } - - [Fact] - public void PropertyDepth_return_3_with_three_directories() - { - // Arrange - const string path = @"directory/subdirectory/subsubdirectory"; - - // Act - var vfsPath = new VFSDirectoryPath(path); - - // Assert - vfsPath.Depth.Should().Be(3); - } - } - - public class PropertyHasParent - { - [Fact] - public void PropertyHasParent_return_false_when_path_is_root() - { - // Arrange - const string path = @"/"; - - // Act - var vfsPath = new VFSDirectoryPath(path); - - // Assert - vfsPath.HasParent.Should().BeFalse(); - } - - [Fact] - public void PropertyHasParent_return_true_when_path_has_parent() - { - // Arrange - const string path = @"directory/subdirectory"; - - // Act - var vfsPath = new VFSDirectoryPath(path); - - // Assert - vfsPath.HasParent.Should().BeTrue(); - } - } - - public class MethodGetAbsoluteParentPath - { - [Fact] - public void MethodGetAbsoluteParentPath_return_root_when_path_is_root() - { - // Arrange - const string path = @"/"; - const string expectedPath = @"vfs://"; - var vfsPath = new VFSDirectoryPath(path); - - // Act - var parent = vfsPath.GetAbsoluteParentPath(0); - - // Assert - parent.Should().NotBeNull(); - parent.Value.Should().Be(expectedPath); - } - - [Fact] - public void MethodGetAbsoluteParentPath_throw_exception_when_depth_is_negative() - { - // Arrange - const string path = @"directory/subdirectory"; - var vfsPath = new VFSDirectoryPath(path); - - // Act - Action action = () => vfsPath.GetAbsoluteParentPath(-1); - - // Assert - action.Should().Throw(); - } - - [Fact] - public void MethodGetAbsoluteParentPath_return_root_when_depth_is_zero() - { - // Arrange - const string path = @"directory/subdirectory"; - const string expectedPath = @"vfs://"; - var vfsPath = new VFSDirectoryPath(path); - - // Act - var parent = vfsPath.GetAbsoluteParentPath(0); - - // Assert - parent.Should().NotBeNull(); - parent.Value.Should().Be(expectedPath); - } - - [Fact] - public void MethodGetAbsoluteParentPath_return_parent_when_depth_is_one() - { - // Arrange - const string path = @"directory/subdirectory"; - const string expectedPath = @"vfs://directory"; - var vfsPath = new VFSDirectoryPath(path); - - // Act - var parent = vfsPath.GetAbsoluteParentPath(1); - - // Assert - parent.Should().NotBeNull(); - parent.Value.Should().Be(expectedPath); - } - - [Fact] - public void MethodGetAbsoluteParentPath_return_grandparent_when_depth_is_two() - { - // Arrange - const string path = @"directory/subdirectory/subsubdirectory"; - const string expectedPath = @"vfs://directory/subdirectory"; - var vfsPath = new VFSDirectoryPath(path); - - // Act - var parent = vfsPath.GetAbsoluteParentPath(2); - - // Assert - parent.Should().NotBeNull(); - parent.Value.Should().Be(expectedPath); - } - } - - public class MethodToString - { - [Fact] - public void ToString_returns_value() - { - // Arrange - const string path = @"valid/path"; - const string expectedPath = @"vfs://valid/path"; - - // Act - var vfsPath = new VFSDirectoryPath(path); - - // Assert - vfsPath.ToString().Should().Be(expectedPath); - } - } - - public class Equality - { - [Fact] - public void Equals_returns_true_when_paths_are_equal() - { - // Arrange - const string path = @"valid/path"; - - // Act - var vfsPath1 = new VFSDirectoryPath(path); - var vfsPath2 = new VFSDirectoryPath(path); - - // Assert - vfsPath1.Equals(vfsPath2).Should().BeTrue(); - } - - [Fact] - public void Equals_returns_true_when_paths_are_the_same() - { - // Arrange - const string path = @"valid/path"; - - // Act - var vfsPath1 = new VFSDirectoryPath(path); - - // Assert - vfsPath1.Equals(vfsPath1).Should().BeTrue(); - } - - [Fact] - public void Equals_returns_false_when_paths_are_not_equal() - { - // Arrange - const string path1 = @"valid/path"; - const string path2 = @"valid/path2"; - - // Act - var vfsPath1 = new VFSDirectoryPath(path1); - var vfsPath2 = new VFSDirectoryPath(path2); - - // Assert - vfsPath1.Equals(vfsPath2).Should().BeFalse(); - } - - [Fact] - public void Equals_returns_false_when_paths_are_not_equal_and_one_is_file() - { - // Arrange - const string path1 = @"valid/path"; - const string path2 = @"valid/path/file.txt"; - - // Act - var vfsPath1 = new VFSDirectoryPath(path1); - var vfsPath2 = new VFSFilePath(path2); - - // Assert - vfsPath1.Equals(vfsPath2).Should().BeFalse(); - } - } +// Copyright (c) 2022, Atypical Consulting SRL +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +namespace VirtualFileSystem.UnitTests.ValueObjects; + +public class VFSPathTest +{ + public class Constructor + { + // Business rules: + // - The path must not be null or empty. + // - The path contains only valid characters (a-z, A-Z, 0-9, /, ., _). + // - The path must not contain any relative path segments (..). + // - The path must not contain any consecutive slashes (//). + // - The path must not end with a slash (/). + + [Fact] + public void Constructor_throw_ArgumentNullException_when_path_is_null() + { + // Arrange + const string path = null!; + + // Act + var action = () => + { + var _ = new VFSDirectoryPath(path!); + }; + + // Assert + action.Should().Throw(); + } + + [Fact] + public void Constructor_throw_ArgumentException_when_path_is_empty() + { + // Arrange + const string path = ""; + + // Act + var action = () => + { + var _ = new VFSDirectoryPath(path); + }; + + // Assert + action.Should().Throw(); + } + + [Fact] + public void Constructor_throw_ArgumentException_when_path_contains_invalid_characters() + { + // Arrange + const string path = @"invalid\path"; + + // Act + var action = () => + { + var _ = new VFSDirectoryPath(path); + }; + + // Assert + action.Should().Throw(); + } + + [Fact] + public void Constructor_throw_ArgumentException_when_path_contains_relative_path_segments() + { + // Arrange + const string path = @"invalid/../path"; + + // Act + var action = () => + { + var _ = new VFSDirectoryPath(path); + }; + + // Assert + action.Should().Throw(); + } + + [Fact] + public void Constructor_throw_ArgumentException_when_path_contains_consecutive_slashes() + { + // Arrange + const string path = @"invalid//path"; + + // Act + var action = () => + { + var _ = new VFSDirectoryPath(path); + }; + + // Assert + action.Should().Throw(); + } + + [Fact] + public void Constructor_create_instance_when_path_is_valid() + { + // Arrange + const string path = @"valid/path"; + const string expectedPath = @"vfs://valid/path"; + + // Act + var vfsPath = new VFSDirectoryPath(path); + + // Assert + vfsPath.Value.Should().NotBeNull(); + vfsPath.Value.Should().Be(expectedPath); + } + + [Fact] + public void Constructor_create_instance_when_path_is_valid_and_ends_with_slash() + { + // Arrange + const string path = @"valid/path/"; + const string expectedPath = @"vfs://valid/path"; + + // Act + var vfsPath = new VFSDirectoryPath(path); + + // Assert + vfsPath.Value.Should().NotBeNull(); + vfsPath.Value.Should().Be(expectedPath); + } + + [Fact] + public void Constructor_create_instance_when_path_is_valid_and_starts_with_slash() + { + // Arrange + const string path = @"/valid/path"; + const string expectedPath = @"vfs://valid/path"; + + // Act + var vfsPath = new VFSDirectoryPath(path); + + // Assert + vfsPath.Value.Should().NotBeNull(); + vfsPath.Value.Should().Be(expectedPath); + } + + [Fact] + public void Constructor_create_instance_when_path_is_valid_and_starts_and_ends_with_slash() + { + // Arrange + const string path = @"/valid/path/"; + const string expectedPath = @"vfs://valid/path"; + + // Act + var vfsPath = new VFSDirectoryPath(path); + + // Assert + vfsPath.Value.Should().NotBeNull(); + vfsPath.Value.Should().Be(expectedPath); + } + + [Fact] + public void PropertyParentPath_return_null_when_path_is_root() + { + // Arrange + const string path = @"/"; + + // Act + var vfsPath = new VFSDirectoryPath(path); + + // Assert + vfsPath.Parent.Should().BeNull(); + } + + [Fact] + public void Constructor_create_instance_when_path_is_valid_and_has_parent() + { + // Arrange + const string path = @"valid/path"; + const string expectedPath = @"vfs://valid/path"; + const string expectedParentPath = @"vfs://valid"; + + // Act + var vfsPath = new VFSDirectoryPath(path); + + // Assert + vfsPath.Value.Should().NotBeNull(); + vfsPath.Value.Should().Be(expectedPath); + vfsPath.Parent.Should().NotBeNull(); + vfsPath.Parent!.Value.Should().Be(expectedParentPath); + } + } + + public class PropertyName + { + [Fact] + public void PropertyName_return_name_of_directory() + { + // Arrange + const string path = @"valid/path"; + const string expectedName = @"path"; + + // Act + var vfsPath = new VFSDirectoryPath(path); + + // Assert + vfsPath.Name.Should().Be(expectedName); + } + + [Fact] + public void PropertyName_return_name_of_file() + { + // Arrange + const string path = @"valid/path/file.txt"; + const string expectedName = @"file.txt"; + + // Act + var vfsPath = new VFSFilePath(path); + + // Assert + vfsPath.Name.Should().Be(expectedName); + } + + [Fact] + public void PropertyName_return_name_of_root_directory() + { + // Arrange + const string path = @"/"; + const string expectedName = @"vfs://"; + + // Act + var vfsPath = new VFSFilePath(path); + + // Assert + vfsPath.Name.Should().Be(expectedName); + } + } + + public class PropertyDepth + { + [Fact] + public void PropertyDepth_return_0_when_path_is_root() + { + // Arrange + const string path = @"/"; + + // Act + var vfsPath = new VFSDirectoryPath(path); + + // Assert + vfsPath.Depth.Should().Be(0); + } + + [Fact] + public void PropertyDepth_return_1_with_one_directory() + { + // Arrange + const string path = @"directory"; + + // Act + var vfsPath = new VFSDirectoryPath(path); + + // Assert + vfsPath.Depth.Should().Be(1); + } + + [Fact] + public void PropertyDepth_return_2_with_two_directories() + { + // Arrange + const string path = @"directory/subdirectory"; + + // Act + var vfsPath = new VFSDirectoryPath(path); + + // Assert + vfsPath.Depth.Should().Be(2); + } + + [Fact] + public void PropertyDepth_return_3_with_three_directories() + { + // Arrange + const string path = @"directory/subdirectory/subsubdirectory"; + + // Act + var vfsPath = new VFSDirectoryPath(path); + + // Assert + vfsPath.Depth.Should().Be(3); + } + } + + public class PropertyHasParent + { + [Fact] + public void PropertyHasParent_return_false_when_path_is_root() + { + // Arrange + const string path = @"/"; + + // Act + var vfsPath = new VFSDirectoryPath(path); + + // Assert + vfsPath.HasParent.Should().BeFalse(); + } + + [Fact] + public void PropertyHasParent_return_true_when_path_has_parent() + { + // Arrange + const string path = @"directory/subdirectory"; + + // Act + var vfsPath = new VFSDirectoryPath(path); + + // Assert + vfsPath.HasParent.Should().BeTrue(); + } + } + + public class MethodGetAbsoluteParentPath + { + [Fact] + public void MethodGetAbsoluteParentPath_return_root_when_path_is_root() + { + // Arrange + const string path = @"/"; + const string expectedPath = @"vfs://"; + var vfsPath = new VFSDirectoryPath(path); + + // Act + var parent = vfsPath.GetAbsoluteParentPath(0); + + // Assert + parent.Should().NotBeNull(); + parent.Value.Should().Be(expectedPath); + } + + [Fact] + public void MethodGetAbsoluteParentPath_throw_exception_when_depth_is_negative() + { + // Arrange + const string path = @"directory/subdirectory"; + var vfsPath = new VFSDirectoryPath(path); + + // Act + Action action = () => vfsPath.GetAbsoluteParentPath(-1); + + // Assert + action.Should().Throw(); + } + + [Fact] + public void MethodGetAbsoluteParentPath_return_root_when_depth_is_zero() + { + // Arrange + const string path = @"directory/subdirectory"; + const string expectedPath = @"vfs://"; + var vfsPath = new VFSDirectoryPath(path); + + // Act + var parent = vfsPath.GetAbsoluteParentPath(0); + + // Assert + parent.Should().NotBeNull(); + parent.Value.Should().Be(expectedPath); + } + + [Fact] + public void MethodGetAbsoluteParentPath_return_parent_when_depth_is_one() + { + // Arrange + const string path = @"directory/subdirectory"; + const string expectedPath = @"vfs://directory"; + var vfsPath = new VFSDirectoryPath(path); + + // Act + var parent = vfsPath.GetAbsoluteParentPath(1); + + // Assert + parent.Should().NotBeNull(); + parent.Value.Should().Be(expectedPath); + } + + [Fact] + public void MethodGetAbsoluteParentPath_return_grandparent_when_depth_is_two() + { + // Arrange + const string path = @"directory/subdirectory/subsubdirectory"; + const string expectedPath = @"vfs://directory/subdirectory"; + var vfsPath = new VFSDirectoryPath(path); + + // Act + var parent = vfsPath.GetAbsoluteParentPath(2); + + // Assert + parent.Should().NotBeNull(); + parent.Value.Should().Be(expectedPath); + } + } + + public class MethodToString + { + [Fact] + public void ToString_returns_value() + { + // Arrange + const string path = @"valid/path"; + const string expectedPath = @"vfs://valid/path"; + + // Act + var vfsPath = new VFSDirectoryPath(path); + + // Assert + vfsPath.ToString().Should().Be(expectedPath); + } + } + + public class Equality + { + [Fact] + public void Equals_returns_true_when_paths_are_equal() + { + // Arrange + const string path = @"valid/path"; + + // Act + var vfsPath1 = new VFSDirectoryPath(path); + var vfsPath2 = new VFSDirectoryPath(path); + + // Assert + vfsPath1.Equals(vfsPath2).Should().BeTrue(); + } + + [Fact] + public void Equals_returns_true_when_paths_are_the_same() + { + // Arrange + const string path = @"valid/path"; + + // Act + var vfsPath1 = new VFSDirectoryPath(path); + + // Assert + vfsPath1.Equals(vfsPath1).Should().BeTrue(); + } + + [Fact] + public void Equals_returns_false_when_paths_are_not_equal() + { + // Arrange + const string path1 = @"valid/path"; + const string path2 = @"valid/path2"; + + // Act + var vfsPath1 = new VFSDirectoryPath(path1); + var vfsPath2 = new VFSDirectoryPath(path2); + + // Assert + vfsPath1.Equals(vfsPath2).Should().BeFalse(); + } + + [Fact] + public void Equals_returns_false_when_paths_are_not_equal_and_one_is_file() + { + // Arrange + const string path1 = @"valid/path"; + const string path2 = @"valid/path/file.txt"; + + // Act + var vfsPath1 = new VFSDirectoryPath(path1); + var vfsPath2 = new VFSFilePath(path2); + + // Assert + vfsPath1.Equals(vfsPath2).Should().BeFalse(); + } + } } \ No newline at end of file diff --git a/tests/Atypical.VirtualFileSystem.UnitTests/ValueObjects/VFSRootPathTests.cs b/tests/Atypical.VirtualFileSystem.UnitTests/ValueObjects/VFSRootPathTests.cs index da4520f..2e42aa4 100644 --- a/tests/Atypical.VirtualFileSystem.UnitTests/ValueObjects/VFSRootPathTests.cs +++ b/tests/Atypical.VirtualFileSystem.UnitTests/ValueObjects/VFSRootPathTests.cs @@ -1,79 +1,79 @@ -// Copyright (c) 2022, Atypical Consulting SRL -// All rights reserved. -// -// This source code is licensed under the BSD-style license found in the -// LICENSE file in the root directory of this source tree. - -namespace VirtualFileSystem.UnitTests.ValueObjects; - -public class VFSRootPathTests -{ - public class Constructor - { - [Fact] - public void Constructor_create_instance() - { - // Arrange - const string expectedPath = @"vfs://"; - - // Act - var directoryPath = new VFSRootPath(); - - // Assert - directoryPath.Should().NotBeNull(); - directoryPath.Value.Should().Be(expectedPath); - directoryPath.IsRoot.Should().BeTrue(); - directoryPath.Parent.Should().BeNull(); - } - } - - public class MethodToString - { - [Fact] - public void ToString_returns_value() - { - // Arrange - const string expectedPath = @"vfs://"; - - // Act - var directoryPath = new VFSRootPath(); - - // Assert - directoryPath.ToString().Should().Be(expectedPath); - } - } - - public class ImplicitOperator - { - [Fact] - public void ImplicitOperator_ToString_returns_value() - { - // Arrange - var rootPath = new VFSRootPath(); - const string expectedPath = "vfs://"; - - // Act - string result = rootPath; - - // Assert - result.Should().Be(expectedPath); - } - } - - public class Equality - { - [Fact] - public void Equals_returns_true_when_paths_are_equal() - { - // Arrange - var rootPath1 = new VFSRootPath(); - var rootPath2 = new VFSRootPath(); - - // Act - var result = rootPath1.Equals(rootPath2); - - // Assert - result.Should().BeTrue(); - } - } +// Copyright (c) 2022, Atypical Consulting SRL +// All rights reserved. +// +// This source code is licensed under the BSD-style license found in the +// LICENSE file in the root directory of this source tree. + +namespace VirtualFileSystem.UnitTests.ValueObjects; + +public class VFSRootPathTests +{ + public class Constructor + { + [Fact] + public void Constructor_create_instance() + { + // Arrange + const string expectedPath = @"vfs://"; + + // Act + var directoryPath = new VFSRootPath(); + + // Assert + directoryPath.Should().NotBeNull(); + directoryPath.Value.Should().Be(expectedPath); + directoryPath.IsRoot.Should().BeTrue(); + directoryPath.Parent.Should().BeNull(); + } + } + + public class MethodToString + { + [Fact] + public void ToString_returns_value() + { + // Arrange + const string expectedPath = @"vfs://"; + + // Act + var directoryPath = new VFSRootPath(); + + // Assert + directoryPath.ToString().Should().Be(expectedPath); + } + } + + public class ImplicitOperator + { + [Fact] + public void ImplicitOperator_ToString_returns_value() + { + // Arrange + var rootPath = new VFSRootPath(); + const string expectedPath = "vfs://"; + + // Act + string result = rootPath; + + // Assert + result.Should().Be(expectedPath); + } + } + + public class Equality + { + [Fact] + public void Equals_returns_true_when_paths_are_equal() + { + // Arrange + var rootPath1 = new VFSRootPath(); + var rootPath2 = new VFSRootPath(); + + // Act + var result = rootPath1.Equals(rootPath2); + + // Assert + result.Should().BeTrue(); + } + } } \ No newline at end of file