Skip to content

Commit efef869

Browse files
authored
fix(TestingHelpers): correctly handle FileMode/FileAccess when creating MockFileStreams (#618)
BREAKING CHANGE: MockFileStream.StreamType and the related constructors are gone. The desired behavior should be specified by passing the appropriate FileMode/FileAccess/FileOptions in the constructor instead. Fixes #610
1 parent a14979e commit efef869

8 files changed

Lines changed: 83 additions & 127 deletions

File tree

Directory.Build.props

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
<SignAssembly Condition="'$(Configuration)' == 'Release'">True</SignAssembly>
77
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)StrongName.snk</AssemblyOriginatorKeyFile>
88
<DefineConstants Condition="'$(TargetFramework)' == 'netcoreapp3.0' OR '$(TargetFramework)' == 'netstandard2.1'">$(DefineConstants);FEATURE_ASYNC_FILE;FEATURE_ENUMERATION_OPTIONS;FEATURE_ADVANCED_PATH_OPERATIONS</DefineConstants>
9-
109
</PropertyGroup>
1110
<ItemGroup>
1211
<PackageReference Include="Nerdbank.GitVersioning" Version="3.1.91">

System.IO.Abstractions.TestingHelpers.Tests/MockFileStreamFactoryTests.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,5 +134,18 @@ public void MockFileStreamFactory_CreateWithRelativePath_CreatesFileInCurrentDir
134134
// Assert
135135
Assert.True(fileSystem.File.Exists(XFS.Path("./some_random_file.txt")));
136136
}
137+
138+
[Test]
139+
public void MockFileStream_CanRead_ReturnsFalseForAWriteOnlyStream()
140+
{
141+
// Arrange
142+
var fileSystem = new MockFileSystem();
143+
144+
// Act
145+
var fileStream = fileSystem.FileStream.Create("file.txt", FileMode.CreateNew, FileAccess.Write);
146+
147+
// Assert
148+
Assert.IsFalse(fileStream.CanRead);
149+
}
137150
}
138151
}

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

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@ public void MockFileStream_Flush_WritesByteToFile()
1717
var fileSystem = new MockFileSystem(new Dictionary<string, MockFileData>());
1818
fileSystem.AddDirectory(XFS.Path(@"C:\something"));
1919

20-
var cut = new MockFileStream(fileSystem, filepath, MockFileStream.StreamType.WRITE);
20+
var cut = new MockFileStream(fileSystem, filepath, FileMode.Create);
2121

2222
// Act
2323
cut.WriteByte(255);
2424
cut.Flush();
2525

2626
// Assert
27-
CollectionAssert.AreEqual(new byte[]{255}, fileSystem.GetFile(filepath).Contents);
27+
CollectionAssert.AreEqual(new byte[] { 255 }, fileSystem.GetFile(filepath).Contents);
2828
}
2929

3030
[Test]
@@ -58,7 +58,7 @@ public void MockFileStream_Constructor_Reading_Nonexistent_File_Throws_Exception
5858
fileSystem.AddDirectory(XFS.Path(@"C:\something"));
5959

6060
// Act
61-
Assert.Throws<FileNotFoundException>(() => new MockFileStream(fileSystem, nonexistentFilePath, MockFileStream.StreamType.READ));
61+
Assert.Throws<FileNotFoundException>(() => new MockFileStream(fileSystem, nonexistentFilePath, FileMode.Open));
6262

6363
// Assert - expect an exception
6464
}
@@ -74,50 +74,50 @@ public void MockFileStream_Constructor_ReadTypeNotWritable()
7474
});
7575

7676
// Act
77-
var stream = new MockFileStream(fileSystem, filePath, MockFileStream.StreamType.READ);
77+
var stream = new MockFileStream(fileSystem, filePath, FileMode.Open, FileAccess.Read);
7878

