Skip to content

Stabilize SystemFolder watcher tests #97

Merged
Arlodotexe merged 1 commit intomainfrom
fix/system-folder-watcher-tests
Aug 18, 2025
Merged

Stabilize SystemFolder watcher tests #97
Arlodotexe merged 1 commit intomainfrom
fix/system-folder-watcher-tests

Conversation

@Arlodotexe
Copy link
Owner

Background

OwlCore.Storage includes integration tests for the SystemFolder watcher built on FileSystemWatcher. These tests validate that create/delete events are surfaced via IFolderWatcher.CollectionChanged.

Problem

  • The full test run intermittently crashed with:
    • InvalidOperationException: “An attempt was made to transition a task to a final state when it had already completed.” This was caused by TaskCompletionSource.SetResult being invoked multiple times due to duplicate/rapid FileSystemWatcher events.
    • UnauthorizedAccessException when attempting to create files/folders directly under %TEMP%, and occasional name collisions from predictable test item names.

Solution

  • Stabilized watcher tests in tests/OwlCore.Storage.Tests/SystemIO/SystemFolderTests.cs:
    • Create a unique temp subfolder per test run (%TEMP%\{Guid}) to avoid permission issues and cross-test interference.
    • Replace TaskCompletionSource.SetResult with TrySetResult and use TaskCreationOptions.RunContinuationsAsynchronously to safely handle duplicate events without deadlocks.
    • Use unique, GUID-based file/folder names to avoid collisions.
    • Add timeout of 2000ms for filesystem/event variability.
  • No product code or public APIs changed; this is a tests-only stabilization.
  • Result: Full suite passes locally (328/328) succeeded on net8.0,

@Arlodotexe Arlodotexe self-assigned this Aug 17, 2025
@Arlodotexe Arlodotexe requested a review from Copilot August 17, 2025 22:09
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR stabilizes flaky SystemFolder watcher tests by addressing race conditions and filesystem permission issues that were causing intermittent test failures.

  • Creates unique temporary directories per test run to avoid permission issues and cross-test interference
  • Replaces unsafe TaskCompletionSource.SetResult calls with TrySetResult and proper async continuation options to handle duplicate filesystem events
  • Uses GUID-based names for test files/folders to prevent naming collisions

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

