Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ public NullFileSystemStream() : base(Null, ".", true)
private readonly MockFileData fileData;
private bool disposed;

private byte[] lastKnownContents;

/// <inheritdoc />
public MockFileStream(
IMockFileDataAccessor mockFileDataAccessor,
Expand Down Expand Up @@ -104,6 +106,7 @@ public MockFileStream(
mockFileDataAccessor.FileHandles.AddHandle(path, guid, access, share);
this.access = access;
this.share = share;
lastKnownContents = fileData.Contents;
}

private static void ThrowIfInvalidModeAccess(FileMode mode, FileAccess access)
Expand Down Expand Up @@ -138,11 +141,46 @@ private static void ThrowIfInvalidModeAccess(FileMode mode, FileAccess access)
/// <inheritdoc />
public override int Read(byte[] buffer, int offset, int count)
{
RefreshBufferFromSharedFileData();
mockFileDataAccessor.AdjustTimes(fileData,
TimeAdjustments.LastAccessTime);
return base.Read(buffer, offset, count);
}

#if FEATURE_SPAN
/// <inheritdoc />
public override int Read(Span<byte> buffer)
{
RefreshBufferFromSharedFileData();
return base.Read(buffer);
}
#endif

/// <inheritdoc />
public override int ReadByte()
{
RefreshBufferFromSharedFileData();
return base.ReadByte();
}

/// <inheritdoc />
public override Task<int> ReadAsync(byte[] buffer, int offset, int count,
CancellationToken cancellationToken)
{
RefreshBufferFromSharedFileData();
return base.ReadAsync(buffer, offset, count, cancellationToken);
}

#if FEATURE_SPAN
/// <inheritdoc />
public override ValueTask<int> ReadAsync(Memory<byte> buffer,
CancellationToken cancellationToken = new())
{
RefreshBufferFromSharedFileData();
return base.ReadAsync(buffer, cancellationToken);
}
#endif

/// <inheritdoc />
protected override void Dispose(bool disposing)
{
Expand Down Expand Up @@ -296,16 +334,63 @@ private void InternalFlush()
/* reset back to the beginning .. */
var position = Position;
Seek(0, SeekOrigin.Begin);
/* .. read everything out */
/* .. read everything out (bypassing the read-refresh, so we capture this handle's own buffer) */
var data = new byte[Length];
_ = Read(data, 0, (int)Length);
_ = base.Read(data, 0, (int)Length);
/* restore to original position */
Seek(position, SeekOrigin.Begin);
/* .. put it in the mock system */
mockFileData.Contents = data;
/* .. put it in the mock system, but only if this handle actually changed the contents since it last synchronized. */
if (!ContentsEqual(data, lastKnownContents))
{
mockFileData.Contents = data;
lastKnownContents = data;
}
}
}

private static bool ContentsEqual(byte[] left, byte[] right)
{
if (ReferenceEquals(left, right))
{
return true;
}

if (left == null || right == null || left.Length != right.Length)
{
return false;
}

for (var i = 0; i < left.Length; i++)
{
if (left[i] != right[i])
{
return false;
}
}

return true;
}

private void RefreshBufferFromSharedFileData()
{
var sharedContents = fileData.Contents;
if (ReferenceEquals(sharedContents, lastKnownContents))
{
return;
}

var position = base.Position;
base.Position = 0;
base.SetLength(sharedContents.Length);
if (sharedContents.Length > 0)
{
base.Write(sharedContents, 0, sharedContents.Length);
}

base.Position = position;
lastKnownContents = sharedContents;
}