7979
Assert.IsFalse(stream.CanWrite);
8080
Assert.Throws<NotSupportedException>(() => stream.WriteByte(1));
8181
}
82-
82+
8383
[Test]
84-
[TestCase(FileShare.None, MockFileStream.StreamType.READ)]
85-
[TestCase(FileShare.None, MockFileStream.StreamType.WRITE)]
86-
[TestCase(FileShare.None, MockFileStream.StreamType.APPEND)]
87-
[TestCase(FileShare.None, MockFileStream.StreamType.TRUNCATE)]
88-
[TestCase(FileShare.Read, MockFileStream.StreamType.WRITE)]
89-
[TestCase(FileShare.Read, MockFileStream.StreamType.APPEND)]
90-
[TestCase(FileShare.Read, MockFileStream.StreamType.TRUNCATE)]
91-
[TestCase(FileShare.Write, MockFileStream.StreamType.READ)]
92-
public void MockFileStream_Constructor_Insufficient_FileShare_Throws_Exception(FileShare allowedFileShare, MockFileStream.StreamType streamType)
84+
[TestCase(FileShare.None, FileAccess.Read)]
85+
[TestCase(FileShare.None, FileAccess.ReadWrite)]
86+
[TestCase(FileShare.None, FileAccess.Write)]
87+
[TestCase(FileShare.Read, FileAccess.Write)]
88+
[TestCase(FileShare.Read, FileAccess.ReadWrite)]
89+
[TestCase(FileShare.Write, FileAccess.Read)]
90+
public void MockFileStream_Constructor_Insufficient_FileShare_Throws_Exception(
91+
FileShare allowedFileShare,
92+
FileAccess fileAccess)
9393
{
94-
var filePath = @"C:\locked.txt";
94+
var filePath = @"C:\locked.txt";
9595
var fileSystem = new MockFileSystem(new Dictionary<string, MockFileData>
9696
{
9797
{ filePath, new MockFileData("cannot access") { AllowedFileShare = allowedFileShare } }
9898
});
99-
100-
Assert.Throws<IOException>(() => new MockFileStream(fileSystem, filePath, streamType));
99+
100+
Assert.Throws<IOException>(() => new MockFileStream(fileSystem, filePath, FileMode.Open, fileAccess));
101101
}
102-
102+
103103
[Test]
104-
[TestCase(FileShare.Read, MockFileStream.StreamType.READ)]
105-
[TestCase(FileShare.Read | FileShare.Write, MockFileStream.StreamType.READ)]
106-
[TestCase(FileShare.Read | FileShare.Write, MockFileStream.StreamType.APPEND)]
107-
[TestCase(FileShare.Read | FileShare.Write, MockFileStream.StreamType.TRUNCATE)]
108-
[TestCase(FileShare.ReadWrite, MockFileStream.StreamType.READ)]
109-
[TestCase(FileShare.ReadWrite, MockFileStream.StreamType.WRITE)]
110-
[TestCase(FileShare.ReadWrite, MockFileStream.StreamType.APPEND)]
111-
[TestCase(FileShare.ReadWrite, MockFileStream.StreamType.TRUNCATE)]
112-
public void MockFileStream_Constructor_Sufficient_FileShare_Does_Not_Throw_Exception(FileShare allowedFileShare, MockFileStream.StreamType streamType)
104+
[TestCase(FileShare.Read, FileAccess.Read)]
105+
[TestCase(FileShare.Read | FileShare.Write, FileAccess.Read)]
106+
[TestCase(FileShare.Read | FileShare.Write, FileAccess.ReadWrite)]
107+
[TestCase(FileShare.ReadWrite, FileAccess.Read)]
108+
[TestCase(FileShare.ReadWrite, FileAccess.ReadWrite)]
109+
[TestCase(FileShare.ReadWrite, FileAccess.Write)]
110+
public void MockFileStream_Constructor_Sufficient_FileShare_Does_Not_Throw_Exception(
111+
FileShare allowedFileShare,
112+
FileAccess fileAccess)
113113
{
114-
var filePath = @"C:\locked.txt";
114+
var filePath = @"C:\locked.txt";
115115
var fileSystem = new MockFileSystem(new Dictionary<string, MockFileData>
116116
{
117117
{ filePath, new MockFileData("cannot access") { AllowedFileShare = allowedFileShare } }
118118
});
119-
120-
Assert.DoesNotThrow(() => new MockFileStream(fileSystem, filePath, streamType));
119+
120+
Assert.DoesNotThrow(() => new MockFileStream(fileSystem, filePath, FileMode.Open, fileAccess));
121121
}
122122

123123
[Test]

System.IO.Abstractions.TestingHelpers/MockFile.cs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ public override Stream Open(string path, FileMode mode)
369369
{
370370
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
371371

372-
return Open(path, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), FileShare.None);
372+
return Open(path, mode, FileAccess.ReadWrite, FileShare.None);
373373
}
374374

