Skip to content

Commit f4afa40

Browse files
authored
fix: Throw correct exception if opening read-only file for writing (#1135)
fix: Throw exception if opening readonly file for writing Mock classes did not match .Net's behavior of throwing UnauthorizedAccessExceptions when opening readonly files with FileAccess.Write, making it impossible to test those scenarios. This commit fixes the mock classes so that the exception will be thrown correctly.
1 parent 42be965 commit f4afa40

File tree

2 files changed

+54
-2
lines changed

2 files changed

+54
-2
lines changed

src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFileData.cs

+8-2
Original file line numberDiff line numberDiff line change
@@ -203,18 +203,24 @@ public FileSecurity AccessControl
203203
public FileShare AllowedFileShare { get; set; } = FileShare.ReadWrite | FileShare.Delete;
204204
/// <summary>
205205
/// Checks whether the file is accessible for this type of FileAccess.
206-
/// MockfileData can be configured to have FileShare.None, which indicates it is locked by a 'different process'.
206+
/// MockFileData can be configured to have FileShare.None, which indicates it is locked by a 'different process'.
207207
///
208208
/// If the file is 'locked by a different process', an IOException will be thrown.
209+
/// If the file is read-only and is accessed for writing, an UnauthorizedAccessException will be thrown.
209210
/// </summary>
210-
/// <param name="path">The path is used in the IOException message to match the message in real life situations</param>
211+
/// <param name="path">The path is used in the exception message to match the message in real life situations</param>
211212
/// <param name="access">The access type to check</param>
212213
internal void CheckFileAccess(string path, FileAccess access)
213214
{
214215
if (!AllowedFileShare.HasFlag((FileShare)access))
215216
{
216217
throw CommonExceptions.ProcessCannotAccessFileInUse(path);
217218
}
219+
220+
if (Attributes.HasFlag(FileAttributes.ReadOnly) && access.HasFlag(FileAccess.Write))
221+
{
222+
throw CommonExceptions.AccessDenied(path);
223+
}
218224
}
219225

220226
internal virtual MockFileData Clone()

tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileStreamTests.cs

+46
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,52 @@ public void MockFileStream_Constructor_ReadTypeNotWritable()
104104
Assert.Throws<NotSupportedException>(() => stream.WriteByte(1));
105105
}
106106

107+
[Test]
108+
[TestCase(FileAccess.Write)]
109+
[TestCase(FileAccess.ReadWrite)]
110+
public void MockFileStream_Constructor_WriteAccessOnReadOnlyFile_Throws_Exception(
111+
FileAccess fileAccess)
112+
{
113+
// Arrange
114+
var filePath = @"C:\test.txt";
115+
var fileSystem = new MockFileSystem(new Dictionary<string, MockFileData>
116+
{
117+
{ filePath, new MockFileData("hi") { Attributes = FileAttributes.ReadOnly } }
118+
});
119+
120+
// Act
121+
Assert.Throws<UnauthorizedAccessException>(() => new MockFileStream(fileSystem, filePath, FileMode.Open, fileAccess));
122+
}
123+
124+
[Test]
125+
public void MockFileStream_Constructor_ReadAccessOnReadOnlyFile_Does_Not_Throw_Exception()
126+
{
127+
// Arrange
128+
var filePath = @"C:\test.txt";
129+
var fileSystem = new MockFileSystem(new Dictionary<string, MockFileData>
130+
{
131+
{ filePath, new MockFileData("hi") { Attributes = FileAttributes.ReadOnly } }
132+
});
133+
134+
// Act
135+
Assert.DoesNotThrow(() => new MockFileStream(fileSystem, filePath, FileMode.Open, FileAccess.Read));
136+
}
137+
138+
139+
[Test]
140+
public void MockFileStream_Constructor_WriteAccessOnNonReadOnlyFile_Does_Not_Throw_Exception()
141+
{
142+
// Arrange
143+
var filePath = @"C:\test.txt";
144+
var fileSystem = new MockFileSystem(new Dictionary<string, MockFileData>
145+
{
146+
{ filePath, new MockFileData("hi") { Attributes = FileAttributes.Normal } }
147+
});
148+
149+
// Act
150+
Assert.DoesNotThrow(() => new MockFileStream(fileSystem, filePath, FileMode.Open, FileAccess.Write));
151+
}
152+
107153
[Test]
108154
[TestCase(FileShare.None, FileAccess.Read)]
109155
[TestCase(FileShare.None, FileAccess.ReadWrite)]

0 commit comments

Comments
 (0)