private void OnClose()
{
if (options.HasFlag(FileOptions.DeleteOnClose) && mockFileDataAccessor.FileExists(path))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,11 @@ namespace System.IO.Abstractions.TestingHelpers
public object GetAccessControl() { }
[System.Runtime.Versioning.SupportedOSPlatform("windows")]
public object GetAccessControl(System.IO.Abstractions.IFileSystemAclSupport.AccessControlSections includeSections) { }
public override int Read(System.Span<byte> buffer) { }
public override int Read(byte[] buffer, int offset, int count) { }
public override System.Threading.Tasks.ValueTask<int> ReadAsync(System.Memory<byte> buffer, System.Threading.CancellationToken cancellationToken = default) { }
public override System.Threading.Tasks.Task<int> ReadAsync(byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) { }
public override int ReadByte() { }
[System.Runtime.Versioning.SupportedOSPlatform("windows")]
public void SetAccessControl(object value) { }
public override void SetLength(long value) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,8 @@ namespace System.IO.Abstractions.TestingHelpers
public object GetAccessControl() { }
public object GetAccessControl(System.IO.Abstractions.IFileSystemAclSupport.AccessControlSections includeSections) { }
public override int Read(byte[] buffer, int offset, int count) { }
public override System.Threading.Tasks.Task<int> ReadAsync(byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) { }
public override int ReadByte() { }
public void SetAccessControl(object value) { }
public override void SetLength(long value) { }
public override void Write(byte[] buffer, int offset, int count) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,11 @@ namespace System.IO.Abstractions.TestingHelpers
public object GetAccessControl() { }
[System.Runtime.Versioning.SupportedOSPlatform("windows")]
public object GetAccessControl(System.IO.Abstractions.IFileSystemAclSupport.AccessControlSections includeSections) { }
public override int Read(System.Span<byte> buffer) { }
public override int Read(byte[] buffer, int offset, int count) { }
public override System.Threading.Tasks.ValueTask<int> ReadAsync(System.Memory<byte> buffer, System.Threading.CancellationToken cancellationToken = default) { }
public override System.Threading.Tasks.Task<int> ReadAsync(byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) { }
public override int ReadByte() { }
[System.Runtime.Versioning.SupportedOSPlatform("windows")]
public void SetAccessControl(object value) { }
public override void SetLength(long value) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,11 @@ namespace System.IO.Abstractions.TestingHelpers
public object GetAccessControl() { }
[System.Runtime.Versioning.SupportedOSPlatform("windows")]
public object GetAccessControl(System.IO.Abstractions.IFileSystemAclSupport.AccessControlSections includeSections) { }
public override int Read(System.Span<byte> buffer) { }
public override int Read(byte[] buffer, int offset, int count) { }
public override System.Threading.Tasks.ValueTask<int> ReadAsync(System.Memory<byte> buffer, System.Threading.CancellationToken cancellationToken = default) { }
public override System.Threading.Tasks.Task<int> ReadAsync(byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) { }
public override int ReadByte() { }
[System.Runtime.Versioning.SupportedOSPlatform("windows")]
public void SetAccessControl(object value) { }
public override void SetLength(long value) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,11 @@ namespace System.IO.Abstractions.TestingHelpers
public object GetAccessControl() { }
[System.Runtime.Versioning.SupportedOSPlatform("windows")]
public object GetAccessControl(System.IO.Abstractions.IFileSystemAclSupport.AccessControlSections includeSections) { }
public override int Read(System.Span<byte> buffer) { }
public override int Read(byte[] buffer, int offset, int count) { }
public override System.Threading.Tasks.ValueTask<int> ReadAsync(System.Memory<byte> buffer, System.Threading.CancellationToken cancellationToken = default) { }
public override System.Threading.Tasks.Task<int> ReadAsync(byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) { }
public override int ReadByte() { }
[System.Runtime.Versioning.SupportedOSPlatform("windows")]
public void SetAccessControl(object value) { }
public override void SetLength(long value) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,8 @@ namespace System.IO.Abstractions.TestingHelpers
public object GetAccessControl() { }
public object GetAccessControl(System.IO.Abstractions.IFileSystemAclSupport.AccessControlSections includeSections) { }
public override int Read(byte[] buffer, int offset, int count) { }
public override System.Threading.Tasks.Task<int> ReadAsync(byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) { }
public override int ReadByte() { }
public void SetAccessControl(object value) { }
public override void SetLength(long value) { }
public override void Write(byte[] buffer, int offset, int count) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,11 @@ namespace System.IO.Abstractions.TestingHelpers
public override System.Threading.Tasks.Task FlushAsync(System.Threading.CancellationToken cancellationToken) { }
public object GetAccessControl() { }
public object GetAccessControl(System.IO.Abstractions.IFileSystemAclSupport.AccessControlSections includeSections) { }
public override int Read(System.Span<byte> buffer) { }
public override int Read(byte[] buffer, int offset, int count) { }
public override System.Threading.Tasks.ValueTask<int> ReadAsync(System.Memory<byte> buffer, System.Threading.CancellationToken cancellationToken = default) { }
public override System.Threading.Tasks.Task<int> ReadAsync(byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) { }
public override int ReadByte() { }
public void SetAccessControl(object value) { }
public override void SetLength(long value) { }
public override void Write(System.ReadOnlySpan<byte> buffer) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,39 @@
[TestFixture]
public class MockFileStreamTests
{
[Test]
public async Task MockFileStream_SharedReadWrite_SecondHandleSeesWritesFromFirstHandle()
{
// Arrange
var filename = XFS.Path(@"C:\temp\shared.bin");
var fileSystem = new MockFileSystem();
fileSystem.AddEmptyFile(filename);
var results = new List<int>();

// Act
using (var file1 = fileSystem.FileStream.New(filename, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite))
using (var file2 = fileSystem.FileStream.New(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
{
var buffer = new byte[4];
for (int ix = 0; ix < 10; ix++)
{
var bytes = BitConverter.GetBytes(ix);
file1.Position = 0;
file1.Write(bytes, 0, bytes.Length);
file1.Flush();

file2.Position = 0;
file2.Flush();
var bytesRead = file2.Read(buffer, 0, buffer.Length);
await That(bytesRead).IsEqualTo(buffer.Length);
results.Add(BitConverter.ToInt32(buffer, 0));
}
}

// Assert
await That(results).IsEqualTo(new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 });

Check warning on line 43 in tests/TestableIO.System.IO.Abstractions.TestingHelpers.Tests/MockFileStreamTests.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer 'static readonly' fields over constant array arguments if the called method is called repeatedly and is not mutating the passed array

See more on https://sonarcloud.io/project/issues?id=TestableIO_System.IO.Abstractions&issues=AZ9Wab_XbXNPmtFBAPsd&open=AZ9Wab_XbXNPmtFBAPsd&pullRequest=1507
}

[Test]
public async Task MockFileStream_Flush_WritesByteToFile()
{
Expand Down
Loading