375375
public override Stream Open(string path, FileMode mode, FileAccess access)
@@ -412,13 +412,8 @@ private Stream OpenInternal(
412412
mockFileData.CheckFileAccess(path, access);
413413

414414
var length = mockFileData.Contents.Length;
415-
MockFileStream.StreamType streamType = MockFileStream.StreamType.WRITE;
416-
if (access == FileAccess.Read)
417-
streamType = MockFileStream.StreamType.READ;
418-
else if (mode == FileMode.Append)
419-
streamType = MockFileStream.StreamType.APPEND;
420415

421-
return new MockFileStream(mockFileDataAccessor, path, streamType, options, mode);
416+
return new MockFileStream(mockFileDataAccessor, path, mode, access, options);
422417
}
423418

424419
public override Stream OpenRead(string path)
@@ -490,9 +485,9 @@ public override string[] ReadAllLines(string path, Encoding encoding)
490485

491486
using (var ms = new MemoryStream(mockFileDataAccessor.GetFile(path).Contents))
492487
using (var sr = new StreamReader(ms, encoding))
493-
{
488+
{
494489
return sr.ReadToEnd().SplitLines();
495-
}
490+
}
496491
}
497492

498493
public override string ReadAllText(string path)

System.IO.Abstractions.TestingHelpers/MockFileInfo.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ public override string Name
152152

153153
public override StreamWriter AppendText()
154154
{
155-
return new StreamWriter(new MockFileStream(mockFileSystem, FullName, MockFileStream.StreamType.APPEND));
155+
return new StreamWriter(new MockFileStream(mockFileSystem, FullName, FileMode.Append));
156156
}
157157

158158
public override IFileInfo CopyTo(string destFileName)
@@ -237,7 +237,7 @@ public override Stream Open(FileMode mode, FileAccess access, FileShare share)
237237
public override Stream OpenRead()
238238
{
239239
if (MockFileData == null) throw CommonExceptions.FileNotFound(path);
240-
return new MockFileStream(mockFileSystem, path, MockFileStream.StreamType.READ);
240+
return new MockFileStream(mockFileSystem, path, FileMode.Open, FileAccess.Read);
241241
}
242242

243243
public override StreamReader OpenText()
@@ -247,7 +247,7 @@ public override StreamReader OpenText()
247247

248248
public override Stream OpenWrite()
249249
{
250-
return new MockFileStream(mockFileSystem, path, MockFileStream.StreamType.WRITE);
250+
return new MockFileStream(mockFileSystem, path, FileMode.Open);
251251
}
252252

253253
public override IFileInfo Replace(string destinationFileName, string destinationBackupFileName)

System.IO.Abstractions.TestingHelpers/MockFileStream.cs

Lines changed: 17 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -5,63 +5,41 @@ public class MockFileStream : MemoryStream
55
{
66
private readonly IMockFileDataAccessor mockFileDataAccessor;
77
private readonly string path;
8-
private readonly bool canWrite = true;
8+
private readonly FileAccess access = FileAccess.ReadWrite;
99
private readonly FileOptions options;
1010

1111
private bool disposed;
1212

13-
public enum StreamType
14-
{
15-
READ,
16-
WRITE,
17-
APPEND,
18-
TRUNCATE
19-
}
20-
21-
public MockFileStream(
22-
IMockFileDataAccessor mockFileDataAccessor,
23-
string path,
24-
StreamType streamType,
25-
FileMode fileMode)
26-
: this(mockFileDataAccessor, path, streamType, FileOptions.None, fileMode)
27-
{
28-
}
29-
3013
public MockFileStream(
3114
IMockFileDataAccessor mockFileDataAccessor,
3215
string path,
33-
StreamType streamType)
34-
: this(mockFileDataAccessor, path, streamType, FileOptions.None, FileMode.Append)
35-
{
36-
}
16+
FileMode mode,
17+
FileAccess access = FileAccess.ReadWrite,
18+
FileOptions options = FileOptions.None)
3719

