fix: share one cached state across FileSystemInfo properties like the real file system - #1113
Merged
Merged
Conversation
… real file system `FileSystemInfo` takes a single snapshot of a file's state on the first read of any cached property (Exists, Length, Attributes, the times and UnixFileMode) and serves it until `Refresh()` or an operation on the same instance that invalidates it. Instances returned by an enumeration carry the state found during the enumeration. The mock only cached `Exists` and read every other property live, so reading `Length` after the file grew, or `Exists` on a `GetFiles()` result after the file was deleted, returned values the real file system does not. The mock now captures all cached properties together: - lazily on first access, eagerly on `Refresh()` and for enumerated instances; - reset by the property setters, `Delete`, `Create`, `MoveTo`, `CreateAsSymbolicLink`, and `CreateText`/`AppendText` (.NET 8+), which are the operations the runtime invalidates on; `Open`, `CopyTo`, `Replace`, `Encrypt` and `Decrypt` do not; - `LinkTarget` is cached separately and lazily, as in the runtime. `Encrypt_Decrypt_ShouldChangeEncryptedFileAttribute` now calls `Refresh()` after `Decrypt()`, as `FileInfo.Decrypt` does not invalidate the cached attributes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TymoqrVYgZwdNSper5ZZB1
There was a problem hiding this comment.
Copilot review overview
🟢 Approval recommended
The implementation consistently matches the documented runtime behavior and is covered by comprehensive parity tests.
Review effort: Balanced
Findings: None
What changed in this PR
Aligns mock FileSystemInfo metadata caching with the real file system.
Changes:
- Introduces unified cached metadata state with appropriate invalidation.
- Initializes enumeration results with captured state.
- Adds parity tests across files, directories, links, and operations.
| File | Description |
|---|---|
Tests/.../FileSystemInfo/Tests.cs |
Tests independent link-target caching. |
Tests/.../FileInfo/RefreshTests.cs |
Tests file metadata snapshots and refresh behavior. |
Tests/.../FileInfo/OpenTests.cs |
Verifies opening does not invalidate state. |
Tests/.../FileInfo/MoveToTests.cs |
Verifies moving invalidates state. |
Tests/.../FileInfo/EncryptDecryptTests.cs |
Refreshes cached attributes after decryption. |
Tests/.../FileInfo/DeleteTests.cs |
Tests deletion invalidation across frameworks. |
Tests/.../FileInfo/CreateAsSymbolicLinkTests.cs |
Tests link creation invalidation. |
Tests/.../FileInfo/AppendTextTests.cs |
Tests framework-specific append invalidation. |
Tests/.../DirectoryInfo/RefreshTests.cs |
Tests directory metadata caching. |
Tests/.../DirectoryInfo/GetFileSystemInfosTests.cs |
Tests captured enumeration state. |
Tests/.../DirectoryInfo/GetFilesTests.cs |
Tests cached file results. |
Tests/.../DirectoryInfo/GetDirectoriesTests.cs |
Tests cached directory results. |
Tests/.../DirectoryInfo/EnumerateFileSystemInfosTests.cs |
Tests lazy mixed enumeration state. |
Tests/.../DirectoryInfo/EnumerateFilesTests.cs |
Tests lazy file enumeration state. |
Tests/.../DirectoryInfo/EnumerateDirectoriesTests.cs |
Tests lazy directory enumeration state. |
Source/.../FileSystemInfoMock.cs |
Implements shared metadata and link-target caches. |
Source/.../FileInfoMock.cs |
Applies cache semantics to file operations. |
Source/.../DirectoryInfoMock.cs |
Initializes enumeration state and invalidates moves. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
This pull request does not currently match the merge queue conditions, so it cannot be queued from here. The box comes back if it matches again. |
|
This is addressed in release v7.1.1. |
This was referenced Oct 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
FileSystemInfotakes one snapshot of a file's state on the first read of any cached property:Exists,Length,Attributes, the times andUnixFileMode. It serves that snapshot untilRefresh(), or until an operation on the same instance invalidates it. Instances returned by an enumeration carry the state found during the enumeration. The mock cachedExiststhe same way, but read every other property live. So a test could observe values the real file system never returns:The same happens with
Attributesand the times after they change on disk. AGetFiles()result also reportedExists == falseafter its file was deleted, where the real file system still reportstrue.Change
FileSystemInfoMockcaptures all cached properties together, in one state:Refresh()and for instances returned by enumeration.Delete,Create,MoveTo,CreateAsSymbolicLink, andCreateText/AppendTexton .NET 8+. These are the operations the runtime invalidates on.Open,CopyTo,Replace,EncryptandDecryptdon't reset it, as in the runtime.AppendTextdidn't reset it before.LinkTargetis cached separately and read lazily, as in the runtime.There is no public API change.
Tests
The tests are added to
Testably.Abstractions.Tests, so each one also runs against the real file system:FileInfo/RefreshTests(14 tests) andDirectoryInfo/RefreshTests(5 tests).DeleteTests,MoveToTests,OpenTests,CreateAsSymbolicLinkTests,AppendTextTests,FileSystemInfo/Tests, and the sixDirectoryInfoGet/Enumerate test files.Against the current mock, 79 mock runs of these tests fail, and all the real-file-system runs pass.
Encrypt_Decrypt_ShouldChangeEncryptedFileAttributenow callsRefresh()afterDecrypt().FileInfo.Decryptonly callsFile.Decryptand does not invalidate the cached attributes. That test only runs on the simulated Windows file system, so this follows the runtime's code rather than a run on a real file system.Validation
Scenario harness: 78 scenarios through a real file system and
MockFileSystem, on net8.0, net9.0 and net10.0. Before, 31 differed from the real file system; with this change, none do.Test suites (Release, macOS):
Testably.Abstractions.TestsTestably.Abstractions.Testing.TestsAll pass, as do the API, Core API and parity tests.
Worth knowing
Tests that change a file through
File.*and then readLength,Attributesor a time from aFileInfothey had already read will now see the old value until they callRefresh(). That matches the real file system, but it is a behaviour change.🤖 Generated with Claude Code
https://claude.ai/code/session_01TymoqrVYgZwdNSper5ZZB1