Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Sep 11, 2025

The DirectoryMock.EnumerateDirectories method was not matching the behavior of the real Directory.EnumerateDirectories implementation. The real implementation throws IO exceptions (like DirectoryNotFoundException) immediately when the method is called, but the mock implementation only threw these exceptions when the returned IEnumerable was actually enumerated.

Problem

// Real file system behavior - throws immediately
var enumerable = Directory.EnumerateDirectories("nonexistent");  // Throws DirectoryNotFoundException

// Mock file system behavior - throws only during enumeration  
var enumerable = mockFileSystem.Directory.EnumerateDirectories("nonexistent");  // No exception
var list = enumerable.ToList();  // DirectoryNotFoundException thrown here

This discrepancy could cause tests to pass when they should fail, or miss edge cases in exception handling logic.

Solution

Split InMemoryStorage.EnumerateLocations into two methods:

  1. EnumerateLocations() - Performs immediate validation (directory exists, valid search pattern) and throws exceptions synchronously
  2. EnumerateLocationsImpl() - Contains the actual yield-based enumeration logic that executes during iteration

This ensures validation and exception throwing happens at method call time, while preserving the lazy enumeration behavior for the actual iteration over results.

Changes

  • InMemoryStorage.cs: Refactored enumeration to validate immediately and enumerate lazily
  • Test files: Added tests verifying immediate exception throwing for all enumeration methods
  • Statistics tests: Updated to create directories first, testing successful paths rather than exception scenarios

The fix applies to all enumeration methods:

  • EnumerateDirectories()
  • EnumerateFiles()
  • EnumerateFileSystemEntries()

Copilot AI and others added 2 commits September 11, 2025 04:17
Split InMemoryStorage.EnumerateLocations into validation and implementation methods.
Validation and exceptions now occur immediately when method is called,
while enumeration logic happens during iteration as expected.

Co-authored-by: vbreuss <[email protected]>
Updated EnumerateDirectories statistics tests to create directories first,
ensuring they test successful method calls rather than exception paths.
The fix maintains proper statistics tracking while enabling immediate
exception throwing for missing directories.

Co-authored-by: vbreuss <[email protected]>
Copilot AI changed the title [WIP] DirectoryMock.EnumerateDirectories does not throw exceptions until enumerated fix: DirectoryMock.EnumerateDirectories does not throw exceptions until enumerated Sep 11, 2025
Copilot AI requested a review from vbreuss September 11, 2025 04:23
Copilot finished work on behalf of vbreuss September 11, 2025 04:23
@vbreuss vbreuss marked this pull request as ready for review September 11, 2025 04:56
Copilot AI review requested due to automatic review settings September 11, 2025 04:56
Copy link

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 fixes a bug where DirectoryMock.EnumerateDirectories (and related enumeration methods) did not match the behavior of the real Directory.EnumerateDirectories implementation. The real file system throws IO exceptions immediately when the enumeration method is called on a non-existent directory, but the mock implementation only threw exceptions during iteration.

  • Split InMemoryStorage.EnumerateLocations into validation and implementation phases to throw exceptions immediately
  • Added new test cases to verify immediate exception throwing behavior for all enumeration methods
  • Updated statistics tests to create directories before enumeration to avoid exception scenarios

Reviewed Changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
Source/Testably.Abstractions.Testing/Storage/InMemoryStorage.cs Refactored EnumerateLocations to separate immediate validation from lazy enumeration implementation
Tests/Testably.Abstractions.Tests/FileSystem/Directory/EnumerateDirectoriesTests.cs Added test verifying immediate exception throwing on missing directory
Tests/Testably.Abstractions.Tests/FileSystem/Directory/EnumerateFilesTests.cs Added test verifying immediate exception throwing on missing directory
Tests/Testably.Abstractions.Tests/FileSystem/Directory/EnumerateFileSystemInfosTests.cs Added test verifying immediate exception throwing on missing directory
Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/DirectoryStatisticsTests.cs Updated enumeration statistics tests to create directories first
Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/DirectoryInfoStatisticsTests.cs Updated enumeration statistics tests to create directories first

