Skip to content

Commit e84ace2

Browse files
authored
fix: throw correct exception when creating a FileStream with invalid mode/access combination (#973)
Fixes #884. In addition to the thrown exceptions some implementations and tests had to be adapted: - [`File.Open`](https://github.com/dotnet/runtime/blob/v6.0.16/src/libraries/System.Private.CoreLib/src/System/IO/File.cs#L152) with Append mode should use Write access - [`FileInfo.AppendText`](https://github.com/dotnet/runtime/blob/v6.0.16/src/libraries/System.Private.CoreLib/src/System/IO/FileInfo.cs#L85) should open the stream with Write access The corresponding tests had to be adapted!
1 parent 2e515ed commit e84ace2

File tree

8 files changed

+69
-15
lines changed

8 files changed

+69
-15
lines changed

Diff for: src/TestableIO.System.IO.Abstractions.TestingHelpers/CommonExceptions.cs

+6
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ public static IOException ProcessCannotAccessFileInUse(string paramName = null)
6565
public static IOException FileAlreadyExists(string paramName) =>
6666
new IOException(string.Format(StringResources.Manager.GetString("FILE_ALREADY_EXISTS"), paramName));
6767

68+
public static ArgumentException InvalidAccessCombination(FileMode mode, FileAccess access)
69+
=> new ArgumentException(string.Format(StringResources.Manager.GetString("INVALID_ACCESS_COMBINATION"), mode, access), nameof(access));
70+
71+
public static ArgumentException AppendAccessOnlyInWriteOnlyMode()
72+
=> new ArgumentException(string.Format(StringResources.Manager.GetString("APPEND_ACCESS_ONLY_IN_WRITE_ONLY_MODE")), "access");
73+
6874
public static NotImplementedException NotImplemented() =>
6975
new NotImplementedException(StringResources.Manager.GetString("NOT_IMPLEMENTED_EXCEPTION"));
7076
}

Diff for: src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFile.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -544,7 +544,7 @@ public override FileSystemStream Open(string path, FileMode mode)
544544
{
545545
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
546546

547-
return Open(path, mode, FileAccess.ReadWrite, FileShare.None);
547+
return Open(path, mode, mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite, FileShare.None);
548548
}
549549

550550
/// <inheritdoc />

Diff for: src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFileInfo.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ public override string Name
208208
/// <inheritdoc />
209209
public override StreamWriter AppendText()
210210
{
211-
return new StreamWriter(new MockFileStream(mockFileSystem, FullName, FileMode.Append));
211+
return new StreamWriter(new MockFileStream(mockFileSystem, FullName, FileMode.Append, FileAccess.Write));
212212
}
213213

214214
/// <inheritdoc />

Diff for: src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFileStream.cs

+25
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ public MockFileStream(
2828
(options & FileOptions.Asynchronous) != 0)
2929

