From f5e93dfd2587d0926472cacc545ca67a2611abe0 Mon Sep 17 00:00:00 2001 From: muthumalla Date: Tue, 23 Oct 2018 11:31:02 -0700 Subject: [PATCH 01/24] Correcting build status indentation (#377) --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 64a1e40e2..1b6229872 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ [![NuGet](https://img.shields.io/nuget/v/System.IO.Abstractions.svg)](https://www.nuget.org/packages/System.IO.Abstractions) - [![Windows build status](https://ci.appveyor.com/api/projects/status/em172apw1v5k70vq/branch/master?svg=true)](https://ci.appveyor.com/project/tathamoddie/system-io-abstractions/branch/master) on Windows - [![Linux build status](https://travis-ci.org/System-IO-Abstractions/System.IO.Abstractions.svg?branch=master)](https://travis-ci.org/System-IO-Abstractions/System.IO.Abstractions) on Linux --- From 0a8ed37d71a97775121439400f313d93e25acfdd Mon Sep 17 00:00:00 2001 From: Erik Date: Wed, 24 Oct 2018 22:23:53 +0300 Subject: [PATCH 02/24] Clone binary contents rather than text contents (#378) Fixes #375 When copying a file, TextContents (and underlying a TextReader) was used to copy a file rather than the actual byte[] Contents. This caused an issue when the bytes could not be read using the default UTF8 encoding. --- .../MockFileCopyTests.cs | 22 +++++++++++++++++++ .../MockFileData.cs | 2 +- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/System.IO.Abstractions.TestingHelpers.Tests/MockFileCopyTests.cs b/System.IO.Abstractions.TestingHelpers.Tests/MockFileCopyTests.cs index c41b353d5..53ccb5d13 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/MockFileCopyTests.cs +++ b/System.IO.Abstractions.TestingHelpers.Tests/MockFileCopyTests.cs @@ -48,6 +48,28 @@ public void MockFile_Copy_ShouldCloneContents() Assert.AreEqual("Original", mockFileSystem.File.ReadAllText(destFileName)); } + [Test] + public void MockFile_Copy_ShouldCloneBinaryContents() + { + var sourceFileName = XFS.Path(@"c:\source\demo.bin"); + var destFileName = XFS.Path(@"c:\source\demo_copy.bin"); + + byte[] original = new byte[] { 0xC0 }; + var mockFileSystem = new MockFileSystem(); + mockFileSystem.AddFile(sourceFileName, new MockFileData(original)); + mockFileSystem.File.Copy(sourceFileName, destFileName); + + using (var stream = mockFileSystem.File.Open(sourceFileName, FileMode.Open, FileAccess.ReadWrite)) + { + var binaryWriter = new System.IO.BinaryWriter(stream); + + binaryWriter.Seek(0, SeekOrigin.Begin); + binaryWriter.Write("Modified"); + } + + CollectionAssert.AreEqual(original, mockFileSystem.File.ReadAllBytes(destFileName)); + } + [Test] public void MockFile_Copy_ShouldCreateFileAtNewDestination() { diff --git a/System.IO.Abstractions.TestingHelpers/MockFileData.cs b/System.IO.Abstractions.TestingHelpers/MockFileData.cs index 6d433bc28..15aa84298 100644 --- a/System.IO.Abstractions.TestingHelpers/MockFileData.cs +++ b/System.IO.Abstractions.TestingHelpers/MockFileData.cs @@ -96,7 +96,7 @@ public MockFileData(MockFileData template) accessControl = template.accessControl; Attributes = template.Attributes; - TextContents = template.TextContents; + Contents = template.Contents.ToArray(); CreationTime = template.CreationTime; LastAccessTime = template.LastAccessTime; LastWriteTime = template.LastWriteTime; From f05c7fdadfc67eb451df4d803459226d46790782 Mon Sep 17 00:00:00 2001 From: Florian Greinacher Date: Thu, 25 Oct 2018 19:03:27 +0200 Subject: [PATCH 03/24] Use Nerdbank.GitVersioning (#369) - Calculate version number dynamically - Support pushing prerelease versions on NuGet - Reduce downstream maintenance efforts by fixing the version number to MAJOR.0.0.0 - Create GitHub releases for everything that's pushed to NuGet --- .travis.yml | 2 ++ CONTRIBUTING.md | 18 +++++++++++++++ ...stem.IO.Abstractions.TestingHelpers.csproj | 9 +++++++- .../System.IO.Abstractions.csproj | 14 +++++++++--- appveyor.yml | 22 +++++++++---------- version.json | 16 ++++++++++++++ 6 files changed, 66 insertions(+), 15 deletions(-) create mode 100644 CONTRIBUTING.md create mode 100644 version.json diff --git a/.travis.yml b/.travis.yml index c49eb8654..29bffca69 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,3 +5,5 @@ script: - dotnet build System.IO.Abstractions --framework netstandard1.4 - dotnet build System.IO.Abstractions.TestingHelpers --framework netstandard1.4 - dotnet test System.IO.Abstractions.TestingHelpers.Tests --framework netcoreapp2.0 +git: + depth: false \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..fee603cf8 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,18 @@ +# Contributor guide + +## Versioning + +This library uses [Nerdbank.GitVersioning](https://github.com/AArnott/Nerdbank.GitVersioning) for generating stable and reproducible version numbers. + +The so-called base version is manually maintained in [the version config](version.json). Every build calculates its final version number based on the base version and the number of changes that occured since the last change to the version config. + +The base version represents the _next_ version that we will released. During development it contains a prerelease suffix, like `-beta` which is appended to the generated NuGet packages. + +Every successful commit on `master` deploys packages to `nuget.org` and a creates GitHub release. As long as we have the prelease suffix both will be marked as such. + +### Release workflow + +1. Remove prelease suffix from `version.json`. +1. Wait for the completion of the deployment. +1. Remove the prerelease flag from the newly created GitHub release. +1. Increment the version number in `version.json` and again add the prelease suffix (usually `beta` is fine). \ No newline at end of file diff --git a/System.IO.Abstractions.TestingHelpers/System.IO.Abstractions.TestingHelpers.csproj b/System.IO.Abstractions.TestingHelpers/System.IO.Abstractions.TestingHelpers.csproj index b2dea9e9b..09239e755 100644 --- a/System.IO.Abstractions.TestingHelpers/System.IO.Abstractions.TestingHelpers.csproj +++ b/System.IO.Abstractions.TestingHelpers/System.IO.Abstractions.TestingHelpers.csproj @@ -2,7 +2,6 @@ net40;netstandard1.4;netstandard2.0; System.IO.Abstractions.TestingHelpers - 0.0.0.1 A set of pre-built mocks to help when testing file system interactions. System.IO.Abstractions @@ -36,4 +35,12 @@ + + + + runtime; build; native; contentfiles; analyzers + all + + + diff --git a/System.IO.Abstractions/System.IO.Abstractions.csproj b/System.IO.Abstractions/System.IO.Abstractions.csproj index 01be60823..33156eb48 100644 --- a/System.IO.Abstractions/System.IO.Abstractions.csproj +++ b/System.IO.Abstractions/System.IO.Abstractions.csproj @@ -2,7 +2,6 @@ net40;netstandard1.4;netstandard2.0; - 0.0.0.1 Tatham Oddie A set of abstractions to help make file system interactions testable. @@ -11,7 +10,8 @@ https://github.com/System-IO-Abstractions/System.IO.Abstractions/blob/master/LICENSE https://github.com/System-IO-Abstractions/System.IO.Abstractions testing - true + true + $(NoWarn);CS1591;CS1574 @@ -27,7 +27,7 @@ 4.5.0 - + 4.5.0 @@ -47,4 +47,12 @@ + + + + runtime; build; native; contentfiles; analyzers + all + + + diff --git a/appveyor.yml b/appveyor.yml index 0c6c946fd..844593d3f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -9,17 +9,6 @@ branches: only: - master -version: 2.1.0.{build} - -pull_requests: - do_not_increment_build_number: true - -dotnet_csproj: - patch: true - file: '**\*.csproj' - version: '{version}' - package_version: '{version}' - environment: INHERITDOC_VERSION: 1.2.2.1 TEMP_DIR: c:\temp @@ -46,3 +35,14 @@ deploy: artifact: /.*\.nupkg/ on: branch: master + +- provider: GitHub + tag: v$(GitBuildVersion) + prerelease: true + artifact: /.*\.nupkg/ + auth_token: + secure: 4ejpp3dzvpOwfD7TOcypCsT0z8j+qYiihiZszS3uVI78vZpAtM+P+A8NK2JZmYMo + on: + branch: master + + \ No newline at end of file diff --git a/version.json b/version.json new file mode 100644 index 000000000..10153a30b --- /dev/null +++ b/version.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://raw.githubusercontent.com/AArnott/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "2.2-beta", + "assemblyVersion": { + "precision": "major" + }, + "publicReleaseRefSpec": [ + "^refs/heads/master$" + ], + "cloudBuild": { + "buildNumber": { + "enabled": true, + "setVersionVariables": true + } + } +} \ No newline at end of file From 0550b0088ab3a9901a74611e05afccaa3524abc7 Mon Sep 17 00:00:00 2001 From: Tatham Oddie Date: Fri, 26 Oct 2018 08:00:21 +1100 Subject: [PATCH 04/24] Update NuGet API key (#382) Expires every 12 months --- appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 844593d3f..a60d454f7 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -30,7 +30,7 @@ before_package: deploy: - provider: NuGet api_key: - secure: lUmSV95QwsQtvPkHT02PT5fswmVcW0oMIJvOGEincUNnUNtTe3DOIjVEOpC0CMkQ + secure: z0Zh4VyDdeKlFrTEnYbhqDc0xw9M5ahv/fQJsShk3HqmUWjQ/bh4M/r6ZWZgY/ZB skip_symbols: false artifact: /.*\.nupkg/ on: @@ -45,4 +45,4 @@ deploy: on: branch: master - \ No newline at end of file + From dd4a4b84e54b0747e013af2d2c2d477fcb7427d2 Mon Sep 17 00:00:00 2001 From: Michael Wildman Date: Tue, 30 Oct 2018 08:23:10 +1300 Subject: [PATCH 05/24] Stop calling real IO.Path.GetTempPath() when creating a MockFileSystem (#372) * Stop calling real IO.Path.GetTempPath() when creating a MockFileSystem * Ensure current directory of MockFileSystem exists on creation * fix failing test on linux Fixes #350 --- .../MockDirectoryTests.cs | 9 +++++---- .../MockFileSystemTests.cs | 14 +++++++++++++- .../MockFileSystem.cs | 9 ++++++++- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/System.IO.Abstractions.TestingHelpers.Tests/MockDirectoryTests.cs b/System.IO.Abstractions.TestingHelpers.Tests/MockDirectoryTests.cs index 9cd17fa75..d7f48234c 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/MockDirectoryTests.cs +++ b/System.IO.Abstractions.TestingHelpers.Tests/MockDirectoryTests.cs @@ -1184,11 +1184,12 @@ public void MockDirectory_GetCurrentDirectory_ShouldReturnValueFromFileSystemCon Assert.AreEqual(directory, actual); } - - + [Test] - public void MockDirectory_GetCurrentDirectory_ShouldReturnDefaultPathWhenNotSet() { - string directory = Path.GetTempPath(); + public void MockDirectory_GetCurrentDirectory_ShouldReturnDefaultPathWhenNotSet() + { + string directory = XFS.Path(@"C:\"); + var fileSystem = new MockFileSystem(); var actual = fileSystem.Directory.GetCurrentDirectory(); diff --git a/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemTests.cs b/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemTests.cs index 6ddcd4fba..34fc81400 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemTests.cs +++ b/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemTests.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; @@ -292,5 +292,17 @@ public void MockFileSystem_GetFiles_ThrowsArgumentExceptionForInvalidCharacters( // Assert Assert.Throws(getFilesWithInvalidCharacterInPath); } + + [Test] + [TestCase(null)] + [TestCase(@"C:\somepath")] + public void MockFileSystem_DefaultState_CurrentDirectoryExists(string currentDirectory) + { + var fs = new MockFileSystem(null, currentDirectory); + + var actualCurrentDirectory = fs.DirectoryInfo.FromDirectoryName("."); + + Assert.IsTrue(actualCurrentDirectory.Exists); + } } } diff --git a/System.IO.Abstractions.TestingHelpers/MockFileSystem.cs b/System.IO.Abstractions.TestingHelpers/MockFileSystem.cs index 186dcebbe..ca540cd51 100644 --- a/System.IO.Abstractions.TestingHelpers/MockFileSystem.cs +++ b/System.IO.Abstractions.TestingHelpers/MockFileSystem.cs @@ -9,6 +9,8 @@ namespace System.IO.Abstractions.TestingHelpers [Serializable] public class MockFileSystem : IFileSystem, IMockFileDataAccessor { + private const string DEFAULT_CURRENT_DIRECTORY = @"C:\"; + private readonly IDictionary files; [NonSerialized] private readonly PathVerifier pathVerifier; @@ -19,7 +21,7 @@ public MockFileSystem(IDictionary files, string currentDir { if (string.IsNullOrEmpty(currentDirectory)) { - currentDirectory = IO.Path.GetTempPath(); + currentDirectory = XFS.Path(DEFAULT_CURRENT_DIRECTORY); } pathVerifier = new PathVerifier(this); @@ -42,6 +44,11 @@ public MockFileSystem(IDictionary files, string currentDir AddFile(entry.Key, entry.Value); } } + + if (!FileExists(currentDirectory)) + { + AddDirectory(currentDirectory); + } } public FileBase File { get; } From 080e14809ae83c01868d1060517e3d4d9f56ea2a Mon Sep 17 00:00:00 2001 From: Florian Greinacher Date: Tue, 30 Oct 2018 11:10:17 +0100 Subject: [PATCH 06/24] Update GitHub auth token (#386) --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index a60d454f7..0bf8871e3 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -41,7 +41,7 @@ deploy: prerelease: true artifact: /.*\.nupkg/ auth_token: - secure: 4ejpp3dzvpOwfD7TOcypCsT0z8j+qYiihiZszS3uVI78vZpAtM+P+A8NK2JZmYMo + secure: Rrk1zp93EpCyxec/GXKnRnJjX7vU+ClNZEBnxbM+1j6l+C56qSV7B/fUrVsJuZYV on: branch: master From 4ba02414a5f99dfe5caedb57a9c9a35d3c32cac4 Mon Sep 17 00:00:00 2001 From: Florian Greinacher Date: Tue, 30 Oct 2018 13:15:54 +0100 Subject: [PATCH 07/24] Use simple GitBuildVersion for GitHub release --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 0bf8871e3..90a3e31f4 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -37,7 +37,7 @@ deploy: branch: master - provider: GitHub - tag: v$(GitBuildVersion) + tag: v$(GitBuildVersionSimple) prerelease: true artifact: /.*\.nupkg/ auth_token: From 01a127ca8ee3c2d6df6e3a3f2fe831327ece6f3d Mon Sep 17 00:00:00 2001 From: Florian Greinacher Date: Tue, 30 Oct 2018 20:38:27 +0100 Subject: [PATCH 08/24] Use AppVeyor build version for GitHub release --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 90a3e31f4..b4bf5221d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -37,7 +37,7 @@ deploy: branch: master - provider: GitHub - tag: v$(GitBuildVersionSimple) + tag: v$(appveyor_build_version) prerelease: true artifact: /.*\.nupkg/ auth_token: From 127d89f9a367ee61b4372d676eda9cb5aecefbc5 Mon Sep 17 00:00:00 2001 From: Peter Sereda Date: Mon, 5 Nov 2018 13:54:24 +0300 Subject: [PATCH 09/24] Make MockFile.Delete throw exception when file does not exist. (#387) Fixes #284 --- .../MockFileDeleteTests.cs | 28 +++++++++++++++++-- .../MockFileStreamTests.cs | 4 ++- .../MockFileTests.cs | 11 ++++++-- .../MockFile.cs | 11 +++++++- 4 files changed, 47 insertions(+), 7 deletions(-) diff --git a/System.IO.Abstractions.TestingHelpers.Tests/MockFileDeleteTests.cs b/System.IO.Abstractions.TestingHelpers.Tests/MockFileDeleteTests.cs index c5a1e9c01..6f9fd93fc 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/MockFileDeleteTests.cs +++ b/System.IO.Abstractions.TestingHelpers.Tests/MockFileDeleteTests.cs @@ -1,6 +1,7 @@ namespace System.IO.Abstractions.TestingHelpers.Tests { - using NUnit.Framework; + using System.Collections.Generic; + using NUnit.Framework; using XFS = MockUnixSupport; @@ -10,7 +11,7 @@ public class MockFileDeleteTests public void MockFile_Delete_ShouldDeleteFile() { var fileSystem = new MockFileSystem(); - var path = XFS.Path("C:\\test"); + var path = XFS.Path("C:\\some_folder\\test"); var directory = fileSystem.Path.GetDirectoryName(path); fileSystem.AddFile(path, new MockFileData("Bla")); @@ -35,5 +36,28 @@ public void MockFile_Delete_ShouldThrowArgumentExceptionIfPathContainsOnlyWhites // Assert Assert.Throws(action); } + + [Test] + public void MockFile_Delete_ShouldThrowDirectoryNotFoundExceptionIfParentFolderAbsent() + { + var fileSystem = new MockFileSystem(); + var path = XFS.Path("C:\\test\\somefile.txt"); + + Assert.Throws(() => fileSystem.File.Delete(path)); + } + + [Test] + public void MockFile_Delete_ShouldSilentlyReturnIfNonExistingFileInExistingFolder() + { + var fileSystem = new MockFileSystem(new Dictionary() + { + { XFS.Path("C:\\temp\\exist.txt"), new MockFileData("foobar") }, + }); + + string filePath = XFS.Path("C:\\temp\\somefile.txt"); + + // Delete() returns void, so there is nothing to check here beside absense of an exception + Assert.DoesNotThrow(() => fileSystem.File.Delete(filePath)); + } } } \ No newline at end of file diff --git a/System.IO.Abstractions.TestingHelpers.Tests/MockFileStreamTests.cs b/System.IO.Abstractions.TestingHelpers.Tests/MockFileStreamTests.cs index b2a14abfb..ec3781b4c 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/MockFileStreamTests.cs +++ b/System.IO.Abstractions.TestingHelpers.Tests/MockFileStreamTests.cs @@ -28,8 +28,10 @@ public void MockFileStream_Flush_WritesByteToFile() [Test] public void MockFileStream_Dispose_ShouldNotResurrectFile() { + // path in this test case is a subject to Directory.GetParent(path) Linux issue + // https://github.com/System-IO-Abstractions/System.IO.Abstractions/issues/395 var fileSystem = new MockFileSystem(); - var path = XFS.Path("C:\\test"); + var path = XFS.Path("C:\\some_folder\\test"); var directory = fileSystem.Path.GetDirectoryName(path); fileSystem.AddFile(path, new MockFileData("Bla")); var stream = fileSystem.File.Open(path, FileMode.Open, FileAccess.ReadWrite, FileShare.Delete); diff --git a/System.IO.Abstractions.TestingHelpers.Tests/MockFileTests.cs b/System.IO.Abstractions.TestingHelpers.Tests/MockFileTests.cs index 2468c9824..d26a382bd 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/MockFileTests.cs +++ b/System.IO.Abstractions.TestingHelpers.Tests/MockFileTests.cs @@ -482,9 +482,14 @@ public void MockFile_Delete_Should_RemoveFiles() [Test] public void MockFile_Delete_No_File_Does_Nothing() - { - string filePath = XFS.Path(@"c:\something\demo.txt"); - var fileSystem = new MockFileSystem(); + { + var fileSystem = new MockFileSystem(new Dictionary() + { + { XFS.Path(@"c:\something\exist.txt"), new MockFileData("Demo text content") }, + }); + + string filePath = XFS.Path(@"c:\something\not_exist.txt"); + fileSystem.File.Delete(filePath); } diff --git a/System.IO.Abstractions.TestingHelpers/MockFile.cs b/System.IO.Abstractions.TestingHelpers/MockFile.cs index 1322e2e29..b77d644e7 100644 --- a/System.IO.Abstractions.TestingHelpers/MockFile.cs +++ b/System.IO.Abstractions.TestingHelpers/MockFile.cs @@ -191,6 +191,11 @@ public override void Delete(string path) { mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path"); + // We mimic exact behavior of the standard File.Delete() method + // which throws exception only if the folder does not exist, + // but silently returns if deleting a non-existing file in an existing folder. + VerifyDirectoryExists(path); + mockFileDataAccessor.RemoveFile(path); } @@ -991,7 +996,11 @@ private void VerifyDirectoryExists(string path) DirectoryInfoBase dir = mockFileDataAccessor.Directory.GetParent(path); if (!dir.Exists) { - throw new DirectoryNotFoundException(string.Format(CultureInfo.InvariantCulture, StringResources.Manager.GetString("COULD_NOT_FIND_PART_OF_PATH_EXCEPTION"), dir)); + throw new DirectoryNotFoundException( + string.Format( + CultureInfo.InvariantCulture, + StringResources.Manager.GetString("COULD_NOT_FIND_PART_OF_PATH_EXCEPTION"), + dir)); } } } From 2b27e2a6753bf58b6013b63b4835f4419ce553b3 Mon Sep 17 00:00:00 2001 From: Logan Dam Date: Fri, 9 Nov 2018 12:42:54 +0200 Subject: [PATCH 10/24] Call base.Close() in MockFileStream.Close() (#398) Fixes #397 --- .../MockFileStreamTests.cs | 15 +++++++++++++++ .../MockFileStream.cs | 7 +++++++ 2 files changed, 22 insertions(+) diff --git a/System.IO.Abstractions.TestingHelpers.Tests/MockFileStreamTests.cs b/System.IO.Abstractions.TestingHelpers.Tests/MockFileStreamTests.cs index ec3781b4c..89fe72551 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/MockFileStreamTests.cs +++ b/System.IO.Abstractions.TestingHelpers.Tests/MockFileStreamTests.cs @@ -106,5 +106,20 @@ public void MockFileStream_Dispose_MultipleCallsDontThrow() // Assert Assert.DoesNotThrow(() => stream.Dispose()); } + + [Test] + public void MockFileStream_Dispose_OperationsAfterDisposeThrow() + { + var fileSystem = new MockFileSystem(); + var path = XFS.Path("C:\\test"); + fileSystem.AddFile(path, new MockFileData(new byte[0])); + var stream = fileSystem.FileInfo.FromFileName(path).OpenWrite(); + + // Act + stream.Dispose(); + + // Assert + Assert.Throws(() => stream.WriteByte(0)); + } } } diff --git a/System.IO.Abstractions.TestingHelpers/MockFileStream.cs b/System.IO.Abstractions.TestingHelpers/MockFileStream.cs index 68095d85a..b9dc51e6a 100644 --- a/System.IO.Abstractions.TestingHelpers/MockFileStream.cs +++ b/System.IO.Abstractions.TestingHelpers/MockFileStream.cs @@ -8,6 +8,7 @@ public class MockFileStream : MemoryStream private readonly bool canWrite = true; private readonly FileOptions options; private bool disposed; + private bool closed; public enum StreamType { @@ -64,8 +65,14 @@ public MockFileStream( #if NET40 public override void Close() { + if (closed) + { + return; + } InternalFlush(); + base.Close(); OnClose(); + closed = true; } #else protected override void Dispose(bool disposing) From ec1ca83aa4afc6a1bc0b9c3759283c4e87eef6bc Mon Sep 17 00:00:00 2001 From: Florian Greinacher Date: Fri, 16 Nov 2018 22:47:31 +0100 Subject: [PATCH 11/24] Add support for running dotnet commands against solution --- .vscode/launch.json | 28 +++++++++++++++++++ .vscode/tasks.json | 15 ++++++++++ Directory.Build.targets | 5 ++++ ...O.Abstractions.TestingHelpers.Tests.csproj | 4 ++- ...stem.IO.Abstractions.TestingHelpers.csproj | 5 ++-- .../System.IO.Abstractions.csproj | 5 ++-- after.System.IO.Abstractions.sln.targets | 5 ++++ 7 files changed, 62 insertions(+), 5 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 .vscode/tasks.json create mode 100644 Directory.Build.targets create mode 100644 after.System.IO.Abstractions.sln.targets diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..2f1977e33 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,28 @@ +{ + // Use IntelliSense to find out which attributes exist for C# debugging + // Use hover for the description of the existing attributes + // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md + "version": "0.2.0", + "configurations": [ + { + "name": ".NET Core Launch (console)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + // If you have changed target frameworks, make sure to update the program path. + "program": "${workspaceFolder}/System.IO.Abstractions.TestingHelpers.Tests/bin/Debug/netcoreapp2.0/System.IO.Abstractions.TestingHelpers.Tests.dll", + "args": [], + "cwd": "${workspaceFolder}/System.IO.Abstractions.TestingHelpers.Tests", + // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window + "console": "internalConsole", + "stopAtEntry": false, + "internalConsoleOptions": "openOnSessionStart" + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach", + "processId": "${command:pickProcess}" + } + ,] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 000000000..a4c910c67 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,15 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/System.IO.Abstractions.TestingHelpers.Tests/System.IO.Abstractions.TestingHelpers.Tests.csproj" + ], + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/Directory.Build.targets b/Directory.Build.targets new file mode 100644 index 000000000..a4b4cac9e --- /dev/null +++ b/Directory.Build.targets @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/System.IO.Abstractions.TestingHelpers.Tests/System.IO.Abstractions.TestingHelpers.Tests.csproj b/System.IO.Abstractions.TestingHelpers.Tests/System.IO.Abstractions.TestingHelpers.Tests.csproj index ab213d6ef..00f7fba8d 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/System.IO.Abstractions.TestingHelpers.Tests.csproj +++ b/System.IO.Abstractions.TestingHelpers.Tests/System.IO.Abstractions.TestingHelpers.Tests.csproj @@ -1,6 +1,7 @@  - net40;netcoreapp2.0 + netcoreapp2.0 + $(TargetFrameworks);net40 0.0.0.1 The unit tests for our pre-built mocks @@ -10,6 +11,7 @@ System.IO.Abstractions.TestingHelpers.Tests ../StrongName.snk false + true Full diff --git a/System.IO.Abstractions.TestingHelpers/System.IO.Abstractions.TestingHelpers.csproj b/System.IO.Abstractions.TestingHelpers/System.IO.Abstractions.TestingHelpers.csproj index 09239e755..7c83ae1bd 100644 --- a/System.IO.Abstractions.TestingHelpers/System.IO.Abstractions.TestingHelpers.csproj +++ b/System.IO.Abstractions.TestingHelpers/System.IO.Abstractions.TestingHelpers.csproj @@ -1,6 +1,7 @@ - net40;netstandard1.4;netstandard2.0; + netstandard1.4;netstandard2.0 + $(TargetFrameworks);net40 System.IO.Abstractions.TestingHelpers A set of pre-built mocks to help when testing file system interactions. @@ -37,7 +38,7 @@ - + runtime; build; native; contentfiles; analyzers all diff --git a/System.IO.Abstractions/System.IO.Abstractions.csproj b/System.IO.Abstractions/System.IO.Abstractions.csproj index 33156eb48..5f8b13f8b 100644 --- a/System.IO.Abstractions/System.IO.Abstractions.csproj +++ b/System.IO.Abstractions/System.IO.Abstractions.csproj @@ -1,7 +1,8 @@ - net40;netstandard1.4;netstandard2.0; + netstandard1.4;netstandard2.0 + $(TargetFrameworks);net40 Tatham Oddie A set of abstractions to help make file system interactions testable. @@ -49,7 +50,7 @@ - + runtime; build; native; contentfiles; analyzers all diff --git a/after.System.IO.Abstractions.sln.targets b/after.System.IO.Abstractions.sln.targets new file mode 100644 index 000000000..cf28d8236 --- /dev/null +++ b/after.System.IO.Abstractions.sln.targets @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file From ec1b673d6acd081c5040b0fdff2470a5559de67d Mon Sep 17 00:00:00 2001 From: Florian Greinacher Date: Wed, 28 Nov 2018 21:53:57 +0100 Subject: [PATCH 12/24] Fix file operations that fail when used with relative paths (#407) Root cause for the problems is https://github.com/wleader/System.IO.Abstractions/commit/95ead3a2a3fbe848e34642931f2e5fafc2368c5a#diff-e76db4b13ed41e41943b80f8f1075287 where we started using `Path.GetDirectoryName` to get the parent directory, but unfortunately this method returns an empty string when the path does not contain directory information (e.g. a plain file name). The changes here make the paths absolute before passing them on to `Path.GetDirectoryName`. Fixes #404 and #401 --- .../MockFileAppendAllTextTests.cs | 11 +++++++++++ .../MockFileCopyTests.cs | 13 +++++++++++++ .../MockFileCreateTests.cs | 15 +++++++++++++-- .../MockFileOpenTests.cs | 15 +++++++++++++-- System.IO.Abstractions.TestingHelpers/MockFile.cs | 8 +++++--- 5 files changed, 55 insertions(+), 7 deletions(-) diff --git a/System.IO.Abstractions.TestingHelpers.Tests/MockFileAppendAllTextTests.cs b/System.IO.Abstractions.TestingHelpers.Tests/MockFileAppendAllTextTests.cs index 8cc5c85f2..50c1defbf 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/MockFileAppendAllTextTests.cs +++ b/System.IO.Abstractions.TestingHelpers.Tests/MockFileAppendAllTextTests.cs @@ -143,5 +143,16 @@ public void MockFile_AppendAllText_ShouldPersistNewTextWithCustomEncoding() expected, file.ReadAllBytes(path)); } + + [Test] + public void MockFile_AppendAllText_ShouldWorkWithRelativePath() + { + var file = "file.txt"; + var fileSystem = new MockFileSystem(); + + fileSystem.File.AppendAllText(file, "Foo"); + + Assert.That(fileSystem.File.Exists(file)); + } } } \ No newline at end of file diff --git a/System.IO.Abstractions.TestingHelpers.Tests/MockFileCopyTests.cs b/System.IO.Abstractions.TestingHelpers.Tests/MockFileCopyTests.cs index 53ccb5d13..f351c5757 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/MockFileCopyTests.cs +++ b/System.IO.Abstractions.TestingHelpers.Tests/MockFileCopyTests.cs @@ -368,5 +368,18 @@ public void MockFile_Copy_ShouldThrowFileNotFoundExceptionWhenSourceDoesNotExist Assert.Throws(action); } + + [Test] + public void MockFile_Copy_ShouldWorkWithRelativePaths() + { + var sourceFile = "source_file.txt"; + var destinationFile = "destination_file.txt"; + var fileSystem = new MockFileSystem(); + + fileSystem.File.Create(sourceFile).Close(); + fileSystem.File.Copy(sourceFile, destinationFile); + + Assert.That(fileSystem.File.Exists(destinationFile)); + } } } \ No newline at end of file diff --git a/System.IO.Abstractions.TestingHelpers.Tests/MockFileCreateTests.cs b/System.IO.Abstractions.TestingHelpers.Tests/MockFileCreateTests.cs index 9be6ff795..71cae6d86 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/MockFileCreateTests.cs +++ b/System.IO.Abstractions.TestingHelpers.Tests/MockFileCreateTests.cs @@ -155,12 +155,12 @@ public void MockFile_Create_ShouldThrowDirectoryNotFoundExceptionIfCreatingAndPa { // Arrange var fileSystem = new MockFileSystem(); + var file = XFS.Path("C:\\path\\NotFound.ext"); // Act - TestDelegate action = () => fileSystem.File.Create("C:\\Path\\NotFound.ext"); + TestDelegate action = () => fileSystem.File.Create(file); // Assert - Assert.IsFalse(fileSystem.Directory.Exists("C:\\path")); var exception = Assert.Throws(action); Assert.That(exception.Message, Does.StartWith("Could not find a part of the path")); } @@ -279,5 +279,16 @@ public void MockFile_Create_EncryptedOption_EncryptsFileWhenStreamIsClose() Assert.IsTrue(fileInfo.Attributes.HasFlag(FileAttributes.Encrypted)); } #endif + + [Test] + public void MockFile_Create_ShouldWorkWithRelativePath() + { + var relativeFile = "file.txt"; + var fileSystem = new MockFileSystem(); + + fileSystem.File.Create(relativeFile).Close(); + + Assert.That(fileSystem.File.Exists(relativeFile)); + } } } diff --git a/System.IO.Abstractions.TestingHelpers.Tests/MockFileOpenTests.cs b/System.IO.Abstractions.TestingHelpers.Tests/MockFileOpenTests.cs index 5e7a065a1..2042f1b34 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/MockFileOpenTests.cs +++ b/System.IO.Abstractions.TestingHelpers.Tests/MockFileOpenTests.cs @@ -249,14 +249,25 @@ public void MockFile_Open_ShouldThrowDirectoryNotFoundExceptionIfFileModeCreateA { // Arrange var fileSystem = new MockFileSystem(); + var file = XFS.Path("C:\\path\\NotFound.ext"); // Act - TestDelegate action = () => fileSystem.File.Open("C:\\Path\\NotFound.ext", FileMode.Create); + TestDelegate action = () => fileSystem.File.Open(file, FileMode.Create); // Assert - Assert.IsFalse(fileSystem.Directory.Exists("C:\\path")); var exception = Assert.Throws(action); Assert.That(exception.Message, Does.StartWith("Could not find a part of the path")); } + + [Test] + public void MockFile_OpenWrite_ShouldWorkWithRelativePath() + { + var file = "file.txt"; + var fileSystem = new MockFileSystem(); + + fileSystem.File.OpenWrite(file).Close(); + + Assert.That(fileSystem.File.Exists(file)); + } } } \ No newline at end of file diff --git a/System.IO.Abstractions.TestingHelpers/MockFile.cs b/System.IO.Abstractions.TestingHelpers/MockFile.cs index b77d644e7..7cf1ebafc 100644 --- a/System.IO.Abstractions.TestingHelpers/MockFile.cs +++ b/System.IO.Abstractions.TestingHelpers/MockFile.cs @@ -57,7 +57,8 @@ public override void AppendAllText(string path, string contents, Encoding encodi if (!mockFileDataAccessor.FileExists(path)) { - var dir = mockFileDataAccessor.Path.GetDirectoryName(path); + var mockPath = mockFileDataAccessor.Path; + var dir = mockPath.GetDirectoryName(mockPath.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)); @@ -112,7 +113,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 = mockPath.GetDirectoryName(destFileName); + var directoryNameOfDestination = mockPath.GetDirectoryName(mockPath.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)); @@ -155,7 +156,8 @@ private Stream CreateInternal(string path, int bufferSize, FileOptions options, } mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, nameof(path)); - var directoryPath = mockPath.GetDirectoryName(path); + + var directoryPath = mockPath.GetDirectoryName(mockPath.GetFullPath(path)); if (!mockFileDataAccessor.Directory.Exists(directoryPath)) { From ccc8000dd0f4a7f9d5651b245234e8408c7de460 Mon Sep 17 00:00:00 2001 From: Robert Koeninger Date: Sat, 1 Dec 2018 05:25:20 -0500 Subject: [PATCH 13/24] Made FileSystemWatcher on MockFileSystem mockable (#409) * Added set; to MockFileSystem.FileSystemWatcher * Added FILE_SYSTEM_WATCHER_NOT_IMPLEMENTED_EXCEPTION to StringResources * Made MockFileSystemWatcherFactory throw NotImplementedException * Removed MockFileSystemWatcher * MockFileSystemWatcherFactoryTests now assert exception thrown * Added MockFileSystem test to assert FileSystemWatcher is assignable * Elaborated on FILE_SYSTEM_WATCHER_NOT_IMPLEMENTED_EXCEPTION message * Corrected name of MockFileSystemWatcherFactory test * Update System.IO.Abstractions.TestingHelpers/Properties/Resources.resx Co-Authored-By: rkoeninger --- .../MockFileSystemTests.cs | 35 ++++++++++++++++ .../MockFileSystemWatcherFactoryTests.cs | 40 +++---------------- .../MockFileSystem.cs | 2 +- .../MockFileSystemWatcher.cs | 38 ------------------ .../MockFileSystemWatcherFactory.cs | 19 +++------ .../Properties/Resources.resx | 5 ++- 6 files changed, 51 insertions(+), 88 deletions(-) delete mode 100644 System.IO.Abstractions.TestingHelpers/MockFileSystemWatcher.cs diff --git a/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemTests.cs b/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemTests.cs index 34fc81400..513460d3b 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemTests.cs +++ b/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemTests.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Reflection; using System.Text; @@ -304,5 +305,39 @@ public void MockFileSystem_DefaultState_CurrentDirectoryExists(string currentDir Assert.IsTrue(actualCurrentDirectory.Exists); } + + [Test] + public void MockFileSystem_FileSystemWatcher_ShouldBeAssignable() + { + var path = XFS.Path(@"C:\root"); + var fileSystem = new MockFileSystem {FileSystemWatcher = new TestFileSystemWatcherFactory()}; + var watcher = fileSystem.FileSystemWatcher.FromPath(path); + Assert.AreEqual(path, watcher.Path); + } + + private class TestFileSystemWatcherFactory : IFileSystemWatcherFactory + { + public FileSystemWatcherBase CreateNew() => new TestFileSystemWatcher(null); + public FileSystemWatcherBase FromPath(string path) => new TestFileSystemWatcher(path); + } + + private class TestFileSystemWatcher : FileSystemWatcherBase + { + public TestFileSystemWatcher(string path) => Path = path; + public override string Path { get; set; } + public override bool IncludeSubdirectories { get; set; } + public override bool EnableRaisingEvents { get; set; } + public override string Filter { get; set; } + public override int InternalBufferSize { get; set; } + public override NotifyFilters NotifyFilter { get; set; } +#if NET40 + public override ISite Site { get; set; } + public override ISynchronizeInvoke SynchronizingObject { get; set; } + public override void BeginInit() {} + public override void EndInit() {} +#endif + public override WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType) => default(WaitForChangedResult); + public override WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, int timeout) => default(WaitForChangedResult); + } } } diff --git a/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemWatcherFactoryTests.cs b/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemWatcherFactoryTests.cs index 53cc83ec5..49696508a 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemWatcherFactoryTests.cs +++ b/System.IO.Abstractions.TestingHelpers.Tests/MockFileSystemWatcherFactoryTests.cs @@ -1,8 +1,5 @@ using NUnit.Framework; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using XFS = System.IO.Abstractions.TestingHelpers.MockUnixSupport; namespace System.IO.Abstractions.TestingHelpers.Tests { @@ -10,43 +7,18 @@ namespace System.IO.Abstractions.TestingHelpers.Tests public class MockFileSystemWatcherFactoryTests { [Test] - public void MockFileSystemWatcherFactory_CreateNew_ShouldReturnNonNullMockWatcher() + public void MockFileSystemWatcherFactory_CreateNew_ShouldThrowNotImplementedException() { - // Arrange var factory = new MockFileSystemWatcherFactory(); - - // Act - var result = factory.CreateNew(); - - // Assert - Assert.IsNotNull(result); + Assert.Throws(() => factory.CreateNew()); } [Test] - public void MockFileSystemWatcherFactory_FromPath_ShouldReturnNonNullMockWatcher() + public void MockFileSystemWatcherFactory_FromPath_ShouldThrowNotImplementedException() { - // Arrange + var path = XFS.Path(@"y:\test"); var factory = new MockFileSystemWatcherFactory(); - - // Act - var result = factory.FromPath(@"y:\test"); - - // Assert - Assert.IsNotNull(result); - } - - [Test] - public void MockFileSystemWatcherFactory_FromPath_ShouldReturnWatcherForSpecifiedPath() - { - // Arrange - const string path = @"z:\test"; - var factory = new MockFileSystemWatcherFactory(); - - // Act - var result = factory.FromPath(path); - - // Assert - Assert.AreEqual(path, result.Path); + Assert.Throws(() => factory.FromPath(path)); } } } diff --git a/System.IO.Abstractions.TestingHelpers/MockFileSystem.cs b/System.IO.Abstractions.TestingHelpers/MockFileSystem.cs index ca540cd51..f66d2f7f8 100644 --- a/System.IO.Abstractions.TestingHelpers/MockFileSystem.cs +++ b/System.IO.Abstractions.TestingHelpers/MockFileSystem.cs @@ -65,7 +65,7 @@ public MockFileSystem(IDictionary files, string currentDir public IDriveInfoFactory DriveInfo { get; } - public IFileSystemWatcherFactory FileSystemWatcher { get; } + public IFileSystemWatcherFactory FileSystemWatcher { get; set; } public IFileSystem FileSystem => this; diff --git a/System.IO.Abstractions.TestingHelpers/MockFileSystemWatcher.cs b/System.IO.Abstractions.TestingHelpers/MockFileSystemWatcher.cs deleted file mode 100644 index a1204412d..000000000 --- a/System.IO.Abstractions.TestingHelpers/MockFileSystemWatcher.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.ComponentModel; - -namespace System.IO.Abstractions.TestingHelpers -{ - public class MockFileSystemWatcher : FileSystemWatcherBase - { - public override bool IncludeSubdirectories { get; set; } - public override bool EnableRaisingEvents { get; set; } - public override string Filter { get; set; } - public override int InternalBufferSize { get; set; } - public override NotifyFilters NotifyFilter { get; set; } - public override string Path { get; set; } -#if NET40 - public override ISite Site { get; set; } - public override ISynchronizeInvoke SynchronizingObject { get; set; } -#endif - -#if NET40 - public override void BeginInit() - { - } - - public override void EndInit() - { - } -#endif - - public override WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType) - { - throw new NotImplementedException(); - } - - public override WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, int timeout) - { - throw new NotImplementedException(); - } - } -} diff --git a/System.IO.Abstractions.TestingHelpers/MockFileSystemWatcherFactory.cs b/System.IO.Abstractions.TestingHelpers/MockFileSystemWatcherFactory.cs index 14a494074..fa832ba13 100644 --- a/System.IO.Abstractions.TestingHelpers/MockFileSystemWatcherFactory.cs +++ b/System.IO.Abstractions.TestingHelpers/MockFileSystemWatcherFactory.cs @@ -1,21 +1,12 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace System.IO.Abstractions.TestingHelpers +namespace System.IO.Abstractions.TestingHelpers { [Serializable] public class MockFileSystemWatcherFactory : IFileSystemWatcherFactory { - public FileSystemWatcherBase CreateNew() - { - return new MockFileSystemWatcher(); - } + public FileSystemWatcherBase CreateNew() => + throw new NotImplementedException(StringResources.Manager.GetString("FILE_SYSTEM_WATCHER_NOT_IMPLEMENTED_EXCEPTION")); - public FileSystemWatcherBase FromPath(string path) - { - return new MockFileSystemWatcher {Path = path}; - } + public FileSystemWatcherBase FromPath(string path) => + throw new NotImplementedException(StringResources.Manager.GetString("FILE_SYSTEM_WATCHER_NOT_IMPLEMENTED_EXCEPTION")); } } diff --git a/System.IO.Abstractions.TestingHelpers/Properties/Resources.resx b/System.IO.Abstractions.TestingHelpers/Properties/Resources.resx index 5e8750581..13097ff13 100644 --- a/System.IO.Abstractions.TestingHelpers/Properties/Resources.resx +++ b/System.IO.Abstractions.TestingHelpers/Properties/Resources.resx @@ -144,4 +144,7 @@ Could not find file '{0}'. - \ No newline at end of file + + MockFileSystem does not have a built-in FileSystemWatcher implementation. You must provide your own mock or implementation of IFileSystemWatcherFactory and assign it to MockFileSystem.FileSystemWatcher. + + From a9208eeff1e54eba8f1d0d55d6195d26dec3aef6 Mon Sep 17 00:00:00 2001 From: Florian Greinacher Date: Sat, 1 Dec 2018 15:06:41 +0100 Subject: [PATCH 14/24] Add Dependabot badge to readme (#414) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1b6229872..1b83d0a4c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![NuGet](https://img.shields.io/nuget/v/System.IO.Abstractions.svg)](https://www.nuget.org/packages/System.IO.Abstractions) -[![Windows build status](https://ci.appveyor.com/api/projects/status/em172apw1v5k70vq/branch/master?svg=true)](https://ci.appveyor.com/project/tathamoddie/system-io-abstractions/branch/master) on Windows -[![Linux build status](https://travis-ci.org/System-IO-Abstractions/System.IO.Abstractions.svg?branch=master)](https://travis-ci.org/System-IO-Abstractions/System.IO.Abstractions) on Linux +[![Windows build status](https://ci.appveyor.com/api/projects/status/em172apw1v5k70vq/branch/master?svg=true)](https://ci.appveyor.com/project/tathamoddie/system-io-abstractions/branch/master) +[![Linux build status](https://travis-ci.org/System-IO-Abstractions/System.IO.Abstractions.svg?branch=master)](https://travis-ci.org/System-IO-Abstractions/System.IO.Abstractions) [![Dependabot Status](https://api.dependabot.com/badges/status?host=github&repo=System-IO-Abstractions/System.IO.Abstractions)](https://dependabot.com) --- From 7e41fa519888748ae4fab6d150a633a04b88caee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sat, 1 Dec 2018 19:07:42 +0100 Subject: [PATCH 15/24] Bump Microsoft.NET.Test.Sdk from 15.0.0 to 15.9.0 (#413) Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 15.0.0 to 15.9.0. - [Release notes](https://github.com/microsoft/vstest/releases) - [Commits](https://github.com/microsoft/vstest/compare/v15.0.0...v15.9.0) Signed-off-by: dependabot[bot] --- .../System.IO.Abstractions.TestingHelpers.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/System.IO.Abstractions.TestingHelpers.Tests/System.IO.Abstractions.TestingHelpers.Tests.csproj b/System.IO.Abstractions.TestingHelpers.Tests/System.IO.Abstractions.TestingHelpers.Tests.csproj index 00f7fba8d..60c9a79b2 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/System.IO.Abstractions.TestingHelpers.Tests.csproj +++ b/System.IO.Abstractions.TestingHelpers.Tests/System.IO.Abstractions.TestingHelpers.Tests.csproj @@ -41,7 +41,7 @@ - + From 202a732e65b3fc310b8b6c8938bac93aa6cee491 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sat, 1 Dec 2018 19:35:18 +0100 Subject: [PATCH 16/24] Bump NUnit3TestAdapter from 3.9.0 to 3.11.2 (#411) Bumps [NUnit3TestAdapter](https://github.com/nunit/nunit3-vs-adapter) from 3.9.0 to 3.11.2. - [Release notes](https://github.com/nunit/nunit3-vs-adapter/releases) - [Commits](https://github.com/nunit/nunit3-vs-adapter/commits/3.11.2) Signed-off-by: dependabot[bot] --- .../System.IO.Abstractions.TestingHelpers.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/System.IO.Abstractions.TestingHelpers.Tests/System.IO.Abstractions.TestingHelpers.Tests.csproj b/System.IO.Abstractions.TestingHelpers.Tests/System.IO.Abstractions.TestingHelpers.Tests.csproj index 60c9a79b2..c070f49bb 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/System.IO.Abstractions.TestingHelpers.Tests.csproj +++ b/System.IO.Abstractions.TestingHelpers.Tests/System.IO.Abstractions.TestingHelpers.Tests.csproj @@ -47,7 +47,7 @@ - + From 45ee947f1231114ac34f1ea0f79a65a28b470cfe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Sat, 1 Dec 2018 19:46:19 +0100 Subject: [PATCH 17/24] Bump nunit from 3.8.1 to 3.11.0 (#412) Bumps [nunit](https://github.com/nunit/nunit) from 3.8.1 to 3.11.0. - [Release notes](https://github.com/nunit/nunit/releases) - [Changelog](https://github.com/nunit/nunit/blob/master/CHANGES.md) - [Commits](https://github.com/nunit/nunit/commits) Signed-off-by: dependabot[bot] --- .../System.IO.Abstractions.TestingHelpers.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/System.IO.Abstractions.TestingHelpers.Tests/System.IO.Abstractions.TestingHelpers.Tests.csproj b/System.IO.Abstractions.TestingHelpers.Tests/System.IO.Abstractions.TestingHelpers.Tests.csproj index c070f49bb..67a48bfdb 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/System.IO.Abstractions.TestingHelpers.Tests.csproj +++ b/System.IO.Abstractions.TestingHelpers.Tests/System.IO.Abstractions.TestingHelpers.Tests.csproj @@ -46,7 +46,7 @@ - + From 6df48cd0eb764b19dc42a677c4c768ca2552b4e6 Mon Sep 17 00:00:00 2001 From: Florian Greinacher Date: Sat, 1 Dec 2018 22:58:14 +0100 Subject: [PATCH 18/24] Enable linux configuration in AppVeyor (#415) * Enable linux configuration in AppVeyor * Add explicit NuGet restore * Deploy only from Windows job * Delete .travis.yml --- .travis.yml | 9 --------- appveyor.yml | 53 +++++++++++++++++++++++++++++----------------------- 2 files changed, 30 insertions(+), 32 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 29bffca69..000000000 --- a/.travis.yml +++ /dev/null @@ -1,9 +0,0 @@ -language: csharp -mono: none -dotnet: 2.1.4 -script: - - dotnet build System.IO.Abstractions --framework netstandard1.4 - - dotnet build System.IO.Abstractions.TestingHelpers --framework netstandard1.4 - - dotnet test System.IO.Abstractions.TestingHelpers.Tests --framework netcoreapp2.0 -git: - depth: false \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml index b4bf5221d..3dbf40d81 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,7 @@ -image: Visual Studio 2017 +image: +- Visual Studio 2017 +- Ubuntu + configuration: Release skip_branch_with_pr: true @@ -12,14 +15,15 @@ branches: environment: INHERITDOC_VERSION: 1.2.2.1 TEMP_DIR: c:\temp + APPVEYOR_YML_DISABLE_PS_LINUX: true install: # Temporarily install InheritDoc using the NuGet CLI -- nuget install inheritdoc -Version %INHERITDOC_VERSION% -OutputDirectory %TEMP_DIR% +- cmd: nuget install inheritdoc -Version %INHERITDOC_VERSION% -OutputDirectory %TEMP_DIR% -before_build: +before_build: - nuget restore - + build: publish_nuget: true @@ -27,22 +31,25 @@ before_package: # This step replaces the inheritdoc tags in the xml documentation with documentation from mscorlib.xml - ps: '& $env:TEMP_DIR\InheritDoc.$env:INHERITDOC_VERSION\tools\InheritDoc.exe -b $env:APPVEYOR_BUILD_FOLDER -g "c:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.X\mscorlib.xml" -o' -deploy: -- provider: NuGet - api_key: - secure: z0Zh4VyDdeKlFrTEnYbhqDc0xw9M5ahv/fQJsShk3HqmUWjQ/bh4M/r6ZWZgY/ZB - skip_symbols: false - artifact: /.*\.nupkg/ - on: - branch: master - -- provider: GitHub - tag: v$(appveyor_build_version) - prerelease: true - artifact: /.*\.nupkg/ - auth_token: - secure: Rrk1zp93EpCyxec/GXKnRnJjX7vU+ClNZEBnxbM+1j6l+C56qSV7B/fUrVsJuZYV - on: - branch: master - - +for: +- + matrix: + only: + - image: Visual Studio 2017 + deploy: + - provider: NuGet + api_key: + secure: z0Zh4VyDdeKlFrTEnYbhqDc0xw9M5ahv/fQJsShk3HqmUWjQ/bh4M/r6ZWZgY/ZB + skip_symbols: false + artifact: /.*\.nupkg/ + on: + branch: master + + - provider: GitHub + tag: v$(appveyor_build_version) + prerelease: true + artifact: /.*\.nupkg/ + auth_token: + secure: Rrk1zp93EpCyxec/GXKnRnJjX7vU+ClNZEBnxbM+1j6l+C56qSV7B/fUrVsJuZYV + on: + branch: master From c34ca37a94a88f5570ae077a6bcd7343090b3dac Mon Sep 17 00:00:00 2001 From: Florian Greinacher Date: Sun, 2 Dec 2018 12:00:48 +0100 Subject: [PATCH 19/24] Remove TravisCI badge from README (#416) --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 1b83d0a4c..4aa074087 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ [![NuGet](https://img.shields.io/nuget/v/System.IO.Abstractions.svg)](https://www.nuget.org/packages/System.IO.Abstractions) -[![Windows build status](https://ci.appveyor.com/api/projects/status/em172apw1v5k70vq/branch/master?svg=true)](https://ci.appveyor.com/project/tathamoddie/system-io-abstractions/branch/master) -[![Linux build status](https://travis-ci.org/System-IO-Abstractions/System.IO.Abstractions.svg?branch=master)](https://travis-ci.org/System-IO-Abstractions/System.IO.Abstractions) [![Dependabot Status](https://api.dependabot.com/badges/status?host=github&repo=System-IO-Abstractions/System.IO.Abstractions)](https://dependabot.com) +[![Build status](https://ci.appveyor.com/api/projects/status/em172apw1v5k70vq/branch/master?svg=true)](https://ci.appveyor.com/project/tathamoddie/system-io-abstractions/branch/master) [![Dependabot Status](https://api.dependabot.com/badges/status?host=github&repo=System-IO-Abstractions/System.IO.Abstractions)](https://dependabot.com) --- From c0da0c0e57bb75ed65f51f75f1e4b256b1b2fc7e Mon Sep 17 00:00:00 2001 From: Aman Baloch Date: Mon, 3 Dec 2018 19:15:27 +0000 Subject: [PATCH 20/24] Return original path from Mock*Info.ToString() (#406) Contributes to #400 --- .../MockDirectoryInfoTests.cs | 32 +++++++++---------- .../MockDriveInfoFactoryTests.cs | 6 ++-- .../MockDriveInfoTests.cs | 23 +++++++++++-- .../MockFileInfoTests.cs | 20 ++++++++++-- .../MockDirectoryInfo.cs | 13 ++++---- .../MockDriveInfo.cs | 11 +++++-- .../MockFileInfo.cs | 9 ++++-- 7 files changed, 78 insertions(+), 36 deletions(-) diff --git a/System.IO.Abstractions.TestingHelpers.Tests/MockDirectoryInfoTests.cs b/System.IO.Abstractions.TestingHelpers.Tests/MockDirectoryInfoTests.cs index 194dde31b..a850a157d 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/MockDirectoryInfoTests.cs +++ b/System.IO.Abstractions.TestingHelpers.Tests/MockDirectoryInfoTests.cs @@ -36,8 +36,8 @@ public static IEnumerable MockDirectoryInfo_Exists_Cases { get { - yield return new object[]{ XFS.Path(@"c:\temp\folder"), true }; - yield return new object[]{ XFS.Path(@"c:\temp\folder\notExistant"), false }; + yield return new object[] { XFS.Path(@"c:\temp\folder"), true }; + yield return new object[] { XFS.Path(@"c:\temp\folder\notExistant"), false }; } } @@ -152,8 +152,8 @@ public void MockDirectoryInfo_GetParent_ShouldReturnDirectoriesAndNamesWithSearc [Test] public void MockDirectoryInfo_EnumerateFiles_ShouldReturnAllFiles() { - // Arrange - var fileSystem = new MockFileSystem(new Dictionary + // Arrange + var fileSystem = new MockFileSystem(new Dictionary { //Files "above" in folder we're querying { XFS.Path(@"c:\temp\a.txt"), "" }, @@ -166,11 +166,11 @@ public void MockDirectoryInfo_EnumerateFiles_ShouldReturnAllFiles() { XFS.Path(@"c:\temp\folder\deeper\d.txt"), "" } }); - // Act - var directoryInfo = new MockDirectoryInfo(fileSystem, XFS.Path(@"c:\temp\folder")); + // Act + var directoryInfo = new MockDirectoryInfo(fileSystem, XFS.Path(@"c:\temp\folder")); - // Assert - Assert.AreEqual(new[]{"b.txt", "c.txt"}, directoryInfo.EnumerateFiles().ToList().Select(x => x.Name).ToArray()); + // Assert + Assert.AreEqual(new[] { "b.txt", "c.txt" }, directoryInfo.EnumerateFiles().ToList().Select(x => x.Name).ToArray()); } [Test] @@ -238,7 +238,7 @@ public void MockDirectoryInfo_Constructor_ShouldThrowArgumentNullException_IfArg var fileSystem = new MockFileSystem(); // Act - TestDelegate action = () => new MockDirectoryInfo(fileSystem, null); + TestDelegate action = () => new MockDirectoryInfo(fileSystem, null); // Assert var exception = Assert.Throws(action); @@ -272,20 +272,18 @@ public void MockDirectoryInfo_Constructor_ShouldThrowArgumentException_IfArgumen Assert.That(exception.Message, Does.StartWith("The path is not of a legal form.")); } - [Test] - public void MockDirectoryInfo_ToString_ShouldReturnDirectoryName() + [TestCase(@"c:\temp\folder\folder")] + [TestCase(@"..\..\..\Desktop")] + public void MockDirectoryInfo_ToString_ShouldReturnDirectoryName(string directoryName) { - var directoryPath = XFS.Path(@"c:\temp\folder\folder"); - // Arrange - var fileSystem = new MockFileSystem(); - var directoryInfo = new MockDirectoryInfo(fileSystem, directoryPath); + var directoryPath = XFS.Path(directoryName); // Act - var str = directoryInfo.ToString(); + var mockDirectoryInfo = new MockDirectoryInfo(new MockFileSystem(), directoryPath); // Assert - Assert.AreEqual(directoryPath, str); + Assert.AreEqual(directoryPath, mockDirectoryInfo.ToString()); } } } \ No newline at end of file diff --git a/System.IO.Abstractions.TestingHelpers.Tests/MockDriveInfoFactoryTests.cs b/System.IO.Abstractions.TestingHelpers.Tests/MockDriveInfoFactoryTests.cs index 2b10ec883..c6b7d5900 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/MockDriveInfoFactoryTests.cs +++ b/System.IO.Abstractions.TestingHelpers.Tests/MockDriveInfoFactoryTests.cs @@ -26,7 +26,7 @@ public void MockDriveInfoFactory_GetDrives_ShouldReturnDrives() var actualNames = actualResults.Select(d => d.Name); // Assert - Assert.That(actualNames, Is.EquivalentTo(new[] { @"C:\", @"Z:\", @"D:\" })); + Assert.That(actualNames, Is.EquivalentTo(new[] { @"C:\", @"Z:\", @"d:\" })); } [Test] @@ -47,7 +47,7 @@ public void MockDriveInfoFactory_GetDrives_ShouldReturnDrivesWithNoDuplicates() var actualNames = actualResults.Select(d => d.Name); // Assert - Assert.That(actualNames, Is.EquivalentTo(new[] { @"C:\", @"Z:\", @"D:\" })); + Assert.That(actualNames, Is.EquivalentTo(new[] { @"C:\", @"Z:\", @"d:\" })); } [Test] @@ -67,7 +67,7 @@ public void MockDriveInfoFactory_GetDrives_ShouldReturnOnlyLocalDrives() var actualNames = actualResults.Select(d => d.Name); // Assert - Assert.That(actualNames, Is.EquivalentTo(new[] { @"C:\", @"Z:\", @"D:\" })); + Assert.That(actualNames, Is.EquivalentTo(new[] { @"C:\", @"Z:\", @"d:\" })); } [Test] diff --git a/System.IO.Abstractions.TestingHelpers.Tests/MockDriveInfoTests.cs b/System.IO.Abstractions.TestingHelpers.Tests/MockDriveInfoTests.cs index 8b33fd16f..88524b2f4 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/MockDriveInfoTests.cs +++ b/System.IO.Abstractions.TestingHelpers.Tests/MockDriveInfoTests.cs @@ -21,7 +21,7 @@ public void MockDriveInfo_Constructor_ShouldInitializeLocalWindowsDrives(string var driveInfo = new MockDriveInfo(fileSystem, path); // Assert - Assert.AreEqual(@"C:\", driveInfo.Name); + Assert.AreEqual(@"c:\", driveInfo.Name); } [Test] @@ -35,7 +35,7 @@ public void MockDriveInfo_Constructor_ShouldInitializeLocalWindowsDrives_Special var driveInfo = new MockDriveInfo(fileSystem, "c"); // Assert - Assert.AreEqual(@"C:\", driveInfo.Name); + Assert.AreEqual(@"c:\", driveInfo.Name); } [TestCase(@"\\unc\share")] @@ -59,7 +59,7 @@ public void MockDriveInfo_RootDirectory_ShouldReturnTheDirectoryBase() var fileSystem = new MockFileSystem(); fileSystem.AddDirectory(XFS.Path(@"c:\Test")); var driveInfo = new MockDriveInfo(fileSystem, "c:"); - var expectedDirectory = XFS.Path(@"C:\"); + var expectedDirectory = XFS.Path(@"c:\"); // Act var actualDirectory = driveInfo.RootDirectory; @@ -67,5 +67,22 @@ public void MockDriveInfo_RootDirectory_ShouldReturnTheDirectoryBase() // Assert Assert.AreEqual(expectedDirectory, actualDirectory.FullName); } + + [TestCase("c:","c:\\")] + [TestCase("C:","C:\\")] + [TestCase("d:","d:\\")] + [TestCase("e:","e:\\")] + [TestCase("f:","f:\\")] + public void MockDriveInfo_ToString_ShouldReturnTheDrivePath(string path, string expectedPath) + { + // Arrange + var directoryPath = XFS.Path(path); + + // Act + var mockDriveInfo = new MockDriveInfo(new MockFileSystem(), directoryPath); + + // Assert + Assert.AreEqual(expectedPath, mockDriveInfo.ToString()); + } } } diff --git a/System.IO.Abstractions.TestingHelpers.Tests/MockFileInfoTests.cs b/System.IO.Abstractions.TestingHelpers.Tests/MockFileInfoTests.cs index e75cb3cec..c3b2a03b6 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/MockFileInfoTests.cs +++ b/System.IO.Abstractions.TestingHelpers.Tests/MockFileInfoTests.cs @@ -167,7 +167,7 @@ public void MockFileInfo_IsReadOnly_ShouldSetReadOnlyAttributeOfFileInMemoryFile [Test] public void MockFileInfo_IsReadOnly_ShouldSetNotReadOnlyAttributeOfFileInMemoryFileSystem() { - var fileData = new MockFileData("Demo text content") {Attributes = FileAttributes.ReadOnly}; + var fileData = new MockFileData("Demo text content") { Attributes = FileAttributes.ReadOnly }; var fileSystem = new MockFileSystem(new Dictionary { { XFS.Path(@"c:\a.txt"), fileData } @@ -210,7 +210,7 @@ public void MockFileInfo_OpenWrite_ShouldAddDataToFileInMemoryFileSystem() { XFS.Path(@"c:\a.txt"), fileData } }); var fileInfo = new MockFileInfo(fileSystem, XFS.Path(@"c:\a.txt")); - var bytesToAdd = new byte[] {65, 66, 67, 68, 69}; + var bytesToAdd = new byte[] { 65, 66, 67, 68, 69 }; using (var file = fileInfo.OpenWrite()) @@ -491,6 +491,22 @@ public void MockFileInfo_CopyTo_ThrowsExceptionIfSourceDoesntExist() Assert.Throws(action); } + [TestCase(@"..\..\..\c.txt")] + [TestCase(@"c:\a\b\c.txt")] + [TestCase(@"c:\a\c.txt")] + [TestCase(@"c:\c.txt")] + public void MockFileInfo_ToString_ShouldReturnOriginalFilePath(string path) + { + //Arrange + var filePath = XFS.Path(path); + + //Act + var mockFileInfo = new MockFileInfo(new MockFileSystem(), filePath); + + //Assert + Assert.AreEqual(filePath, mockFileInfo.ToString()); + } + #if NET40 [Test] public void MockFileInfo_Replace_ShouldReplaceFileContents() diff --git a/System.IO.Abstractions.TestingHelpers/MockDirectoryInfo.cs b/System.IO.Abstractions.TestingHelpers/MockDirectoryInfo.cs index 90f2ce9a1..ed806e493 100644 --- a/System.IO.Abstractions.TestingHelpers/MockDirectoryInfo.cs +++ b/System.IO.Abstractions.TestingHelpers/MockDirectoryInfo.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Globalization; using System.Linq; using System.Security.AccessControl; @@ -10,6 +9,7 @@ public class MockDirectoryInfo : DirectoryInfoBase { private readonly IMockFileDataAccessor mockFileDataAccessor; private readonly string directoryPath; + private readonly string originalPath; /// /// Initializes a new instance of the class. @@ -21,6 +21,7 @@ public MockDirectoryInfo(IMockFileDataAccessor mockFileDataAccessor, string dire { this.mockFileDataAccessor = mockFileDataAccessor ?? throw new ArgumentNullException(nameof(mockFileDataAccessor)); + originalPath = directoryPath; directoryPath = mockFileDataAccessor.Path.GetFullPath(directoryPath); this.directoryPath = directoryPath.TrimSlashes(); @@ -283,11 +284,6 @@ public override DirectoryInfoBase Root } } - public override string ToString() - { - return FullName; - } - private MockFileData GetMockFileDataForRead() { return mockFileDataAccessor.GetFile(directoryPath) ?? MockFileData.NullObject; @@ -298,5 +294,10 @@ private MockFileData GetMockFileDataForWrite() return mockFileDataAccessor.GetFile(directoryPath) ?? throw new FileNotFoundException(StringResources.Manager.GetString("COULD_NOT_FIND_FILE_EXCEPTION"), directoryPath); } + + public override string ToString() + { + return originalPath; + } } } diff --git a/System.IO.Abstractions.TestingHelpers/MockDriveInfo.cs b/System.IO.Abstractions.TestingHelpers/MockDriveInfo.cs index 918c147f0..e2ee47700 100644 --- a/System.IO.Abstractions.TestingHelpers/MockDriveInfo.cs +++ b/System.IO.Abstractions.TestingHelpers/MockDriveInfo.cs @@ -20,15 +20,15 @@ public MockDriveInfo(IMockFileDataAccessor mockFileDataAccessor, string name) : const string DRIVE_SEPARATOR = @":\"; if (name.Length == 1) { - name = char.ToUpperInvariant(name[0]) + DRIVE_SEPARATOR; + name = name[0] + DRIVE_SEPARATOR; } else if (name.Length == 2 && name[1] == ':') { - name = char.ToUpperInvariant(name[0]) + DRIVE_SEPARATOR; + name = name[0] + DRIVE_SEPARATOR; } else if (name.Length == 3 && name.EndsWith(DRIVE_SEPARATOR, StringComparison.Ordinal)) { - name = char.ToUpperInvariant(name[0]) + DRIVE_SEPARATOR; + name = name[0] + DRIVE_SEPARATOR; } else { @@ -63,6 +63,11 @@ public override DirectoryInfoBase RootDirectory } } + public override string ToString() + { + return Name; + } + public new long TotalFreeSpace { get; protected set; } public new long TotalSize { get; protected set; } public override string VolumeLabel { get; set; } diff --git a/System.IO.Abstractions.TestingHelpers/MockFileInfo.cs b/System.IO.Abstractions.TestingHelpers/MockFileInfo.cs index 298d054be..0d11fea89 100644 --- a/System.IO.Abstractions.TestingHelpers/MockFileInfo.cs +++ b/System.IO.Abstractions.TestingHelpers/MockFileInfo.cs @@ -242,7 +242,7 @@ public override Stream OpenRead() public override StreamReader OpenText() { - return new StreamReader(OpenRead()); + return new StreamReader(OpenRead()); } public override Stream OpenWrite() @@ -296,7 +296,7 @@ public override bool IsReadOnly set { if (MockFileData == null) throw new FileNotFoundException("File not found", path); - if(value) + if (value) MockFileData.Attributes |= FileAttributes.ReadOnly; else MockFileData.Attributes &= ~FileAttributes.ReadOnly; @@ -311,5 +311,10 @@ public override long Length return MockFileData.Contents.Length; } } + + public override string ToString() + { + return path; + } } } From ff528316eefa39cc3a6a6024dda60e1155b87a8d Mon Sep 17 00:00:00 2001 From: Florian Greinacher Date: Tue, 4 Dec 2018 19:41:11 +0100 Subject: [PATCH 21/24] Deploy only from release branch (#410) * Deploy only from release branch * Adapt version.json for deploy from release branch * Update CONTRIBUTING.md * Update CONTRIBUTING.md * Update appveyor.yml * Add link to create release PR --- CONTRIBUTING.md | 18 +++++++++++------- appveyor.yml | 11 ++++++++--- version.json | 4 ++-- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fee603cf8..220091947 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,15 +4,19 @@ This library uses [Nerdbank.GitVersioning](https://github.com/AArnott/Nerdbank.GitVersioning) for generating stable and reproducible version numbers. -The so-called base version is manually maintained in [the version config](version.json). Every build calculates its final version number based on the base version and the number of changes that occured since the last change to the version config. +The base version is manually maintained in [the version config](version.json). Every build calculates its final version number based on the base version and the number of changes that occured since the last change to the version config. -The base version represents the _next_ version that we will released. During development it contains a prerelease suffix, like `-beta` which is appended to the generated NuGet packages. +The base version represents the MAJOR and MINOR parts of [SemVer](https://semver.org). If a PR contains breaking changes or new features the base version has to be changed accordingly. If a PR solely contains minor changes (bug fixes, code improvements) nothing needs to be done as the PATCH number will automatically increment with each commit. -Every successful commit on `master` deploys packages to `nuget.org` and a creates GitHub release. As long as we have the prelease suffix both will be marked as such. +## Branches / tags + +* `master` contains the latest sources - this is where we develop. +* `release` contains the sources for the latest version on `nuget.org` - this is where we deploy from. +* All versions on `nuget.org` have a matching GitHub release/tag ### Release workflow -1. Remove prelease suffix from `version.json`. -1. Wait for the completion of the deployment. -1. Remove the prerelease flag from the newly created GitHub release. -1. Increment the version number in `version.json` and again add the prelease suffix (usually `beta` is fine). \ No newline at end of file +1. Create a [PR from `master` to `release`](https://github.com/System-IO-Abstractions/System.IO.Abstractions/compare/release...master?expand=1) and wait for CI to finish. +1. Inspect CI run (test results, version number) +1. Merge PR and wait for deployment +1. Inspect newly created package versions on NuGet.org and newly created GitHub release diff --git a/appveyor.yml b/appveyor.yml index 3dbf40d81..6e709c648 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -4,13 +4,17 @@ image: configuration: Release +# Don't start CI for branches that already have a PR skip_branch_with_pr: true +# Don't start CI when tags are pushed skip_tags: true +# Limit CI to master/release branches (and PRs targeting them) branches: only: - master + - release environment: INHERITDOC_VERSION: 1.2.2.1 @@ -31,6 +35,7 @@ before_package: # This step replaces the inheritdoc tags in the xml documentation with documentation from mscorlib.xml - ps: '& $env:TEMP_DIR\InheritDoc.$env:INHERITDOC_VERSION\tools\InheritDoc.exe -b $env:APPVEYOR_BUILD_FOLDER -g "c:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.X\mscorlib.xml" -o' +# Perform deployments only for Windows job for: - matrix: @@ -43,13 +48,13 @@ for: skip_symbols: false artifact: /.*\.nupkg/ on: - branch: master + branch: release - provider: GitHub tag: v$(appveyor_build_version) - prerelease: true + prerelease: false artifact: /.*\.nupkg/ auth_token: secure: Rrk1zp93EpCyxec/GXKnRnJjX7vU+ClNZEBnxbM+1j6l+C56qSV7B/fUrVsJuZYV on: - branch: master + branch: release diff --git a/version.json b/version.json index 10153a30b..bdad27842 100644 --- a/version.json +++ b/version.json @@ -5,7 +5,7 @@ "precision": "major" }, "publicReleaseRefSpec": [ - "^refs/heads/master$" + "^refs/heads/release$" ], "cloudBuild": { "buildNumber": { @@ -13,4 +13,4 @@ "setVersionVariables": true } } -} \ No newline at end of file +} From 47f8baad5ff9f711c2abd695c6d9fa35629ac771 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Tue, 4 Dec 2018 21:29:21 +0100 Subject: [PATCH 22/24] Bump Moq from 4.10.0 to 4.10.1 (#417) Bumps [Moq](https://github.com/moq/moq4) from 4.10.0 to 4.10.1. - [Release notes](https://github.com/moq/moq4/releases) - [Changelog](https://github.com/moq/moq4/blob/master/CHANGELOG.md) - [Commits](https://github.com/moq/moq4/compare/v4.10.0...v4.10.1) Signed-off-by: dependabot[bot] --- .../System.IO.Abstractions.TestingHelpers.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/System.IO.Abstractions.TestingHelpers.Tests/System.IO.Abstractions.TestingHelpers.Tests.csproj b/System.IO.Abstractions.TestingHelpers.Tests/System.IO.Abstractions.TestingHelpers.Tests.csproj index 67a48bfdb..2b48a0d01 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/System.IO.Abstractions.TestingHelpers.Tests.csproj +++ b/System.IO.Abstractions.TestingHelpers.Tests/System.IO.Abstractions.TestingHelpers.Tests.csproj @@ -42,7 +42,7 @@ - + From ade59aca57b8485f4b6d3bc1a317487310191940 Mon Sep 17 00:00:00 2001 From: Robert Koeninger Date: Fri, 7 Dec 2018 02:48:42 -0500 Subject: [PATCH 23/24] Make paths case-sensitive on Unix (#418) Fixes #321 Fixes #395 Fixes #405 * Added StringComparer to MockFileSystem, used in Mock* classes * Removed drive letter ToUpper in MockDriveInfo * Labelled tests as being Windows/Unix specific based on case sensitivity * Corrected UnixOnly test to check for null * Adjusted *Specifics wording * Fixed MockDirectory.GetParent to correctly identify and return '/' root * Fixed MockDirectory.GetFiles to handle un-normalized slashes * Made NormalizeSlashes handle UNC paths * Moved Exists check in MockDirectory.GetFiles into GetFilesInternal after path normalization * Made MockDirectory.GetLogicalDrives return drive letters in upper case instead of lower case * Fixed GetLogicalDrives test * De-triplicated check for Directory.Exists(DirName(FullPath(p))) * Added StringOperations, used throughout Mock* classes Cleaned up MockFileExistsTests, MockFileSystemTests * Removed WindowsSpecifics.EmptyInvalidPathChars Removed WindowsOnly label from the one that that used this reason * Made MockDirectory_GetParent_ShouldThr... WindowsOnly again because of Windows' stricter path rules * Combined private CheckDirectoryExists and VerifyDir... in MockFile * Cleanup, review change, in MockFileExistsTests --- .../MockDirectoryTests.cs | 71 +++++++-- .../MockFileExistsTests.cs | 60 ++++--- .../MockFileSystemTests.cs | 47 +++++- .../MockUnixSupportTests.cs | 8 +- .../UnixSpecifics.cs | 4 +- .../WindowsOnlyAttribute.cs | 2 +- .../WindowsSpecifics.cs | 2 + .../IMockFileDataAccessor.cs | 2 + .../MockDirectory.cs | 149 ++++++++++-------- .../MockDirectoryInfo.cs | 3 +- .../MockDriveInfo.cs | 27 +--- .../MockDriveInfoFactory.cs | 43 +++-- .../MockFile.cs | 46 ++---- .../MockFileInfo.cs | 2 +- .../MockFileStream.cs | 6 +- .../MockFileSystem.cs | 41 ++--- .../MockPath.cs | 47 ++---- .../MockUnixSupport.cs | 27 +--- .../PathVerifier.cs | 43 ++++- .../StringExtensions.cs | 41 ++++- .../StringOperations.cs | 29 ++++ 21 files changed, 413 insertions(+), 287 deletions(-) create mode 100644 System.IO.Abstractions.TestingHelpers/StringOperations.cs diff --git a/System.IO.Abstractions.TestingHelpers.Tests/MockDirectoryTests.cs b/System.IO.Abstractions.TestingHelpers.Tests/MockDirectoryTests.cs index d7f48234c..250241dd2 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] @@ -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() { @@ -849,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 @@ -1250,14 +1285,9 @@ public void MockDirectory_GetParent_ShouldReturnADirectoryInfoIfPathDoesNotExist } [Test] + [WindowsOnly(WindowsSpecifics.StrictPathRules)] public void MockDirectory_GetParent_ShouldThrowArgumentExceptionIfPathHasIllegalCharacters() { - if (XFS.IsUnixPlatform()) - { - Assert.Pass("Path.GetInvalidChars() does not return anything on Mono"); - return; - } - // Arrange var fileSystem = new MockFileSystem(); @@ -1282,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.Tests/MockFileExistsTests.cs b/System.IO.Abstractions.TestingHelpers.Tests/MockFileExistsTests.cs index 868c249ba..3a4b37f1f 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/MockFileExistsTests.cs +++ b/System.IO.Abstractions.TestingHelpers.Tests/MockFileExistsTests.cs @@ -6,59 +6,70 @@ 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\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); } [Test] + [WindowsOnly(WindowsSpecifics.CaseInsensitivity)] 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); } [Test] - public void MockFile_Exists_ShouldReturnFalseForEntirelyDifferentPath() + [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 }) } + { "/something/demo.txt", new MockFileData("Demo text content") } }); - var file = new MockFile(fileSystem); + // Act + var result = fileSystem.File.Exists("/SomeThing/DEMO.txt"); + + // Assert + Assert.IsFalse(result); + } + + [Test] + 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("gif content") } + }); // 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); @@ -67,9 +78,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] @@ -78,14 +94,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 513460d3b..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"); @@ -41,13 +41,14 @@ public void MockFileSystem_GetFile_ShouldReturnFileRegisteredInConstructor() } [Test] + [WindowsOnly(WindowsSpecifics.CaseInsensitivity)] public void MockFileSystem_GetFile_ShouldReturnFileRegisteredInConstructorWhenPathsDifferByCase() { var file1 = new MockFileData("Demo\r\ntext\ncontent\rvalue"); 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"); @@ -55,15 +56,47 @@ public void MockFileSystem_GetFile_ShouldReturnFileRegisteredInConstructorWhenPa Assert.AreEqual(file1, result); } + [Test] + [UnixOnly(UnixSpecifics.CaseSensitivity)] + public void MockFileSystem_GetFile_ShouldNotReturnFileRegisteredInConstructorWhenPathsDifferByCase() + { + var fileSystem = new MockFileSystem(new Dictionary + { + { "/something/demo.txt", new MockFileData("Demo\r\ntext\ncontent\rvalue") }, + { "/something/other.gif", new MockFileData("gif content") } + }); + + var result = fileSystem.GetFile("/SomeThing/DEMO.txt"); + + Assert.IsNull(result); + } + + [Test] + public void MockFileSystem_AddFile_ShouldHandleUnnormalizedSlashes() + { + var path = XFS.Path(@"c:\d1\d2\file.txt"); + 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(alternateParentPath).Length; + var fileExists = fs.File.Exists(alternatePath); + + Assert.AreEqual(1, fileCount); + Assert.IsTrue(fileExists); + } + [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 +104,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 +190,7 @@ public void MockFileSystem_AddFile_ShouldMatchCapitalization_PerfectMatch() } [Test] + [WindowsOnly(WindowsSpecifics.CaseInsensitivity)] public void MockFileSystem_AddFile_ShouldMatchCapitalization_PartialMatch() { var fileSystem = new MockFileSystem(); @@ -175,6 +209,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/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/UnixSpecifics.cs b/System.IO.Abstractions.TestingHelpers.Tests/UnixSpecifics.cs index 6e81e34c8..bf7cd4f52 100644 --- a/System.IO.Abstractions.TestingHelpers.Tests/UnixSpecifics.cs +++ b/System.IO.Abstractions.TestingHelpers.Tests/UnixSpecifics.cs @@ -2,6 +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-sensitive on Unix"; } } 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..86bde580e 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 CaseInsensitivity = "Paths are case-insensitive on Windows"; } } diff --git a/System.IO.Abstractions.TestingHelpers/IMockFileDataAccessor.cs b/System.IO.Abstractions.TestingHelpers/IMockFileDataAccessor.cs index fefb8ede4..6c2c6350a 100644 --- a/System.IO.Abstractions.TestingHelpers/IMockFileDataAccessor.cs +++ b/System.IO.Abstractions.TestingHelpers/IMockFileDataAccessor.cs @@ -54,6 +54,8 @@ public interface IMockFileDataAccessor /// IEnumerable AllDirectories { get; } + StringOperations StringOperations { 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..c38d0ce4a 100644 --- a/System.IO.Abstractions.TestingHelpers/MockDirectory.cs +++ b/System.IO.Abstractions.TestingHelpers/MockDirectory.cs @@ -11,17 +11,19 @@ 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) + // 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; this.mockFileDataAccessor = mockFileDataAccessor ?? throw new ArgumentNullException(nameof(mockFileDataAccessor)); - this.fileBase = fileBase; } public override DirectoryInfoBase CreateDirectory(string path) @@ -35,6 +37,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 +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, StringComparison.OrdinalIgnoreCase)) + .Where(p => mockFileDataAccessor.StringOperations.StartsWith(p, path)) .ToList(); if (!affectedPaths.Any()) @@ -99,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, StringComparison.OrdinalIgnoreCase)); + return mockFileDataAccessor.AllDirectories.Any(p => mockFileDataAccessor.StringOperations.Equals(p, path)); } catch (Exception) { @@ -128,12 +131,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() @@ -174,45 +177,55 @@ 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 = 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 ? @"[^/]*?/?" : @"[^\\]*?\\?"; @@ -244,22 +257,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(); } @@ -278,22 +278,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 +302,7 @@ public override string[] GetLogicalDrives() return mockFileDataAccessor .AllDirectories .Select(d => new MockDirectoryInfo(mockFileDataAccessor, d).Root.FullName) - .Select(r => r.ToLowerInvariant()) + .Select(r => mockFileDataAccessor.StringOperations.ToUpper(r)) .Distinct() .ToArray(); } @@ -320,19 +320,22 @@ 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"); } 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, StringComparison.OrdinalIgnoreCase) ? absolutePath.Length - 1 : absolutePath.Length; + var startIndex = mockFileDataAccessor.StringOperations.EndsWith(absolutePath, sepAsString) + ? absolutePath.Length - 1 + : absolutePath.Length; lastIndex = absolutePath.LastIndexOf(mockFileDataAccessor.Path.DirectorySeparatorChar, startIndex - 1); + if (lastIndex < 0) { return null; @@ -340,13 +343,25 @@ 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; } - var parent = new MockDirectoryInfo(mockFileDataAccessor, parentPath); - return parent; + return new MockDirectoryInfo(mockFileDataAccessor, parentPath); } public override void Move(string sourceDirName, string destDirName) @@ -354,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, StringComparison.OrdinalIgnoreCase)) + 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, StringComparison.OrdinalIgnoreCase)) + + if (!mockFileDataAccessor.StringOperations.Equals(sourceRoot, destinationRoot)) { throw new IOException("Source and destination path must have identical roots. Move will not work across volumes."); } @@ -395,12 +411,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 +426,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) @@ -445,17 +461,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 => string.Compare(p, path, StringComparison.OrdinalIgnoreCase) != 0); + return GetFilesInternal(mockFileDataAccessor.AllDirectories, path, searchPattern, searchOption) + .Where(p => !mockFileDataAccessor.StringOperations.Equals(p, path)); } public override IEnumerable EnumerateFiles(string path) @@ -501,7 +510,7 @@ private string EnsureAbsolutePath(string path) : Path.Combine(GetCurrentDirectory(), path); } - static void CheckSearchPattern(string searchPattern) + private void CheckSearchPattern(string searchPattern) { if (searchPattern == null) { @@ -511,15 +520,17 @@ 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 (mockFileDataAccessor.StringOperations.EndsWith(searchPattern, TWO_DOTS)) { throw createException(); } - int position; - if ((position = searchPattern.IndexOf(TWO_DOTS, StringComparison.OrdinalIgnoreCase)) >= 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 ed806e493..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, StringComparison.OrdinalIgnoreCase)) + + 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 e2ee47700..61ec413b4 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,32 +15,25 @@ public MockDriveInfo(IMockFileDataAccessor mockFileDataAccessor, string name) : } const string DRIVE_SEPARATOR = @":\"; - if (name.Length == 1) - { - name = name[0] + DRIVE_SEPARATOR; - } - else if (name.Length == 2 && name[1] == ':') - { - name = 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 && mockFileDataAccessor.StringOperations.EndsWith(name, DRIVE_SEPARATOR))) { name = 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) || mockFileDataAccessor.StringOperations.StartsWith(name, @"\\")) { throw new ArgumentException( @"Object must be a root directory (""C:\"") or a drive letter (""C"")."); } } - this.mockFileDataAccessor = mockFileDataAccessor; - Name = name; IsReady = true; } @@ -58,8 +48,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..bd1596a00 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 && mockFileSystem.StringOperations.EndsWith(driveName, @":\")) { - return char.ToUpperInvariant(driveName[0]) + @":\"; + return mockFileSystem.StringOperations.ToUpper(driveName[0]) + @":\"; } - if (driveName.StartsWith(@"\\", StringComparison.OrdinalIgnoreCase)) + if (mockFileSystem.StringOperations.StartsWith(driveName, @"\\")) { return null; } @@ -62,34 +62,27 @@ private string NormalizeDriveName(string driveName) private class DriveEqualityComparer : IEqualityComparer { - public bool Equals(string x, string y) - { - if (ReferenceEquals(x, y)) - { - return true; - } + private readonly IMockFileDataAccessor mockFileSystem; - if (ReferenceEquals(x, null)) - { - return false; - } - - if (ReferenceEquals(y, null)) - { - return false; - } + public DriveEqualityComparer(IMockFileDataAccessor mockFileSystem) + { + this.mockFileSystem = mockFileSystem ?? throw new ArgumentNullException(nameof(mockFileSystem)); + } - if (x[1] == ':' && y[1] == ':') - { - return char.ToUpperInvariant(x[0]) == char.ToUpperInvariant(y[0]); - } + public bool Equals(string x, string y) + { + 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 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 7cf1ebafc..a2d24d91f 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,13 +55,7 @@ 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)); - if (!mockFileDataAccessor.Directory.Exists(dir)) - { - throw new DirectoryNotFoundException(string.Format(CultureInfo.InvariantCulture, StringResources.Manager.GetString("COULD_NOT_FIND_PART_OF_PATH_EXCEPTION"), path)); - } - + VerifyDirectoryExists(path); mockFileDataAccessor.AddFile(path, new MockFileData(contents, encoding)); } else @@ -113,11 +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 = mockPath.GetDirectoryName(mockPath.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)); - } + VerifyDirectoryExists(destFileName); var fileExists = mockFileDataAccessor.FileExists(destFileName); if (fileExists) @@ -156,17 +144,7 @@ private Stream CreateInternal(string path, int bufferSize, FileOptions options, } mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, nameof(path)); - - var directoryPath = mockPath.GetDirectoryName(mockPath.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)); - } + VerifyDirectoryExists(path); var mockFileData = new MockFileData(new byte[0]) { @@ -571,15 +549,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, overwrite: true); } - mockFile.Delete(destinationFileName); - mockFile.Move(sourceFileName, destinationFileName); + Delete(destinationFileName); + Move(sourceFileName, destinationFileName); } #endif @@ -995,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)); } } } diff --git a/System.IO.Abstractions.TestingHelpers/MockFileInfo.cs b/System.IO.Abstractions.TestingHelpers/MockFileInfo.cs index 0d11fea89..257f3317c 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..cd3d15f64 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,13 @@ public MockFileSystem(IDictionary files, string currentDir currentDirectory = XFS.Path(DEFAULT_CURRENT_DIRECTORY); } + StringOperations = new StringOperations(XFS.IsUnixPlatform()); pathVerifier = new PathVerifier(this); + this.files = new Dictionary(StringOperations.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 +52,16 @@ public MockFileSystem(IDictionary files, string currentDir } } + public StringOperations StringOperations { 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 +93,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 => StringOperations.Equals(dir, leftHalf)); return baseDirectory + Path.DirectorySeparatorChar + rightHalf; } } @@ -152,7 +145,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) { @@ -161,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(@"\\", StringComparison.OrdinalIgnoreCase) || - fixedPath.StartsWith(@"//", StringComparison.OrdinalIgnoreCase); + var isUnc = + StringOperations.StartsWith(fixedPath, @"\\") || + StringOperations.StartsWith(fixedPath, @"//"); if (isUnc) { //First, confirm they aren't trying to create '\\server\' - lastIndex = fixedPath.IndexOf(separator, 2, StringComparison.OrdinalIgnoreCase); + lastIndex = StringOperations.IndexOf(fixedPath, separator, 2); + if (lastIndex < 0) throw new ArgumentException(@"The UNC path should be of the form \\server\share.", "path"); @@ -179,7 +172,7 @@ public void AddDirectory(string path) */ } - while ((lastIndex = fixedPath.IndexOf(separator, lastIndex + 1, StringComparison.OrdinalIgnoreCase)) > -1) + while ((lastIndex = StringOperations.IndexOf(fixedPath, separator, lastIndex + 1)) > -1) { var segment = fixedPath.Substring(0, lastIndex + 1); if (!Directory.Exists(segment)) @@ -188,7 +181,7 @@ public void AddDirectory(string path) } } - var s = fixedPath.EndsWith(separator, StringComparison.OrdinalIgnoreCase) ? fixedPath : fixedPath + separator; + var s = StringOperations.EndsWith(fixedPath, separator) ? fixedPath : fixedPath + separator; SetEntry(s, new MockDirectoryData()); } } @@ -234,12 +227,12 @@ public void MoveDirectory(string sourcePath, string destPath) lock (files) { var affectedPaths = files.Keys - .Where(p => p.StartsWith(sourcePath, StringComparison.OrdinalIgnoreCase)) + .Where(p => StringOperations.StartsWith(p, sourcePath)) .ToList(); foreach(var path in affectedPaths) { - var newPath = path.Replace(sourcePath, destPath, StringComparison.OrdinalIgnoreCase); + 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 ae44854ea..a1929b457 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); + mockFileDataAccessor.StringOperations.StartsWith(path, @"\\") || + mockFileDataAccessor.StringOperations.StartsWith(path, @"//"); string root = GetPathRoot(path); @@ -58,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, StringComparison.OrdinalIgnoreCase) || @"/".Equals(root, StringComparison.OrdinalIgnoreCase)) + 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); @@ -69,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), StringComparison.OrdinalIgnoreCase); + var isUnixRooted = mockFileDataAccessor.StringOperations.StartsWith( + mockFileDataAccessor.Directory.GetCurrentDirectory(), + string.Format(CultureInfo.InvariantCulture, "{0}", DirectorySeparatorChar)); var minPathSegments = isUnc ? 2 @@ -80,7 +79,7 @@ public override string GetFullPath(string path) var stack = new Stack(); foreach (var segment in pathSegments) { - if ("..".Equals(segment, StringComparison.OrdinalIgnoreCase)) + if (mockFileDataAccessor.StringOperations.Equals("..", segment)) { // only pop, if afterwards are at least the minimal amount of path segments if (stack.Count > minPathSegments) @@ -88,7 +87,7 @@ public override string GetFullPath(string path) stack.Pop(); } } - else if (".".Equals(segment, StringComparison.OrdinalIgnoreCase)) + else if (mockFileDataAccessor.StringOperations.Equals(".", segment)) { // ignore . } @@ -137,33 +136,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..a1d11cfdd 100644 --- a/System.IO.Abstractions.TestingHelpers/PathVerifier.cs +++ b/System.IO.Abstractions.TestingHelpers/PathVerifier.cs @@ -2,8 +2,11 @@ namespace System.IO.Abstractions.TestingHelpers { + using XFS = MockUnixSupport; + public class PathVerifier { + private static readonly char[] AdditionalInvalidPathChars = { '*', '?' }; private readonly IMockFileDataAccessor _mockFileDataAccessor; internal PathVerifier(IMockFileDataAccessor mockFileDataAccessor) @@ -28,12 +31,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 +42,8 @@ public void IsLegalAbsoluteOrRelative(string path, string paramName) } var filePath = ExtractFilePath(path); - if (MockPath.HasIllegalCharacters(filePath, false)) + + if (HasIllegalCharacters(filePath, checkAdditional: false)) { throw new ArgumentException(StringResources.Manager.GetString("ILLEGAL_CHARACTERS_IN_PATH_EXCEPTION")); } @@ -68,5 +69,35 @@ private string ExtractFilePath(string fullFileName) _mockFileDataAccessor.Path.AltDirectorySeparatorChar); return string.Join(_mockFileDataAccessor.Path.DirectorySeparatorChar.ToString(), extractFilePath.Take(extractFilePath.Length - 1)); } + + 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..f8640c861 100644 --- a/System.IO.Abstractions.TestingHelpers/StringExtensions.cs +++ b/System.IO.Abstractions.TestingHelpers/StringExtensions.cs @@ -1,9 +1,12 @@ using System.Collections.Generic; using System.Diagnostics.Contracts; +using System.Linq; using System.Text; namespace System.IO.Abstractions.TestingHelpers { + using XFS = MockUnixSupport; + public static class StringExtensions { [Pure] @@ -66,14 +69,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] == ':') @@ -83,5 +86,39 @@ 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; + + 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 prefixSeps + path; + } + + path = newPath; + } + } } } \ No newline at end of file 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 98f15a1d8b4a2a835ba16233ce81f9ee36be05cc Mon Sep 17 00:00:00 2001 From: Florian Greinacher Date: Fri, 7 Dec 2018 09:05:30 +0100 Subject: [PATCH 24/24] Bump base version to 3.0 (#408) --- version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.json b/version.json index bdad27842..292054aac 100644 --- a/version.json +++ b/version.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/AArnott/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", - "version": "2.2-beta", + "version": "3.0", "assemblyVersion": { "precision": "major" },