Skip to content

Commit

Permalink
fix: Throw correct exception when using File.Replace with case-only…
Browse files Browse the repository at this point in the history
… changes on MacOS (#638)

* Add tests for case-only changes in File.Copy, File.Move and File.Replace
* Ensure correct casing in `Directory.Move` tests
* Throw correct exception on MacOS
  • Loading branch information
vbreuss authored Aug 12, 2024
1 parent aa39bb6 commit 5e768d1
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,10 @@ internal static IOException ProcessCannotAccessTheFile(string path, int hResult)
$"The process cannot access the file '{path}' because it is being used by another process.",
hResult);

internal static IOException ReplaceSourceMustBeDifferentThanDestination(
string sourcePath, string destinationPath)
=> new($"The source '{sourcePath}' and destination '{destinationPath}' are the same file.", -2146232800);

#pragma warning disable MA0015 // Specify the parameter name
internal static ArgumentException SearchPatternCannotContainTwoDots()
=> new(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,12 @@ public IStorageContainer GetOrCreateContainer(
throw ExceptionFactory.AccessToPathDenied(source.FullPath);
}

if (_fileSystem.Execute.IsMac &&
source.FullPath.Equals(destination.FullPath, StringComparison.OrdinalIgnoreCase))
{
throw ExceptionFactory.ReplaceSourceMustBeDifferentThanDestination(source.FullPath, destination.FullPath);
}

using (_ = sourceContainer.RequestAccess(
FileAccess.ReadWrite,
FileShare.None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public void Move_CaseOnlyChange_ShouldMoveDirectoryWithContent(string path)
FileSystem.Directory.Exists(source).Should().Be(!Test.RunsOnLinux);
FileSystem.Should().HaveDirectory(destination);
FileSystem.Directory.GetDirectories(".").Should()
.ContainSingle(d => d.Contains(destination));
.ContainSingle(d => d.Contains(destination, StringComparison.Ordinal));
FileSystem.Directory.GetFiles(destination, initialized[1].Name)
.Should().ContainSingle();
FileSystem.Directory.GetDirectories(destination, initialized[2].Name)
Expand Down
27 changes: 27 additions & 0 deletions Tests/Testably.Abstractions.Tests/FileSystem/File/CopyTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,33 @@ public abstract partial class CopyTests<TFileSystem>
: FileSystemTestBase<TFileSystem>
where TFileSystem : IFileSystem
{
[SkippableTheory]
[AutoData]
public void Copy_CaseOnlyChange_ShouldThrowIOException_ExceptOnLinux(
string name, string contents)
{
string sourceName = name.ToLowerInvariant();
string destinationName = name.ToUpperInvariant();
FileSystem.File.WriteAllText(sourceName, contents);

Exception? exception = Record.Exception(() =>
{
FileSystem.File.Copy(sourceName, destinationName);
});

if (Test.RunsOnLinux)
{
exception.Should().BeNull();
FileSystem.File.Exists(sourceName).Should().BeTrue();
FileSystem.File.Exists(destinationName).Should().BeTrue();
}
else
{
exception.Should()
.BeException<IOException>(hResult: Test.RunsOnWindows ? -2147024816 : 17);
}
}

[SkippableTheory]
[AutoData]
public void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ public void Move_CaseOnlyChange_ShouldMoveFileWithContent(

FileSystem.Should().HaveFile(destinationName)
.Which.HasContent(contents);
FileSystem.Directory.GetFiles(".").Should()
.ContainSingle(d => d.Contains(destinationName, StringComparison.Ordinal));
}

[SkippableTheory]
Expand Down
36 changes: 36 additions & 0 deletions Tests/Testably.Abstractions.Tests/FileSystem/File/ReplaceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,42 @@ public abstract partial class ReplaceTests<TFileSystem>
: FileSystemTestBase<TFileSystem>
where TFileSystem : IFileSystem
{
[SkippableTheory]
[AutoData]
public void Replace_CaseOnlyChange_ShouldThrowIOException(
string name, string contents)
{
string sourceName = name.ToLowerInvariant();
string destinationName = name.ToUpperInvariant();
FileSystem.File.WriteAllText(sourceName, contents);
FileSystem.File.WriteAllText(destinationName, "other-content");

Exception? exception = Record.Exception(() =>
{
FileSystem.File.Replace(sourceName, destinationName, null);
});


if (Test.RunsOnLinux)
{
exception.Should().BeNull();
FileSystem.File.Exists(sourceName).Should().BeFalse();
FileSystem.File.Exists(destinationName).Should().BeTrue();
}
else if (Test.RunsOnMac)
{
exception.Should().BeException<IOException>(
hResult: -2146232800,
messageContains: $"The source '{FileSystem.Path.GetFullPath(sourceName)}' and destination '{FileSystem.Path.GetFullPath(destinationName)}' are the same file");
}
else
{
exception.Should().BeException<IOException>(
hResult: -2147024864,
messageContains: "The process cannot access the file");
}
}

[SkippableTheory]
[AutoData]
public void
Expand Down

0 comments on commit 5e768d1

Please sign in to comment.