From fc242418e07573e236a99fa72368155b38dadfab Mon Sep 17 00:00:00 2001 From: Anant Date: Wed, 2 Apr 2025 22:15:43 +0530 Subject: [PATCH 1/3] fix: handle case-insensitive file overwrite on Windows test: add tests for Windows file copy behavior --- .../MockFile.cs | 45 +++++++++++++------ .../MockFileCopyTests.cs | 13 ++++++ .../MockFileMoveTests.cs | 20 +++++++++ .../MockFileTests.cs | 22 +++++++-- 4 files changed, 83 insertions(+), 17 deletions(-) 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..2f10d0c7c 100644 --- a/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileCopyTests.cs +++ b/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileCopyTests.cs @@ -246,6 +246,19 @@ public async Task MockFile_Copy_ShouldThrowNotSupportedExceptionWhenSourcePathCo await That(action).Throws(); } + [Test] + [WindowsOnly(WindowsSpecifics.Drives)] + public async Task Copy_Should_ThrowIOException_When_OverwritingWithSameNameDifferentCase() + { + var fileSystem = new MockFileSystem(); + string path = @"C:\Temp\file.txt"; + string pathUpper = @"C:\Temp\FILE.TXT"; + + fileSystem.File.WriteAllText(path, "Hello"); + + await That(() => fileSystem.File.Copy(path, pathUpper, true)).Throws().HasMessage($"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() diff --git a/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileMoveTests.cs b/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileMoveTests.cs index 0305c7b1e..4517a1c7a 100644 --- a/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileMoveTests.cs +++ b/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileMoveTests.cs @@ -249,6 +249,26 @@ public async Task MockFile_Move_ShouldThrowNotSupportedExceptionWhenDestinationP await That(action).Throws(); } + [Test] + [WindowsOnly(WindowsSpecifics.Drives)] + public async Task MockFile_Move_CaseOnlyRename_ShouldChangeCase() + { + string sourceFilePath = @"c:\something\demo.txt"; + string destFilePath = @"c:\something\DEMO.TXT"; + string sourceFileContent = "content"; + + var fileSystem = new MockFileSystem(new Dictionary + { + { + sourceFilePath, new MockFileData(sourceFileContent)} + }); + + fileSystem.File.Move(sourceFilePath, destFilePath); + + await That(fileSystem.FileExists(destFilePath)).IsTrue(); + await That(fileSystem.GetFile(destFilePath).TextContents).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..9baf13428 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_SameSourceAndDestination_ShouldThrowIOException() + { + string sourceFilePath = @"c:\something\demo.txt"; + string destFilePath = @"c:\something\Demo.txt"; + string fileContent = "content"; + + var fileSystem = new MockFileSystem(new Dictionary + { + { sourceFilePath, new MockFileData(fileContent) } + }); + + await That(() => fileSystem.File.Replace(sourceFilePath, destFilePath, null, true)).Throws().HasMessage("The process cannot access the file because it is being used by another process."); + } } \ No newline at end of file From fff7d7277519f72d6a8be083cce3186b78e57a93 Mon Sep 17 00:00:00 2001 From: ana1250 <85818017+ana1250@users.noreply.github.com> Date: Wed, 2 Apr 2025 23:46:10 +0530 Subject: [PATCH 2/3] Fixed name of Test --- .../MockFileCopyTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileCopyTests.cs b/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileCopyTests.cs index 2f10d0c7c..6e1346872 100644 --- a/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileCopyTests.cs +++ b/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileCopyTests.cs @@ -248,7 +248,7 @@ public async Task MockFile_Copy_ShouldThrowNotSupportedExceptionWhenSourcePathCo [Test] [WindowsOnly(WindowsSpecifics.Drives)] - public async Task Copy_Should_ThrowIOException_When_OverwritingWithSameNameDifferentCase() + public async Task MockFile_Copy_ShouldThrowIOExceptionWhenOverwritingWithSameNameDifferentCase() { var fileSystem = new MockFileSystem(); string path = @"C:\Temp\file.txt"; @@ -430,4 +430,4 @@ public async Task MockFile_Copy_ShouldThrowIOExceptionForInvalidFileShare() await That(action).Throws(); } -} \ No newline at end of file +} From a6ce31529edf295b4786e86307544b53cba5d3be Mon Sep 17 00:00:00 2001 From: Anant Date: Fri, 4 Apr 2025 17:24:17 +0530 Subject: [PATCH 3/3] fix : Resolved comments --- .../MockFileCopyTests.cs | 5 ++++- .../MockFileMoveTests.cs | 16 ++++++---------- .../MockFileTests.cs | 16 ++++++++-------- 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileCopyTests.cs b/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileCopyTests.cs index 6e1346872..7ffca2a0b 100644 --- a/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileCopyTests.cs +++ b/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileCopyTests.cs @@ -256,7 +256,10 @@ public async Task MockFile_Copy_ShouldThrowIOExceptionWhenOverwritingWithSameNam fileSystem.File.WriteAllText(path, "Hello"); - await That(() => fileSystem.File.Copy(path, pathUpper, true)).Throws().HasMessage($"The process cannot access the file '{pathUpper}' because it is being used by another process."); + 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] diff --git a/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileMoveTests.cs b/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileMoveTests.cs index 4517a1c7a..8032d3ad0 100644 --- a/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileMoveTests.cs +++ b/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileMoveTests.cs @@ -253,20 +253,16 @@ public async Task MockFile_Move_ShouldThrowNotSupportedExceptionWhenDestinationP [WindowsOnly(WindowsSpecifics.Drives)] public async Task MockFile_Move_CaseOnlyRename_ShouldChangeCase() { - string sourceFilePath = @"c:\something\demo.txt"; - string destFilePath = @"c:\something\DEMO.TXT"; + var fileSystem = new MockFileSystem(); + string sourceFilePath = @"c:\temp\demo.txt"; + string destFilePath = @"c:\temp\DEMO.TXT"; string sourceFileContent = "content"; - - var fileSystem = new MockFileSystem(new Dictionary - { - { - sourceFilePath, new MockFileData(sourceFileContent)} - }); + fileSystem.File.WriteAllText(sourceFilePath, sourceFileContent); fileSystem.File.Move(sourceFilePath, destFilePath); - await That(fileSystem.FileExists(destFilePath)).IsTrue(); - await That(fileSystem.GetFile(destFilePath).TextContents).IsEqualTo(sourceFileContent); + await That(fileSystem.File.Exists(destFilePath)).IsTrue(); + await That(fileSystem.File.ReadAllText(destFilePath)).IsEqualTo(sourceFileContent); } [Test] diff --git a/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileTests.cs b/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileTests.cs index 9baf13428..3694c466e 100644 --- a/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileTests.cs +++ b/tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileTests.cs @@ -733,17 +733,17 @@ public async Task MockFile_OpenRead_ShouldReturnReadOnlyStream() [Test] [WindowsOnly(WindowsSpecifics.Drives)] - public async Task MockFile_Replace_SameSourceAndDestination_ShouldThrowIOException() + public async Task MockFile_Replace_SourceAndDestinationDifferOnlyInCasing_ShouldThrowIOException() { - string sourceFilePath = @"c:\something\demo.txt"; - string destFilePath = @"c:\something\Demo.txt"; + var fileSystem = new MockFileSystem(); + string sourceFilePath = @"c:\temp\demo.txt"; + string destFilePath = @"c:\temp\DEMO.txt"; string fileContent = "content"; + fileSystem.File.WriteAllText(sourceFilePath, fileContent); - var fileSystem = new MockFileSystem(new Dictionary - { - { sourceFilePath, new MockFileData(fileContent) } - }); + void Act() => fileSystem.File.Replace(sourceFilePath, destFilePath, null, true); - await That(() => fileSystem.File.Replace(sourceFilePath, destFilePath, null, true)).Throws().HasMessage("The process cannot access the file because it is being used by another process."); + 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