Conversation
…Info The real FileSystemInfo does not read the file's state when it is constructed. It takes one snapshot on the first read of a cached property (Exists, Length, Attributes, the times) and serves every property from it until Refresh() or an operation on the same instance invalidates it. MockFileInfo and MockDirectoryInfo took the snapshot in their constructor, so an instance created before a file or directory was created, deleted or changed reported the state from construction time: Exists false for a file created afterwards, Exists true for one deleted afterwards, stale Length, Attributes and times, and FileNotFoundException from Length for a file that exists by the time it is read. The constructors now only mark the snapshot as pending, so it is taken on first access and then cached as before; Refresh() clears the pending flag. Instances returned by DirectoryInfo enumeration are refreshed while enumerating, matching the real enumeration, which fills the snapshot from the enumeration data. MoveTo discards the snapshot, as it does in the runtime. The two "ShouldReturnCachedData" tests asserted the construction-time snapshot; they now read Exists once before the change so they keep testing that the snapshot is cached after the first read. 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 described runtime semantics and includes comprehensive regression coverage.
Review effort: Balanced
Findings: None
What changed in this PR
Aligns mock file-system metadata caching with real FileSystemInfo lazy snapshot behavior.
Changes:
- Defers metadata snapshots until first cached-property access.
- Invalidates snapshots after moves.
- Preserves enumeration-time snapshots and adds regression tests.
| File | Description |
|---|---|
MockFileInfo.cs |
Implements lazy refresh and move invalidation. |
MockDirectoryInfo.cs |
Implements lazy refresh, enumeration snapshots, and move invalidation. |
MockFileInfoTests.cs |
Tests lazy and cached file metadata behavior. |
MockDirectoryInfoTests.cs |
Tests lazy and cached directory metadata behavior. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Contributor
|
Tick the box to add this pull request to the merge queue (same as
|
This branch has not been deployed
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.
The real
FileSystemInfodoes not read a file's state when it is constructed. It takes one snapshot on the first read of any cached property (Exists,Length,Attributes, the times), and serves every property from that snapshot untilRefresh(), or until an operation on the same instance invalidates it.MockFileInfoandMockDirectoryInfotake that snapshot in their constructor instead. As a result, a test that creates the info object first and changes the file afterwards sees something the real file system never reports:Code that passes against a real disk fails against the mock unless it adds a
Refresh()that production code doesn't need.Change
refreshOnNextReadflag. The snapshot is taken on first access and cached exactly as before.Refresh()clears the pending flag.DirectoryInfoenumeration are refreshed while enumerating, as the runtime fills their snapshot from the enumeration data.MoveTodiscards the snapshot, as it does in the runtime.Validated against the real file system
I ran 21 scenarios through
new FileSystem()andnew MockFileSystem()on .NET 10 and compared the results.FileInfocreated, then file written,ExistsExistsread, file written,ExistsagainRefresh()FileInfocreated, file deleted,ExistsFileInfocreated, file grows,LengthLengthread, file grows,LengthagainFileInfocreated before the file,LengthFileInfo.Create()/Delete()thenExistsDirectoryInfocreated, then directory created,ExistsDirectoryInfoon existing directory, deleted,ExistsAttributes/LastWriteTimechanged before first readAttributes/LastWriteTimeread, changed, read againExistsread, file grows,LengthFileInfofromGetFiles(), file deleted,ExistsLengthread, file grows,MoveTo,LengthDirectoryInfoattributes read, changed,MoveTo, attributesTests
MockFileInfoTestsandMockDirectoryInfoTests. 12 of them fail on the current code, one for each scenario that was wrong.…_Exists_ShouldReturnCachedDatatests asserted the snapshot from construction time. They now readExistsonce before the change, so they still test that the snapshot stays cached after the first read.In Release on net8.0, net9.0 and net10.0 (macOS): TestingHelpers, Wrappers, API and parity tests all pass.
Worth knowing
Code that relied on the construction-time snapshot will now see the current state. For example: creating a
FileInfo, changing the file, and expecting the old values.MockFileSystem.AddFile(IFileInfo, …)still refreshes the instance it is given, so that path is unchanged.🤖 Generated with Claude Code
https://claude.ai/code/session_01TymoqrVYgZwdNSper5ZZB1