38-
public MockFileStream(
39-
IMockFileDataAccessor mockFileDataAccessor,
40-
string path,
41-
StreamType streamType,
42-
FileOptions options,
43-
FileMode fileMode = FileMode.Append)
4420
{
4521
this.mockFileDataAccessor = mockFileDataAccessor ?? throw new ArgumentNullException(nameof(mockFileDataAccessor));
4622
this.path = path;
4723
this.options = options;
4824

4925
if (mockFileDataAccessor.FileExists(path))
5026
{
51-
if (fileMode.Equals(FileMode.CreateNew))
27+
if (mode.Equals(FileMode.CreateNew))
5228
{
5329
throw CommonExceptions.FileAlreadyExists(path);
5430
}
5531

5632
var fileData = mockFileDataAccessor.GetFile(path);
57-
fileData.CheckFileAccess(path, streamType != StreamType.READ ? FileAccess.Write : FileAccess.Read);
33+
fileData.CheckFileAccess(path, access);
5834

59-
/* only way to make an expandable MemoryStream that starts with a particular content */
60-
var data = fileData.Contents;
61-
if (data != null && data.Length > 0 && streamType != StreamType.TRUNCATE)
35+
var existingContents = fileData.Contents;
36+
var keepExistingContents =
37+
existingContents?.Length > 0 &&
38+
mode != FileMode.Truncate && mode != FileMode.Create;
39+
if (keepExistingContents)
6240
{
63-
Write(data, 0, data.Length);
64-
Seek(0, StreamType.APPEND.Equals(streamType)
41+
Write(existingContents, 0, existingContents.Length);
42+
Seek(0, mode == FileMode.Append
6543
? SeekOrigin.End
6644
: SeekOrigin.Begin);
6745
}
@@ -74,20 +52,19 @@ public MockFileStream(
7452
throw CommonExceptions.CouldNotFindPartOfPath(path);
7553
}
7654

77-
if (StreamType.READ.Equals(streamType)
78-
|| fileMode.Equals(FileMode.Open)
79-
|| fileMode.Equals(FileMode.Truncate))
55+
if (mode.Equals(FileMode.Open) || mode.Equals(FileMode.Truncate))
8056
{
8157
throw CommonExceptions.FileNotFound(path);
8258
}
8359

8460
mockFileDataAccessor.AddFile(path, new MockFileData(new byte[] { }));
8561
}
8662

87-
canWrite = streamType != StreamType.READ;
63+
this.access = access;
8864
}
8965

90-
public override bool CanWrite => canWrite;
66+
public override bool CanRead => access.HasFlag(FileAccess.Read);
67+
public override bool CanWrite => access.HasFlag(FileAccess.Write);
9168

9269
protected override void Dispose(bool disposing)
9370
{

System.IO.Abstractions.TestingHelpers/MockFileStreamFactory.cs

Lines changed: 15 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -12,80 +12,52 @@ public MockFileStreamFactory(IMockFileDataAccessor mockFileSystem)
1212
=> this.mockFileSystem = mockFileSystem ?? throw new ArgumentNullException(nameof(mockFileSystem));
1313

1414
public Stream Create(string path, FileMode mode)
15-
=> new MockFileStream(mockFileSystem, path, GetStreamType(mode), mode);
15+
=> new MockFileStream(mockFileSystem, path, mode);
1616

1717
public Stream Create(string path, FileMode mode, FileAccess access)
18-
=> new MockFileStream(mockFileSystem, path, GetStreamType(mode, access), mode);
18+
=> new MockFileStream(mockFileSystem, path, mode, access);
1919

2020
public Stream Create(string path, FileMode mode, FileAccess access, FileShare share)
21-
=> new MockFileStream(mockFileSystem, path, GetStreamType(mode, access), mode);
21+
=> new MockFileStream(mockFileSystem, path, mode, access);
2222

2323
public Stream Create(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize)
24-
=> new MockFileStream(mockFileSystem, path, GetStreamType(mode, access), mode);
24+
=> new MockFileStream(mockFileSystem, path, mode, access);
2525

2626
public Stream Create(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, FileOptions options)
27-
=> new MockFileStream(mockFileSystem, path, GetStreamType(mode, access), options, mode);
27+
=> new MockFileStream(mockFileSystem, path, mode, access, options);
2828

2929
public Stream Create(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool useAsync)
30-
=> new MockFileStream(mockFileSystem, path, GetStreamType(mode, access), mode);
30+
=> new MockFileStream(mockFileSystem, path, mode, access);
3131

3232
public Stream Create(string path, FileMode mode, FileSystemRights rights, FileShare share, int bufferSize, FileOptions options, FileSecurity fileSecurity)
33-
=> new MockFileStream(mockFileSystem, path, GetStreamType(mode), options, mode);
33+
=> new MockFileStream(mockFileSystem, path, mode, options: options);
3434

3535
public Stream Create(string path, FileMode mode, FileSystemRights rights, FileShare share, int bufferSize, FileOptions options)
36-
=> new MockFileStream(mockFileSystem, path, GetStreamType(mode), options, mode);
36+
=> new MockFileStream(mockFileSystem, path, mode, options: options);
3737

3838
[Obsolete("This method has been deprecated. Please use new Create(SafeFileHandle handle, FileAccess access) instead. http://go.microsoft.com/fwlink/?linkid=14202")]
3939
public Stream Create(IntPtr handle, FileAccess access)
40-
=> new MockFileStream(mockFileSystem, handle.ToString(), GetStreamType(FileMode.Append, access));
40+
=> new MockFileStream(mockFileSystem, handle.ToString(), FileMode.Open, access: access);
4141

4242
[Obsolete("This method has been deprecated. Please use new Create(SafeFileHandle handle, FileAccess access) instead, and optionally make a new SafeFileHandle with ownsHandle=false if needed. http://go.microsoft.com/fwlink/?linkid=14202")]
4343
public Stream Create(IntPtr handle, FileAccess access, bool ownsHandle)
44-
=> new MockFileStream(mockFileSystem, handle.ToString(), GetStreamType(FileMode.Append, access));
44+
=> new MockFileStream(mockFileSystem, handle.ToString(), FileMode.Open, access: access);
4545

4646
[Obsolete("This method has been deprecated. Please use new Create(SafeFileHandle handle, FileAccess access, int bufferSize) instead, and optionally make a new SafeFileHandle with ownsHandle=false if needed. http://go.microsoft.com/fwlink/?linkid=14202")]
4747
public Stream Create(IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize)
48-
=> new MockFileStream(mockFileSystem, handle.ToString(), GetStreamType(FileMode.Append, access));
48+
=> new MockFileStream(mockFileSystem, handle.ToString(), FileMode.Open, access: access);
4949

5050
[Obsolete("This method has been deprecated. Please use new Create(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) instead, and optionally make a new SafeFileHandle with ownsHandle=false if needed. http://go.microsoft.com/fwlink/?linkid=14202")]
5151
public Stream Create(IntPtr handle, FileAccess access, bool ownsHandle, int bufferSize, bool isAsync)
52-
=> new MockFileStream(mockFileSystem, handle.ToString(), GetStreamType(FileMode.Append, access));
52+
=> new MockFileStream(mockFileSystem, handle.ToString(), FileMode.Open, access: access);
5353

5454
public Stream Create(SafeFileHandle handle, FileAccess access)
55-
=> new MockFileStream(mockFileSystem, handle.ToString(), GetStreamType(FileMode.Append, access));
55+
=> new MockFileStream(mockFileSystem, handle.ToString(), FileMode.Open, access: access);
5656

5757
public Stream Create(SafeFileHandle handle, FileAccess access, int bufferSize)
58-
=> new MockFileStream(mockFileSystem, handle.ToString(), GetStreamType(FileMode.Append, access));
58+
=> new MockFileStream(mockFileSystem, handle.ToString(), FileMode.Open, access: access);
5959

6060
public Stream Create(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync)
61-
=> new MockFileStream(mockFileSystem, handle.ToString(), GetStreamType(FileMode.Append, access));
62-
63-
private static MockFileStream.StreamType GetStreamType(FileMode mode, FileAccess access = FileAccess.ReadWrite)
64-
{
65-
if (access == FileAccess.Read)
66-
{
67-
return MockFileStream.StreamType.READ;
68-
}
69-
else if (mode == FileMode.Append)
70-
{
71-
return MockFileStream.StreamType.APPEND;
72-
}
73-
else if (mode == FileMode.Truncate)
74-
{
75-
return MockFileStream.StreamType.TRUNCATE;
76-
}
77-
else if (mode == FileMode.Create)
78-
{
79-
return MockFileStream.StreamType.TRUNCATE;
80-
}
81-
else if (mode == FileMode.CreateNew)
82-
{
83-
return MockFileStream.StreamType.TRUNCATE;
84-
}
85-
else
86-
{
87-
return MockFileStream.StreamType.WRITE;
88-
}
89-
}
61+
=> new MockFileStream(mockFileSystem, handle.ToString(), FileMode.Open, access: access);
9062
}
9163
}

0 commit comments

Comments
 (0)