diff --git a/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFile.cs b/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFile.cs index 92b5a6530..0f9a97a48 100644 --- a/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFile.cs +++ b/src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFile.cs @@ -48,7 +48,7 @@ public override void AppendAllBytes(string path, ReadOnlySpan bytes) AppendAllBytes(path, bytes.ToArray()); } #endif - + /// public override void AppendAllLines(string path, IEnumerable contents) { @@ -163,6 +163,11 @@ public override void Copy(string sourceFileName, string destFileName, bool overw throw CommonExceptions.FileAlreadyExists(destFileName); } + if (string.Equals(sourceFileName, destFileName, StringComparison.OrdinalIgnoreCase) && XFS.IsWindowsPlatform()) + { + throw CommonExceptions.ProcessCannotAccessFileInUse(destFileName); + } + mockFileDataAccessor.RemoveFile(destFileName); } @@ -522,30 +527,37 @@ public override void Move(string sourceFileName, string destFileName) mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(sourceFileName, nameof(sourceFileName)); mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(destFileName, nameof(destFileName)); - if (mockFileDataAccessor.GetFile(destFileName) != null) - { - if (mockFileDataAccessor.StringOperations.Equals(destFileName, sourceFileName)) - { - return; - } - else - { - throw new IOException("A file can not be created if it already exists."); - } - } - var sourceFile = mockFileDataAccessor.GetFile(sourceFileName); if (sourceFile == null) { throw CommonExceptions.FileNotFound(sourceFileName); } + if (!sourceFile.AllowedFileShare.HasFlag(FileShare.Delete)) { throw CommonExceptions.ProcessCannotAccessFileInUse(); } + VerifyDirectoryExists(destFileName); + if (mockFileDataAccessor.GetFile(destFileName) != null) + { + if (mockFileDataAccessor.StringOperations.Equals(destFileName, sourceFileName)) + { + if (XFS.IsWindowsPlatform()) + { + mockFileDataAccessor.RemoveFile(sourceFileName); + mockFileDataAccessor.AddFile(destFileName, mockFileDataAccessor.AdjustTimes(new MockFileData(sourceFile), TimeAdjustments.LastAccessTime), false); + } + return; + } + else + { + throw new IOException("A file can not be created if it already exists."); + } + } + mockFileDataAccessor.RemoveFile(sourceFileName, false); mockFileDataAccessor.AddFile(destFileName, mockFileDataAccessor.AdjustTimes(new MockFileData(sourceFile), TimeAdjustments.LastAccessTime), false); } @@ -822,6 +834,11 @@ public override void Replace(string sourceFileName, string destinationFileName, throw CommonExceptions.FileNotFound(destinationFileName); } + if (mockFileDataAccessor.StringOperations.Equals(sourceFileName, destinationFileName) && XFS.IsWindowsPlatform()) + { + throw CommonExceptions.ProcessCannotAccessFileInUse(); + } + if (destinationBackupFileName != null) { Copy(destinationFileName, destinationBackupFileName, overwrite: true); @@ -1066,7 +1083,7 @@ public override void WriteAllBytes(string path, byte[] bytes) mockFileDataAccessor.AddFile(path, mockFileDataAccessor.AdjustTimes(new MockFileData(bytes.ToArray()), TimeAdjustments.All)); } - + #if FEATURE_FILE_SPAN /// public override void WriteAllBytes(string path, ReadOnlySpan bytes) diff --git a/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileCopyTests.cs b/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileCopyTests.cs index ddfb7992f..7ffca2a0b 100644 --- a/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileCopyTests.cs +++ b/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileCopyTests.cs @@ -246,6 +246,22 @@ public async Task MockFile_Copy_ShouldThrowNotSupportedExceptionWhenSourcePathCo await That(action).Throws(); } + [Test] + [WindowsOnly(WindowsSpecifics.Drives)] + public async Task MockFile_Copy_ShouldThrowIOExceptionWhenOverwritingWithSameNameDifferentCase() + { + var fileSystem = new MockFileSystem(); + string path = @"C:\Temp\file.txt"; + string pathUpper = @"C:\Temp\FILE.TXT"; + + fileSystem.File.WriteAllText(path, "Hello"); + + void Act() => fileSystem.File.Copy(path, pathUpper, true); + + await That(Act).Throws() + .WithMessage($"The process cannot access the file '{pathUpper}' because it is being used by another process."); + } + [Test] [WindowsOnly(WindowsSpecifics.Drives)] public async Task MockFile_Copy_ShouldThrowNotSupportedExceptionWhenSourcePathContainsInvalidDriveLetter() @@ -417,4 +433,4 @@ public async Task MockFile_Copy_ShouldThrowIOExceptionForInvalidFileShare() await That(action).Throws(); } -} \ No newline at end of file +} diff --git a/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileMoveTests.cs b/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileMoveTests.cs index 0305c7b1e..8032d3ad0 100644 --- a/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileMoveTests.cs +++ b/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileMoveTests.cs @@ -249,6 +249,22 @@ public async Task MockFile_Move_ShouldThrowNotSupportedExceptionWhenDestinationP await That(action).Throws(); } + [Test] + [WindowsOnly(WindowsSpecifics.Drives)] + public async Task MockFile_Move_CaseOnlyRename_ShouldChangeCase() + { + var fileSystem = new MockFileSystem(); + string sourceFilePath = @"c:\temp\demo.txt"; + string destFilePath = @"c:\temp\DEMO.TXT"; + string sourceFileContent = "content"; + fileSystem.File.WriteAllText(sourceFilePath, sourceFileContent); + + fileSystem.File.Move(sourceFilePath, destFilePath); + + await That(fileSystem.File.Exists(destFilePath)).IsTrue(); + await That(fileSystem.File.ReadAllText(destFilePath)).IsEqualTo(sourceFileContent); + } + [Test] public async Task MockFile_Move_ShouldThrowArgumentExceptionWhenSourceIsEmpty_Message() { diff --git a/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileTests.cs b/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileTests.cs index 9cda3d7b2..3694c466e 100644 --- a/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileTests.cs +++ b/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileTests.cs @@ -547,7 +547,7 @@ public async Task MockFile_AppendText_CreatesNewFileForAppendToNonExistingFile() await That(file.TextContents).IsEqualTo("New too!"); await That(filesystem.FileExists(filepath)).IsTrue(); } - + #if !NET9_0_OR_GREATER [Test] public void Serializable_works() @@ -567,7 +567,7 @@ public void Serializable_works() Assert.Pass(); } #endif - + #if !NET9_0_OR_GREATER [Test] public async Task Serializable_can_deserialize() @@ -670,7 +670,7 @@ public async Task MockFile_Replace_ShouldCreateBackup() fileSystem.File.Replace(path1, path2, path3); await That(fileSystem.File.ReadAllText(path3)).IsEqualTo("2"); - } + } [Test] public async Task MockFile_Replace_ShouldThrowIfDirectoryOfBackupPathDoesNotExist() @@ -730,4 +730,20 @@ public async Task MockFile_OpenRead_ShouldReturnReadOnlyStream() await That(stream.CanWrite).IsFalse(); await That(() => stream.WriteByte(0)).Throws(); } + + [Test] + [WindowsOnly(WindowsSpecifics.Drives)] + public async Task MockFile_Replace_SourceAndDestinationDifferOnlyInCasing_ShouldThrowIOException() + { + var fileSystem = new MockFileSystem(); + string sourceFilePath = @"c:\temp\demo.txt"; + string destFilePath = @"c:\temp\DEMO.txt"; + string fileContent = "content"; + fileSystem.File.WriteAllText(sourceFilePath, fileContent); + + void Act() => fileSystem.File.Replace(sourceFilePath, destFilePath, null, true); + + await That(Act).Throws() + .HasMessage("The process cannot access the file because it is being used by another process."); + } } \ No newline at end of file