@Arlodotexe Arlodotexe merged commit 2a53b4d into main Aug 18, 2025
1 check passed
Arlodotexe added a commit that referenced this pull request Aug 18, 2025
OwlCore.Storage 0.14.0 PRs closed:
- [Add DFS/BFS traversal, override/composite folders, truncation](#95)
- [Add ranged ReadText/WriteText extensions, IFile.CopyToAsync, tests](#96)
- [Stabilize SystemFolder watcher tests](#97)
- [Added async-iterable relative path traversal helpers](#98)
- [Add CreateRelativeStorageExtensions and tests](#99)
- [Add net9.0 as TFM, upgrade CI to .NET 9 SDK](#100)

[New]
Added DepthFirstRecursiveFolder: depth-first async traversal wrapper over any IFolder with optional MaxDepth.
Added BreadthFirstRecursiveFolder: breadth-first async traversal wrapper over any IFolder with optional MaxDepth.
Added ItemsOverrideFolder: override a folder's GetItemsAsync by projecting its async enumerable.
Added ReadOnlyCompositeFolder: present a read-only folder view materialized from a set of files.
Added ParentOverrideChildFile: wrap any IFile with a custom parent, exposing it as IChildFile.
Added TruncatedFile: wrap a file and cap the readable length of its underlying stream.
Added TruncatedFilesFolder: wrap a folder so each file is exposed with a maximum byte length.
Added TruncatedStream: stream wrapper enforcing a maximum readable length.
Added FileReadExtensions.ReadTextAsync (line + column range): iterate a specific line range and slice per-line columns.
Added FileWriteExtensions.WriteTextAsync (line range; line + column range): write sliced text content into a file (line end inclusive, column end exclusive).
Added CopyExtensions.CopyToAsync: async file-to-file copy extension over IFile.

Added CreateByRelativePathAsync (start from folder or child file): create a file or folder at a relative path (supports "." and ".."; explicit target type).
Added CreateFolderByRelativePathAsync (start from folder or child file): convenience wrapper for creating folders by relative path.
Added CreateFileByRelativePathAsync (start from folder or child file): convenience wrapper for creating files by relative path.
Added CreateFoldersAlongRelativePathAsync (start from folder or child file): async sequence yielding each folder visited/created along a relative path.
Added CreateAlongRelativePathAsync (start from folder or child file): async sequence yielding parents, then the file when targeting a file; folders only when targeting a folder.

[Improvements]
Added StorableExtensions.GetItemsAlongRelativePathAsync: navigates a relative path from any IStorable, yielding each visited node in order (supports '.', '..', and normalizes path separators).
Added FolderExtensions.GetItemsAlongRelativePathToAsync: yields items along the path from a source IFolder to a descendant IStorableChild in traversal order.

[Tests]
Added traversal tests for DepthFirstRecursiveFolder and BreadthFirstRecursiveFolder covering BFS/DFS order, type filtering, and MaxDepth semantics.
Added range read/write tests for IFile covering line range and line + column range.
Arlodotexe added a commit that referenced this pull request Aug 18, 2025
… truncated IO, range read/write extensions (#101)

* Release 0.14.0. See release notes or expand full commit message.

OwlCore.Storage 0.14.0 PRs closed:
- [Add DFS/BFS traversal, override/composite folders, truncation](#95)
- [Add ranged ReadText/WriteText extensions, IFile.CopyToAsync, tests](#96)
- [Stabilize SystemFolder watcher tests](#97)
- [Added async-iterable relative path traversal helpers](#98)
- [Add CreateRelativeStorageExtensions and tests](#99)
- [Add net9.0 as TFM, upgrade CI to .NET 9 SDK](#100)

[New]
Added DepthFirstRecursiveFolder: depth-first async traversal wrapper over any IFolder with optional MaxDepth.
Added BreadthFirstRecursiveFolder: breadth-first async traversal wrapper over any IFolder with optional MaxDepth.
Added ItemsOverrideFolder: override a folder's GetItemsAsync by projecting its async enumerable.
Added ReadOnlyCompositeFolder: present a read-only folder view materialized from a set of files.
Added ParentOverrideChildFile: wrap any IFile with a custom parent, exposing it as IChildFile.
Added TruncatedFile: wrap a file and cap the readable length of its underlying stream.
Added TruncatedFilesFolder: wrap a folder so each file is exposed with a maximum byte length.
Added TruncatedStream: stream wrapper enforcing a maximum readable length.
Added FileReadExtensions.ReadTextAsync (line + column range): iterate a specific line range and slice per-line columns.
Added FileWriteExtensions.WriteTextAsync (line range; line + column range): write sliced text content into a file (line end inclusive, column end exclusive).
Added CopyExtensions.CopyToAsync: async file-to-file copy extension over IFile.

Added CreateByRelativePathAsync (start from folder or child file): create a file or folder at a relative path (supports "." and ".."; explicit target type).
Added CreateFolderByRelativePathAsync (start from folder or child file): convenience wrapper for creating folders by relative path.
Added CreateFileByRelativePathAsync (start from folder or child file): convenience wrapper for creating files by relative path.
Added CreateFoldersAlongRelativePathAsync (start from folder or child file): async sequence yielding each folder visited/created along a relative path.
Added CreateAlongRelativePathAsync (start from folder or child file): async sequence yielding parents, then the file when targeting a file; folders only when targeting a folder.

[Improvements]
Added StorableExtensions.GetItemsAlongRelativePathAsync: navigates a relative path from any IStorable, yielding each visited node in order (supports '.', '..', and normalizes path separators).
Added FolderExtensions.GetItemsAlongRelativePathToAsync: yields items along the path from a source IFolder to a descendant IStorableChild in traversal order.

[Tests]
Added traversal tests for DepthFirstRecursiveFolder and BreadthFirstRecursiveFolder covering BFS/DFS order, type filtering, and MaxDepth semantics.
Added range read/write tests for IFile covering line range and line + column range.

* Fixed build errors around compilations conditionals uncovered by merge
@Arlodotexe Arlodotexe deleted the fix/system-folder-watcher-tests branch August 18, 2025 03:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant