Skip to content

fix: handle shared file contents across handles - #1507

Merged
vbreuss merged 3 commits into
mainfrom
fix/1131-mockfilestream-shared-contents
Jul 12, 2026
Merged

vbreuss merged 3 commits into
mainfrom
fix/1131-mockfilestream-shared-contents

Conversation

@vbreuss

@vbreuss vbreuss commented Jul 12, 2026

Copy link
Copy Markdown
Member

Each MockFileStream kept its own MemoryStream buffer that was only synced to the shared MockFileData.Contents on flush, so a second handle opened with FileShare.ReadWrite never saw writes made through another handle. On top of that, InternalFlush unconditionally wrote its buffer back, so a read-only handle's Flush could clobber another handle's writes.

Track the last-synced Contents reference and reload the buffer from it on every read entry point (Read, ReadByte, ReadAsync, span overloads) when another handle has replaced it. InternalFlush now writes back only when the buffer actually differs from the last synced snapshot, matching the no-op behavior of a real FileStream.Flush with no pending writes.


Each MockFileStream kept its own MemoryStream buffer that was only synced to the shared MockFileData.Contents on flush, so a second handle opened with FileShare.ReadWrite never saw writes made through another handle. On top of that, InternalFlush unconditionally wrote its buffer back, so a read-only handle's Flush could clobber another handle's writes.

Track the last-synced Contents reference and reload the buffer from it on every read entry point (Read, ReadByte, ReadAsync, span overloads) when another handle has replaced it. InternalFlush now writes back only when the buffer actually differs from the last synced snapshot, matching the no-op behavior of a real FileStream.Flush with no pending writes.
@github-actions

github-actions Bot commented Jul 12, 2026 •

Copy link
Copy Markdown

Test Results

    31 files  ± 0      31 suites  ±0   17s ⏱️ -4s
 1 155 tests + 1   1 147 ✅  - 6    1 💤 ±0  7 ❌ +7 
11 097 runs  +10  10 251 ✅ +3  839 💤 ±0  7 ❌ +7 

For more details on these failures, see this check.

Results for commit 7b85ec1. ± Comparison against base commit 9cb6266.

♻️ This comment has been updated with latest results.

vbreuss added 2 commits July 12, 2026 15:03
The shared-contents regression test ignored the byte count returned by
Stream.Read, which the analyzer flags as CA2022 (inexact read) and the
build treats as an error. Capture the count and assert it equals the
requested length.
The shared-contents fix added public overrides to MockFileStream
(ReadByte and ReadAsync(byte[]) on all frameworks; Read(Span) and
ReadAsync(Memory) where FEATURE_SPAN is defined). Regenerate the
expected public API surface via the AcceptApiChanges test so
VerifyPublicApiForTestingHelpers passes.
@sonarqubecloud

Copy link
Copy Markdown

@vbreuss
vbreuss enabled auto-merge (squash) July 12, 2026 13:16
@vbreuss
vbreuss merged commit ec68061 into main Jul 12, 2026
11 checks passed
@vbreuss
vbreuss deleted the fix/1131-mockfilestream-shared-contents branch July 12, 2026 13:17
@github-actions

Copy link
Copy Markdown

This is addressed in release v22.2.0.

@github-actions github-actions Bot added the state: released Issues that are released label Jul 12, 2026
This was referenced Jul 12, 2026
microsoft-github-policy-service Bot pushed a commit to Azure/template-reference-generator that referenced this pull request Oct 4, 2026
Updated
[System.IO.Abstractions](https://github.com/TestableIO/System.IO.Abstractions)
from 22.1.1 to 22.3.0.

<details>
<summary>Release notes</summary>

_Sourced from [System.IO.Abstractions's
releases](https://github.com/TestableIO/System.IO.Abstractions/releases)._

## 22.3.0

## What's Changed
* chore(deps): update dependency sharpcompress to 0.50.0 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1508
* chore(deps): update dependency microsoft.net.test.sdk to 18.8.0 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1509
* chore(deps): update dependency microsoft.net.test.sdk to 18.8.1 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1510
* chore(deps): update dependency system.text.json to v10.0.10 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1511
* chore(deps): update dependency dotnet-sdk to v10.0.302 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1512
* chore(deps): update actions/setup-dotnet action to v6 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1513
* chore(deps): update dependency libgit2sharp to 0.32.0 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1514
* chore(deps): update dependency sharpcompress to 0.50.1 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1516
* chore(deps): update dependency githubactionstestlogger to 3.0.5 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1515
* chore(deps): update dependency mockolate to 3.4.0 by @​renovate[bot]
in TestableIO/System.IO.Abstractions#1517
* chore(deps): update dependency sharpcompress to 0.50.2 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1518
* chore(deps): update dependency sharpcompress to 0.50.3 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1519
* chore(deps): update dependency sharpcompress to 0.50.4 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1520
* chore(deps): update dotnet monorepo by @​renovate[bot] in
TestableIO/System.IO.Abstractions#1523
* chore(deps): update dependency microsoft.net.test.sdk to 18.9.0 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1524
* chore(deps): update dependency nunit3testadapter to 6.3.0 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1525
* chore(deps): update dependency mockolate to 3.4.1 by @​renovate[bot]
in TestableIO/System.IO.Abstractions#1526
* chore(deps): update dependency awexpect to 2.36.0 by @​renovate[bot]
in TestableIO/System.IO.Abstractions#1527
* chore(deps): update dependency awexpect.mockolate to 3.2.0 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1528
* chore(deps): update dependency awexpect.testably to 1.1.0 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1529
* chore(deps): update dependency awexpect to 2.37.0 by @​renovate[bot]
in TestableIO/System.IO.Abstractions#1530
* chore(deps): update dependency awexpect to 2.38.0 by @​renovate[bot]
in TestableIO/System.IO.Abstractions#1531
* chore(deps): update dependency mockolate to 3.5.0 by @​renovate[bot]
in TestableIO/System.IO.Abstractions#1532
* chore(deps): update dependency awexpect to 2.40.0 by @​renovate[bot]
in TestableIO/System.IO.Abstractions#1533
* chore(deps): update dependency system.text.json to v10.0.12 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1534
* chore(deps): update dependency dotnet-sdk to v10.0.401 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1535
* chore(deps): update dependency microsoft.net.test.sdk to 18.10.0 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1536
* chore(deps): update dependency nunit.analyzers to 4.15.0 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1537
* chore(deps): update dependency microsoft.net.test.sdk to 18.10.1 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1538
* chore(deps): update dependency mockolate to 3.5.1 by @​renovate[bot]
in TestableIO/System.IO.Abstractions#1539
* chore(deps): update dependency nunit to v5 by @​renovate[bot] in
TestableIO/System.IO.Abstractions#1540
* chore: bump Testably.Abstractions.FileSystem.Interface to v10.4.0 by
@​vbreuss in
TestableIO/System.IO.Abstractions#1542
* chore(deps): update dependency coverlet.collector to 10.1.0 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1543
* chore: remove unused IDE configuration files by @​vbreuss in
TestableIO/System.IO.Abstractions#1545
* chore(deps): update Fallout to 10.4.0 by @​vbreuss in
TestableIO/System.IO.Abstractions#1544


**Full Changelog**:
TestableIO/System.IO.Abstractions@v22.2.0...v22.3.0

## 22.2.0

## What's Changed
* chore(deps): update dependency sharpcompress to 0.47.4 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1458
* chore(deps): update dependency githubactionstestlogger to 3.0.3 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1457
* chore(deps): update dotnet monorepo by @​renovate[bot] in
TestableIO/System.IO.Abstractions#1459
* chore(deps): update dependency microsoft.net.test.sdk to 18.4.0 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1460
* chore(deps): update dependency
testably.abstractions.filesystem.interface to 10.2.0 by @​renovate[bot]
in TestableIO/System.IO.Abstractions#1462
* chore(deps): update dependency nunit3testadapter to 6.2.0 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1461
* chore(deps): update softprops/action-gh-release action to v3 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1463
* chore(deps): update dependency coverlet.collector to v10 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1464
* chore(deps): update dependency system.text.json to 10.0.7 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1466
* chore(deps): update dependency dotnet-sdk to v10.0.203 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1467
* chore(deps): update dependency microsoft.net.test.sdk to 18.5.0 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1468
* chore(deps): update dependency microsoft.net.test.sdk to 18.5.1 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1469
* chore: bump Mockolate and aweXpect.Mockolate to v3.0.0 by @​vbreuss in
TestableIO/System.IO.Abstractions#1472
* chore(deps): update dependency mockolate to 3.1.0 by @​renovate[bot]
in TestableIO/System.IO.Abstractions#1471
* chore(deps): update dependency githubactionstestlogger to 3.0.4 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1473
* chore(deps): update dependency nunit.analyzers to 4.13.0 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1474
* chore(deps): update dependency nunit to 4.6.0 by @​renovate[bot] in
TestableIO/System.IO.Abstractions#1475
* chore(deps): update dependency sharpcompress to 0.48.0 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1476
* chore(deps): update dependency awexpect.mockolate to 3.1.0 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1477
* chore(deps): update dependency awexpect to 2.32.0 by @​renovate[bot]
in TestableIO/System.IO.Abstractions#1478
* chore(deps): update dependency awexpect to 2.33.0 by @​renovate[bot]
in TestableIO/System.IO.Abstractions#1479
* chore(deps): update dependency mockolate to 3.2.0 by @​renovate[bot]
in TestableIO/System.IO.Abstractions#1480
* chore(deps): update dependency system.text.json to 10.0.8 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1481
* chore(deps): update dependency dotnet-sdk to v10.0.300 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1482
* chore(deps): update dependency sharpcompress to 0.48.1 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1483
* chore(deps): update dependency awexpect to 2.34.0 by @​renovate[bot]
in TestableIO/System.IO.Abstractions#1484
* chore(deps): update dependency coverlet.collector to 10.0.1 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1485
* chore(deps): update dependency awexpect.testably to 0.14.0 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1486
* chore(deps): update dependency nunit to 4.6.1 by @​renovate[bot] in
TestableIO/System.IO.Abstractions#1487
* chore(deps): update dependency awexpect.testably to 0.15.0 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1488
* chore(deps): update dependency mockolate to 3.2.1 by @​renovate[bot]
in TestableIO/System.IO.Abstractions#1489
* chore(deps): update dependency microsoft.net.test.sdk to 18.6.0 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1490
* chore(deps): update dependency sharpcompress to 0.49.0 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1491
* chore(deps): update dependency sharpcompress to 0.49.1 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1492
* chore(deps): update dependency nunit.analyzers to 4.14.0 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1493
* chore(deps): update dotnet monorepo to v10 by @​renovate[bot] in
TestableIO/System.IO.Abstractions#1494
* chore(deps): update dotnet monorepo to v10.0.9 by @​renovate[bot] in
TestableIO/System.IO.Abstractions#1495
* chore(deps): update dotnet monorepo to v10.0.301 by @​renovate[bot] in
TestableIO/System.IO.Abstractions#1496
* chore(deps): update dependency awexpect to 2.35.0 by @​renovate[bot]
in TestableIO/System.IO.Abstractions#1498
* chore(deps): update dotnet monorepo to v10 by @​renovate[bot] in
TestableIO/System.IO.Abstractions#1500
* chore(deps): update dotnet monorepo to 10.0.9 by @​renovate[bot] in
TestableIO/System.IO.Abstractions#1497
* chore(deps): update dependency awexpect.testably to v1 by
@​renovate[bot] in
TestableIO/System.IO.Abstractions#1499
* chore(deps): update actions/checkout action to v7 by @​renovate[bot]
in TestableIO/System.IO.Abstractions#1501
* chore(deps): update dependency mockolate to 3.3.0 by @​renovate[bot]
in TestableIO/System.IO.Abstractions#1502
* chore(deps): update vstest monorepo by @​renovate[bot] in
TestableIO/System.IO.Abstractions#1503
* chore: bump Testably.Abstractions.FileSystem.Interface to v10.3.0 by
@​vbreuss in
TestableIO/System.IO.Abstractions#1504
* ci(mergify): upgrade configuration to current format by @​mergify[bot]
in TestableIO/System.IO.Abstractions#1505
* refactor: Switch to fork of NUKE: Fallout by @​vbreuss in
TestableIO/System.IO.Abstractions#1506
* fix: handle shared file contents across handles by @​vbreuss in
TestableIO/System.IO.Abstractions#1507
 ... (truncated)

Commits viewable in [compare
view](TestableIO/System.IO.Abstractions@v22.1.1...v22.3.0).
</details>

[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=System.IO.Abstractions&package-manager=nuget&previous-version=22.1.1&new-version=22.3.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state: released Issues that are released

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The mock FileStream class does not handle shared file contents correctly.

1 participant