From 0edd9217500ca2439cd8a0c0d53dc3d93a492e02 Mon Sep 17 00:00:00 2001 From: Robert Koeninger Date: Tue, 4 Dec 2018 01:42:54 -0500 Subject: [PATCH 01/17] Added StringComparer to MockFileSystem, used in Mock* classes --- .../MockDirectoryTests.cs | 17 ++---- .../MockUnixSupportTests.cs | 8 ++- .../WindowsOnlyAttribute.cs | 2 +- .../WindowsSpecifics.cs | 2 + .../IMockFileDataAccessor.cs | 4 ++ .../MockDirectory.cs | 56 +++++++++---------- .../MockDirectoryInfo.cs | 2 +- .../MockDriveInfo.cs | 34 +++++------ .../MockDriveInfoFactory.cs | 19 +++++-- .../MockFile.cs | 22 ++++---- .../MockFileInfo.cs | 2 +- .../MockFileStream.cs | 6 +- .../MockFileSystem.cs | 41 +++++++------- .../MockPath.cs | 42 ++------------ .../MockUnixSupport.cs | 27 ++------- .../PathVerifier.cs | 44 +++++++++++++-- .../StringExtensions.cs | 6 +- 17 files changed, 163 insertions(+), 171 deletions(-) diff --git a/System.IO.Abstractions.TestingHelpers.Tests/MockDirectoryTests.cs b/System.IO.Abstractions.TestingHelpers.Tests/MockDirectoryTests.cs index d7f48234c..ed20680af 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/MockDirectoryTests.cs +++ b/System.IO.Abstractions.TestingHelpers.Tests/MockDirectoryTests.cs @@ -547,10 +547,10 @@ public void MockDirectory_CreateDirectory_ShouldWorkWithUNCPath() var fileSystem = new MockFileSystem(); // Act - fileSystem.Directory.CreateDirectory(XFS.Path(@"\\server\share\path\to\create", () => false)); + fileSystem.Directory.CreateDirectory(@"\\server\share\path\to\create"); // Assert - Assert.IsTrue(fileSystem.Directory.Exists(XFS.Path(@"\\server\share\path\to\create\", () => false))); + Assert.IsTrue(fileSystem.Directory.Exists(@"\\server\share\path\to\create\")); } [Test] @@ -561,7 +561,7 @@ public void MockDirectory_CreateDirectory_ShouldFailIfTryingToCreateUNCPathOnlyS var fileSystem = new MockFileSystem(); // Act - var ex = Assert.Throws(() => fileSystem.Directory.CreateDirectory(XFS.Path(@"\\server", () => false))); + var ex = Assert.Throws(() => fileSystem.Directory.CreateDirectory(@"\\server")); // Assert StringAssert.StartsWith("The UNC path should be of the form \\\\server\\share.", ex.Message); @@ -576,10 +576,10 @@ public void MockDirectory_CreateDirectory_ShouldSucceedIfTryingToCreateUNCPathSh var fileSystem = new MockFileSystem(); // Act - fileSystem.Directory.CreateDirectory(XFS.Path(@"\\server\share", () => false)); + fileSystem.Directory.CreateDirectory(@"\\server\share"); // Assert - Assert.IsTrue(fileSystem.Directory.Exists(XFS.Path(@"\\server\share\", () => false))); + Assert.IsTrue(fileSystem.Directory.Exists(@"\\server\share\")); } [Test] @@ -1250,14 +1250,9 @@ public void MockDirectory_GetParent_ShouldReturnADirectoryInfoIfPathDoesNotExist } [Test] + [WindowsOnly(WindowsSpecifics.EmptyInvalidPathChars)] public void MockDirectory_GetParent_ShouldThrowArgumentExceptionIfPathHasIllegalCharacters() { - if (XFS.IsUnixPlatform()) - { - Assert.Pass("Path.GetInvalidChars() does not return anything on Mono"); - return; - } - // Arrange var fileSystem = new MockFileSystem(); diff --git a/System.IO.Abstractions.TestingHelpers.Tests/MockUnixSupportTests.cs b/System.IO.Abstractions.TestingHelpers.Tests/MockUnixSupportTests.cs index 30dd6c0de..2641e431b 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/MockUnixSupportTests.cs +++ b/System.IO.Abstractions.TestingHelpers.Tests/MockUnixSupportTests.cs @@ -2,19 +2,23 @@ namespace System.IO.Abstractions.TestingHelpers.Tests { + using XFS = MockUnixSupport; + [TestFixture] public class MockUnixSupportTests { [Test] + [UnixOnly(UnixSpecifics.SlashRoot)] public void Should_Convert_Backslashes_To_Slashes_On_Unix() { - Assert.AreEqual("/test/", MockUnixSupport.Path(@"\test\", () => true)); + Assert.AreEqual("/test/", XFS.Path(@"\test\")); } [Test] + [UnixOnly(UnixSpecifics.SlashRoot)] public void Should_Remove_Drive_Letter_On_Unix() { - Assert.AreEqual("/test/", MockUnixSupport.Path(@"c:\test\", () => true)); + Assert.AreEqual("/test/", XFS.Path(@"c:\test\")); } } } diff --git a/System.IO.Abstractions.TestingHelpers.Tests/WindowsOnlyAttribute.cs b/System.IO.Abstractions.TestingHelpers.Tests/WindowsOnlyAttribute.cs index d14431f3f..49be416ed 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/WindowsOnlyAttribute.cs +++ b/System.IO.Abstractions.TestingHelpers.Tests/WindowsOnlyAttribute.cs @@ -16,7 +16,7 @@ public WindowsOnlyAttribute(string reason) public void BeforeTest(ITest test) { - if (MockUnixSupport.IsUnixPlatform()) + if (!MockUnixSupport.IsWindowsPlatform()) { Assert.Inconclusive(reason); } diff --git a/System.IO.Abstractions.TestingHelpers.Tests/WindowsSpecifics.cs b/System.IO.Abstractions.TestingHelpers.Tests/WindowsSpecifics.cs index a44dea526..15ae65c12 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/WindowsSpecifics.cs +++ b/System.IO.Abstractions.TestingHelpers.Tests/WindowsSpecifics.cs @@ -9,5 +9,7 @@ internal static class WindowsSpecifics public const string UNCPaths = "UNC paths are a Windows-only concept"; public const string StrictPathRules = "Windows has stricter path rules than other platforms"; + + public const string EmptyInvalidPathChars = "Path.GetInvalidPathChars() doesn't return anything on Mono"; } } diff --git a/System.IO.Abstractions.TestingHelpers/IMockFileDataAccessor.cs b/System.IO.Abstractions.TestingHelpers/IMockFileDataAccessor.cs index fefb8ede4..8ba49c4ca 100644 --- a/System.IO.Abstractions.TestingHelpers/IMockFileDataAccessor.cs +++ b/System.IO.Abstractions.TestingHelpers/IMockFileDataAccessor.cs @@ -54,6 +54,10 @@ public interface IMockFileDataAccessor /// IEnumerable AllDirectories { get; } + bool CaseSensitive { get; } + StringComparer Comparer { get; } + StringComparison Comparison { get; } + FileBase File { get; } DirectoryBase Directory { get; } IFileInfoFactory FileInfo {get; } diff --git a/System.IO.Abstractions.TestingHelpers/MockDirectory.cs b/System.IO.Abstractions.TestingHelpers/MockDirectory.cs index 250b9d541..35c2657a2 100644 --- a/System.IO.Abstractions.TestingHelpers/MockDirectory.cs +++ b/System.IO.Abstractions.TestingHelpers/MockDirectory.cs @@ -11,17 +11,13 @@ namespace System.IO.Abstractions.TestingHelpers [Serializable] public class MockDirectory : DirectoryBase { - private readonly FileBase fileBase; - private readonly IMockFileDataAccessor mockFileDataAccessor; - private string currentDirectory; - public MockDirectory(IMockFileDataAccessor mockFileDataAccessor, FileBase fileBase, string currentDirectory) : base(mockFileDataAccessor?.FileSystem) + public MockDirectory(IMockFileDataAccessor mockFileDataAccessor, string currentDirectory) : base(mockFileDataAccessor?.FileSystem) { this.currentDirectory = currentDirectory; this.mockFileDataAccessor = mockFileDataAccessor ?? throw new ArgumentNullException(nameof(mockFileDataAccessor)); - this.fileBase = fileBase; } public override DirectoryInfoBase CreateDirectory(string path) @@ -35,6 +31,7 @@ public override DirectoryInfoBase CreateDirectory(string path, DirectorySecurity return CreateDirectoryInternal(path, directorySecurity); } #endif + private DirectoryInfoBase CreateDirectoryInternal(string path, DirectorySecurity directorySecurity) { if (path == null) @@ -74,7 +71,7 @@ public override void Delete(string path, bool recursive) path = mockFileDataAccessor.Path.GetFullPath(path).TrimSlashes(); var affectedPaths = mockFileDataAccessor .AllPaths - .Where(p => p.StartsWith(path, StringComparison.OrdinalIgnoreCase)) + .Where(p => p.StartsWith(path, mockFileDataAccessor.Comparison)) .ToList(); if (!affectedPaths.Any()) @@ -99,7 +96,7 @@ public override bool Exists(string path) { path = path.TrimSlashes(); path = mockFileDataAccessor.Path.GetFullPath(path); - return mockFileDataAccessor.AllDirectories.Any(p => p.Equals(path, StringComparison.OrdinalIgnoreCase)); + return mockFileDataAccessor.AllDirectories.Any(p => p.Equals(path, mockFileDataAccessor.Comparison)); } catch (Exception) { @@ -128,12 +125,12 @@ public override DirectorySecurity GetAccessControl(string path, AccessControlSec public override DateTime GetCreationTime(string path) { - return fileBase.GetCreationTime(path); + return mockFileDataAccessor.File.GetCreationTime(path); } public override DateTime GetCreationTimeUtc(string path) { - return fileBase.GetCreationTimeUtc(path); + return mockFileDataAccessor.File.GetCreationTimeUtc(path); } public override string GetCurrentDirectory() @@ -278,22 +275,22 @@ public override string[] GetFileSystemEntries(string path, string searchPattern) public override DateTime GetLastAccessTime(string path) { - return fileBase.GetLastAccessTime(path); + return mockFileDataAccessor.File.GetLastAccessTime(path); } public override DateTime GetLastAccessTimeUtc(string path) { - return fileBase.GetLastAccessTimeUtc(path); + return mockFileDataAccessor.File.GetLastAccessTimeUtc(path); } public override DateTime GetLastWriteTime(string path) { - return fileBase.GetLastWriteTime(path); + return mockFileDataAccessor.File.GetLastWriteTime(path); } public override DateTime GetLastWriteTimeUtc(string path) { - return fileBase.GetLastWriteTimeUtc(path); + return mockFileDataAccessor.File.GetLastWriteTimeUtc(path); } #if NET40 @@ -302,7 +299,7 @@ public override string[] GetLogicalDrives() return mockFileDataAccessor .AllDirectories .Select(d => new MockDirectoryInfo(mockFileDataAccessor, d).Root.FullName) - .Select(r => r.ToLowerInvariant()) + .Select(r => mockFileDataAccessor.CaseSensitive ? r : r.ToLowerInvariant()) .Distinct() .ToArray(); } @@ -320,7 +317,7 @@ public override DirectoryInfoBase GetParent(string path) throw new ArgumentException(StringResources.Manager.GetString("PATH_CANNOT_BE_THE_EMPTY_STRING_OR_ALL_WHITESPACE"), "path"); } - if (MockPath.HasIllegalCharacters(path, false)) + if (mockFileDataAccessor.PathVerifier.HasIllegalCharacters(path, false)) { throw new ArgumentException("Path contains invalid path characters.", "path"); } @@ -331,7 +328,7 @@ public override DirectoryInfoBase GetParent(string path) var lastIndex = 0; if (absolutePath != sepAsString) { - var startIndex = absolutePath.EndsWith(sepAsString, StringComparison.OrdinalIgnoreCase) ? absolutePath.Length - 1 : absolutePath.Length; + var startIndex = absolutePath.EndsWith(sepAsString, mockFileDataAccessor.Comparison) ? absolutePath.Length - 1 : absolutePath.Length; lastIndex = absolutePath.LastIndexOf(mockFileDataAccessor.Path.DirectorySeparatorChar, startIndex - 1); if (lastIndex < 0) { @@ -345,8 +342,7 @@ public override DirectoryInfoBase GetParent(string path) return null; } - var parent = new MockDirectoryInfo(mockFileDataAccessor, parentPath); - return parent; + return new MockDirectoryInfo(mockFileDataAccessor, parentPath); } public override void Move(string sourceDirName, string destDirName) @@ -354,14 +350,14 @@ public override void Move(string sourceDirName, string destDirName) var fullSourcePath = mockFileDataAccessor.Path.GetFullPath(sourceDirName).TrimSlashes(); var fullDestPath = mockFileDataAccessor.Path.GetFullPath(destDirName).TrimSlashes(); - if (string.Equals(fullSourcePath, fullDestPath, StringComparison.OrdinalIgnoreCase)) + if (string.Equals(fullSourcePath, fullDestPath, mockFileDataAccessor.Comparison)) { throw new IOException("Source and destination path must be different."); } var sourceRoot = mockFileDataAccessor.Path.GetPathRoot(fullSourcePath); var destinationRoot = mockFileDataAccessor.Path.GetPathRoot(fullDestPath); - if (!string.Equals(sourceRoot, destinationRoot, StringComparison.OrdinalIgnoreCase)) + if (!string.Equals(sourceRoot, destinationRoot, mockFileDataAccessor.Comparison)) { throw new IOException("Source and destination path must have identical roots. Move will not work across volumes."); } @@ -395,12 +391,12 @@ public override void SetAccessControl(string path, DirectorySecurity directorySe public override void SetCreationTime(string path, DateTime creationTime) { - fileBase.SetCreationTime(path, creationTime); + mockFileDataAccessor.File.SetCreationTime(path, creationTime); } public override void SetCreationTimeUtc(string path, DateTime creationTimeUtc) { - fileBase.SetCreationTimeUtc(path, creationTimeUtc); + mockFileDataAccessor.File.SetCreationTimeUtc(path, creationTimeUtc); } public override void SetCurrentDirectory(string path) @@ -410,22 +406,22 @@ public override void SetCurrentDirectory(string path) public override void SetLastAccessTime(string path, DateTime lastAccessTime) { - fileBase.SetLastAccessTime(path, lastAccessTime); + mockFileDataAccessor.File.SetLastAccessTime(path, lastAccessTime); } public override void SetLastAccessTimeUtc(string path, DateTime lastAccessTimeUtc) { - fileBase.SetLastAccessTimeUtc(path, lastAccessTimeUtc); + mockFileDataAccessor.File.SetLastAccessTimeUtc(path, lastAccessTimeUtc); } public override void SetLastWriteTime(string path, DateTime lastWriteTime) { - fileBase.SetLastWriteTime(path, lastWriteTime); + mockFileDataAccessor.File.SetLastWriteTime(path, lastWriteTime); } public override void SetLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc) { - fileBase.SetLastWriteTimeUtc(path, lastWriteTimeUtc); + mockFileDataAccessor.File.SetLastWriteTimeUtc(path, lastWriteTimeUtc); } public override IEnumerable EnumerateDirectories(string path) @@ -455,7 +451,7 @@ public override IEnumerable EnumerateDirectories(string path, string sea } var dirs = GetFilesInternal(mockFileDataAccessor.AllDirectories, path, searchPattern, searchOption); - return dirs.Where(p => string.Compare(p, path, StringComparison.OrdinalIgnoreCase) != 0); + return dirs.Where(p => mockFileDataAccessor.Comparer.Compare(p, path) != 0); } public override IEnumerable EnumerateFiles(string path) @@ -501,7 +497,7 @@ private string EnsureAbsolutePath(string path) : Path.Combine(GetCurrentDirectory(), path); } - static void CheckSearchPattern(string searchPattern) + private void CheckSearchPattern(string searchPattern) { if (searchPattern == null) { @@ -511,13 +507,13 @@ static void CheckSearchPattern(string searchPattern) const string TWO_DOTS = ".."; Func createException = () => new ArgumentException(@"Search pattern cannot contain "".."" to move up directories and can be contained only internally in file/directory names, as in ""a..b"".", searchPattern); - if (searchPattern.EndsWith(TWO_DOTS, StringComparison.OrdinalIgnoreCase)) + if (searchPattern.EndsWith(TWO_DOTS, mockFileDataAccessor.Comparison)) { throw createException(); } int position; - if ((position = searchPattern.IndexOf(TWO_DOTS, StringComparison.OrdinalIgnoreCase)) >= 0) + if ((position = searchPattern.IndexOf(TWO_DOTS, mockFileDataAccessor.Comparison)) >= 0) { var characterAfterTwoDots = searchPattern[position + 2]; if (characterAfterTwoDots == Path.DirectorySeparatorChar || characterAfterTwoDots == Path.AltDirectorySeparatorChar) diff --git a/System.IO.Abstractions.TestingHelpers/MockDirectoryInfo.cs b/System.IO.Abstractions.TestingHelpers/MockDirectoryInfo.cs index 90f2ce9a1..36fe6ceca 100644 --- a/System.IO.Abstractions.TestingHelpers/MockDirectoryInfo.cs +++ b/System.IO.Abstractions.TestingHelpers/MockDirectoryInfo.cs @@ -73,7 +73,7 @@ public override string FullName get { var root = mockFileDataAccessor.Path.GetPathRoot(directoryPath); - if (string.Equals(directoryPath, root, StringComparison.OrdinalIgnoreCase)) + if (string.Equals(directoryPath, root, mockFileDataAccessor.Comparison)) { // drives have the trailing slash return directoryPath; diff --git a/System.IO.Abstractions.TestingHelpers/MockDriveInfo.cs b/System.IO.Abstractions.TestingHelpers/MockDriveInfo.cs index 918c147f0..d2cf23e26 100644 --- a/System.IO.Abstractions.TestingHelpers/MockDriveInfo.cs +++ b/System.IO.Abstractions.TestingHelpers/MockDriveInfo.cs @@ -7,10 +7,7 @@ public class MockDriveInfo : DriveInfoBase public MockDriveInfo(IMockFileDataAccessor mockFileDataAccessor, string name) : base(mockFileDataAccessor?.FileSystem) { - if (mockFileDataAccessor == null) - { - throw new ArgumentNullException(nameof(mockFileDataAccessor)); - } + this.mockFileDataAccessor = mockFileDataAccessor ?? throw new ArgumentNullException(nameof(mockFileDataAccessor)); if (name == null) { @@ -18,36 +15,34 @@ public MockDriveInfo(IMockFileDataAccessor mockFileDataAccessor, string name) : } const string DRIVE_SEPARATOR = @":\"; - if (name.Length == 1) - { - name = char.ToUpperInvariant(name[0]) + DRIVE_SEPARATOR; - } - else if (name.Length == 2 && name[1] == ':') - { - name = char.ToUpperInvariant(name[0]) + DRIVE_SEPARATOR; - } - else if (name.Length == 3 && name.EndsWith(DRIVE_SEPARATOR, StringComparison.Ordinal)) + + if (name.Length == 1 + || (name.Length == 2 && name[1] == ':') + || (name.Length == 3 && name.EndsWith(DRIVE_SEPARATOR, mockFileDataAccessor.Comparison))) { - name = char.ToUpperInvariant(name[0]) + DRIVE_SEPARATOR; + name = ToUpper(name[0]) + DRIVE_SEPARATOR; } else { - MockPath.CheckInvalidPathChars(name); + mockFileDataAccessor.PathVerifier.CheckInvalidPathChars(name); name = mockFileDataAccessor.Path.GetPathRoot(name); - if (string.IsNullOrEmpty(name) || name.StartsWith(@"\\", StringComparison.Ordinal)) + if (string.IsNullOrEmpty(name) || name.StartsWith(@"\\", mockFileDataAccessor.Comparison)) { throw new ArgumentException( @"Object must be a root directory (""C:\"") or a drive letter (""C"")."); } } - this.mockFileDataAccessor = mockFileDataAccessor; - Name = name; IsReady = true; } + private char ToUpper(char c) + { + return mockFileDataAccessor.CaseSensitive ? c : char.ToUpperInvariant(c); + } + public new long AvailableFreeSpace { get; set; } public new string DriveFormat { get; set; } public new DriveType DriveType { get; set; } @@ -58,8 +53,7 @@ public override DirectoryInfoBase RootDirectory { get { - var directory = mockFileDataAccessor.DirectoryInfo.FromDirectoryName(Name); - return directory; + return mockFileDataAccessor.DirectoryInfo.FromDirectoryName(Name); } } diff --git a/System.IO.Abstractions.TestingHelpers/MockDriveInfoFactory.cs b/System.IO.Abstractions.TestingHelpers/MockDriveInfoFactory.cs index 00a8212dc..1f384632a 100644 --- a/System.IO.Abstractions.TestingHelpers/MockDriveInfoFactory.cs +++ b/System.IO.Abstractions.TestingHelpers/MockDriveInfoFactory.cs @@ -14,7 +14,7 @@ public MockDriveInfoFactory(IMockFileDataAccessor mockFileSystem) public DriveInfoBase[] GetDrives() { - var driveLetters = new HashSet(new DriveEqualityComparer()); + var driveLetters = new HashSet(new DriveEqualityComparer(mockFileSystem)); foreach (var path in mockFileSystem.AllPaths) { var pathRoot = mockFileSystem.Path.GetPathRoot(path); @@ -47,12 +47,12 @@ public DriveInfoBase FromDriveName(string driveName) private string NormalizeDriveName(string driveName) { - if (driveName.Length == 3 && driveName.EndsWith(@":\", StringComparison.OrdinalIgnoreCase)) + if (driveName.Length == 3 && driveName.EndsWith(@":\", mockFileSystem.Comparison)) { - return char.ToUpperInvariant(driveName[0]) + @":\"; + return (mockFileSystem.CaseSensitive ? driveName[0] : char.ToUpperInvariant(driveName[0])) + @":\"; } - if (driveName.StartsWith(@"\\", StringComparison.OrdinalIgnoreCase)) + if (driveName.StartsWith(@"\\", mockFileSystem.Comparison)) { return null; } @@ -62,6 +62,13 @@ private string NormalizeDriveName(string driveName) private class DriveEqualityComparer : IEqualityComparer { + private readonly IMockFileDataAccessor mockFileSystem; + + public DriveEqualityComparer(IMockFileDataAccessor mockFileSystem) + { + this.mockFileSystem = mockFileSystem ?? throw new ArgumentNullException(nameof(mockFileSystem)); + } + public bool Equals(string x, string y) { if (ReferenceEquals(x, y)) @@ -81,7 +88,7 @@ public bool Equals(string x, string y) if (x[1] == ':' && y[1] == ':') { - return char.ToUpperInvariant(x[0]) == char.ToUpperInvariant(y[0]); + return mockFileSystem.Comparer.Compare(x.Substring(0, 1), y.Substring(0, 1)) == 0; } return false; @@ -89,7 +96,7 @@ public bool Equals(string x, string y) public int GetHashCode(string obj) { - return obj.ToUpperInvariant().GetHashCode(); + return (mockFileSystem.CaseSensitive ? obj : obj.ToUpperInvariant()).GetHashCode(); } } } diff --git a/System.IO.Abstractions.TestingHelpers/MockFile.cs b/System.IO.Abstractions.TestingHelpers/MockFile.cs index 7cf1ebafc..854fc3e4e 100644 --- a/System.IO.Abstractions.TestingHelpers/MockFile.cs +++ b/System.IO.Abstractions.TestingHelpers/MockFile.cs @@ -10,12 +10,10 @@ namespace System.IO.Abstractions.TestingHelpers public class MockFile : FileBase { private readonly IMockFileDataAccessor mockFileDataAccessor; - private readonly MockPath mockPath; public MockFile(IMockFileDataAccessor mockFileDataAccessor) : base(mockFileDataAccessor?.FileSystem) { this.mockFileDataAccessor = mockFileDataAccessor ?? throw new ArgumentNullException(nameof(mockFileDataAccessor)); - mockPath = new MockPath(mockFileDataAccessor); } public override void AppendAllLines(string path, IEnumerable contents) @@ -57,8 +55,9 @@ public override void AppendAllText(string path, string contents, Encoding encodi if (!mockFileDataAccessor.FileExists(path)) { - var mockPath = mockFileDataAccessor.Path; - var dir = mockPath.GetDirectoryName(mockPath.GetFullPath(path)); + var dir = mockFileDataAccessor.Path.GetDirectoryName( + mockFileDataAccessor.Path.GetFullPath(path)); + if (!mockFileDataAccessor.Directory.Exists(dir)) { throw new DirectoryNotFoundException(string.Format(CultureInfo.InvariantCulture, StringResources.Manager.GetString("COULD_NOT_FIND_PART_OF_PATH_EXCEPTION"), path)); @@ -113,7 +112,9 @@ public override void Copy(string sourceFileName, string destFileName, bool overw throw new FileNotFoundException(string.Format(CultureInfo.InvariantCulture, StringResources.Manager.GetString("COULD_NOT_FIND_FILE_EXCEPTION"), sourceFileName)); } - var directoryNameOfDestination = mockPath.GetDirectoryName(mockPath.GetFullPath(destFileName)); + var directoryNameOfDestination = mockFileDataAccessor.Path.GetDirectoryName( + mockFileDataAccessor.Path.GetFullPath(destFileName)); + if (!mockFileDataAccessor.Directory.Exists(directoryNameOfDestination)) { throw new DirectoryNotFoundException(string.Format(CultureInfo.InvariantCulture, StringResources.Manager.GetString("COULD_NOT_FIND_PART_OF_PATH_EXCEPTION"), destFileName)); @@ -157,7 +158,8 @@ private Stream CreateInternal(string path, int bufferSize, FileOptions options, mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, nameof(path)); - var directoryPath = mockPath.GetDirectoryName(mockPath.GetFullPath(path)); + var directoryPath = mockFileDataAccessor.Path.GetDirectoryName( + mockFileDataAccessor.Path.GetFullPath(path)); if (!mockFileDataAccessor.Directory.Exists(directoryPath)) { @@ -571,15 +573,13 @@ public override void Replace(string sourceFileName, string destinationFileName, throw new FileNotFoundException(string.Format(CultureInfo.InvariantCulture, StringResources.Manager.GetString("COULD_NOT_FIND_FILE_EXCEPTION"), destinationFileName)); } - var mockFile = new MockFile(mockFileDataAccessor); - if (destinationBackupFileName != null) { - mockFile.Copy(destinationFileName, destinationBackupFileName, true); + Copy(destinationFileName, destinationBackupFileName, true); } - mockFile.Delete(destinationFileName); - mockFile.Move(sourceFileName, destinationFileName); + Delete(destinationFileName); + Move(sourceFileName, destinationFileName); } #endif diff --git a/System.IO.Abstractions.TestingHelpers/MockFileInfo.cs b/System.IO.Abstractions.TestingHelpers/MockFileInfo.cs index 298d054be..8e77a9b1f 100644 --- a/System.IO.Abstractions.TestingHelpers/MockFileInfo.cs +++ b/System.IO.Abstractions.TestingHelpers/MockFileInfo.cs @@ -168,7 +168,7 @@ public override FileInfoBase CopyTo(string destFileName, bool overwrite) { return this; } - new MockFile(mockFileSystem).Copy(FullName, destFileName, overwrite); + mockFileSystem.File.Copy(FullName, destFileName, overwrite); return mockFileSystem.FileInfo.FromFileName(destFileName); } diff --git a/System.IO.Abstractions.TestingHelpers/MockFileStream.cs b/System.IO.Abstractions.TestingHelpers/MockFileStream.cs index b9dc51e6a..7e3d2f291 100644 --- a/System.IO.Abstractions.TestingHelpers/MockFileStream.cs +++ b/System.IO.Abstractions.TestingHelpers/MockFileStream.cs @@ -7,8 +7,12 @@ public class MockFileStream : MemoryStream private readonly string path; private readonly bool canWrite = true; private readonly FileOptions options; - private bool disposed; + +#if NET40 private bool closed; +#else + private bool disposed; +#endif public enum StreamType { diff --git a/System.IO.Abstractions.TestingHelpers/MockFileSystem.cs b/System.IO.Abstractions.TestingHelpers/MockFileSystem.cs index f66d2f7f8..664c20a09 100644 --- a/System.IO.Abstractions.TestingHelpers/MockFileSystem.cs +++ b/System.IO.Abstractions.TestingHelpers/MockFileSystem.cs @@ -2,10 +2,11 @@ using System.Globalization; using System.Linq; using System.Reflection; -using XFS = System.IO.Abstractions.TestingHelpers.MockUnixSupport; namespace System.IO.Abstractions.TestingHelpers { + using XFS = MockUnixSupport; + [Serializable] public class MockFileSystem : IFileSystem, IMockFileDataAccessor { @@ -24,13 +25,15 @@ public MockFileSystem(IDictionary files, string currentDir currentDirectory = XFS.Path(DEFAULT_CURRENT_DIRECTORY); } + CaseSensitive = XFS.IsUnixPlatform(); + Comparer = CaseSensitive ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase; + Comparison = CaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase; pathVerifier = new PathVerifier(this); + this.files = new Dictionary(Comparer); - this.files = new Dictionary(StringComparer.OrdinalIgnoreCase); - Path = new MockPath(this); File = new MockFile(this); - Directory = new MockDirectory(this, File, currentDirectory); + Directory = new MockDirectory(this, currentDirectory); FileInfo = new MockFileInfoFactory(this); FileStream = new MockFileStreamFactory(this); DirectoryInfo = new MockDirectoryInfoFactory(this); @@ -51,24 +54,18 @@ public MockFileSystem(IDictionary files, string currentDir } } + public bool CaseSensitive { get; } + public StringComparer Comparer { get; } + public StringComparison Comparison { get; } public FileBase File { get; } - public DirectoryBase Directory { get; } - public IFileInfoFactory FileInfo { get; } - public IFileStreamFactory FileStream { get; } - public PathBase Path { get; } - public IDirectoryInfoFactory DirectoryInfo { get; } - public IDriveInfoFactory DriveInfo { get; } - public IFileSystemWatcherFactory FileSystemWatcher { get; set; } - public IFileSystem FileSystem => this; - public PathVerifier PathVerifier => pathVerifier; private string FixPath(string path, bool checkCaps = false) @@ -100,7 +97,7 @@ private string GetPathWithCorrectDirectoryCapitalization(string fullPath) if (Directory.Exists(leftHalf)) { leftHalf = Path.GetFullPath(leftHalf).TrimSlashes(); - string baseDirectory = AllDirectories.First(dir => dir.Equals(leftHalf, StringComparison.OrdinalIgnoreCase)); + string baseDirectory = AllDirectories.First(dir => dir.Equals(leftHalf, Comparison)); return baseDirectory + Path.DirectorySeparatorChar + rightHalf; } } @@ -152,7 +149,7 @@ public void AddFile(string path, MockFileData mockFile) public void AddDirectory(string path) { var fixedPath = FixPath(path, true); - var separator = XFS.Separator(); + var separator = Path.DirectorySeparatorChar.ToString(); lock (files) { @@ -163,13 +160,13 @@ public void AddDirectory(string path) var lastIndex = 0; bool isUnc = - fixedPath.StartsWith(@"\\", StringComparison.OrdinalIgnoreCase) || - fixedPath.StartsWith(@"//", StringComparison.OrdinalIgnoreCase); + fixedPath.StartsWith(@"\\", Comparison) || + fixedPath.StartsWith(@"//", Comparison); if (isUnc) { //First, confirm they aren't trying to create '\\server\' - lastIndex = fixedPath.IndexOf(separator, 2, StringComparison.OrdinalIgnoreCase); + lastIndex = fixedPath.IndexOf(separator, 2, Comparison); if (lastIndex < 0) throw new ArgumentException(@"The UNC path should be of the form \\server\share.", "path"); @@ -179,7 +176,7 @@ public void AddDirectory(string path) */ } - while ((lastIndex = fixedPath.IndexOf(separator, lastIndex + 1, StringComparison.OrdinalIgnoreCase)) > -1) + while ((lastIndex = fixedPath.IndexOf(separator, lastIndex + 1, Comparison)) > -1) { var segment = fixedPath.Substring(0, lastIndex + 1); if (!Directory.Exists(segment)) @@ -188,7 +185,7 @@ public void AddDirectory(string path) } } - var s = fixedPath.EndsWith(separator, StringComparison.OrdinalIgnoreCase) ? fixedPath : fixedPath + separator; + var s = fixedPath.EndsWith(separator, Comparison) ? fixedPath : fixedPath + separator; SetEntry(s, new MockDirectoryData()); } } @@ -234,12 +231,12 @@ public void MoveDirectory(string sourcePath, string destPath) lock (files) { var affectedPaths = files.Keys - .Where(p => p.StartsWith(sourcePath, StringComparison.OrdinalIgnoreCase)) + .Where(p => p.StartsWith(sourcePath, Comparison)) .ToList(); foreach(var path in affectedPaths) { - var newPath = path.Replace(sourcePath, destPath, StringComparison.OrdinalIgnoreCase); + var newPath = path.Replace(sourcePath, destPath, Comparison); files[newPath] = files[path]; files.Remove(path); } diff --git a/System.IO.Abstractions.TestingHelpers/MockPath.cs b/System.IO.Abstractions.TestingHelpers/MockPath.cs index ae44854ea..ec645da09 100644 --- a/System.IO.Abstractions.TestingHelpers/MockPath.cs +++ b/System.IO.Abstractions.TestingHelpers/MockPath.cs @@ -12,8 +12,6 @@ public class MockPath : PathWrapper { private readonly IMockFileDataAccessor mockFileDataAccessor; - private static readonly char[] InvalidAdditionalPathChars = { '*', '?' }; - public MockPath(IMockFileDataAccessor mockFileDataAccessor) : base(mockFileDataAccessor?.FileSystem) { this.mockFileDataAccessor = mockFileDataAccessor ?? throw new ArgumentNullException(nameof(mockFileDataAccessor)); @@ -34,8 +32,8 @@ public override string GetFullPath(string path) path = path.Replace(AltDirectorySeparatorChar, DirectorySeparatorChar); bool isUnc = - path.StartsWith(@"\\", StringComparison.OrdinalIgnoreCase) || - path.StartsWith(@"//", StringComparison.OrdinalIgnoreCase); + path.StartsWith(@"\\", mockFileDataAccessor.Comparison) || + path.StartsWith(@"//", mockFileDataAccessor.Comparison); string root = GetPathRoot(path); @@ -58,7 +56,7 @@ public override string GetFullPath(string path) throw new ArgumentException(@"The UNC path should be of the form \\server\share.", "path"); } } - else if (@"\".Equals(root, StringComparison.OrdinalIgnoreCase) || @"/".Equals(root, StringComparison.OrdinalIgnoreCase)) + else if (@"\".Equals(root, mockFileDataAccessor.Comparison) || @"/".Equals(root, mockFileDataAccessor.Comparison)) { // absolute path on the current drive or volume pathSegments = GetSegments(GetPathRoot(mockFileDataAccessor.Directory.GetCurrentDirectory()), path); @@ -71,7 +69,7 @@ public override string GetFullPath(string path) // unc paths need at least two segments, the others need one segment bool isUnixRooted = mockFileDataAccessor.Directory.GetCurrentDirectory() - .StartsWith(string.Format(CultureInfo.InvariantCulture, "{0}", DirectorySeparatorChar), StringComparison.OrdinalIgnoreCase); + .StartsWith(string.Format(CultureInfo.InvariantCulture, "{0}", DirectorySeparatorChar), mockFileDataAccessor.Comparison); var minPathSegments = isUnc ? 2 @@ -80,7 +78,7 @@ public override string GetFullPath(string path) var stack = new Stack(); foreach (var segment in pathSegments) { - if ("..".Equals(segment, StringComparison.OrdinalIgnoreCase)) + if ("..".Equals(segment, mockFileDataAccessor.Comparison)) { // only pop, if afterwards are at least the minimal amount of path segments if (stack.Count > minPathSegments) @@ -88,7 +86,7 @@ public override string GetFullPath(string path) stack.Pop(); } } - else if (".".Equals(segment, StringComparison.OrdinalIgnoreCase)) + else if (".".Equals(segment, mockFileDataAccessor.Comparison)) { // ignore . } @@ -137,33 +135,5 @@ public override string GetTempFileName() return fullPath; } - - internal static bool HasIllegalCharacters(string path, bool checkAdditional) - { - if (path == null) - { - throw new ArgumentNullException(nameof(path)); - } - - if (checkAdditional) - { - return path.IndexOfAny(Path.GetInvalidPathChars().Concat(InvalidAdditionalPathChars).ToArray()) >= 0; - } - - return path.IndexOfAny(Path.GetInvalidPathChars()) >= 0; - } - - internal static void CheckInvalidPathChars(string path, bool checkAdditional = false) - { - if (path == null) - { - throw new ArgumentNullException(nameof(path)); - } - - if (HasIllegalCharacters(path, checkAdditional)) - { - throw new ArgumentException(StringResources.Manager.GetString("ILLEGAL_CHARACTERS_IN_PATH_EXCEPTION")); - } - } } } diff --git a/System.IO.Abstractions.TestingHelpers/MockUnixSupport.cs b/System.IO.Abstractions.TestingHelpers/MockUnixSupport.cs index cb3321c91..d96acd332 100644 --- a/System.IO.Abstractions.TestingHelpers/MockUnixSupport.cs +++ b/System.IO.Abstractions.TestingHelpers/MockUnixSupport.cs @@ -4,28 +4,13 @@ namespace System.IO.Abstractions.TestingHelpers { public static class MockUnixSupport { - public static string Path(string path, Func isUnixF = null) - { - var isUnix = isUnixF ?? IsUnixPlatform; + private static readonly Regex pathTransform = new Regex(@"^[a-zA-Z]:(?.*)$"); - if (isUnix()) - { - path = Regex.Replace(path, @"^[a-zA-Z]:(?.*)$", "${path}"); - path = path.Replace(@"\", "/"); - } + public static string Path(string path) => IsUnixPlatform() + ? pathTransform.Replace(path, "${path}").Replace(@"\", "/") + : path; - return path; - } - - public static string Separator(Func isUnixF = null) - { - var isUnix = isUnixF ?? IsUnixPlatform; - return isUnix() ? "/" : @"\"; - } - - public static bool IsUnixPlatform() - { - return IO.Path.DirectorySeparatorChar == '/'; - } + public static bool IsUnixPlatform() => IO.Path.DirectorySeparatorChar == '/'; + public static bool IsWindowsPlatform() => IO.Path.DirectorySeparatorChar == '\\'; } } diff --git a/System.IO.Abstractions.TestingHelpers/PathVerifier.cs b/System.IO.Abstractions.TestingHelpers/PathVerifier.cs index 43cc8adf3..eb103aa23 100644 --- a/System.IO.Abstractions.TestingHelpers/PathVerifier.cs +++ b/System.IO.Abstractions.TestingHelpers/PathVerifier.cs @@ -2,6 +2,8 @@ namespace System.IO.Abstractions.TestingHelpers { + using XFS = MockUnixSupport; + public class PathVerifier { private readonly IMockFileDataAccessor _mockFileDataAccessor; @@ -28,12 +30,9 @@ public void IsLegalAbsoluteOrRelative(string path, string paramName) throw new ArgumentException(StringResources.Manager.GetString("THE_PATH_IS_NOT_OF_A_LEGAL_FORM"), paramName); } - if (!MockUnixSupport.IsUnixPlatform()) + if (XFS.IsWindowsPlatform() && !IsValidUseOfVolumeSeparatorChar(path)) { - if (!IsValidUseOfVolumeSeparatorChar(path)) - { - throw new NotSupportedException(StringResources.Manager.GetString("THE_PATH_IS_NOT_OF_A_LEGAL_FORM")); - } + throw new NotSupportedException(StringResources.Manager.GetString("THE_PATH_IS_NOT_OF_A_LEGAL_FORM")); } if (ExtractFileName(path).IndexOfAny(_mockFileDataAccessor.Path.GetInvalidFileNameChars()) > -1) @@ -42,7 +41,8 @@ public void IsLegalAbsoluteOrRelative(string path, string paramName) } var filePath = ExtractFilePath(path); - if (MockPath.HasIllegalCharacters(filePath, false)) + + if (HasIllegalCharacters(filePath, false)) { throw new ArgumentException(StringResources.Manager.GetString("ILLEGAL_CHARACTERS_IN_PATH_EXCEPTION")); } @@ -68,5 +68,37 @@ private string ExtractFilePath(string fullFileName) _mockFileDataAccessor.Path.AltDirectorySeparatorChar); return string.Join(_mockFileDataAccessor.Path.DirectorySeparatorChar.ToString(), extractFilePath.Take(extractFilePath.Length - 1)); } + + private readonly char[] AdditionalInvalidPathChars = { '*', '?' }; + + public bool HasIllegalCharacters(string path, bool checkAdditional) + { + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + + var invalidPathChars = _mockFileDataAccessor.Path.GetInvalidPathChars(); + + if (checkAdditional) + { + return path.IndexOfAny(invalidPathChars.Concat(AdditionalInvalidPathChars).ToArray()) >= 0; + } + + return path.IndexOfAny(invalidPathChars) >= 0; + } + + public void CheckInvalidPathChars(string path, bool checkAdditional = false) + { + if (path == null) + { + throw new ArgumentNullException(nameof(path)); + } + + if (HasIllegalCharacters(path, checkAdditional)) + { + throw new ArgumentException(StringResources.Manager.GetString("ILLEGAL_CHARACTERS_IN_PATH_EXCEPTION")); + } + } } } diff --git a/System.IO.Abstractions.TestingHelpers/StringExtensions.cs b/System.IO.Abstractions.TestingHelpers/StringExtensions.cs index b4a58919f..82cd92939 100644 --- a/System.IO.Abstractions.TestingHelpers/StringExtensions.cs +++ b/System.IO.Abstractions.TestingHelpers/StringExtensions.cs @@ -4,6 +4,8 @@ namespace System.IO.Abstractions.TestingHelpers { + using XFS = MockUnixSupport; + public static class StringExtensions { [Pure] @@ -66,14 +68,14 @@ public static string TrimSlashes(this string path) var trimmed = path.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); - if (MockUnixSupport.IsUnixPlatform() + if (XFS.IsUnixPlatform() && (path[0] == Path.DirectorySeparatorChar || path[0] == Path.AltDirectorySeparatorChar) && trimmed == "") { return Path.DirectorySeparatorChar.ToString(); } - if (!MockUnixSupport.IsUnixPlatform() + if (XFS.IsWindowsPlatform() && trimmed.Length == 2 && char.IsLetter(trimmed[0]) && trimmed[1] == ':') From 2c95458a5fc31ad04d858c01d129785ee0ae9875 Mon Sep 17 00:00:00 2001 From: Robert Koeninger Date: Tue, 4 Dec 2018 01:50:17 -0500 Subject: [PATCH 02/17] Removed drive letter ToUpper in MockDriveInfo --- System.IO.Abstractions.TestingHelpers/MockDriveInfo.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/System.IO.Abstractions.TestingHelpers/MockDriveInfo.cs b/System.IO.Abstractions.TestingHelpers/MockDriveInfo.cs index c3161d43a..7b8398856 100644 --- a/System.IO.Abstractions.TestingHelpers/MockDriveInfo.cs +++ b/System.IO.Abstractions.TestingHelpers/MockDriveInfo.cs @@ -20,7 +20,7 @@ public MockDriveInfo(IMockFileDataAccessor mockFileDataAccessor, string name) : || (name.Length == 2 && name[1] == ':') || (name.Length == 3 && name.EndsWith(DRIVE_SEPARATOR, mockFileDataAccessor.Comparison))) { - name = ToUpper(name[0]) + DRIVE_SEPARATOR; + name = name[0] + DRIVE_SEPARATOR; } else { @@ -38,11 +38,6 @@ public MockDriveInfo(IMockFileDataAccessor mockFileDataAccessor, string name) : IsReady = true; } - private char ToUpper(char c) - { - return mockFileDataAccessor.CaseSensitive ? c : char.ToUpperInvariant(c); - } - public new long AvailableFreeSpace { get; set; } public new string DriveFormat { get; set; } public new DriveType DriveType { get; set; } From be0439c3bee520494411f639900da9051e788d56 Mon Sep 17 00:00:00 2001 From: Robert Koeninger Date: Tue, 4 Dec 2018 02:46:25 -0500 Subject: [PATCH 03/17] Labelled tests as being Windows/Unix specific based on case sensitivity --- .../MockDirectoryTests.cs | 35 +++++++++++++++++++ .../MockFileExistsTests.cs | 21 +++++++++++ .../MockFileSystemTests.cs | 26 ++++++++++++-- .../UnixSpecifics.cs | 2 ++ .../WindowsSpecifics.cs | 2 ++ 5 files changed, 83 insertions(+), 3 deletions(-) diff --git a/System.IO.Abstractions.TestingHelpers.Tests/MockDirectoryTests.cs b/System.IO.Abstractions.TestingHelpers.Tests/MockDirectoryTests.cs index ed20680af..60caf8f31 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/MockDirectoryTests.cs +++ b/System.IO.Abstractions.TestingHelpers.Tests/MockDirectoryTests.cs @@ -599,6 +599,7 @@ public void MockDirectory_Delete_ShouldDeleteDirectory() } [Test] + [WindowsOnly(WindowsSpecifics.CaseInsensitivity)] public void MockDirectory_Delete_ShouldDeleteDirectoryCaseInsensitively() { // Arrange @@ -614,6 +615,40 @@ public void MockDirectory_Delete_ShouldDeleteDirectoryCaseInsensitively() Assert.IsFalse(fileSystem.Directory.Exists(XFS.Path(@"c:\bar"))); } + [Test] + [UnixOnly(UnixSpecifics.CaseSensitivity)] + public void MockDirectory_Delete_ShouldThrowDirectoryNotFoundException_WhenSpecifiedWithInDifferentCase() + { + // Arrange + var fileSystem = new MockFileSystem(new Dictionary + { + { "/bar/foo.txt", new MockFileData("Demo text content") } + }); + + // Act + TestDelegate action = () => fileSystem.Directory.Delete("/BAR", true); + + // Assert + Assert.Throws(action); + } + + [Test] + [UnixOnly(UnixSpecifics.CaseSensitivity)] + public void MockDirectory_Delete_ShouldDeleteDirectoryCaseSensitively() + { + // Arrange + var fileSystem = new MockFileSystem(new Dictionary + { + { "/bar/foo.txt", new MockFileData("Demo text content") } + }); + + // Act + fileSystem.Directory.Delete("/bar", true); + + // Assert + Assert.IsFalse(fileSystem.Directory.Exists("/bar")); + } + [Test] public void MockDirectory_Delete_ShouldThrowDirectoryNotFoundException() { diff --git a/System.IO.Abstractions.TestingHelpers.Tests/MockFileExistsTests.cs b/System.IO.Abstractions.TestingHelpers.Tests/MockFileExistsTests.cs index 868c249ba..8e68da5e4 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/MockFileExistsTests.cs +++ b/System.IO.Abstractions.TestingHelpers.Tests/MockFileExistsTests.cs @@ -27,6 +27,7 @@ public void MockFile_Exists_ShouldReturnTrueForSamePath() } [Test] + [WindowsOnly(WindowsSpecifics.CaseInsensitivity)] public void MockFile_Exists_ShouldReturnTrueForPathVaryingByCase() { // Arrange @@ -45,6 +46,26 @@ public void MockFile_Exists_ShouldReturnTrueForPathVaryingByCase() Assert.IsTrue(result); } + [Test] + [UnixOnly(UnixSpecifics.CaseSensitivity)] + public void MockFile_Exists_ShouldReturnFalseForPathVaryingByCase() + { + // Arrange + var fileSystem = new MockFileSystem(new Dictionary + { + { XFS.Path(@"c:\something\demo.txt"), new MockFileData("Demo text content") }, + { XFS.Path(@"c:\something\other.gif"), new MockFileData(new byte[] { 0x21, 0x58, 0x3f, 0xa9 }) } + }); + + var file = new MockFile(fileSystem); + + // Act + var result = file.Exists(XFS.Path(@"c:\SomeThing\Other.gif")); + + // Assert + Assert.IsFalse(result); + } + [Test] public void MockFile_Exists_ShouldReturnFalseForEntirelyDifferentPath() { diff --git a/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemTests.cs b/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemTests.cs index 513460d3b..b5802f71e 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemTests.cs +++ b/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemTests.cs @@ -41,6 +41,7 @@ public void MockFileSystem_GetFile_ShouldReturnFileRegisteredInConstructor() } [Test] + [WindowsOnly(WindowsSpecifics.CaseInsensitivity)] public void MockFileSystem_GetFile_ShouldReturnFileRegisteredInConstructorWhenPathsDifferByCase() { var file1 = new MockFileData("Demo\r\ntext\ncontent\rvalue"); @@ -55,15 +56,32 @@ public void MockFileSystem_GetFile_ShouldReturnFileRegisteredInConstructorWhenPa Assert.AreEqual(file1, result); } + [Test] + [UnixOnly(UnixSpecifics.CaseSensitivity)] + public void MockFileSystem_GetFile_ShouldNotReturnFileRegisteredInConstructorWhenPathsDifferByCase() + { + var file1 = new MockFileData("Demo\r\ntext\ncontent\rvalue"); + var fileSystem = new MockFileSystem(new Dictionary + { + { "/something/demo.txt", file1 }, + { "/something/other.gif", new MockFileData(new byte[] { 0x21, 0x58, 0x3f, 0xa9 }) } + }); + + var result = fileSystem.GetFile("/SomeThing/DEMO.txt"); + + Assert.AreEqual(file1, result); + } + [Test] public void MockFileSystem_AddFile_ShouldHandleNullFileDataAsEmpty() { + var path = XFS.Path(@"c:\something\nullish.txt"); var fileSystem = new MockFileSystem(new Dictionary { - { @"c:\something\nullish.txt", null } + { path, null } }); - var result = fileSystem.File.ReadAllText(@"c:\SomeThing\nullish.txt"); + var result = fileSystem.File.ReadAllText(path); Assert.IsEmpty(result, "Null MockFileData should be allowed for and result in an empty file."); } @@ -71,7 +89,7 @@ public void MockFileSystem_AddFile_ShouldHandleNullFileDataAsEmpty() [Test] public void MockFileSystem_AddFile_ShouldRepaceExistingFile() { - const string path = @"c:\some\file.txt"; + var path = XFS.Path(@"c:\some\file.txt"); const string existingContent = "Existing content"; var fileSystem = new MockFileSystem(new Dictionary { @@ -157,6 +175,7 @@ public void MockFileSystem_AddFile_ShouldMatchCapitalization_PerfectMatch() } [Test] + [WindowsOnly(WindowsSpecifics.CaseInsensitivity)] public void MockFileSystem_AddFile_ShouldMatchCapitalization_PartialMatch() { var fileSystem = new MockFileSystem(); @@ -175,6 +194,7 @@ public void MockFileSystem_AddFile_ShouldMatchCapitalization_PartialMatch() } [Test] + [WindowsOnly(WindowsSpecifics.CaseInsensitivity)] public void MockFileSystem_AddFile_ShouldMatchCapitalization_PartialMatch_FurtherLeft() { var fileSystem = new MockFileSystem(); diff --git a/System.IO.Abstractions.TestingHelpers.Tests/UnixSpecifics.cs b/System.IO.Abstractions.TestingHelpers.Tests/UnixSpecifics.cs index 6e81e34c8..33bba67e8 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/UnixSpecifics.cs +++ b/System.IO.Abstractions.TestingHelpers.Tests/UnixSpecifics.cs @@ -3,5 +3,7 @@ internal static class UnixSpecifics { public const string SlashRoot = "Filesystem root is just '/' in Unix"; + + public const string CaseSensitivity = "Paths are case-sensitivity in Unix"; } } diff --git a/System.IO.Abstractions.TestingHelpers.Tests/WindowsSpecifics.cs b/System.IO.Abstractions.TestingHelpers.Tests/WindowsSpecifics.cs index 15ae65c12..3f0367ab9 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/WindowsSpecifics.cs +++ b/System.IO.Abstractions.TestingHelpers.Tests/WindowsSpecifics.cs @@ -11,5 +11,7 @@ internal static class WindowsSpecifics public const string StrictPathRules = "Windows has stricter path rules than other platforms"; public const string EmptyInvalidPathChars = "Path.GetInvalidPathChars() doesn't return anything on Mono"; + + public const string CaseInsensitivity = "Windows paths are case-insensitivity"; } } From 0cd82fe4719cc9507c6d379a1d4e4872d070c3aa Mon Sep 17 00:00:00 2001 From: Robert Koeninger Date: Tue, 4 Dec 2018 02:53:12 -0500 Subject: [PATCH 04/17] Corrected UnixOnly test to check for null --- .../MockFileSystemTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemTests.cs b/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemTests.cs index b5802f71e..82b41f698 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemTests.cs +++ b/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemTests.cs @@ -69,7 +69,7 @@ public void MockFileSystem_GetFile_ShouldNotReturnFileRegisteredInConstructorWhe var result = fileSystem.GetFile("/SomeThing/DEMO.txt"); - Assert.AreEqual(file1, result); + Assert.IsNull(result); } [Test] From c892fb1419b9a24ccff1e50af8b45c7fd20e95f6 Mon Sep 17 00:00:00 2001 From: Robert Koeninger Date: Tue, 4 Dec 2018 13:57:33 -0500 Subject: [PATCH 05/17] Adjusted *Specifics wording --- System.IO.Abstractions.TestingHelpers.Tests/UnixSpecifics.cs | 4 ++-- .../WindowsSpecifics.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/System.IO.Abstractions.TestingHelpers.Tests/UnixSpecifics.cs b/System.IO.Abstractions.TestingHelpers.Tests/UnixSpecifics.cs index 33bba67e8..bf7cd4f52 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/UnixSpecifics.cs +++ b/System.IO.Abstractions.TestingHelpers.Tests/UnixSpecifics.cs @@ -2,8 +2,8 @@ { internal static class UnixSpecifics { - public const string SlashRoot = "Filesystem root is just '/' in Unix"; + public const string SlashRoot = "Filesystem root is just '/' on Unix"; - public const string CaseSensitivity = "Paths are case-sensitivity in Unix"; + public const string CaseSensitivity = "Paths are case-sensitive on Unix"; } } diff --git a/System.IO.Abstractions.TestingHelpers.Tests/WindowsSpecifics.cs b/System.IO.Abstractions.TestingHelpers.Tests/WindowsSpecifics.cs index 3f0367ab9..71626f6ec 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/WindowsSpecifics.cs +++ b/System.IO.Abstractions.TestingHelpers.Tests/WindowsSpecifics.cs @@ -12,6 +12,6 @@ internal static class WindowsSpecifics public const string EmptyInvalidPathChars = "Path.GetInvalidPathChars() doesn't return anything on Mono"; - public const string CaseInsensitivity = "Windows paths are case-insensitivity"; + public const string CaseInsensitivity = "Paths are case-insensitive on Windows"; } } From 533bc8a48d3c6afda21a904e6ba94b151c8b707c Mon Sep 17 00:00:00 2001 From: Robert Koeninger Date: Tue, 4 Dec 2018 14:29:22 -0500 Subject: [PATCH 06/17] Fixed MockDirectory.GetParent to correctly identify and return '/' root --- .../MockDirectoryTests.cs | 15 +++++++++++++ .../MockDirectory.cs | 22 ++++++++++++++++--- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/System.IO.Abstractions.TestingHelpers.Tests/MockDirectoryTests.cs b/System.IO.Abstractions.TestingHelpers.Tests/MockDirectoryTests.cs index 60caf8f31..e07402d91 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/MockDirectoryTests.cs +++ b/System.IO.Abstractions.TestingHelpers.Tests/MockDirectoryTests.cs @@ -1312,6 +1312,21 @@ public void MockDirectory_GetParent_ShouldReturnNullIfPathIsRoot() Assert.IsNull(actualResult); } + [Test] + [UnixOnly(UnixSpecifics.SlashRoot)] + public void MockDirectory_GetParent_ShouldReturnRootIfDirectoryIsInRoot() + { + // Arrange + var fileSystem = new MockFileSystem(); + fileSystem.AddDirectory("/bar"); + + // Act + var parent = fileSystem.Directory.GetParent("/bar"); + + // Assert + Assert.AreEqual("/", parent.FullName); + } + public static IEnumerable MockDirectory_GetParent_Cases { get diff --git a/System.IO.Abstractions.TestingHelpers/MockDirectory.cs b/System.IO.Abstractions.TestingHelpers/MockDirectory.cs index 35c2657a2..92935fc86 100644 --- a/System.IO.Abstractions.TestingHelpers/MockDirectory.cs +++ b/System.IO.Abstractions.TestingHelpers/MockDirectory.cs @@ -323,13 +323,16 @@ public override DirectoryInfoBase GetParent(string path) } var absolutePath = mockFileDataAccessor.Path.GetFullPath(path); - var sepAsString = string.Format(CultureInfo.InvariantCulture, "{0}", mockFileDataAccessor.Path.DirectorySeparatorChar); - + var sepAsString = mockFileDataAccessor.Path.DirectorySeparatorChar.ToString(); var lastIndex = 0; + if (absolutePath != sepAsString) { - var startIndex = absolutePath.EndsWith(sepAsString, mockFileDataAccessor.Comparison) ? absolutePath.Length - 1 : absolutePath.Length; + var startIndex = absolutePath.EndsWith(sepAsString, mockFileDataAccessor.Comparison) + ? absolutePath.Length - 1 + : absolutePath.Length; lastIndex = absolutePath.LastIndexOf(mockFileDataAccessor.Path.DirectorySeparatorChar, startIndex - 1); + if (lastIndex < 0) { return null; @@ -337,8 +340,21 @@ public override DirectoryInfoBase GetParent(string path) } var parentPath = absolutePath.Substring(0, lastIndex); + if (string.IsNullOrEmpty(parentPath)) { + // On the Unix platform, the parent of a path consisting of a slash followed by + // non-slashes is the root, '/'. + if (XFS.IsUnixPlatform()) + { + absolutePath = absolutePath.TrimSlashes(); + + if (absolutePath.Length > 1 && absolutePath.LastIndexOf(mockFileDataAccessor.Path.DirectorySeparatorChar) == 0) + { + return new MockDirectoryInfo(mockFileDataAccessor, mockFileDataAccessor.Path.DirectorySeparatorChar.ToString()); + } + } + return null; } From 642faf9152407cc1be2538f3ce865a12a8e93e3c Mon Sep 17 00:00:00 2001 From: Robert Koeninger Date: Tue, 4 Dec 2018 20:58:02 -0500 Subject: [PATCH 07/17] Fixed MockDirectory.GetFiles to handle un-normalized slashes --- .../MockFileSystemTests.cs | 16 ++++++++++++ .../MockDirectory.cs | 26 +++++-------------- .../StringExtensions.cs | 20 ++++++++++++++ 3 files changed, 43 insertions(+), 19 deletions(-) diff --git a/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemTests.cs b/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemTests.cs index 82b41f698..9afc16deb 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemTests.cs +++ b/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemTests.cs @@ -72,6 +72,22 @@ public void MockFileSystem_GetFile_ShouldNotReturnFileRegisteredInConstructorWhe Assert.IsNull(result); } + [Test] + public void MockFileSystem_AddFile_ShouldHandleUnnormalizedSlashes() + { + var path = XFS.Path(@"c:\d1\d2\file.txt"); + var altPath = XFS.Path("c:/d1/d2/file.txt"); + var altParentPath = XFS.Path("c://d1//d2/"); + var fs = new MockFileSystem(); + fs.AddFile(path, new MockFileData("Hello")); + + var fileCount = fs.Directory.GetFiles(altParentPath).Length; + var fileExists = fs.File.Exists(altPath); + + Assert.AreEqual(1, fileCount); + Assert.IsTrue(fileExists); + } + [Test] public void MockFileSystem_AddFile_ShouldHandleNullFileDataAsEmpty() { diff --git a/System.IO.Abstractions.TestingHelpers/MockDirectory.cs b/System.IO.Abstractions.TestingHelpers/MockDirectory.cs index 92935fc86..5c9a684cf 100644 --- a/System.IO.Abstractions.TestingHelpers/MockDirectory.cs +++ b/System.IO.Abstractions.TestingHelpers/MockDirectory.cs @@ -194,22 +194,23 @@ private string[] GetFilesInternal(IEnumerable files, string path, string { CheckSearchPattern(searchPattern); path = path.TrimSlashes(); + path = path.NormalizeSlashes(); path = EnsureAbsolutePath(path); - if (!path.EndsWith(Path.DirectorySeparatorChar.ToString()) - && !path.EndsWith(Path.AltDirectorySeparatorChar.ToString())) + if (!path.EndsWith(Path.DirectorySeparatorChar.ToString())) { path += Path.DirectorySeparatorChar; } - bool isUnix = XFS.IsUnixPlatform(); + var isUnix = XFS.IsUnixPlatform(); - string allDirectoriesPattern = isUnix + var allDirectoriesPattern = isUnix ? @"([^<>:""/|?*]*/)*" : @"([^<>:""/\\|?*]*\\)*"; string fileNamePattern; string pathPatternSpecial = null; + if (searchPattern == "*") { fileNamePattern = isUnix ? @"[^/]*?/?" : @"[^\\]*?\\?"; @@ -241,22 +242,9 @@ private string[] GetFilesInternal(IEnumerable files, string path, string searchOption == SearchOption.AllDirectories ? allDirectoriesPattern : string.Empty, fileNamePattern); - return files - .Where(p => - { - if (Regex.IsMatch(p, pathPattern)) - { - return true; - } - - if (pathPatternSpecial != null && Regex.IsMatch(p, pathPatternSpecial)) - { - return true; - } - - return false; - }) + .Where(p => Regex.IsMatch(p, pathPattern) + || (pathPatternSpecial != null && Regex.IsMatch(p, pathPatternSpecial))) .ToArray(); } diff --git a/System.IO.Abstractions.TestingHelpers/StringExtensions.cs b/System.IO.Abstractions.TestingHelpers/StringExtensions.cs index 82cd92939..930e8da06 100644 --- a/System.IO.Abstractions.TestingHelpers/StringExtensions.cs +++ b/System.IO.Abstractions.TestingHelpers/StringExtensions.cs @@ -85,5 +85,25 @@ public static string TrimSlashes(this string path) return trimmed; } + + [Pure] + public static string NormalizeSlashes(this string path) + { + path = path.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar); + var sep = Path.DirectorySeparatorChar.ToString(); + var doubleSep = sep + sep; + + while (true) + { + var newPath = path.Replace(doubleSep, sep); + + if (path == newPath) + { + return path; + } + + path = newPath; + } + } } } \ No newline at end of file From 38e7e600ef8a3dbae55448730b903e31e9384397 Mon Sep 17 00:00:00 2001 From: Robert Koeninger Date: Tue, 4 Dec 2018 21:08:19 -0500 Subject: [PATCH 08/17] Made NormalizeSlashes handle UNC paths --- .../StringExtensions.cs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/System.IO.Abstractions.TestingHelpers/StringExtensions.cs b/System.IO.Abstractions.TestingHelpers/StringExtensions.cs index 930e8da06..f8640c861 100644 --- a/System.IO.Abstractions.TestingHelpers/StringExtensions.cs +++ b/System.IO.Abstractions.TestingHelpers/StringExtensions.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Diagnostics.Contracts; +using System.Linq; using System.Text; namespace System.IO.Abstractions.TestingHelpers @@ -93,13 +94,27 @@ public static string NormalizeSlashes(this string path) var sep = Path.DirectorySeparatorChar.ToString(); var doubleSep = sep + sep; + var prefixSeps = new string(path.TakeWhile(c => c == Path.DirectorySeparatorChar).ToArray()); + path = path.Substring(prefixSeps.Length); + + // UNC Paths start with double slash but no reason + // to have more than 2 slashes at the start of a path + if (XFS.IsWindowsPlatform() && prefixSeps.Length > 2) + { + prefixSeps = prefixSeps.Substring(0, 2); + } + else if (prefixSeps.Length > 1) + { + prefixSeps = prefixSeps.Substring(0, 1); + } + while (true) { var newPath = path.Replace(doubleSep, sep); if (path == newPath) { - return path; + return prefixSeps + path; } path = newPath; From d3ee172ee268ec588792f434beb13ce356099535 Mon Sep 17 00:00:00 2001 From: Robert Koeninger Date: Tue, 4 Dec 2018 21:23:06 -0500 Subject: [PATCH 09/17] Moved Exists check in MockDirectory.GetFiles into GetFilesInternal after path normalization --- .../MockDirectory.cs | 40 ++++++++++--------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/System.IO.Abstractions.TestingHelpers/MockDirectory.cs b/System.IO.Abstractions.TestingHelpers/MockDirectory.cs index 5c9a684cf..1a2ac7172 100644 --- a/System.IO.Abstractions.TestingHelpers/MockDirectory.cs +++ b/System.IO.Abstractions.TestingHelpers/MockDirectory.cs @@ -171,30 +171,39 @@ public override string[] GetFiles(string path, string searchPattern) } public override string[] GetFiles(string path, string searchPattern, SearchOption searchOption) + { + return GetFilesInternal(mockFileDataAccessor.AllFiles, path, searchPattern, searchOption); + } + + private string[] GetFilesInternal( + IEnumerable files, + string path, + string searchPattern, + SearchOption searchOption) { if (path == null) { throw new ArgumentNullException(nameof(path)); } - + if (path.Any(c => Path.GetInvalidPathChars().Contains(c))) { throw new ArgumentException("Invalid character(s) in path", nameof(path)); } + CheckSearchPattern(searchPattern); + path = path.TrimSlashes(); + path = path.NormalizeSlashes(); + if (!Exists(path)) { - throw new DirectoryNotFoundException(string.Format(CultureInfo.InvariantCulture, StringResources.Manager.GetString("COULD_NOT_FIND_PART_OF_PATH_EXCEPTION"), path)); + throw new DirectoryNotFoundException( + string.Format( + CultureInfo.InvariantCulture, + StringResources.Manager.GetString("COULD_NOT_FIND_PART_OF_PATH_EXCEPTION"), + path)); } - return GetFilesInternal(mockFileDataAccessor.AllFiles, path, searchPattern, searchOption); - } - - private string[] GetFilesInternal(IEnumerable files, string path, string searchPattern, SearchOption searchOption) - { - CheckSearchPattern(searchPattern); - path = path.TrimSlashes(); - path = path.NormalizeSlashes(); path = EnsureAbsolutePath(path); if (!path.EndsWith(Path.DirectorySeparatorChar.ToString())) @@ -445,17 +454,10 @@ public override IEnumerable EnumerateDirectories(string path, string sea public override IEnumerable EnumerateDirectories(string path, string searchPattern, SearchOption searchOption) { mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path"); - path = path.TrimSlashes(); path = mockFileDataAccessor.Path.GetFullPath(path); - - if (!Exists(path)) - { - throw new DirectoryNotFoundException(string.Format(CultureInfo.InvariantCulture, StringResources.Manager.GetString("COULD_NOT_FIND_PART_OF_PATH_EXCEPTION"), path)); - } - - var dirs = GetFilesInternal(mockFileDataAccessor.AllDirectories, path, searchPattern, searchOption); - return dirs.Where(p => mockFileDataAccessor.Comparer.Compare(p, path) != 0); + return GetFilesInternal(mockFileDataAccessor.AllDirectories, path, searchPattern, searchOption) + .Where(p => mockFileDataAccessor.Comparer.Compare(p, path) != 0); } public override IEnumerable EnumerateFiles(string path) From be2c3a65d0658b6a7dd884e71b7e619ee7caf694 Mon Sep 17 00:00:00 2001 From: Robert Koeninger Date: Wed, 5 Dec 2018 20:32:06 -0500 Subject: [PATCH 10/17] Made MockDirectory.GetLogicalDrives return drive letters in upper case instead of lower case --- System.IO.Abstractions.TestingHelpers/MockDirectory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/System.IO.Abstractions.TestingHelpers/MockDirectory.cs b/System.IO.Abstractions.TestingHelpers/MockDirectory.cs index 1a2ac7172..c58573239 100644 --- a/System.IO.Abstractions.TestingHelpers/MockDirectory.cs +++ b/System.IO.Abstractions.TestingHelpers/MockDirectory.cs @@ -296,7 +296,7 @@ public override string[] GetLogicalDrives() return mockFileDataAccessor .AllDirectories .Select(d => new MockDirectoryInfo(mockFileDataAccessor, d).Root.FullName) - .Select(r => mockFileDataAccessor.CaseSensitive ? r : r.ToLowerInvariant()) + .Select(r => mockFileDataAccessor.CaseSensitive ? r : r.ToUpperInvariant()) .Distinct() .ToArray(); } From 8a46f8130977259b459563f37845de2c3dbb0b3c Mon Sep 17 00:00:00 2001 From: Robert Koeninger Date: Wed, 5 Dec 2018 20:53:46 -0500 Subject: [PATCH 11/17] Fixed GetLogicalDrives test --- .../MockDirectoryTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/System.IO.Abstractions.TestingHelpers.Tests/MockDirectoryTests.cs b/System.IO.Abstractions.TestingHelpers.Tests/MockDirectoryTests.cs index e07402d91..d479fbbd3 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/MockDirectoryTests.cs +++ b/System.IO.Abstractions.TestingHelpers.Tests/MockDirectoryTests.cs @@ -884,8 +884,8 @@ public void MockDirectory_GetLogicalDrives_Returns_LogicalDrives() else { Assert.AreEqual(2, drives.Length); - Assert.IsTrue(drives.Contains("c:\\")); - Assert.IsTrue(drives.Contains("d:\\")); + Assert.IsTrue(drives.Contains(@"C:\")); + Assert.IsTrue(drives.Contains(@"D:\")); } } #endif From bcc061cd550d0341b4c6ffafdecb48c75946c25d Mon Sep 17 00:00:00 2001 From: Robert Koeninger Date: Thu, 6 Dec 2018 15:46:55 -0500 Subject: [PATCH 12/17] De-triplicated check for Directory.Exists(DirName(FullPath(p))) --- .../MockFile.cs | 47 ++++++++----------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/System.IO.Abstractions.TestingHelpers/MockFile.cs b/System.IO.Abstractions.TestingHelpers/MockFile.cs index 854fc3e4e..da0987a1d 100644 --- a/System.IO.Abstractions.TestingHelpers/MockFile.cs +++ b/System.IO.Abstractions.TestingHelpers/MockFile.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Security.AccessControl; @@ -55,14 +55,7 @@ public override void AppendAllText(string path, string contents, Encoding encodi if (!mockFileDataAccessor.FileExists(path)) { - var dir = mockFileDataAccessor.Path.GetDirectoryName( - mockFileDataAccessor.Path.GetFullPath(path)); - - if (!mockFileDataAccessor.Directory.Exists(dir)) - { - throw new DirectoryNotFoundException(string.Format(CultureInfo.InvariantCulture, StringResources.Manager.GetString("COULD_NOT_FIND_PART_OF_PATH_EXCEPTION"), path)); - } - + CheckDirectoryExists(path); mockFileDataAccessor.AddFile(path, new MockFileData(contents, encoding)); } else @@ -112,13 +105,7 @@ public override void Copy(string sourceFileName, string destFileName, bool overw throw new FileNotFoundException(string.Format(CultureInfo.InvariantCulture, StringResources.Manager.GetString("COULD_NOT_FIND_FILE_EXCEPTION"), sourceFileName)); } - var directoryNameOfDestination = mockFileDataAccessor.Path.GetDirectoryName( - mockFileDataAccessor.Path.GetFullPath(destFileName)); - - if (!mockFileDataAccessor.Directory.Exists(directoryNameOfDestination)) - { - throw new DirectoryNotFoundException(string.Format(CultureInfo.InvariantCulture, StringResources.Manager.GetString("COULD_NOT_FIND_PART_OF_PATH_EXCEPTION"), destFileName)); - } + CheckDirectoryExists(destFileName); var fileExists = mockFileDataAccessor.FileExists(destFileName); if (fileExists) @@ -157,18 +144,7 @@ private Stream CreateInternal(string path, int bufferSize, FileOptions options, } mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, nameof(path)); - - var directoryPath = mockFileDataAccessor.Path.GetDirectoryName( - mockFileDataAccessor.Path.GetFullPath(path)); - - if (!mockFileDataAccessor.Directory.Exists(directoryPath)) - { - throw new DirectoryNotFoundException( - string.Format( - CultureInfo.InvariantCulture, - StringResources.Manager.GetString("COULD_NOT_FIND_PART_OF_PATH_EXCEPTION"), - path)); - } + CheckDirectoryExists(path); var mockFileData = new MockFileData(new byte[0]) { @@ -979,6 +955,21 @@ internal static string ReadAllBytes(byte[] contents, Encoding encoding) } } + private void CheckDirectoryExists(string path) + { + var pathOps = mockFileDataAccessor.Path; + var dir = pathOps.GetDirectoryName(pathOps.GetFullPath(path)); + + if (!mockFileDataAccessor.Directory.Exists(dir)) + { + throw new DirectoryNotFoundException( + string.Format( + CultureInfo.InvariantCulture, + StringResources.Manager.GetString("COULD_NOT_FIND_PART_OF_PATH_EXCEPTION"), + path)); + } + } + private string ReadAllTextInternal(string path, Encoding encoding) { var mockFileData = mockFileDataAccessor.GetFile(path); From 2053fd6140201b29204474f78def9dc4de716e31 Mon Sep 17 00:00:00 2001 From: Robert Koeninger Date: Thu, 6 Dec 2018 18:25:01 -0500 Subject: [PATCH 13/17] Added StringOperations, used throughout Mock* classes Cleaned up MockFileExistsTests, MockFileSystemTests --- .../MockFileExistsTests.cs | 50 ++++++++----------- .../MockFileSystemTests.cs | 19 ++++--- .../IMockFileDataAccessor.cs | 4 +- .../MockDirectory.cs | 29 +++++++---- .../MockDirectoryInfo.cs | 3 +- .../MockDriveInfo.cs | 4 +- .../MockDriveInfoFactory.cs | 34 ++++--------- .../MockFile.cs | 4 +- .../MockFileSystem.cs | 30 +++++------ .../MockPath.cs | 17 ++++--- .../PathVerifier.cs | 5 +- .../StringOperations.cs | 29 +++++++++++ 12 files changed, 120 insertions(+), 108 deletions(-) create mode 100644 System.IO.Abstractions.TestingHelpers/StringOperations.cs diff --git a/System.IO.Abstractions.TestingHelpers.Tests/MockFileExistsTests.cs b/System.IO.Abstractions.TestingHelpers.Tests/MockFileExistsTests.cs index 8e68da5e4..48cf478ab 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/MockFileExistsTests.cs +++ b/System.IO.Abstractions.TestingHelpers.Tests/MockFileExistsTests.cs @@ -6,21 +6,20 @@ namespace System.IO.Abstractions.TestingHelpers.Tests using XFS = MockUnixSupport; - public class MockFileExistsTests { + public class MockFileExistsTests + { [Test] public void MockFile_Exists_ShouldReturnTrueForSamePath() { // Arrange var fileSystem = new MockFileSystem(new Dictionary { - { XFS.Path(@"c:\something\demo.txt"), new MockFileData("Demo text content") }, - { XFS.Path(@"c:\something\other.gif"), new MockFileData(new byte[] { 0x21, 0x58, 0x3f, 0xa9 }) } + { XFS.Path(@"C:\something\demo.txt"), new MockFileData("Demo text content") }, + { XFS.Path(@"C:\something\other.gif"), new MockFileData("gif content") } }); - var file = new MockFile(fileSystem); - // Act - var result = file.Exists(XFS.Path(@"c:\something\other.gif")); + var result = fileSystem.File.Exists(XFS.Path(@"C:\something\other.gif")); // Assert Assert.IsTrue(result); @@ -33,14 +32,11 @@ public void MockFile_Exists_ShouldReturnTrueForPathVaryingByCase() // Arrange var fileSystem = new MockFileSystem(new Dictionary { - { XFS.Path(@"c:\something\demo.txt"), new MockFileData("Demo text content") }, - { XFS.Path(@"c:\something\other.gif"), new MockFileData(new byte[] { 0x21, 0x58, 0x3f, 0xa9 }) } + { @"C:\something\demo.txt", new MockFileData("Demo text content") } }); - var file = new MockFile(fileSystem); - // Act - var result = file.Exists(XFS.Path(@"c:\SomeThing\Other.gif")); + var result = fileSystem.File.Exists(@"C:\SomeThing\DEMO.txt"); // Assert Assert.IsTrue(result); @@ -53,14 +49,11 @@ public void MockFile_Exists_ShouldReturnFalseForPathVaryingByCase() // Arrange var fileSystem = new MockFileSystem(new Dictionary { - { XFS.Path(@"c:\something\demo.txt"), new MockFileData("Demo text content") }, - { XFS.Path(@"c:\something\other.gif"), new MockFileData(new byte[] { 0x21, 0x58, 0x3f, 0xa9 }) } + { "/something/demo.txt", new MockFileData("Demo text content") } }); - var file = new MockFile(fileSystem); - // Act - var result = file.Exists(XFS.Path(@"c:\SomeThing\Other.gif")); + var result = fileSystem.File.Exists("/SomeThing/Other.gif"); // Assert Assert.IsFalse(result); @@ -72,14 +65,12 @@ public void MockFile_Exists_ShouldReturnFalseForEntirelyDifferentPath() // Arrange var fileSystem = new MockFileSystem(new Dictionary { - { XFS.Path(@"c:\something\demo.txt"), new MockFileData("Demo text content") }, - { XFS.Path(@"c:\something\other.gif"), new MockFileData(new byte[] { 0x21, 0x58, 0x3f, 0xa9 }) } + { XFS.Path(@"C:\something\demo.txt"), new MockFileData("Demo text content") }, + { XFS.Path(@"C:\something\other.gif"), new MockFileData("gif content") } }); - var file = new MockFile(fileSystem); - // Act - var result = file.Exists(XFS.Path(@"c:\SomeThing\DoesNotExist.gif")); + var result = fileSystem.File.Exists(XFS.Path(@"C:\SomeThing\DoesNotExist.gif")); // Assert Assert.IsFalse(result); @@ -88,9 +79,14 @@ public void MockFile_Exists_ShouldReturnFalseForEntirelyDifferentPath() [Test] public void MockFile_Exists_ShouldReturnFalseForNullPath() { - var file = new MockFile(new MockFileSystem()); + // Arrange + var fileSystem = new MockFileSystem(); - Assert.That(file.Exists(null), Is.False); + // Act + var result = fileSystem.File.Exists(null); + + // Assert + Assert.IsFalse(result); } [Test] @@ -99,14 +95,12 @@ public void MockFile_Exists_ShouldReturnFalseForDirectories() // Arrange var fileSystem = new MockFileSystem(new Dictionary { - { XFS.Path(@"c:\something\demo.txt"), new MockFileData("Demo text content") }, - { XFS.Path(@"c:\something\other.gif"), new MockFileData(new byte[] { 0x21, 0x58, 0x3f, 0xa9 }) } + { XFS.Path(@"C:\something\demo.txt"), new MockFileData("Demo text content") }, + { XFS.Path(@"C:\something\other.gif"), new MockFileData("gif content") } }); - var file = new MockFile(fileSystem); - // Act - var result = file.Exists(XFS.Path(@"c:\SomeThing\")); + var result = fileSystem.File.Exists(XFS.Path(@"C:\something\")); // Assert Assert.IsFalse(result); diff --git a/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemTests.cs b/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemTests.cs index 9afc16deb..108ef2fe7 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemTests.cs +++ b/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemTests.cs @@ -17,7 +17,7 @@ public void MockFileSystem_GetFile_ShouldReturnNullWhenFileIsNotRegistered() var fileSystem = new MockFileSystem(new Dictionary { { @"c:\something\demo.txt", new MockFileData("Demo\r\ntext\ncontent\rvalue") }, - { @"c:\something\other.gif", new MockFileData(new byte[] { 0x21, 0x58, 0x3f, 0xa9 }) } + { @"c:\something\other.gif", new MockFileData("gif content") } }); var result = fileSystem.GetFile(@"c:\something\else.txt"); @@ -32,7 +32,7 @@ public void MockFileSystem_GetFile_ShouldReturnFileRegisteredInConstructor() var fileSystem = new MockFileSystem(new Dictionary { { @"c:\something\demo.txt", file1 }, - { @"c:\something\other.gif", new MockFileData(new byte[] { 0x21, 0x58, 0x3f, 0xa9 }) } + { @"c:\something\other.gif", new MockFileData("gif content") } }); var result = fileSystem.GetFile(@"c:\something\demo.txt"); @@ -48,7 +48,7 @@ public void MockFileSystem_GetFile_ShouldReturnFileRegisteredInConstructorWhenPa var fileSystem = new MockFileSystem(new Dictionary { { @"c:\something\demo.txt", file1 }, - { @"c:\something\other.gif", new MockFileData(new byte[] { 0x21, 0x58, 0x3f, 0xa9 }) } + { @"c:\something\other.gif", new MockFileData("gif content") } }); var result = fileSystem.GetFile(@"c:\SomeThing\DEMO.txt"); @@ -60,11 +60,10 @@ public void MockFileSystem_GetFile_ShouldReturnFileRegisteredInConstructorWhenPa [UnixOnly(UnixSpecifics.CaseSensitivity)] public void MockFileSystem_GetFile_ShouldNotReturnFileRegisteredInConstructorWhenPathsDifferByCase() { - var file1 = new MockFileData("Demo\r\ntext\ncontent\rvalue"); var fileSystem = new MockFileSystem(new Dictionary { - { "/something/demo.txt", file1 }, - { "/something/other.gif", new MockFileData(new byte[] { 0x21, 0x58, 0x3f, 0xa9 }) } + { "/something/demo.txt", new MockFileData("Demo\r\ntext\ncontent\rvalue") }, + { "/something/other.gif", new MockFileData("gif content") } }); var result = fileSystem.GetFile("/SomeThing/DEMO.txt"); @@ -76,13 +75,13 @@ public void MockFileSystem_GetFile_ShouldNotReturnFileRegisteredInConstructorWhe public void MockFileSystem_AddFile_ShouldHandleUnnormalizedSlashes() { var path = XFS.Path(@"c:\d1\d2\file.txt"); - var altPath = XFS.Path("c:/d1/d2/file.txt"); - var altParentPath = XFS.Path("c://d1//d2/"); + var alternatePath = XFS.Path("c:/d1/d2/file.txt"); + var alternateParentPath = XFS.Path("c://d1//d2/"); var fs = new MockFileSystem(); fs.AddFile(path, new MockFileData("Hello")); - var fileCount = fs.Directory.GetFiles(altParentPath).Length; - var fileExists = fs.File.Exists(altPath); + var fileCount = fs.Directory.GetFiles(alternateParentPath).Length; + var fileExists = fs.File.Exists(alternatePath); Assert.AreEqual(1, fileCount); Assert.IsTrue(fileExists); diff --git a/System.IO.Abstractions.TestingHelpers/IMockFileDataAccessor.cs b/System.IO.Abstractions.TestingHelpers/IMockFileDataAccessor.cs index 8ba49c4ca..6c2c6350a 100644 --- a/System.IO.Abstractions.TestingHelpers/IMockFileDataAccessor.cs +++ b/System.IO.Abstractions.TestingHelpers/IMockFileDataAccessor.cs @@ -54,9 +54,7 @@ public interface IMockFileDataAccessor /// IEnumerable AllDirectories { get; } - bool CaseSensitive { get; } - StringComparer Comparer { get; } - StringComparison Comparison { get; } + StringOperations StringOperations { get; } FileBase File { get; } DirectoryBase Directory { get; } diff --git a/System.IO.Abstractions.TestingHelpers/MockDirectory.cs b/System.IO.Abstractions.TestingHelpers/MockDirectory.cs index c58573239..c38d0ce4a 100644 --- a/System.IO.Abstractions.TestingHelpers/MockDirectory.cs +++ b/System.IO.Abstractions.TestingHelpers/MockDirectory.cs @@ -14,6 +14,12 @@ public class MockDirectory : DirectoryBase private readonly IMockFileDataAccessor mockFileDataAccessor; private string currentDirectory; + // This constructor is retained to avoid breaking change + public MockDirectory(IMockFileDataAccessor mockFileDataAccessor, FileBase fileBase, string currentDirectory) : + this(mockFileDataAccessor, currentDirectory) + { + } + public MockDirectory(IMockFileDataAccessor mockFileDataAccessor, string currentDirectory) : base(mockFileDataAccessor?.FileSystem) { this.currentDirectory = currentDirectory; @@ -71,7 +77,7 @@ public override void Delete(string path, bool recursive) path = mockFileDataAccessor.Path.GetFullPath(path).TrimSlashes(); var affectedPaths = mockFileDataAccessor .AllPaths - .Where(p => p.StartsWith(path, mockFileDataAccessor.Comparison)) + .Where(p => mockFileDataAccessor.StringOperations.StartsWith(p, path)) .ToList(); if (!affectedPaths.Any()) @@ -96,7 +102,7 @@ public override bool Exists(string path) { path = path.TrimSlashes(); path = mockFileDataAccessor.Path.GetFullPath(path); - return mockFileDataAccessor.AllDirectories.Any(p => p.Equals(path, mockFileDataAccessor.Comparison)); + return mockFileDataAccessor.AllDirectories.Any(p => mockFileDataAccessor.StringOperations.Equals(p, path)); } catch (Exception) { @@ -296,7 +302,7 @@ public override string[] GetLogicalDrives() return mockFileDataAccessor .AllDirectories .Select(d => new MockDirectoryInfo(mockFileDataAccessor, d).Root.FullName) - .Select(r => mockFileDataAccessor.CaseSensitive ? r : r.ToUpperInvariant()) + .Select(r => mockFileDataAccessor.StringOperations.ToUpper(r)) .Distinct() .ToArray(); } @@ -325,7 +331,7 @@ public override DirectoryInfoBase GetParent(string path) if (absolutePath != sepAsString) { - var startIndex = absolutePath.EndsWith(sepAsString, mockFileDataAccessor.Comparison) + var startIndex = mockFileDataAccessor.StringOperations.EndsWith(absolutePath, sepAsString) ? absolutePath.Length - 1 : absolutePath.Length; lastIndex = absolutePath.LastIndexOf(mockFileDataAccessor.Path.DirectorySeparatorChar, startIndex - 1); @@ -363,14 +369,15 @@ public override void Move(string sourceDirName, string destDirName) var fullSourcePath = mockFileDataAccessor.Path.GetFullPath(sourceDirName).TrimSlashes(); var fullDestPath = mockFileDataAccessor.Path.GetFullPath(destDirName).TrimSlashes(); - if (string.Equals(fullSourcePath, fullDestPath, mockFileDataAccessor.Comparison)) + if (mockFileDataAccessor.StringOperations.Equals(fullSourcePath, fullDestPath)) { throw new IOException("Source and destination path must be different."); } var sourceRoot = mockFileDataAccessor.Path.GetPathRoot(fullSourcePath); var destinationRoot = mockFileDataAccessor.Path.GetPathRoot(fullDestPath); - if (!string.Equals(sourceRoot, destinationRoot, mockFileDataAccessor.Comparison)) + + if (!mockFileDataAccessor.StringOperations.Equals(sourceRoot, destinationRoot)) { throw new IOException("Source and destination path must have identical roots. Move will not work across volumes."); } @@ -457,7 +464,7 @@ public override IEnumerable EnumerateDirectories(string path, string sea path = path.TrimSlashes(); path = mockFileDataAccessor.Path.GetFullPath(path); return GetFilesInternal(mockFileDataAccessor.AllDirectories, path, searchPattern, searchOption) - .Where(p => mockFileDataAccessor.Comparer.Compare(p, path) != 0); + .Where(p => !mockFileDataAccessor.StringOperations.Equals(p, path)); } public override IEnumerable EnumerateFiles(string path) @@ -513,15 +520,17 @@ private void CheckSearchPattern(string searchPattern) const string TWO_DOTS = ".."; Func createException = () => new ArgumentException(@"Search pattern cannot contain "".."" to move up directories and can be contained only internally in file/directory names, as in ""a..b"".", searchPattern); - if (searchPattern.EndsWith(TWO_DOTS, mockFileDataAccessor.Comparison)) + if (mockFileDataAccessor.StringOperations.EndsWith(searchPattern, TWO_DOTS)) { throw createException(); } - int position; - if ((position = searchPattern.IndexOf(TWO_DOTS, mockFileDataAccessor.Comparison)) >= 0) + var position = mockFileDataAccessor.StringOperations.IndexOf(searchPattern, TWO_DOTS); + + if (position >= 0) { var characterAfterTwoDots = searchPattern[position + 2]; + if (characterAfterTwoDots == Path.DirectorySeparatorChar || characterAfterTwoDots == Path.AltDirectorySeparatorChar) { throw createException(); diff --git a/System.IO.Abstractions.TestingHelpers/MockDirectoryInfo.cs b/System.IO.Abstractions.TestingHelpers/MockDirectoryInfo.cs index 4ce82e50e..0a9b651bc 100644 --- a/System.IO.Abstractions.TestingHelpers/MockDirectoryInfo.cs +++ b/System.IO.Abstractions.TestingHelpers/MockDirectoryInfo.cs @@ -74,7 +74,8 @@ public override string FullName get { var root = mockFileDataAccessor.Path.GetPathRoot(directoryPath); - if (string.Equals(directoryPath, root, mockFileDataAccessor.Comparison)) + + if (mockFileDataAccessor.StringOperations.Equals(directoryPath, root)) { // drives have the trailing slash return directoryPath; diff --git a/System.IO.Abstractions.TestingHelpers/MockDriveInfo.cs b/System.IO.Abstractions.TestingHelpers/MockDriveInfo.cs index 7b8398856..61ec413b4 100644 --- a/System.IO.Abstractions.TestingHelpers/MockDriveInfo.cs +++ b/System.IO.Abstractions.TestingHelpers/MockDriveInfo.cs @@ -18,7 +18,7 @@ public MockDriveInfo(IMockFileDataAccessor mockFileDataAccessor, string name) : if (name.Length == 1 || (name.Length == 2 && name[1] == ':') - || (name.Length == 3 && name.EndsWith(DRIVE_SEPARATOR, mockFileDataAccessor.Comparison))) + || (name.Length == 3 && mockFileDataAccessor.StringOperations.EndsWith(name, DRIVE_SEPARATOR))) { name = name[0] + DRIVE_SEPARATOR; } @@ -27,7 +27,7 @@ public MockDriveInfo(IMockFileDataAccessor mockFileDataAccessor, string name) : mockFileDataAccessor.PathVerifier.CheckInvalidPathChars(name); name = mockFileDataAccessor.Path.GetPathRoot(name); - if (string.IsNullOrEmpty(name) || name.StartsWith(@"\\", mockFileDataAccessor.Comparison)) + if (string.IsNullOrEmpty(name) || mockFileDataAccessor.StringOperations.StartsWith(name, @"\\")) { throw new ArgumentException( @"Object must be a root directory (""C:\"") or a drive letter (""C"")."); diff --git a/System.IO.Abstractions.TestingHelpers/MockDriveInfoFactory.cs b/System.IO.Abstractions.TestingHelpers/MockDriveInfoFactory.cs index 1f384632a..bd1596a00 100644 --- a/System.IO.Abstractions.TestingHelpers/MockDriveInfoFactory.cs +++ b/System.IO.Abstractions.TestingHelpers/MockDriveInfoFactory.cs @@ -47,12 +47,12 @@ public DriveInfoBase FromDriveName(string driveName) private string NormalizeDriveName(string driveName) { - if (driveName.Length == 3 && driveName.EndsWith(@":\", mockFileSystem.Comparison)) + if (driveName.Length == 3 && mockFileSystem.StringOperations.EndsWith(driveName, @":\")) { - return (mockFileSystem.CaseSensitive ? driveName[0] : char.ToUpperInvariant(driveName[0])) + @":\"; + return mockFileSystem.StringOperations.ToUpper(driveName[0]) + @":\"; } - if (driveName.StartsWith(@"\\", mockFileSystem.Comparison)) + if (mockFileSystem.StringOperations.StartsWith(driveName, @"\\")) { return null; } @@ -71,32 +71,18 @@ public DriveEqualityComparer(IMockFileDataAccessor mockFileSystem) public bool Equals(string x, string y) { - if (ReferenceEquals(x, y)) - { - return true; - } - - if (ReferenceEquals(x, null)) - { - return false; - } - - if (ReferenceEquals(y, null)) - { - return false; - } - - if (x[1] == ':' && y[1] == ':') - { - return mockFileSystem.Comparer.Compare(x.Substring(0, 1), y.Substring(0, 1)) == 0; - } + return ReferenceEquals(x, y) || + (HasDrivePrefix(x) && HasDrivePrefix(y) && mockFileSystem.StringOperations.Equals(x[0], y[0])); + } - return false; + private static bool HasDrivePrefix(string x) + { + return x != null && x.Length >= 2 && x[1] == ':'; } public int GetHashCode(string obj) { - return (mockFileSystem.CaseSensitive ? obj : obj.ToUpperInvariant()).GetHashCode(); + return mockFileSystem.StringOperations.ToUpper(obj).GetHashCode(); } } } diff --git a/System.IO.Abstractions.TestingHelpers/MockFile.cs b/System.IO.Abstractions.TestingHelpers/MockFile.cs index da0987a1d..79028bba7 100644 --- a/System.IO.Abstractions.TestingHelpers/MockFile.cs +++ b/System.IO.Abstractions.TestingHelpers/MockFile.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Security.AccessControl; @@ -551,7 +551,7 @@ public override void Replace(string sourceFileName, string destinationFileName, if (destinationBackupFileName != null) { - Copy(destinationFileName, destinationBackupFileName, true); + Copy(destinationFileName, destinationBackupFileName, overwrite: true); } Delete(destinationFileName); diff --git a/System.IO.Abstractions.TestingHelpers/MockFileSystem.cs b/System.IO.Abstractions.TestingHelpers/MockFileSystem.cs index 664c20a09..cd3d15f64 100644 --- a/System.IO.Abstractions.TestingHelpers/MockFileSystem.cs +++ b/System.IO.Abstractions.TestingHelpers/MockFileSystem.cs @@ -25,11 +25,9 @@ public MockFileSystem(IDictionary files, string currentDir currentDirectory = XFS.Path(DEFAULT_CURRENT_DIRECTORY); } - CaseSensitive = XFS.IsUnixPlatform(); - Comparer = CaseSensitive ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase; - Comparison = CaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase; + StringOperations = new StringOperations(XFS.IsUnixPlatform()); pathVerifier = new PathVerifier(this); - this.files = new Dictionary(Comparer); + this.files = new Dictionary(StringOperations.Comparer); Path = new MockPath(this); File = new MockFile(this); @@ -54,9 +52,7 @@ public MockFileSystem(IDictionary files, string currentDir } } - public bool CaseSensitive { get; } - public StringComparer Comparer { get; } - public StringComparison Comparison { get; } + public StringOperations StringOperations { get; } public FileBase File { get; } public DirectoryBase Directory { get; } public IFileInfoFactory FileInfo { get; } @@ -97,7 +93,7 @@ private string GetPathWithCorrectDirectoryCapitalization(string fullPath) if (Directory.Exists(leftHalf)) { leftHalf = Path.GetFullPath(leftHalf).TrimSlashes(); - string baseDirectory = AllDirectories.First(dir => dir.Equals(leftHalf, Comparison)); + string baseDirectory = AllDirectories.First(dir => StringOperations.Equals(dir, leftHalf)); return baseDirectory + Path.DirectorySeparatorChar + rightHalf; } } @@ -158,15 +154,15 @@ public void AddDirectory(string path) throw new UnauthorizedAccessException(string.Format(CultureInfo.InvariantCulture, StringResources.Manager.GetString("ACCESS_TO_THE_PATH_IS_DENIED"), fixedPath)); var lastIndex = 0; - - bool isUnc = - fixedPath.StartsWith(@"\\", Comparison) || - fixedPath.StartsWith(@"//", Comparison); + var isUnc = + StringOperations.StartsWith(fixedPath, @"\\") || + StringOperations.StartsWith(fixedPath, @"//"); if (isUnc) { //First, confirm they aren't trying to create '\\server\' - lastIndex = fixedPath.IndexOf(separator, 2, Comparison); + lastIndex = StringOperations.IndexOf(fixedPath, separator, 2); + if (lastIndex < 0) throw new ArgumentException(@"The UNC path should be of the form \\server\share.", "path"); @@ -176,7 +172,7 @@ public void AddDirectory(string path) */ } - while ((lastIndex = fixedPath.IndexOf(separator, lastIndex + 1, Comparison)) > -1) + while ((lastIndex = StringOperations.IndexOf(fixedPath, separator, lastIndex + 1)) > -1) { var segment = fixedPath.Substring(0, lastIndex + 1); if (!Directory.Exists(segment)) @@ -185,7 +181,7 @@ public void AddDirectory(string path) } } - var s = fixedPath.EndsWith(separator, Comparison) ? fixedPath : fixedPath + separator; + var s = StringOperations.EndsWith(fixedPath, separator) ? fixedPath : fixedPath + separator; SetEntry(s, new MockDirectoryData()); } } @@ -231,12 +227,12 @@ public void MoveDirectory(string sourcePath, string destPath) lock (files) { var affectedPaths = files.Keys - .Where(p => p.StartsWith(sourcePath, Comparison)) + .Where(p => StringOperations.StartsWith(p, sourcePath)) .ToList(); foreach(var path in affectedPaths) { - var newPath = path.Replace(sourcePath, destPath, Comparison); + var newPath = StringOperations.Replace(path, sourcePath, destPath); files[newPath] = files[path]; files.Remove(path); } diff --git a/System.IO.Abstractions.TestingHelpers/MockPath.cs b/System.IO.Abstractions.TestingHelpers/MockPath.cs index ec645da09..a1929b457 100644 --- a/System.IO.Abstractions.TestingHelpers/MockPath.cs +++ b/System.IO.Abstractions.TestingHelpers/MockPath.cs @@ -32,8 +32,8 @@ public override string GetFullPath(string path) path = path.Replace(AltDirectorySeparatorChar, DirectorySeparatorChar); bool isUnc = - path.StartsWith(@"\\", mockFileDataAccessor.Comparison) || - path.StartsWith(@"//", mockFileDataAccessor.Comparison); + mockFileDataAccessor.StringOperations.StartsWith(path, @"\\") || + mockFileDataAccessor.StringOperations.StartsWith(path, @"//"); string root = GetPathRoot(path); @@ -56,7 +56,8 @@ public override string GetFullPath(string path) throw new ArgumentException(@"The UNC path should be of the form \\server\share.", "path"); } } - else if (@"\".Equals(root, mockFileDataAccessor.Comparison) || @"/".Equals(root, mockFileDataAccessor.Comparison)) + else if (mockFileDataAccessor.StringOperations.Equals(@"\", root) || + mockFileDataAccessor.StringOperations.Equals(@"/", root)) { // absolute path on the current drive or volume pathSegments = GetSegments(GetPathRoot(mockFileDataAccessor.Directory.GetCurrentDirectory()), path); @@ -67,9 +68,9 @@ public override string GetFullPath(string path) } // unc paths need at least two segments, the others need one segment - bool isUnixRooted = - mockFileDataAccessor.Directory.GetCurrentDirectory() - .StartsWith(string.Format(CultureInfo.InvariantCulture, "{0}", DirectorySeparatorChar), mockFileDataAccessor.Comparison); + var isUnixRooted = mockFileDataAccessor.StringOperations.StartsWith( + mockFileDataAccessor.Directory.GetCurrentDirectory(), + string.Format(CultureInfo.InvariantCulture, "{0}", DirectorySeparatorChar)); var minPathSegments = isUnc ? 2 @@ -78,7 +79,7 @@ public override string GetFullPath(string path) var stack = new Stack(); foreach (var segment in pathSegments) { - if ("..".Equals(segment, mockFileDataAccessor.Comparison)) + if (mockFileDataAccessor.StringOperations.Equals("..", segment)) { // only pop, if afterwards are at least the minimal amount of path segments if (stack.Count > minPathSegments) @@ -86,7 +87,7 @@ public override string GetFullPath(string path) stack.Pop(); } } - else if (".".Equals(segment, mockFileDataAccessor.Comparison)) + else if (mockFileDataAccessor.StringOperations.Equals(".", segment)) { // ignore . } diff --git a/System.IO.Abstractions.TestingHelpers/PathVerifier.cs b/System.IO.Abstractions.TestingHelpers/PathVerifier.cs index eb103aa23..a1d11cfdd 100644 --- a/System.IO.Abstractions.TestingHelpers/PathVerifier.cs +++ b/System.IO.Abstractions.TestingHelpers/PathVerifier.cs @@ -6,6 +6,7 @@ namespace System.IO.Abstractions.TestingHelpers public class PathVerifier { + private static readonly char[] AdditionalInvalidPathChars = { '*', '?' }; private readonly IMockFileDataAccessor _mockFileDataAccessor; internal PathVerifier(IMockFileDataAccessor mockFileDataAccessor) @@ -42,7 +43,7 @@ public void IsLegalAbsoluteOrRelative(string path, string paramName) var filePath = ExtractFilePath(path); - if (HasIllegalCharacters(filePath, false)) + if (HasIllegalCharacters(filePath, checkAdditional: false)) { throw new ArgumentException(StringResources.Manager.GetString("ILLEGAL_CHARACTERS_IN_PATH_EXCEPTION")); } @@ -69,8 +70,6 @@ private string ExtractFilePath(string fullFileName) return string.Join(_mockFileDataAccessor.Path.DirectorySeparatorChar.ToString(), extractFilePath.Take(extractFilePath.Length - 1)); } - private readonly char[] AdditionalInvalidPathChars = { '*', '?' }; - public bool HasIllegalCharacters(string path, bool checkAdditional) { if (path == null) diff --git a/System.IO.Abstractions.TestingHelpers/StringOperations.cs b/System.IO.Abstractions.TestingHelpers/StringOperations.cs new file mode 100644 index 000000000..289426a8c --- /dev/null +++ b/System.IO.Abstractions.TestingHelpers/StringOperations.cs @@ -0,0 +1,29 @@ +namespace System.IO.Abstractions.TestingHelpers +{ + [Serializable] + public class StringOperations + { + private readonly bool caseSensitive; + private readonly StringComparison comparison; + + public StringOperations(bool caseSensitive) + { + this.caseSensitive = caseSensitive; + comparison = caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase; + } + + public StringComparer Comparer => caseSensitive ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase; + public bool StartsWith(string s, string prefix) => s.StartsWith(prefix, comparison); + public bool EndsWith(string s, string suffix) => s.EndsWith(suffix, comparison); + public bool Equals(string x, string y) => string.Equals(x, y, comparison); + public bool Equals(char x, char y) => caseSensitive ? x == y : char.ToUpper(x) == char.ToUpper(y); + public int IndexOf(string s, string substring) => s.IndexOf(substring, comparison); + public int IndexOf(string s, string substring, int startIndex) => s.IndexOf(substring, startIndex, comparison); + public bool Contains(string s, string substring) => s.IndexOf(substring, comparison) >= 0; + public string Replace(string s, string oldValue, string newValue) => s.Replace(oldValue, newValue, comparison); + public char ToLower(char c) => caseSensitive ? c : char.ToLower(c); + public char ToUpper(char c) => caseSensitive ? c : char.ToUpper(c); + public string ToLower(string s) => caseSensitive ? s : s.ToLower(); + public string ToUpper(string s) => caseSensitive ? s : s.ToUpper(); + } +} From 93d322a4dd569bfa91f7f81df4507b1490206ea4 Mon Sep 17 00:00:00 2001 From: Robert Koeninger Date: Thu, 6 Dec 2018 18:33:02 -0500 Subject: [PATCH 14/17] Removed WindowsSpecifics.EmptyInvalidPathChars Removed WindowsOnly label from the one that that used this reason --- .../MockDirectoryTests.cs | 1 - System.IO.Abstractions.TestingHelpers.Tests/WindowsSpecifics.cs | 2 -- 2 files changed, 3 deletions(-) diff --git a/System.IO.Abstractions.TestingHelpers.Tests/MockDirectoryTests.cs b/System.IO.Abstractions.TestingHelpers.Tests/MockDirectoryTests.cs index d479fbbd3..5de88ce2d 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/MockDirectoryTests.cs +++ b/System.IO.Abstractions.TestingHelpers.Tests/MockDirectoryTests.cs @@ -1285,7 +1285,6 @@ public void MockDirectory_GetParent_ShouldReturnADirectoryInfoIfPathDoesNotExist } [Test] - [WindowsOnly(WindowsSpecifics.EmptyInvalidPathChars)] public void MockDirectory_GetParent_ShouldThrowArgumentExceptionIfPathHasIllegalCharacters() { // Arrange diff --git a/System.IO.Abstractions.TestingHelpers.Tests/WindowsSpecifics.cs b/System.IO.Abstractions.TestingHelpers.Tests/WindowsSpecifics.cs index 71626f6ec..86bde580e 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/WindowsSpecifics.cs +++ b/System.IO.Abstractions.TestingHelpers.Tests/WindowsSpecifics.cs @@ -10,8 +10,6 @@ internal static class WindowsSpecifics public const string StrictPathRules = "Windows has stricter path rules than other platforms"; - public const string EmptyInvalidPathChars = "Path.GetInvalidPathChars() doesn't return anything on Mono"; - public const string CaseInsensitivity = "Paths are case-insensitive on Windows"; } } From b4d5905e584f2df0f00a4d9a77c515cf6c7e480b Mon Sep 17 00:00:00 2001 From: Robert Koeninger Date: Thu, 6 Dec 2018 18:46:45 -0500 Subject: [PATCH 15/17] Made MockDirectory_GetParent_ShouldThr... WindowsOnly again because of Windows' stricter path rules --- .../MockDirectoryTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/System.IO.Abstractions.TestingHelpers.Tests/MockDirectoryTests.cs b/System.IO.Abstractions.TestingHelpers.Tests/MockDirectoryTests.cs index 5de88ce2d..250241dd2 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/MockDirectoryTests.cs +++ b/System.IO.Abstractions.TestingHelpers.Tests/MockDirectoryTests.cs @@ -1285,6 +1285,7 @@ public void MockDirectory_GetParent_ShouldReturnADirectoryInfoIfPathDoesNotExist } [Test] + [WindowsOnly(WindowsSpecifics.StrictPathRules)] public void MockDirectory_GetParent_ShouldThrowArgumentExceptionIfPathHasIllegalCharacters() { // Arrange From 46ae1c21e53e2ebd0616aac4a29d96b6744b599e Mon Sep 17 00:00:00 2001 From: Robert Koeninger Date: Thu, 6 Dec 2018 18:47:17 -0500 Subject: [PATCH 16/17] Combined private CheckDirectoryExists and VerifyDir... in MockFile --- .../MockFile.cs | 31 ++++++------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/System.IO.Abstractions.TestingHelpers/MockFile.cs b/System.IO.Abstractions.TestingHelpers/MockFile.cs index 79028bba7..a2d24d91f 100644 --- a/System.IO.Abstractions.TestingHelpers/MockFile.cs +++ b/System.IO.Abstractions.TestingHelpers/MockFile.cs @@ -55,7 +55,7 @@ public override void AppendAllText(string path, string contents, Encoding encodi if (!mockFileDataAccessor.FileExists(path)) { - CheckDirectoryExists(path); + VerifyDirectoryExists(path); mockFileDataAccessor.AddFile(path, new MockFileData(contents, encoding)); } else @@ -105,7 +105,7 @@ public override void Copy(string sourceFileName, string destFileName, bool overw throw new FileNotFoundException(string.Format(CultureInfo.InvariantCulture, StringResources.Manager.GetString("COULD_NOT_FIND_FILE_EXCEPTION"), sourceFileName)); } - CheckDirectoryExists(destFileName); + VerifyDirectoryExists(destFileName); var fileExists = mockFileDataAccessor.FileExists(destFileName); if (fileExists) @@ -144,7 +144,7 @@ private Stream CreateInternal(string path, int bufferSize, FileOptions options, } mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, nameof(path)); - CheckDirectoryExists(path); + VerifyDirectoryExists(path); var mockFileData = new MockFileData(new byte[0]) { @@ -955,21 +955,6 @@ internal static string ReadAllBytes(byte[] contents, Encoding encoding) } } - private void CheckDirectoryExists(string path) - { - var pathOps = mockFileDataAccessor.Path; - var dir = pathOps.GetDirectoryName(pathOps.GetFullPath(path)); - - if (!mockFileDataAccessor.Directory.Exists(dir)) - { - throw new DirectoryNotFoundException( - string.Format( - CultureInfo.InvariantCulture, - StringResources.Manager.GetString("COULD_NOT_FIND_PART_OF_PATH_EXCEPTION"), - path)); - } - } - private string ReadAllTextInternal(string path, Encoding encoding) { var mockFileData = mockFileDataAccessor.GetFile(path); @@ -986,14 +971,16 @@ private void VerifyValueIsNotNull(object value, string parameterName) private void VerifyDirectoryExists(string path) { - DirectoryInfoBase dir = mockFileDataAccessor.Directory.GetParent(path); - if (!dir.Exists) + var pathOps = mockFileDataAccessor.Path; + var dir = pathOps.GetDirectoryName(pathOps.GetFullPath(path)); + + if (!mockFileDataAccessor.Directory.Exists(dir)) { throw new DirectoryNotFoundException( string.Format( CultureInfo.InvariantCulture, - StringResources.Manager.GetString("COULD_NOT_FIND_PART_OF_PATH_EXCEPTION"), - dir)); + StringResources.Manager.GetString("COULD_NOT_FIND_PART_OF_PATH_EXCEPTION"), + path)); } } } From 057a4e831d6a177884e0c90093cde6da620e68ae Mon Sep 17 00:00:00 2001 From: Robert Koeninger Date: Thu, 6 Dec 2018 18:50:13 -0500 Subject: [PATCH 17/17] Cleanup, review change, in MockFileExistsTests --- .../MockFileExistsTests.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/System.IO.Abstractions.TestingHelpers.Tests/MockFileExistsTests.cs b/System.IO.Abstractions.TestingHelpers.Tests/MockFileExistsTests.cs index 48cf478ab..3a4b37f1f 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/MockFileExistsTests.cs +++ b/System.IO.Abstractions.TestingHelpers.Tests/MockFileExistsTests.cs @@ -14,7 +14,6 @@ public void MockFile_Exists_ShouldReturnTrueForSamePath() // Arrange var fileSystem = new MockFileSystem(new Dictionary { - { XFS.Path(@"C:\something\demo.txt"), new MockFileData("Demo text content") }, { XFS.Path(@"C:\something\other.gif"), new MockFileData("gif content") } }); @@ -53,7 +52,7 @@ public void MockFile_Exists_ShouldReturnFalseForPathVaryingByCase() }); // Act - var result = fileSystem.File.Exists("/SomeThing/Other.gif"); + var result = fileSystem.File.Exists("/SomeThing/DEMO.txt"); // Assert Assert.IsFalse(result);