diff --git a/Source/Testably.Abstractions.Testing/FileSystem/FileInfoMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/FileInfoMock.cs index f86ca07d..21ad0f16 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/FileInfoMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/FileInfoMock.cs @@ -252,7 +252,8 @@ public IFileInfo Replace(string destinationFileName, } throw ExceptionFactory.DirectoryNotFound(FullName); - })) + }), + !Execute.IsWindows) ?? throw ExceptionFactory.FileNotFound(FullName); return _fileSystem.FileInfo.New(location.FullPath); } diff --git a/Source/Testably.Abstractions.Testing/Storage/InMemoryStorage.cs b/Source/Testably.Abstractions.Testing/Storage/InMemoryStorage.cs index 36f79db6..f85f45be 100644 --- a/Source/Testably.Abstractions.Testing/Storage/InMemoryStorage.cs +++ b/Source/Testably.Abstractions.Testing/Storage/InMemoryStorage.cs @@ -382,11 +382,15 @@ public IStorageContainer GetOrCreateContainer( throw ExceptionFactory.AccessToPathDenied(source.FullPath); } - using (_ = sourceContainer.RequestAccess(FileAccess.ReadWrite, FileShare.None, + using (_ = sourceContainer.RequestAccess( + FileAccess.ReadWrite, + FileShare.None, ignoreMetadataErrors: ignoreMetadataErrors)) { - using (_ = destinationContainer.RequestAccess(FileAccess.ReadWrite, - FileShare.None, ignoreMetadataErrors: ignoreMetadataErrors)) + using (_ = destinationContainer.RequestAccess( + FileAccess.ReadWrite, + FileShare.None, + ignoreMetadataErrors: ignoreMetadataErrors)) { if (_containers.TryRemove(destination, out IStorageContainer? existingDestinationContainer)) @@ -412,8 +416,17 @@ public IStorageContainer GetOrCreateContainer( Execute.OnWindowsIf(sourceContainer.Type == FileSystemTypes.File, () => { - existingSourceContainer.Attributes |= + FileAttributes targetAttributes = + existingDestinationContainer.Attributes | FileAttributes.Archive; + if (existingSourceContainer.Attributes.HasFlag(FileAttributes + .ReadOnly)) + { + targetAttributes |= FileAttributes.ReadOnly; + } + + existingSourceContainer.Attributes = targetAttributes; + existingSourceContainer.CreationTime.Set( existingDestinationContainer.CreationTime.Get( DateTimeKind.Utc), @@ -502,7 +515,7 @@ public bool TryAddContainer( return false; } -#endregion + #endregion /// public override string ToString() diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/ReplaceTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/ReplaceTests.cs index 236c9a6c..f65010d7 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/ReplaceTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileInfo/ReplaceTests.cs @@ -156,8 +156,8 @@ public void } [SkippableTheory] - [InlineAutoData(FileAttributes.Hidden, FileAttributes.Hidden)] - [InlineAutoData(FileAttributes.System, FileAttributes.System)] + [InlineAutoData(FileAttributes.Hidden, FileAttributes.System)] + [InlineAutoData(FileAttributes.System, FileAttributes.Hidden)] public void Replace_ShouldAddArchiveAttribute_OnWindows( FileAttributes sourceFileAttributes, FileAttributes destinationFileAttributes, @@ -187,14 +187,48 @@ public void Replace_ShouldAddArchiveAttribute_OnWindows( IFileInfo sut = FileSystem.FileInfo.New(sourceName); - sut.Replace(destinationName, backupName, true); + sut.Replace(destinationName, backupName); - FileSystem.File.GetAttributes(destinationName) - .Should().Be(expectedSourceAttributes); + if (Test.RunsOnMac) + { + FileSystem.File.GetAttributes(destinationName) + .Should().Be(expectedSourceAttributes); + } + else + { + FileSystem.File.GetAttributes(destinationName) + .Should().Be(expectedDestinationAttributes); + } FileSystem.File.GetAttributes(backupName) .Should().Be(expectedDestinationAttributes); } + [SkippableTheory] + [AutoData] + public void Replace_WhenFileIsReadOnly_ShouldThrowUnauthorizedAccessException_OnWindows( + string sourceName, + string destinationName, + string backupName, + string sourceContents, + string destinationContents) + { + Skip.IfNot(Test.RunsOnWindows); + + FileSystem.File.WriteAllText(sourceName, sourceContents); + + FileSystem.File.WriteAllText(destinationName, destinationContents); + FileSystem.File.SetAttributes(destinationName, FileAttributes.ReadOnly); + + IFileInfo sut = FileSystem.FileInfo.New(sourceName); + + Exception? exception = Record.Exception(() => + { + sut.Replace(destinationName, backupName); + }); + + exception.Should().BeException(hResult: -2147024891); + } + [SkippableTheory] [AutoData] public void Replace_ShouldKeepMetadata(