3030
{
31+
ThrowIfInvalidModeAccess(mode, access);
32+
3133
this.mockFileDataAccessor = mockFileDataAccessor ?? throw new ArgumentNullException(nameof(mockFileDataAccessor));
3234
this.path = path;
3335
this.options = options;
@@ -78,6 +80,29 @@ public MockFileStream(
7880
this.access = access;
7981
}
8082

83+
private static void ThrowIfInvalidModeAccess(FileMode mode, FileAccess access)
84+
{
85+
if (mode == FileMode.Append)
86+
{
87+
if (access == FileAccess.Read)
88+
{
89+
throw CommonExceptions.InvalidAccessCombination(mode, access);
90+
}
91+
92+
if (access != FileAccess.Write)
93+
{
94+
throw CommonExceptions.AppendAccessOnlyInWriteOnlyMode();
95+
}
96+
}
97+
98+
if (!access.HasFlag(FileAccess.Write) &&
99+
(mode == FileMode.Truncate || mode == FileMode.CreateNew ||
100+
mode == FileMode.Create || mode == FileMode.Append))
101+
{
102+
throw CommonExceptions.InvalidAccessCombination(mode, access);
103+
}
104+
}
105+
81106
/// <inheritdoc />
82107
public override bool CanRead => access.HasFlag(FileAccess.Read);
83108

Diff for: src/TestableIO.System.IO.Abstractions.TestingHelpers/Properties/Resources.resx

+6
Original file line numberDiff line numberDiff line change
@@ -156,4 +156,10 @@
156156
<data name="FILE_ALREADY_EXISTS" xml:space="preserve">
157157
<value>The file '{0}' already exists.</value>
158158
</data>
159+
<data name="APPEND_ACCESS_ONLY_IN_WRITE_ONLY_MODE" xml:space="preserve">
160+
<value>Append access can be requested only in write-only mode.</value>
161+
</data>
162+
<data name="INVALID_ACCESS_COMBINATION" xml:space="preserve">
163+
<value>Combining FileMode: {0} with FileAccess: {1} is invalid.</value>
164+
</data>
159165
</root>

Diff for: tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileAdjustTimesTest.cs

+1-2
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ public void MockFile_AfterMove_ShouldUpdateLastAccessTime()
7676

7777
[TestCase(FileMode.Open, FileAccess.ReadWrite)]
7878
[TestCase(FileMode.OpenOrCreate, FileAccess.Write)]
79-
[TestCase(FileMode.Append, FileAccess.ReadWrite)]
79+
[TestCase(FileMode.Append, FileAccess.Write)]
8080
public void MockFile_AfterOpen_WithWriteAccess_ShouldUpdateLastAccessAndLastWriteTime(FileMode fileMode, FileAccess fileAccess)
8181
{
8282
var creationTime = DateTime.UtcNow.AddDays(10);
@@ -98,7 +98,6 @@ public void MockFile_AfterOpen_WithWriteAccess_ShouldUpdateLastAccessAndLastWrit
9898

9999
[TestCase(FileMode.Open, FileAccess.Read)]
100100
[TestCase(FileMode.OpenOrCreate, FileAccess.Read)]
101-
[TestCase(FileMode.Append, FileAccess.Read)]
102101
public void MockFile_AfterOpen_WithReadOnlyAccess_ShouldUpdateLastAccessTime(FileMode fileMode, FileAccess fileAccess)
103102
{
104103
var creationTime = DateTime.UtcNow.AddDays(10);

Diff for: tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileOpenTests.cs

-8
Original file line numberDiff line numberDiff line change
@@ -95,14 +95,6 @@ public void MockFile_Open_OpensExistingFileOnAppend()
9595

9696
Assert.That(stream.Position, Is.EqualTo(file.Contents.Length));
9797
Assert.That(stream.Length, Is.EqualTo(file.Contents.Length));
98-
99-
stream.Seek(0, SeekOrigin.Begin);
100-
101-
byte[] data;
102-
using (var br = new BinaryReader(stream))
103-
data = br.ReadBytes((int)stream.Length);
104-
105-
CollectionAssert.AreEqual(file.Contents, data);
10698
}
10799

108100
[Test]

Diff for: tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileStreamFactoryTests.cs

+29-3
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public void MockFileStreamFactory_CreateForExistingFile_ShouldReturnStream(FileM
2323
var fileStreamFactory = new MockFileStreamFactory(fileSystem);
2424

2525
// Act
26-
var result = fileStreamFactory.Create(@"c:\existing.txt", fileMode);
26+
var result = fileStreamFactory.Create(@"c:\existing.txt", fileMode, FileAccess.Write);
2727

2828
// Assert
2929
Assert.IsNotNull(result);
@@ -39,7 +39,7 @@ public void MockFileStreamFactory_CreateForNonExistingFile_ShouldReturnStream(Fi
3939
var fileStreamFactory = new MockFileStreamFactory(fileSystem);
4040

4141
// Act
42-
var result = fileStreamFactory.Create(XFS.Path(@"c:\not_existing.txt"), fileMode);
42+
var result = fileStreamFactory.Create(XFS.Path(@"c:\not_existing.txt"), fileMode, FileAccess.Write);
4343

4444
// Assert
4545
Assert.IsNotNull(result);
@@ -72,7 +72,6 @@ public void MockFileStreamFactory_CreateForAnExistingFile_ShouldReplaceFileConte
7272
[TestCase(FileMode.Create)]
7373
[TestCase(FileMode.Open)]
7474
[TestCase(FileMode.CreateNew)]
75-
[TestCase(FileMode.Append)]
7675
public void MockFileStreamFactory_CreateInNonExistingDirectory_ShouldThrowDirectoryNotFoundException(FileMode fileMode)
7776
{
7877
// Arrange
@@ -86,6 +85,33 @@ public void MockFileStreamFactory_CreateInNonExistingDirectory_ShouldThrowDirect
8685
Assert.Throws<DirectoryNotFoundException>(() => fileStreamFactory.Create(XFS.Path(@"C:\Test\NonExistingDirectory\some_random_file.txt"), fileMode));
8786
}
8887

88+
[Test]
89+
public void MockFileStreamFactory_AppendAccessWithReadWriteMode_ShouldThrowArgumentException()
90+
{
91+
var fileSystem = new MockFileSystem();
92+
93+
Assert.Throws<ArgumentException>(() =>
94+
{
95+
fileSystem.FileStream.New(XFS.Path(@"c:\path.txt"), FileMode.Append, FileAccess.ReadWrite);
96+
});
97+
}
98+
99+
[Test]
100+
[TestCase(FileMode.Append)]
101+
[TestCase(FileMode.Truncate)]
102+
[TestCase(FileMode.Create)]
103+
[TestCase(FileMode.CreateNew)]
104+
[TestCase(FileMode.Append)]
105+
public void MockFileStreamFactory_InvalidModeForReadAccess_ShouldThrowArgumentException(FileMode fileMode)
106+
{
107+
var fileSystem = new MockFileSystem();
108+
109+
Assert.Throws<ArgumentException>(() =>
110+
{
111+
fileSystem.FileStream.New(XFS.Path(@"c:\path.txt"), fileMode, FileAccess.Read);
112+
});
113+
}
114+
89115
[Test]
90116
[TestCase(FileMode.Open)]
91117
[TestCase(FileMode.Truncate)]

0 commit comments

Comments
 (0)