diff --git a/Examples/ZipFile/ZipFile/ZipFileHelper.cs b/Examples/ZipFile/ZipFile/ZipFileHelper.cs index fd390ee60..1dbe52b1a 100644 --- a/Examples/ZipFile/ZipFile/ZipFileHelper.cs +++ b/Examples/ZipFile/ZipFile/ZipFileHelper.cs @@ -54,10 +54,13 @@ public void ExtractZipToDirectory(Stream stream, string directory) { _fileSystem.Directory.CreateDirectory(directoryPath); } - - using MemoryStream ms = new(); - entry.Open().CopyTo(ms); - _fileSystem.File.WriteAllBytes(filePath, ms.ToArray()); + + if (!entry.FullName.EndsWith("/")) + { + using MemoryStream ms = new(); + entry.Open().CopyTo(ms); + _fileSystem.File.WriteAllBytes(filePath, ms.ToArray()); + } } } diff --git a/Source/Testably.Abstractions.Testing/FileSystem/DirectoryInfoMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/DirectoryInfoMock.cs index d0d9cd91a..3f61852c0 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/DirectoryInfoMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/DirectoryInfoMock.cs @@ -46,6 +46,11 @@ public void Create() InMemoryContainer.NewDirectory, this); + if (Container.Type != FileSystemTypes.Directory) + { + throw ExceptionFactory.CannotCreateFileAsAlreadyExists(FullName); + } + ResetCache(!Execute.IsNetFramework); } diff --git a/Source/Testably.Abstractions.Testing/FileSystem/FileMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/FileMock.cs index 870d08683..bfdaa0a5b 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/FileMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/FileMock.cs @@ -12,6 +12,7 @@ #if FEATURE_FILESYSTEM_ASYNC using System.Threading; using System.Threading.Tasks; + #endif namespace Testably.Abstractions.Testing.FileSystem; @@ -73,23 +74,29 @@ public void AppendAllText(string path, string? contents) /// public void AppendAllText(string path, string? contents, Encoding encoding) { - IStorageContainer fileInfo = + IStorageContainer container = _fileSystem.Storage.GetOrCreateContainer( _fileSystem.Storage.GetLocation( path.EnsureValidFormat(FileSystem)), InMemoryContainer.NewFile); + + if (container.Type != FileSystemTypes.File) + { + throw ExceptionFactory.AccessToPathDenied(path); + } + if (contents != null) { - using (fileInfo.RequestAccess( + using (container.RequestAccess( FileAccess.ReadWrite, FileStreamFactoryMock.DefaultShare)) { - if (fileInfo.GetBytes().Length == 0) + if (container.GetBytes().Length == 0) { - fileInfo.WriteBytes(encoding.GetPreamble()); + container.WriteBytes(encoding.GetPreamble()); } - fileInfo.AppendBytes(encoding.GetBytes(contents)); + container.AppendBytes(encoding.GetBytes(contents)); } } } @@ -786,17 +793,25 @@ public void WriteAllBytes(string path, byte[] bytes) _fileSystem.Storage.GetLocation( path.EnsureValidFormat(FileSystem)), InMemoryContainer.NewFile); - if (container is not NullContainer) + + if (container is NullContainer) { - Execute.OnWindowsIf( - container.Attributes.HasFlag(FileAttributes.Hidden), - () => throw ExceptionFactory.AccessToPathDenied()); - using (container.RequestAccess( - FileAccess.Write, - FileStreamFactoryMock.DefaultShare)) - { - container.WriteBytes(bytes); - } + return; + } + + if (container.Type != FileSystemTypes.File) + { + throw ExceptionFactory.AccessToPathDenied(path); + } + + Execute.OnWindowsIf( + container.Attributes.HasFlag(FileAttributes.Hidden), + () => throw ExceptionFactory.AccessToPathDenied()); + using (container.RequestAccess( + FileAccess.Write, + FileStreamFactoryMock.DefaultShare)) + { + container.WriteBytes(bytes); } } @@ -869,7 +884,17 @@ public void WriteAllText(string path, string? contents, Encoding encoding) _fileSystem.Storage.GetLocation( path.EnsureValidFormat(FileSystem)), InMemoryContainer.NewFile); - if (container is not NullContainer && contents != null) + if (container is NullContainer) + { + return; + } + + if (container.Type != FileSystemTypes.File) + { + throw ExceptionFactory.AccessToPathDenied(path); + } + + if (contents != null) { Execute.OnWindowsIf( container.Attributes.HasFlag(FileAttributes.Hidden), diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/Directory/CreateDirectoryTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/Directory/CreateDirectoryTests.cs index 9c31d6dc7..05c9051e4 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/Directory/CreateDirectoryTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/Directory/CreateDirectoryTests.cs @@ -50,6 +50,22 @@ public void CreateDirectory_ReadOnlyParent_ShouldStillCreateDirectory(string par } } + [SkippableTheory] + [AutoData] + public void CreateDirectory_FileWithSameNameAlreadyExists_ShouldThrowIOException(string name) + { + FileSystem.File.WriteAllText(name, ""); + + Exception? exception = Record.Exception(() => + { + FileSystem.Directory.CreateDirectory(name); + }); + + exception.Should().BeException( + hResult: Test.RunsOnWindows ? -2147024713 : 17); + FileSystem.Directory.Exists(name).Should().BeFalse(); + } + [SkippableFact] public void CreateDirectory_Root_ShouldNotThrowException() { diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/CreateSubdirectoryTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/CreateSubdirectoryTests.cs index 37d184e4a..adfc8e2a7 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/CreateSubdirectoryTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/CreateSubdirectoryTests.cs @@ -1,3 +1,5 @@ +using System.IO; + namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; // ReSharper disable once PartialTypeWithSinglePart @@ -5,6 +7,24 @@ public abstract partial class CreateSubdirectoryTests : FileSystemTestBase where TFileSystem : IFileSystem { + [SkippableTheory] + [AutoData] + public void CreateSubdirectory_FileWithSameNameAlreadyExists_ShouldThrowIOException( + string name) + { + FileSystem.File.WriteAllText(name, ""); + IDirectoryInfo sut = FileSystem.DirectoryInfo.New("."); + + Exception? exception = Record.Exception(() => + { + sut.CreateSubdirectory(name); + }); + + exception.Should().BeException( + hResult: Test.RunsOnWindows ? -2147024713 : 17); + FileSystem.Directory.Exists(name).Should().BeFalse(); + } + [SkippableTheory] [AutoData] public void CreateSubdirectory_MissingParent_ShouldCreateDirectory( diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/CreateTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/CreateTests.cs index 537779a61..eada3313f 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/CreateTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/DirectoryInfo/CreateTests.cs @@ -1,3 +1,5 @@ +using System.IO; + namespace Testably.Abstractions.Tests.FileSystem.DirectoryInfo; // ReSharper disable once PartialTypeWithSinglePart @@ -5,6 +7,23 @@ public abstract partial class CreateTests : FileSystemTestBase where TFileSystem : IFileSystem { + [SkippableTheory] + [AutoData] + public void Create_FileWithSameNameAlreadyExists_ShouldThrowIOException(string name) + { + FileSystem.File.WriteAllText(name, ""); + IDirectoryInfo sut = FileSystem.DirectoryInfo.New(name); + + Exception? exception = Record.Exception(() => + { + sut.Create(); + }); + + exception.Should().BeException( + hResult: Test.RunsOnWindows ? -2147024713 : 17); + FileSystem.Directory.Exists(name).Should().BeFalse(); + } + [SkippableTheory] [AutoData] public void Create_ShouldCreateDirectory(string path) diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/File/AppendAllLinesAsyncTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/File/AppendAllLinesAsyncTests.cs index 7c39092cd..dbd1742bb 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/File/AppendAllLinesAsyncTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/File/AppendAllLinesAsyncTests.cs @@ -130,6 +130,25 @@ public async Task AppendAllLinesAsync_ShouldEndWithNewline(string path) FileSystem.File.ReadAllText(path).Should().BeEquivalentTo(expectedResult); } + [SkippableTheory] + [AutoData] + public async Task + AppendAllLinesAsync_WhenDirectoryWithSameNameExists_ShouldThrowUnauthorizedAccessException( + string path, string[] contents) + { + FileSystem.Directory.CreateDirectory(path); + + Exception? exception = await Record.ExceptionAsync(async () => + { + await FileSystem.File.AppendAllLinesAsync(path, contents); + }); + + exception.Should().BeException( + hResult: -2147024891); + FileSystem.Directory.Exists(path).Should().BeTrue(); + FileSystem.File.Exists(path).Should().BeFalse(); + } + [SkippableTheory] [ClassData(typeof(TestDataGetEncodingDifference))] public async Task diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/File/AppendAllLinesTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/File/AppendAllLinesTests.cs index 5b10e6b63..668750435 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/File/AppendAllLinesTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/File/AppendAllLinesTests.cs @@ -82,6 +82,25 @@ public void AppendAllLines_ShouldEndWithNewline(string path) FileSystem.File.ReadAllText(path).Should().BeEquivalentTo(expectedResult); } + [SkippableTheory] + [AutoData] + public void + AppendAllLines_WhenDirectoryWithSameNameExists_ShouldThrowUnauthorizedAccessException( + string path, string[] contents) + { + FileSystem.Directory.CreateDirectory(path); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.AppendAllLines(path, contents); + }); + + exception.Should().BeException( + hResult: -2147024891); + FileSystem.Directory.Exists(path).Should().BeTrue(); + FileSystem.File.Exists(path).Should().BeFalse(); + } + [SkippableTheory] [ClassData(typeof(TestDataGetEncodingDifference))] public void AppendAllLines_WithDifferentEncoding_ShouldNotReturnWrittenText( diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/File/AppendAllTextAsyncTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/File/AppendAllTextAsyncTests.cs index 5aa05c876..1f1b079bc 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/File/AppendAllTextAsyncTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/File/AppendAllTextAsyncTests.cs @@ -92,6 +92,25 @@ public async Task AppendAllTextAsync_ShouldNotEndWithNewline(string path) FileSystem.File.ReadAllText(path).Should().BeEquivalentTo(contents); } + [SkippableTheory] + [AutoData] + public async Task + AppendAllTextAsync_WhenDirectoryWithSameNameExists_ShouldThrowUnauthorizedAccessException( + string path, string contents) + { + FileSystem.Directory.CreateDirectory(path); + + Exception? exception = await Record.ExceptionAsync(async () => + { + await FileSystem.File.AppendAllTextAsync(path, contents); + }); + + exception.Should().BeException( + hResult: -2147024891); + FileSystem.Directory.Exists(path).Should().BeTrue(); + FileSystem.File.Exists(path).Should().BeFalse(); + } + [SkippableTheory] [ClassData(typeof(TestDataGetEncodingDifference))] public async Task AppendAllTextAsync_WithDifferentEncoding_ShouldNotReturnWrittenText( diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/File/AppendAllTextTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/File/AppendAllTextTests.cs index fcdf7d5b3..61ba050ed 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/File/AppendAllTextTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/File/AppendAllTextTests.cs @@ -124,17 +124,22 @@ public void AppendAllText_ShouldNotEndWithNewline(string path) } [SkippableTheory] - [ClassData(typeof(TestDataGetEncodingDifference))] - public void AppendAllText_WithDifferentEncoding_ShouldNotReturnWrittenText( - string contents, Encoding writeEncoding, Encoding readEncoding) + [AutoData] + public void + AppendAllText_WhenDirectoryWithSameNameExists_ShouldThrowUnauthorizedAccessException( + string path) { - string path = new Fixture().Create(); - FileSystem.File.AppendAllText(path, contents, writeEncoding); + FileSystem.Directory.CreateDirectory(path); - string[] result = FileSystem.File.ReadAllLines(path, readEncoding); + Exception? exception = Record.Exception(() => + { + FileSystem.File.AppendAllText(path, null); + }); - result.Should().NotBeEquivalentTo(contents, - $"{contents} should be different when encoding from {writeEncoding} to {readEncoding}."); + exception.Should().BeException( + hResult: -2147024891); + FileSystem.Directory.Exists(path).Should().BeTrue(); + FileSystem.File.Exists(path).Should().BeFalse(); } [SkippableTheory] @@ -152,4 +157,18 @@ public void AppendAllText_WhenFileIsHidden_ShouldNotThrowException( exception.Should().BeNull(); } + + [SkippableTheory] + [ClassData(typeof(TestDataGetEncodingDifference))] + public void AppendAllText_WithDifferentEncoding_ShouldNotReturnWrittenText( + string contents, Encoding writeEncoding, Encoding readEncoding) + { + string path = new Fixture().Create(); + FileSystem.File.AppendAllText(path, contents, writeEncoding); + + string[] result = FileSystem.File.ReadAllLines(path, readEncoding); + + result.Should().NotBeEquivalentTo(contents, + $"{contents} should be different when encoding from {writeEncoding} to {readEncoding}."); + } } diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/File/WriteAllBytesAsyncTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/File/WriteAllBytesAsyncTests.cs index 7d7352523..746116afa 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/File/WriteAllBytesAsyncTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/File/WriteAllBytesAsyncTests.cs @@ -63,6 +63,25 @@ public async Task WriteAllBytesAsync_WhenBytesAreNull_ShouldThrowArgumentNullExc exception.Should().BeException(paramName: "bytes"); } + [SkippableTheory] + [AutoData] + public async Task + WriteAllBytesAsync_WhenDirectoryWithSameNameExists_ShouldThrowUnauthorizedAccessException( + string path, byte[] bytes) + { + FileSystem.Directory.CreateDirectory(path); + + Exception? exception = await Record.ExceptionAsync(async () => + { + await FileSystem.File.WriteAllBytesAsync(path, bytes); + }); + + exception.Should().BeException( + hResult: -2147024891); + FileSystem.Directory.Exists(path).Should().BeTrue(); + FileSystem.File.Exists(path).Should().BeFalse(); + } + [SkippableTheory] [AutoData] public async Task diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/File/WriteAllBytesTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/File/WriteAllBytesTests.cs index c1ef63106..3d7df9726 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/File/WriteAllBytesTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/File/WriteAllBytesTests.cs @@ -21,6 +21,16 @@ public void WriteAllBytes_PreviousFile_ShouldOverwriteFileWithBytes( result.Should().BeEquivalentTo(bytes); } + [SkippableTheory] + [AutoData] + public void WriteAllBytes_ShouldCreateFileWithBytes(string path, byte[] bytes) + { + FileSystem.File.WriteAllBytes(path, bytes); + + byte[] result = FileSystem.File.ReadAllBytes(path); + result.Should().BeEquivalentTo(bytes); + } + [SkippableTheory] [AutoData] public void WriteAllBytes_WhenBytesAreNull_ShouldThrowArgumentNullException(string path) @@ -35,12 +45,21 @@ public void WriteAllBytes_WhenBytesAreNull_ShouldThrowArgumentNullException(stri [SkippableTheory] [AutoData] - public void WriteAllBytes_ShouldCreateFileWithBytes(string path, byte[] bytes) + public void + WriteAllBytes_WhenDirectoryWithSameNameExists_ShouldThrowUnauthorizedAccessException( + string path, byte[] bytes) { - FileSystem.File.WriteAllBytes(path, bytes); + FileSystem.Directory.CreateDirectory(path); - byte[] result = FileSystem.File.ReadAllBytes(path); - result.Should().BeEquivalentTo(bytes); + Exception? exception = Record.Exception(() => + { + FileSystem.File.WriteAllBytes(path, bytes); + }); + + exception.Should().BeException( + hResult: -2147024891); + FileSystem.Directory.Exists(path).Should().BeTrue(); + FileSystem.File.Exists(path).Should().BeFalse(); } [SkippableTheory] diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/File/WriteAllLinesAsyncTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/File/WriteAllLinesAsyncTests.cs index 8034a0a6a..0f8865146 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/File/WriteAllLinesAsyncTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/File/WriteAllLinesAsyncTests.cs @@ -121,6 +121,25 @@ public async Task WriteAllLinesAsync_ShouldCreateFileWithText( result.Should().BeEquivalentTo(contents, o => o.WithStrictOrdering()); } + [SkippableTheory] + [AutoData] + public async Task + WriteAllLinesAsync_WhenDirectoryWithSameNameExists_ShouldThrowUnauthorizedAccessException( + string path, string[] contents) + { + FileSystem.Directory.CreateDirectory(path); + + Exception? exception = await Record.ExceptionAsync(async () => + { + await FileSystem.File.WriteAllLinesAsync(path, contents); + }); + + exception.Should().BeException( + hResult: -2147024891); + FileSystem.Directory.Exists(path).Should().BeTrue(); + FileSystem.File.Exists(path).Should().BeFalse(); + } + [SkippableTheory] [AutoData] public async Task diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/File/WriteAllLinesTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/File/WriteAllLinesTests.cs index 4ddd5a07e..f1a5f73cf 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/File/WriteAllLinesTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/File/WriteAllLinesTests.cs @@ -69,13 +69,21 @@ public void WriteAllLines_ShouldCreateFileWithText(string path, string[] content [SkippableTheory] [AutoData] - public void WriteAllLines_WithEncoding_ShouldCreateFileWithText( - Encoding encoding, string path, string[] contents) + public void + WriteAllLines_WhenDirectoryWithSameNameExists_ShouldThrowUnauthorizedAccessException( + string path, string[] contents) { - FileSystem.File.WriteAllLines(path, contents, encoding); + FileSystem.Directory.CreateDirectory(path); - string[] result = FileSystem.File.ReadAllLines(path, encoding); - result.Should().BeEquivalentTo(contents, o => o.WithStrictOrdering()); + Exception? exception = Record.Exception(() => + { + FileSystem.File.WriteAllLines(path, contents); + }); + + exception.Should().BeException( + hResult: -2147024891); + FileSystem.Directory.Exists(path).Should().BeTrue(); + FileSystem.File.Exists(path).Should().BeFalse(); } [SkippableTheory] @@ -95,4 +103,15 @@ public void WriteAllLines_WhenFileIsHidden_ShouldThrowUnauthorizedAccessExceptio exception.Should().BeException(hResult: -2147024891); } + + [SkippableTheory] + [AutoData] + public void WriteAllLines_WithEncoding_ShouldCreateFileWithText( + Encoding encoding, string path, string[] contents) + { + FileSystem.File.WriteAllLines(path, contents, encoding); + + string[] result = FileSystem.File.ReadAllLines(path, encoding); + result.Should().BeEquivalentTo(contents, o => o.WithStrictOrdering()); + } } diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/File/WriteAllTextAsyncTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/File/WriteAllTextAsyncTests.cs index e5b790507..0ba36e689 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/File/WriteAllTextAsyncTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/File/WriteAllTextAsyncTests.cs @@ -104,6 +104,25 @@ public async Task WriteAllTextAsync_WhenContentIsNull_ShouldNotThrowException(st exception.Should().BeNull(); } + [SkippableTheory] + [AutoData] + public async Task + WriteAllTextAsync_WhenDirectoryWithSameNameExists_ShouldThrowUnauthorizedAccessException( + string path, string contents) + { + FileSystem.Directory.CreateDirectory(path); + + Exception? exception = await Record.ExceptionAsync(async () => + { + await FileSystem.File.WriteAllTextAsync(path, contents); + }); + + exception.Should().BeException( + hResult: -2147024891); + FileSystem.Directory.Exists(path).Should().BeTrue(); + FileSystem.File.Exists(path).Should().BeFalse(); + } + [SkippableTheory] [AutoData] public async Task diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/File/WriteAllTextTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/File/WriteAllTextTests.cs index a0f3cba38..6a636002d 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/File/WriteAllTextTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/File/WriteAllTextTests.cs @@ -149,6 +149,24 @@ public void WriteAllText_WhenContentIsNull_ShouldNotThrowException(string path) exception.Should().BeNull(); } + [SkippableTheory] + [AutoData] + public void WriteAllText_WhenDirectoryWithSameNameExists_ShouldThrowUnauthorizedAccessException( + string path) + { + FileSystem.Directory.CreateDirectory(path); + + Exception? exception = Record.Exception(() => + { + FileSystem.File.WriteAllText(path, null); + }); + + exception.Should().BeException( + hResult: -2147024891); + FileSystem.Directory.Exists(path).Should().BeTrue(); + FileSystem.File.Exists(path).Should().BeFalse(); + } + [SkippableTheory] [AutoData] public void WriteAllText_WhenFileIsHidden_ShouldThrowUnauthorizedAccessException_OnWindows(