@github-actions
Copy link

github-actions bot commented Sep 11, 2025

Test Results

    37 files  ±  0      37 suites  ±0   21m 32s ⏱️ +17s
44 291 tests +100  41 846 ✅ +84  2 429 💤 ±0   16 ❌ + 16 
86 016 runs  + 96  76 877 ✅  - 12  9 031 💤 ±0  108 ❌ +108 

For more details on these failures, see this check.

Results for commit eb0bb1a. ± Comparison against base commit 46eb8ff.

This pull request removes 36227 and adds 36327 tests. Note that renamed tests count towards both.
Testably.Abstractions.AccessControl.Tests.DirectoryInfoAclExtensionsTests+MockFileSystemTests ‑ SetAccessControl_ShouldNotUpdateTimes(path: "path3d74e8a1-44d2-4e06-b51e-2bb949ca78cb")
Testably.Abstractions.AccessControl.Tests.DirectoryInfoAclExtensionsTests+MockFileSystemTests ‑ SetAccessControl_ShouldNotUpdateTimes(path: "path6045e2f7-fdff-44b8-9f36-1255a04a9af2")
Testably.Abstractions.AccessControl.Tests.DirectoryInfoAclExtensionsTests+MockFileSystemTests ‑ SetAccessControl_ShouldNotUpdateTimes(path: "path6c8e8591-b949-45ee-a031-001b82380dba")
Testably.Abstractions.AccessControl.Tests.DirectoryInfoAclExtensionsTests+MockFileSystemTests ‑ SetAccessControl_ShouldNotUpdateTimes(path: "path74370bb0-a2a3-4635-a167-e2f64c9e2f92")
Testably.Abstractions.AccessControl.Tests.DirectoryInfoAclExtensionsTests+MockFileSystemTests ‑ SetAccessControl_ShouldNotUpdateTimes(path: "patha0ffe036-55ef-4fe2-9b0e-cbc771a99b9e")
Testably.Abstractions.AccessControl.Tests.DirectoryInfoAclExtensionsTests+MockFileSystemTests ‑ SetAccessControl_ShouldNotUpdateTimes(path: "patha2e45856-7c5c-494a-996e-28dfb5ba60fd")
Testably.Abstractions.AccessControl.Tests.DirectoryInfoAclExtensionsTests+MockFileSystemTests ‑ SetAccessControl_ShouldNotUpdateTimes(path: "pathded1f901-b9e0-4eec-b26e-2b7377d9ef6e")
Testably.Abstractions.AccessControl.Tests.DirectoryInfoAclExtensionsTests+RealFileSystemTests ‑ SetAccessControl_ShouldNotUpdateTimes(path: "path1c20e9d3-2bbf-4bbf-a463-bd81644df496")
Testably.Abstractions.AccessControl.Tests.DirectoryInfoAclExtensionsTests+RealFileSystemTests ‑ SetAccessControl_ShouldNotUpdateTimes(path: "path4244243d-25e9-48b1-b2f9-e9cd0452657d")
Testably.Abstractions.AccessControl.Tests.DirectoryInfoAclExtensionsTests+RealFileSystemTests ‑ SetAccessControl_ShouldNotUpdateTimes(path: "path5fe38fa5-7556-4170-9d1c-e2286ee1a0b1")
…
Testably.Abstractions.AccessControl.Tests.DirectoryInfoAclExtensionsTests+MockFileSystemTests ‑ SetAccessControl_ShouldNotUpdateTimes(path: "path00f09701-972b-44df-b959-6e007f4707c1")
Testably.Abstractions.AccessControl.Tests.DirectoryInfoAclExtensionsTests+MockFileSystemTests ‑ SetAccessControl_ShouldNotUpdateTimes(path: "path121e8962-b8e9-4faa-9cf9-4c759d5e8e67")
Testably.Abstractions.AccessControl.Tests.DirectoryInfoAclExtensionsTests+MockFileSystemTests ‑ SetAccessControl_ShouldNotUpdateTimes(path: "path43dc22c2-9992-414a-8c4f-4a2e85aee4a6")
Testably.Abstractions.AccessControl.Tests.DirectoryInfoAclExtensionsTests+MockFileSystemTests ‑ SetAccessControl_ShouldNotUpdateTimes(path: "path655d75cf-b048-46d5-9220-6d09149a7e48")
Testably.Abstractions.AccessControl.Tests.DirectoryInfoAclExtensionsTests+MockFileSystemTests ‑ SetAccessControl_ShouldNotUpdateTimes(path: "path97377b68-bf06-49b2-843e-58acccf2aa1e")
Testably.Abstractions.AccessControl.Tests.DirectoryInfoAclExtensionsTests+MockFileSystemTests ‑ SetAccessControl_ShouldNotUpdateTimes(path: "pathb8b8a60c-8ff7-4ee4-95d4-15de3335eaa2")
Testably.Abstractions.AccessControl.Tests.DirectoryInfoAclExtensionsTests+MockFileSystemTests ‑ SetAccessControl_ShouldNotUpdateTimes(path: "pathf64e8304-2419-4cba-8b42-7f2f7b75f758")
Testably.Abstractions.AccessControl.Tests.DirectoryInfoAclExtensionsTests+RealFileSystemTests ‑ SetAccessControl_ShouldNotUpdateTimes(path: "path150cc1fd-802c-48a0-9149-821a35d31f09")
Testably.Abstractions.AccessControl.Tests.DirectoryInfoAclExtensionsTests+RealFileSystemTests ‑ SetAccessControl_ShouldNotUpdateTimes(path: "path60c709c2-d6d2-4ccf-9999-446c46fb3210")
Testably.Abstractions.AccessControl.Tests.DirectoryInfoAclExtensionsTests+RealFileSystemTests ‑ SetAccessControl_ShouldNotUpdateTimes(path: "path68a9d03d-9f5f-4cd1-a3c5-85504e24bdf0")
…
This pull request removes 1784 skipped tests and adds 1784 skipped tests. Note that renamed tests count towards both.
Testably.Abstractions.AccessControl.Tests.DirectoryInfoAclExtensionsTests+MockFileSystemTests ‑ SetAccessControl_ShouldNotUpdateTimes(path: "path6045e2f7-fdff-44b8-9f36-1255a04a9af2")
Testably.Abstractions.AccessControl.Tests.DirectoryInfoAclExtensionsTests+MockFileSystemTests ‑ SetAccessControl_ShouldNotUpdateTimes(path: "path6c8e8591-b949-45ee-a031-001b82380dba")
Testably.Abstractions.AccessControl.Tests.DirectoryInfoAclExtensionsTests+MockFileSystemTests ‑ SetAccessControl_ShouldNotUpdateTimes(path: "patha0ffe036-55ef-4fe2-9b0e-cbc771a99b9e")
Testably.Abstractions.AccessControl.Tests.DirectoryInfoAclExtensionsTests+MockFileSystemTests ‑ SetAccessControl_ShouldNotUpdateTimes(path: "patha2e45856-7c5c-494a-996e-28dfb5ba60fd")
Testably.Abstractions.AccessControl.Tests.DirectoryInfoAclExtensionsTests+RealFileSystemTests ‑ SetAccessControl_ShouldNotUpdateTimes(path: "path1c20e9d3-2bbf-4bbf-a463-bd81644df496")
Testably.Abstractions.AccessControl.Tests.DirectoryInfoAclExtensionsTests+RealFileSystemTests ‑ SetAccessControl_ShouldNotUpdateTimes(path: "path4244243d-25e9-48b1-b2f9-e9cd0452657d")
Testably.Abstractions.AccessControl.Tests.DirectoryInfoAclExtensionsTests+RealFileSystemTests ‑ SetAccessControl_ShouldNotUpdateTimes(path: "path7d881316-54e8-47f6-9006-c455ed811b68")
Testably.Abstractions.AccessControl.Tests.DirectoryInfoAclExtensionsTests+RealFileSystemTests ‑ SetAccessControl_ShouldNotUpdateTimes(path: "pathbe5c81f7-111f-450e-8f7b-96067b2be1ac")
Testably.Abstractions.Testing.Tests.FileSystem.FileMockTests ‑ GetUnixFileMode_SafeFileHandle_ShouldThrowPlatformNotSupportedExceptionOnWindows(path: "path2632d599-b573-4475-abb3-d611b24607b8")
Testably.Abstractions.Testing.Tests.FileSystem.FileMockTests ‑ GetUnixFileMode_SafeFileHandle_ShouldThrowPlatformNotSupportedExceptionOnWindows(path: "path2b5f9bba-91c7-4578-95eb-072329253904")
…
Testably.Abstractions.AccessControl.Tests.DirectoryInfoAclExtensionsTests+MockFileSystemTests ‑ SetAccessControl_ShouldNotUpdateTimes(path: "path121e8962-b8e9-4faa-9cf9-4c759d5e8e67")
Testably.Abstractions.AccessControl.Tests.DirectoryInfoAclExtensionsTests+MockFileSystemTests ‑ SetAccessControl_ShouldNotUpdateTimes(path: "path97377b68-bf06-49b2-843e-58acccf2aa1e")
Testably.Abstractions.AccessControl.Tests.DirectoryInfoAclExtensionsTests+MockFileSystemTests ‑ SetAccessControl_ShouldNotUpdateTimes(path: "pathb8b8a60c-8ff7-4ee4-95d4-15de3335eaa2")
Testably.Abstractions.AccessControl.Tests.DirectoryInfoAclExtensionsTests+MockFileSystemTests ‑ SetAccessControl_ShouldNotUpdateTimes(path: "pathf64e8304-2419-4cba-8b42-7f2f7b75f758")
Testably.Abstractions.AccessControl.Tests.DirectoryInfoAclExtensionsTests+RealFileSystemTests ‑ SetAccessControl_ShouldNotUpdateTimes(path: "path150cc1fd-802c-48a0-9149-821a35d31f09")
Testably.Abstractions.AccessControl.Tests.DirectoryInfoAclExtensionsTests+RealFileSystemTests ‑ SetAccessControl_ShouldNotUpdateTimes(path: "path60c709c2-d6d2-4ccf-9999-446c46fb3210")
Testably.Abstractions.AccessControl.Tests.DirectoryInfoAclExtensionsTests+RealFileSystemTests ‑ SetAccessControl_ShouldNotUpdateTimes(path: "path68a9d03d-9f5f-4cd1-a3c5-85504e24bdf0")
Testably.Abstractions.AccessControl.Tests.DirectoryInfoAclExtensionsTests+RealFileSystemTests ‑ SetAccessControl_ShouldNotUpdateTimes(path: "path946087c3-0bc3-4a5c-83f0-41934c105ea2")
Testably.Abstractions.Testing.Tests.FileSystem.FileMockTests ‑ GetUnixFileMode_SafeFileHandle_ShouldThrowPlatformNotSupportedExceptionOnWindows(path: "path3e6e8fcb-e13d-484c-8029-cae6fae0fd7a")
Testably.Abstractions.Testing.Tests.FileSystem.FileMockTests ‑ GetUnixFileMode_SafeFileHandle_ShouldThrowPlatformNotSupportedExceptionOnWindows(path: "path533988a4-5891-45bc-8780-659a881ffcb1")
…

♻️ This comment has been updated with latest results.

@vbreuss vbreuss enabled auto-merge (squash) September 11, 2025 05:34
@sonarqubecloud
Copy link

sonarqubecloud bot commented Sep 11, 2025

@vbreuss vbreuss merged commit 7336a7a into main Sep 11, 2025
13 checks passed
@vbreuss vbreuss deleted the copilot/fix-856 branch September 11, 2025 06:00
@github-actions
Copy link

This is addressed in release v4.3.6.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DirectoryMock.EnumerateDirectories does not throw exceptions until enumerated

2 participants