From ca749828730215ca3504944b4a933d6784a40ae1 Mon Sep 17 00:00:00 2001 From: Dan Moseley Date: Thu, 19 Mar 2026 20:35:52 -0600 Subject: [PATCH 1/8] Improve FSW test robustness with progressive retry timeouts - Increase SubsequentExpectedWait from 10ms to 500ms so subsequent event-type checks in ExecuteAndVerifyEvents tolerate delayed delivery under thread pool starvation. - Add 50ms settling delay after EnableRaisingEvents=true in ExecuteAndVerifyEvents, ExpectEvents, and TryErrorEvent to allow OS-specific async startup to complete before the test action runs. - Implement progressive timeout on retries in ExpectEvent and TryErrorEvent: attempt 1 uses the base timeout (1000ms), attempt 2 uses 2x, attempt 3 uses 3x. This is the key fix: under thread pool starvation the ReadDirectoryChangesW callback can be delayed beyond the base timeout, but a fixed retry with the same timeout just fails again. Progressive timeout gives later attempts enough headroom. Validated with artificial callback delay injection at 900ms: - Baseline (no fixes): 114 event failures across 20 runs (100% failure rate) - With fixes: 0 failures across 20 runs on both NTFS and ReFS Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../tests/Utility/FileSystemWatcherTest.cs | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/Utility/FileSystemWatcherTest.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/Utility/FileSystemWatcherTest.cs index c4dca309f24231..761034f0d3f4ed 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/Utility/FileSystemWatcherTest.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/Utility/FileSystemWatcherTest.cs @@ -23,7 +23,7 @@ public abstract partial class FileSystemWatcherTest : FileCleanupTestBase // the entire timeout specified. public const int WaitForExpectedEventTimeout = 1000; // ms to wait for an event to happen public const int LongWaitTimeout = 50000; // ms to wait for an event that takes a longer time than the average operation - public const int SubsequentExpectedWait = 10; // ms to wait for checks that occur after the first. + public const int SubsequentExpectedWait = 500; // ms to wait for checks that occur after the first. public const int WaitForExpectedEventTimeout_NoRetry = 3000;// ms to wait for an event that isn't surrounded by a retry. public const int WaitForUnexpectedEventTimeout = 150; // ms to wait for a non-expected event. public const int DefaultAttemptsForExpectedEvent = 3; // Number of times an expected event should be retried if failing. @@ -195,7 +195,12 @@ public static void ExpectEvent(FileSystemWatcher watcher, WatcherChangeTypes exp Thread.Sleep(RetryDelayMilliseconds); } - result = ExecuteAndVerifyEvents(newWatcher, expectedEvents, action, attemptsCompleted == attempts, expectedPaths, timeout); + // Use progressively longer timeouts on retries. The first attempt uses the base timeout + // for fast failure in normal conditions. Subsequent attempts double the timeout to tolerate + // transient delays (thread pool starvation, slow CI machines, etc.). + int effectiveTimeout = timeout * attemptsCompleted; + + result = ExecuteAndVerifyEvents(newWatcher, expectedEvents, action, attemptsCompleted == attempts, expectedPaths, effectiveTimeout); if (cleanup != null) cleanup(); @@ -306,6 +311,11 @@ public static bool ExecuteAndVerifyEvents(FileSystemWatcher watcher, WatcherChan renamed = WatchRenamed(watcher, expectedPaths); watcher.EnableRaisingEvents = true; + + // Allow the OS-specific watcher implementation to finish async startup + // (e.g., ReadDirectoryChangesW registration on Windows, inotify thread on Linux). + Thread.Sleep(50); + action(); // Verify Changed @@ -430,8 +440,11 @@ public static bool TryErrorEvent(FileSystemWatcher watcher, Action action, Actio watcher.EnableRaisingEvents = true; } + // Allow the OS-specific watcher implementation to finish async startup. + Thread.Sleep(50); + action(); - result = errorOccurred.WaitOne(WaitForExpectedEventTimeout); + result = errorOccurred.WaitOne(WaitForExpectedEventTimeout * attemptsCompleted); watcher.EnableRaisingEvents = false; cleanup(); } @@ -529,6 +542,9 @@ internal static List ExpectEvents(FileSystemWatcher watcher, int exp bool raisingEvent = watcher.EnableRaisingEvents; watcher.EnableRaisingEvents = true; + // Allow the OS-specific watcher implementation to finish async startup. + Thread.Sleep(50); + try { action(); From 89ac0d12751bdf2e5d42aef39ceeb64bc63cd7bf Mon Sep 17 00:00:00 2001 From: Dan Moseley Date: Thu, 19 Mar 2026 21:29:24 -0600 Subject: [PATCH 2/8] Add missing cleanup to Create event tests for retry idempotency The MultipleFilters and ModifyFiltersConcurrentWithEvents Create tests pass cleanup: null to ExpectEvent, but the action (File.Create / Directory.CreateDirectory) is not idempotent: on retry the file/dir already exists, so no Created event fires and the retry always fails. The corresponding Delete tests already have proper cleanup (re-creating the deleted item between retries). Apply the same pattern in reverse: delete the created item between retries so the next attempt gets a fresh Created event. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../tests/FileSystemWatcher.unit.cs | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.unit.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.unit.cs index 7b60c4041eb872..ebc0547744e891 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.unit.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.unit.cs @@ -976,9 +976,9 @@ public void FileSystemWatcher_Directory_Create_MultipleFilters() watcher.Filters.Add(Path.GetFileName(directoryOne)); watcher.Filters.Add(Path.GetFileName(directoryTwo)); - ExpectEvent(watcher, WatcherChangeTypes.Created, () => Directory.CreateDirectory(directoryOne), cleanup: null, expectedPath: directoryOne); - ExpectEvent(watcher, WatcherChangeTypes.Created, () => Directory.CreateDirectory(directoryTwo), cleanup: null, expectedPath: directoryTwo); - ExpectNoEvent(watcher, WatcherChangeTypes.Created, () => Directory.CreateDirectory(directoryThree), cleanup: null, expectedPath: directoryThree); + ExpectEvent(watcher, WatcherChangeTypes.Created, () => Directory.CreateDirectory(directoryOne), cleanup: () => Directory.Delete(directoryOne), expectedPath: directoryOne); + ExpectEvent(watcher, WatcherChangeTypes.Created, () => Directory.CreateDirectory(directoryTwo), cleanup: () => Directory.Delete(directoryTwo), expectedPath: directoryTwo); + ExpectNoEvent(watcher, WatcherChangeTypes.Created, () => Directory.CreateDirectory(directoryThree), cleanup: () => { if (Directory.Exists(directoryThree)) Directory.Delete(directoryThree); }, expectedPath: directoryThree); } }, maxAttempts: DefaultAttemptsForExpectedEvent, backoffFunc: (iteration) => RetryDelayMilliseconds, retryWhen: e => e is XunitException); } @@ -1000,9 +1000,9 @@ public void FileSystemWatcher_Directory_Create_Filter_Ctor() { watcher.Filters.Add(Path.GetFileName(directoryTwo)); - ExpectEvent(watcher, WatcherChangeTypes.Created, () => Directory.CreateDirectory(directoryOne), cleanup: null, expectedPath: directoryOne); - ExpectEvent(watcher, WatcherChangeTypes.Created, () => Directory.CreateDirectory(directoryTwo), cleanup: null, expectedPath: directoryTwo); - ExpectNoEvent(watcher, WatcherChangeTypes.Created, () => Directory.CreateDirectory(directoryThree), cleanup: null, expectedPath: directoryThree); + ExpectEvent(watcher, WatcherChangeTypes.Created, () => Directory.CreateDirectory(directoryOne), cleanup: () => Directory.Delete(directoryOne), expectedPath: directoryOne); + ExpectEvent(watcher, WatcherChangeTypes.Created, () => Directory.CreateDirectory(directoryTwo), cleanup: () => Directory.Delete(directoryTwo), expectedPath: directoryTwo); + ExpectNoEvent(watcher, WatcherChangeTypes.Created, () => Directory.CreateDirectory(directoryThree), cleanup: () => { if (Directory.Exists(directoryThree)) Directory.Delete(directoryThree); }, expectedPath: directoryThree); } }, maxAttempts: DefaultAttemptsForExpectedEvent, backoffFunc: (iteration) => RetryDelayMilliseconds, retryWhen: e => e is XunitException); } @@ -1043,9 +1043,9 @@ public void FileSystemWatcher_File_Create_MultipleFilters() watcher.Filters.Add(fileOne.Name); watcher.Filters.Add(fileTwo.Name); - ExpectEvent(watcher, WatcherChangeTypes.Created, () => fileOne.Create().Dispose(), cleanup: null, expectedPath: fileOne.FullName); - ExpectEvent(watcher, WatcherChangeTypes.Created, () => fileTwo.Create().Dispose(), cleanup: null, expectedPath: fileTwo.FullName); - ExpectNoEvent(watcher, WatcherChangeTypes.Created, () => fileThree.Create().Dispose(), cleanup: null, expectedPath: fileThree.FullName); + ExpectEvent(watcher, WatcherChangeTypes.Created, () => fileOne.Create().Dispose(), cleanup: () => fileOne.Delete(), expectedPath: fileOne.FullName); + ExpectEvent(watcher, WatcherChangeTypes.Created, () => fileTwo.Create().Dispose(), cleanup: () => fileTwo.Delete(), expectedPath: fileTwo.FullName); + ExpectNoEvent(watcher, WatcherChangeTypes.Created, () => fileThree.Create().Dispose(), cleanup: () => fileThree.Delete(), expectedPath: fileThree.FullName); } }, maxAttempts: DefaultAttemptsForExpectedEvent, backoffFunc: (iteration) => RetryDelayMilliseconds, retryWhen: e => e is XunitException); } @@ -1079,9 +1079,9 @@ public void FileSystemWatcher_ModifyFiltersConcurrentWithEvents() thread.IsBackground = true; thread.Start(); - ExpectEvent(watcher, WatcherChangeTypes.Created, () => fileOne.Create().Dispose(), cleanup: null, expectedPath: fileOne.FullName); - ExpectEvent(watcher, WatcherChangeTypes.Created, () => fileTwo.Create().Dispose(), cleanup: null, expectedPath: fileTwo.FullName); - ExpectNoEvent(watcher, WatcherChangeTypes.Created, () => fileThree.Create().Dispose(), cleanup: null, expectedPath: fileThree.FullName); + ExpectEvent(watcher, WatcherChangeTypes.Created, () => fileOne.Create().Dispose(), cleanup: () => fileOne.Delete(), expectedPath: fileOne.FullName); + ExpectEvent(watcher, WatcherChangeTypes.Created, () => fileTwo.Create().Dispose(), cleanup: () => fileTwo.Delete(), expectedPath: fileTwo.FullName); + ExpectNoEvent(watcher, WatcherChangeTypes.Created, () => fileThree.Create().Dispose(), cleanup: () => fileThree.Delete(), expectedPath: fileThree.FullName); cts.Cancel(); waitForThread(); From d77ef1ffd7a1bef1e1e769eaba58f7479a3744fb Mon Sep 17 00:00:00 2001 From: Dan Moseley Date: Thu, 19 Mar 2026 22:02:21 -0600 Subject: [PATCH 3/8] Increase FSW test timeouts for better stress tolerance - WaitForExpectedEventTimeout: 1000ms -> 2000ms. With progressive retry this gives 2000/4000/6000ms across 3 attempts, enough headroom for thread pool starvation even on single-core CI machines. - WaitForExpectedEventTimeout_NoRetry: 3000ms -> 5000ms. Tests using the simple ExpectEvent(WaitHandle, string) overload have no retry loop, so a generous single timeout is needed. - ExpectEvents() collection timeout: 5s -> 10s. This method has no retry mechanism and is used by Directory/File Move multi-event tests. These increases only affect the failure path (WaitOne returns immediately when the event arrives). Happy-path execution time is unchanged. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../tests/Utility/FileSystemWatcherTest.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/Utility/FileSystemWatcherTest.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/Utility/FileSystemWatcherTest.cs index 761034f0d3f4ed..0a8aae26f24ebb 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/Utility/FileSystemWatcherTest.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/Utility/FileSystemWatcherTest.cs @@ -21,10 +21,10 @@ public abstract partial class FileSystemWatcherTest : FileCleanupTestBase // going to fail the test. If we don't expect an event to occur, then we need // to keep the timeout short, as in a successful run we'll end up waiting for // the entire timeout specified. - public const int WaitForExpectedEventTimeout = 1000; // ms to wait for an event to happen + public const int WaitForExpectedEventTimeout = 2000; // ms to wait for an event to happen public const int LongWaitTimeout = 50000; // ms to wait for an event that takes a longer time than the average operation public const int SubsequentExpectedWait = 500; // ms to wait for checks that occur after the first. - public const int WaitForExpectedEventTimeout_NoRetry = 3000;// ms to wait for an event that isn't surrounded by a retry. + public const int WaitForExpectedEventTimeout_NoRetry = 5000;// ms to wait for an event that isn't surrounded by a retry. public const int WaitForUnexpectedEventTimeout = 150; // ms to wait for a non-expected event. public const int DefaultAttemptsForExpectedEvent = 3; // Number of times an expected event should be retried if failing. public const int DefaultAttemptsForUnExpectedEvent = 2; // Number of times an unexpected event should be retried if failing. @@ -548,7 +548,7 @@ internal static List ExpectEvents(FileSystemWatcher watcher, int exp try { action(); - eventsOccurred.WaitOne(new TimeSpan(0, 0, 5)); + eventsOccurred.WaitOne(new TimeSpan(0, 0, 10)); } finally { From 6b6e11800dd120bb54fd74bcc5bf8a530efa6e8b Mon Sep 17 00:00:00 2001 From: Dan Moseley Date: Thu, 19 Mar 2026 22:17:27 -0600 Subject: [PATCH 4/8] Re-enable FSW tests previously disabled for Windows flakiness MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove [ActiveIssue] annotations for dotnet/runtime#103584 from 14 test files (22 annotations total, including class-level disabling of entire test classes like Directory_Create_Tests, File_Create_Tests, etc.). Also remove [ActiveIssue] for dotnet/runtime#53366 on FileSystemWatcher_DirectorySymbolicLink_TargetsFile_Fails — the issue has had zero hits since 2021 and the test is Windows-only. The robustness improvements in the preceding commits (progressive retry timeouts, increased base timeouts, retry idempotency cleanup) should prevent the flakiness that originally motivated disabling these tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../tests/FileSystemWatcher.Directory.Changed.cs | 1 - .../tests/FileSystemWatcher.Directory.Create.cs | 1 - .../tests/FileSystemWatcher.Directory.Delete.cs | 1 - .../tests/FileSystemWatcher.Directory.Move.cs | 1 - .../tests/FileSystemWatcher.Directory.NotifyFilter.cs | 1 - .../tests/FileSystemWatcher.File.Changed.cs | 1 - .../tests/FileSystemWatcher.File.Create.cs | 1 - .../tests/FileSystemWatcher.File.Delete.cs | 1 - .../tests/FileSystemWatcher.File.Move.cs | 1 - .../tests/FileSystemWatcher.File.NotifyFilter.cs | 1 - .../tests/FileSystemWatcher.InternalBufferSize.cs | 1 - .../tests/FileSystemWatcher.SymbolicLink.cs | 4 ++-- .../tests/FileSystemWatcher.cs | 1 - .../tests/FileSystemWatcher.unit.cs | 11 ----------- 14 files changed, 2 insertions(+), 25 deletions(-) diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Changed.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Changed.cs index e89158982d92c7..bef5def6c97bcb 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Changed.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Changed.cs @@ -7,7 +7,6 @@ namespace System.IO.Tests { - [ActiveIssue("https://github.com/dotnet/runtime/issues/103584", TestPlatforms.Windows)] public class Directory_Changed_Tests : FileSystemWatcherTest { [Fact] diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Create.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Create.cs index d3777d6aaf6a7e..023cc679eb32b3 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Create.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Create.cs @@ -6,7 +6,6 @@ namespace System.IO.Tests { - [ActiveIssue("https://github.com/dotnet/runtime/issues/103584", TestPlatforms.Windows)] public class Directory_Create_Tests : FileSystemWatcherTest { [Fact] diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Delete.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Delete.cs index 638eda424c3ed8..a30e355acccf69 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Delete.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Delete.cs @@ -7,7 +7,6 @@ namespace System.IO.Tests { - [ActiveIssue("https://github.com/dotnet/runtime/issues/103584", TestPlatforms.Windows)] public class Directory_Delete_Tests : FileSystemWatcherTest { [Fact] diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Move.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Move.cs index da63b362e08aac..4d1640733e2b6c 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Move.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Move.cs @@ -9,7 +9,6 @@ namespace System.IO.Tests { - [ActiveIssue("https://github.com/dotnet/runtime/issues/103584", TestPlatforms.Windows)] public class Directory_Move_Tests : FileSystemWatcherTest { [Fact] diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.NotifyFilter.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.NotifyFilter.cs index cf0677e01634be..2a692ad6056cb6 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.NotifyFilter.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.NotifyFilter.cs @@ -9,7 +9,6 @@ namespace System.IO.Tests { - [ActiveIssue("https://github.com/dotnet/runtime/issues/103584", TestPlatforms.Windows)] public partial class Directory_NotifyFilter_Tests : FileSystemWatcherTest { [LibraryImport("advapi32.dll", EntryPoint = "SetNamedSecurityInfoW", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Changed.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Changed.cs index db3c4043ee1f02..44b41c3778e201 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Changed.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Changed.cs @@ -6,7 +6,6 @@ namespace System.IO.Tests { - [ActiveIssue("https://github.com/dotnet/runtime/issues/103584", TestPlatforms.Windows)] public class File_Changed_Tests : FileSystemWatcherTest { [Fact] diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Create.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Create.cs index b02f07ecd046cd..5c36caf0a38fd1 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Create.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Create.cs @@ -10,7 +10,6 @@ namespace System.IO.Tests { - [ActiveIssue("https://github.com/dotnet/runtime/issues/103584", TestPlatforms.Windows)] public class File_Create_Tests : FileSystemWatcherTest { [Fact] diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Delete.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Delete.cs index 0a3b3ed1a3d100..ac076e4fd2202b 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Delete.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Delete.cs @@ -8,7 +8,6 @@ namespace System.IO.Tests { - [ActiveIssue("https://github.com/dotnet/runtime/issues/103584", TestPlatforms.Windows)] public class File_Delete_Tests : FileSystemWatcherTest { [Fact] diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Move.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Move.cs index 428dd0a47f7ecd..2a2a26a5767571 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Move.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Move.cs @@ -8,7 +8,6 @@ namespace System.IO.Tests { - [ActiveIssue("https://github.com/dotnet/runtime/issues/103584", TestPlatforms.Windows)] public class File_Move_Tests : FileSystemWatcherTest { [Fact] diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.NotifyFilter.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.NotifyFilter.cs index 74f48d1402f95d..80c37d1b8a061f 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.NotifyFilter.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.NotifyFilter.cs @@ -11,7 +11,6 @@ namespace System.IO.Tests { - [ActiveIssue("https://github.com/dotnet/runtime/issues/103584", TestPlatforms.Windows)] public partial class File_NotifyFilter_Tests : FileSystemWatcherTest { [LibraryImport("advapi32.dll", EntryPoint = "SetNamedSecurityInfoW", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.InternalBufferSize.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.InternalBufferSize.cs index 07a3e3c24bb91c..6df52a9395793e 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.InternalBufferSize.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.InternalBufferSize.cs @@ -8,7 +8,6 @@ namespace System.IO.Tests { - [ActiveIssue("https://github.com/dotnet/runtime/issues/103584", TestPlatforms.Windows)] public class InternalBufferSizeTests : FileSystemWatcherTest { // FSW works by calling ReadDirectoryChanges asynchronously, processing the changes diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.SymbolicLink.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.SymbolicLink.cs index a0874f6b3afdb7..258a123acea01c 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.SymbolicLink.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.SymbolicLink.cs @@ -58,7 +58,7 @@ public void FileSystemWatcher_DirectorySymbolicLink_TargetsSelf_Fails() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/103584", TestPlatforms.Windows)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/53366", TestPlatforms.Windows)] public void FileSystemWatcher_SymbolicLink_TargetsDirectory_Create() { // Arrange @@ -123,7 +123,7 @@ public void FileSystemWatcher_SymbolicLink_TargetsDirectory_Create_IncludeSubdir [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/70450", TestPlatforms.OSX)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/103584", TestPlatforms.Windows)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/53366", TestPlatforms.Windows)] public void FileSystemWatcher_SymbolicLink_IncludeSubdirectories_DoNotDereferenceChildLink() { // Arrange diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.cs index f2525543ce2ec4..9f0dce0d70a47f 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.cs @@ -170,7 +170,6 @@ public void BeginInit_PausesEnableRaisingEvents() [Theory] [InlineData(true)] [InlineData(false)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/103584", TestPlatforms.Windows)] public void EndInit_ResumesPausedEnableRaisingEvents(bool setBeforeBeginInit) { FileSystemWatcherTest.Execute(() => diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.unit.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.unit.cs index ebc0547744e891..4bc7bd08fb4e7f 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.unit.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.unit.cs @@ -258,7 +258,6 @@ public void FileSystemWatcher_IncludeSubdirectories() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/103584", TestPlatforms.Windows)] public void FileSystemWatcher_InternalBufferSize() { FileSystemWatcher watcher = new FileSystemWatcher(); @@ -363,7 +362,6 @@ public void FileSystemWatcher_OnCreated() [Fact] [PlatformSpecific(TestPlatforms.OSX | TestPlatforms.Windows)] // Casing matters on Linux - [ActiveIssue("https://github.com/dotnet/runtime/issues/103584", TestPlatforms.Windows)] public void FileSystemWatcher_OnCreatedWithMismatchedCasingGivesExpectedFullPath() { using (var fsw = new FileSystemWatcher(TestDirectory)) @@ -456,7 +454,6 @@ public void FileSystemWatcher_OnRenamed() [Fact] [PlatformSpecific(TestPlatforms.Windows)] // Unix FSW don't trigger on a file rename. - [ActiveIssue("https://github.com/dotnet/runtime/issues/103584", TestPlatforms.Windows)] public void FileSystemWatcher_Windows_OnRenameGivesExpectedFullPath() { string file = CreateTestFile(TestDirectory, "file"); @@ -570,7 +567,6 @@ public void FileSystemWatcher_StopCalledOnBackgroundThreadDoesNotDeadlock() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/103584", TestPlatforms.Windows)] public void FileSystemWatcher_WatchingAliasedFolderResolvesToRealPathWhenWatching() { string dir = CreateTestDirectory(TestDirectory, "dir"); @@ -934,7 +930,6 @@ public void SetAndGetFiltersProperty() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/103584", TestPlatforms.Windows)] public void FileSystemWatcher_File_Delete_MultipleFilters() { // Check delete events against multiple filters @@ -959,7 +954,6 @@ public void FileSystemWatcher_File_Delete_MultipleFilters() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/103584", TestPlatforms.Windows)] public void FileSystemWatcher_Directory_Create_MultipleFilters() { FileSystemWatcherTest.Execute(() => @@ -984,7 +978,6 @@ public void FileSystemWatcher_Directory_Create_MultipleFilters() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/103584", TestPlatforms.Windows)] public void FileSystemWatcher_Directory_Create_Filter_Ctor() { FileSystemWatcherTest.Execute(() => @@ -1008,7 +1001,6 @@ public void FileSystemWatcher_Directory_Create_Filter_Ctor() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/103584", TestPlatforms.Windows)] public void FileSystemWatcher_Directory_Delete_MultipleFilters() { using var tempDir = new TempDirectory(); @@ -1028,7 +1020,6 @@ public void FileSystemWatcher_Directory_Delete_MultipleFilters() } [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/103584", TestPlatforms.Windows)] public void FileSystemWatcher_File_Create_MultipleFilters() { FileSystemWatcherTest.Execute(() => @@ -1051,7 +1042,6 @@ public void FileSystemWatcher_File_Create_MultipleFilters() } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsMultithreadingSupported))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/103584", TestPlatforms.Windows)] public void FileSystemWatcher_ModifyFiltersConcurrentWithEvents() { FileSystemWatcherTest.Execute(() => @@ -1103,7 +1093,6 @@ public DangerousFileSystemWatcherTests(ITestOutputHelper output) [PlatformSpecific(TestPlatforms.Linux)] // Reads MaxUsersWatches from Linux OS files [OuterLoop("This test will use all available watchers and can cause failures in other concurrent tests or system processes.")] [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/103584", TestPlatforms.Windows)] public void FileSystemWatcher_CreateManyConcurrentWatches() { int maxUserWatches = int.Parse(File.ReadAllText("/proc/sys/fs/inotify/max_user_watches")); From 3eedebfd96647d38f95dc8a8ada89b00559e882d Mon Sep 17 00:00:00 2001 From: Dan Moseley Date: Thu, 19 Mar 2026 22:52:02 -0600 Subject: [PATCH 5/8] Fix ExpectNoEvent to use WaitForUnexpectedEventTimeout ExpectNoEvent defaulted to WaitForExpectedEventTimeout (2000ms) but negative tests should use the shorter WaitForUnexpectedEventTimeout (150ms). The codebase already defines this constant for exactly this purpose but it was never wired up. Using the long timeout slows down every test that verifies an event does NOT occur. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../tests/Utility/FileSystemWatcherTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/Utility/FileSystemWatcherTest.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/Utility/FileSystemWatcherTest.cs index 0a8aae26f24ebb..c800594e6d3a57 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/Utility/FileSystemWatcherTest.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/Utility/FileSystemWatcherTest.cs @@ -277,7 +277,7 @@ public static void Execute(Action test, int maxAttempts = 5, Func back /// The Action that will trigger events. /// Optional. Undoes the action and cleans up the watcher so the test may be run again if necessary. /// Optional. Adds path verification to all expected events. - public static void ExpectNoEvent(FileSystemWatcher watcher, WatcherChangeTypes unExpectedEvents, Action action, Action cleanup = null, string expectedPath = null, int timeout = WaitForExpectedEventTimeout) + public static void ExpectNoEvent(FileSystemWatcher watcher, WatcherChangeTypes unExpectedEvents, Action action, Action cleanup = null, string expectedPath = null, int timeout = WaitForUnexpectedEventTimeout) { bool result = ExecuteAndVerifyEvents(watcher, unExpectedEvents, action, false, expectedPath == null ? null : new string[] { expectedPath }, timeout); Assert.False(result, "Expected Event occurred"); From be3820dcd5e1bf0d49b54a465deaff4b33d66926 Mon Sep 17 00:00:00 2001 From: Dan Moseley Date: Fri, 20 Mar 2026 07:59:04 -0600 Subject: [PATCH 6/8] Address review: fix comment wording and wrap cleanup in finally - Fix comment to say 'linearly' instead of 'double' for progressive timeout - Wrap cleanup in try/finally so it runs even if ExecuteAndVerifyEvents throws Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../tests/Utility/FileSystemWatcherTest.cs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/Utility/FileSystemWatcherTest.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/Utility/FileSystemWatcherTest.cs index c800594e6d3a57..af94db8702c6e9 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/Utility/FileSystemWatcherTest.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/Utility/FileSystemWatcherTest.cs @@ -196,14 +196,18 @@ public static void ExpectEvent(FileSystemWatcher watcher, WatcherChangeTypes exp } // Use progressively longer timeouts on retries. The first attempt uses the base timeout - // for fast failure in normal conditions. Subsequent attempts double the timeout to tolerate - // transient delays (thread pool starvation, slow CI machines, etc.). + // for fast failure in normal conditions. Subsequent attempts increase the timeout linearly + // with the attempt count to tolerate transient delays (thread pool starvation, slow CI machines, etc.). int effectiveTimeout = timeout * attemptsCompleted; - result = ExecuteAndVerifyEvents(newWatcher, expectedEvents, action, attemptsCompleted == attempts, expectedPaths, effectiveTimeout); - - if (cleanup != null) - cleanup(); + try + { + result = ExecuteAndVerifyEvents(newWatcher, expectedEvents, action, attemptsCompleted == attempts, expectedPaths, effectiveTimeout); + } + finally + { + cleanup?.Invoke(); + } } } From 3249e98cd62bea8e2acd4c66b8e0ab61cb493f95 Mon Sep 17 00:00:00 2001 From: Dan Moseley Date: Fri, 20 Mar 2026 15:04:25 -0600 Subject: [PATCH 7/8] Increase timeouts for CI resilience, add cancellation-aware retry helper - Increase WaitForExpectedEventTimeout to 5s, SubsequentExpectedWait to 2s, WaitForExpectedEventTimeout_NoRetry to 15s, WaitForUnexpectedEventTimeout to 500ms (all WaitOne-based, so zero happy-path cost) - Remove Thread.Sleep(50) settle delays (no startup race on any platform) - Add WaitUnlessCancelled helper: sleeps via CancellationToken.WaitHandle.WaitOne and throws on cancellation, preparing for xunit v3 migration - Wrap ExpectNoEvent cleanup in try/finally for exception safety Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../tests/Utility/FileSystemWatcherTest.cs | 51 ++++++++++--------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/Utility/FileSystemWatcherTest.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/Utility/FileSystemWatcherTest.cs index af94db8702c6e9..8bc123d0dce8df 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/Utility/FileSystemWatcherTest.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/Utility/FileSystemWatcherTest.cs @@ -21,15 +21,25 @@ public abstract partial class FileSystemWatcherTest : FileCleanupTestBase // going to fail the test. If we don't expect an event to occur, then we need // to keep the timeout short, as in a successful run we'll end up waiting for // the entire timeout specified. - public const int WaitForExpectedEventTimeout = 2000; // ms to wait for an event to happen + public const int WaitForExpectedEventTimeout = 5000; // ms to wait for an event to happen public const int LongWaitTimeout = 50000; // ms to wait for an event that takes a longer time than the average operation - public const int SubsequentExpectedWait = 500; // ms to wait for checks that occur after the first. - public const int WaitForExpectedEventTimeout_NoRetry = 5000;// ms to wait for an event that isn't surrounded by a retry. - public const int WaitForUnexpectedEventTimeout = 150; // ms to wait for a non-expected event. + public const int SubsequentExpectedWait = 2000; // ms to wait for checks that occur after the first. + public const int WaitForExpectedEventTimeout_NoRetry = 15000;// ms to wait for an event that isn't surrounded by a retry. + public const int WaitForUnexpectedEventTimeout = 500; // ms to wait for a non-expected event. public const int DefaultAttemptsForExpectedEvent = 3; // Number of times an expected event should be retried if failing. public const int DefaultAttemptsForUnExpectedEvent = 2; // Number of times an unexpected event should be retried if failing. public const int RetryDelayMilliseconds = 500; // ms to wait when retrying after failure + // Waits for the specified duration unless the test runner signals cancellation, + // in which case it throws OperationCanceledException to fail the test immediately. + private static void WaitUnlessCancelled(int milliseconds) + { + // Replace with TestContext.Current.CancellationToken when migrated to xunit v3 (#125019). + CancellationToken token = CancellationToken.None; + token.WaitHandle.WaitOne(milliseconds); + token.ThrowIfCancellationRequested(); + } + /// /// Watches the Changed WatcherChangeType and unblocks the returned AutoResetEvent when a /// Changed event is thrown by the watcher. @@ -192,7 +202,7 @@ public static void ExpectEvent(FileSystemWatcher watcher, WatcherChangeTypes exp // Most intermittent failures in FSW are caused by either a shortage of resources (e.g. inotify instances) // or by insufficient time to execute (e.g. CI gets bogged down). Immediately re-running a failed test // won't resolve the first issue, so we wait a little while hoping that things clear up for the next run. - Thread.Sleep(RetryDelayMilliseconds); + WaitUnlessCancelled(RetryDelayMilliseconds); } // Use progressively longer timeouts on retries. The first attempt uses the base timeout @@ -268,7 +278,7 @@ public static void Execute(Action test, int maxAttempts = 5, Func back Debug.WriteLine($"RetryHelper: retrying {testName} {i}th time of {maxAttempts}: got {lastException.Message}"); } - Thread.Sleep((backoffFunc ?? s_defaultBackoffFunc)(i)); + WaitUnlessCancelled((backoffFunc ?? s_defaultBackoffFunc)(i)); } } @@ -283,11 +293,15 @@ public static void Execute(Action test, int maxAttempts = 5, Func back /// Optional. Adds path verification to all expected events. public static void ExpectNoEvent(FileSystemWatcher watcher, WatcherChangeTypes unExpectedEvents, Action action, Action cleanup = null, string expectedPath = null, int timeout = WaitForUnexpectedEventTimeout) { - bool result = ExecuteAndVerifyEvents(watcher, unExpectedEvents, action, false, expectedPath == null ? null : new string[] { expectedPath }, timeout); - Assert.False(result, "Expected Event occurred"); - - if (cleanup != null) - cleanup(); + try + { + bool result = ExecuteAndVerifyEvents(watcher, unExpectedEvents, action, false, expectedPath == null ? null : new string[] { expectedPath }, timeout); + Assert.False(result, "Expected Event occurred"); + } + finally + { + cleanup?.Invoke(); + } } /// @@ -315,11 +329,6 @@ public static bool ExecuteAndVerifyEvents(FileSystemWatcher watcher, WatcherChan renamed = WatchRenamed(watcher, expectedPaths); watcher.EnableRaisingEvents = true; - - // Allow the OS-specific watcher implementation to finish async startup - // (e.g., ReadDirectoryChangesW registration on Windows, inotify thread on Linux). - Thread.Sleep(50); - action(); // Verify Changed @@ -418,7 +427,7 @@ public static bool TryErrorEvent(FileSystemWatcher watcher, Action action, Actio // Most intermittent failures in FSW are caused by either a shortage of resources (e.g. inotify instances) // or by insufficient time to execute (e.g. CI gets bogged down). Immediately re-running a failed test // won't resolve the first issue, so we wait a little while hoping that things clear up for the next run. - Thread.Sleep(500); + WaitUnlessCancelled(500); } AutoResetEvent errorOccurred = new AutoResetEvent(false); @@ -444,9 +453,6 @@ public static bool TryErrorEvent(FileSystemWatcher watcher, Action action, Actio watcher.EnableRaisingEvents = true; } - // Allow the OS-specific watcher implementation to finish async startup. - Thread.Sleep(50); - action(); result = errorOccurred.WaitOne(WaitForExpectedEventTimeout * attemptsCompleted); watcher.EnableRaisingEvents = false; @@ -546,13 +552,10 @@ internal static List ExpectEvents(FileSystemWatcher watcher, int exp bool raisingEvent = watcher.EnableRaisingEvents; watcher.EnableRaisingEvents = true; - // Allow the OS-specific watcher implementation to finish async startup. - Thread.Sleep(50); - try { action(); - eventsOccurred.WaitOne(new TimeSpan(0, 0, 10)); + eventsOccurred.WaitOne(new TimeSpan(0, 0, 15)); } finally { From 439457189c617b7de8a126d78c3ad53ed7844302 Mon Sep 17 00:00:00 2001 From: Dan Moseley Date: Fri, 20 Mar 2026 17:48:15 -0600 Subject: [PATCH 8/8] Address review: add cleanup guards, use timeout constant in ExpectEvents - Add Directory.Exists guards to cleanup lambdas in Directory_Create tests - Replace hardcoded TimeSpan(0,0,15) with WaitForExpectedEventTimeout_NoRetry Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../tests/FileSystemWatcher.unit.cs | 8 ++++---- .../tests/Utility/FileSystemWatcherTest.cs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.unit.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.unit.cs index 4bc7bd08fb4e7f..b5109503e4b6cb 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.unit.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.unit.cs @@ -970,8 +970,8 @@ public void FileSystemWatcher_Directory_Create_MultipleFilters() watcher.Filters.Add(Path.GetFileName(directoryOne)); watcher.Filters.Add(Path.GetFileName(directoryTwo)); - ExpectEvent(watcher, WatcherChangeTypes.Created, () => Directory.CreateDirectory(directoryOne), cleanup: () => Directory.Delete(directoryOne), expectedPath: directoryOne); - ExpectEvent(watcher, WatcherChangeTypes.Created, () => Directory.CreateDirectory(directoryTwo), cleanup: () => Directory.Delete(directoryTwo), expectedPath: directoryTwo); + ExpectEvent(watcher, WatcherChangeTypes.Created, () => Directory.CreateDirectory(directoryOne), cleanup: () => { if (Directory.Exists(directoryOne)) Directory.Delete(directoryOne); }, expectedPath: directoryOne); + ExpectEvent(watcher, WatcherChangeTypes.Created, () => Directory.CreateDirectory(directoryTwo), cleanup: () => { if (Directory.Exists(directoryTwo)) Directory.Delete(directoryTwo); }, expectedPath: directoryTwo); ExpectNoEvent(watcher, WatcherChangeTypes.Created, () => Directory.CreateDirectory(directoryThree), cleanup: () => { if (Directory.Exists(directoryThree)) Directory.Delete(directoryThree); }, expectedPath: directoryThree); } }, maxAttempts: DefaultAttemptsForExpectedEvent, backoffFunc: (iteration) => RetryDelayMilliseconds, retryWhen: e => e is XunitException); @@ -993,8 +993,8 @@ public void FileSystemWatcher_Directory_Create_Filter_Ctor() { watcher.Filters.Add(Path.GetFileName(directoryTwo)); - ExpectEvent(watcher, WatcherChangeTypes.Created, () => Directory.CreateDirectory(directoryOne), cleanup: () => Directory.Delete(directoryOne), expectedPath: directoryOne); - ExpectEvent(watcher, WatcherChangeTypes.Created, () => Directory.CreateDirectory(directoryTwo), cleanup: () => Directory.Delete(directoryTwo), expectedPath: directoryTwo); + ExpectEvent(watcher, WatcherChangeTypes.Created, () => Directory.CreateDirectory(directoryOne), cleanup: () => { if (Directory.Exists(directoryOne)) Directory.Delete(directoryOne); }, expectedPath: directoryOne); + ExpectEvent(watcher, WatcherChangeTypes.Created, () => Directory.CreateDirectory(directoryTwo), cleanup: () => { if (Directory.Exists(directoryTwo)) Directory.Delete(directoryTwo); }, expectedPath: directoryTwo); ExpectNoEvent(watcher, WatcherChangeTypes.Created, () => Directory.CreateDirectory(directoryThree), cleanup: () => { if (Directory.Exists(directoryThree)) Directory.Delete(directoryThree); }, expectedPath: directoryThree); } }, maxAttempts: DefaultAttemptsForExpectedEvent, backoffFunc: (iteration) => RetryDelayMilliseconds, retryWhen: e => e is XunitException); diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/Utility/FileSystemWatcherTest.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/Utility/FileSystemWatcherTest.cs index 8bc123d0dce8df..dffe75be379228 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/Utility/FileSystemWatcherTest.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/Utility/FileSystemWatcherTest.cs @@ -555,7 +555,7 @@ internal static List ExpectEvents(FileSystemWatcher watcher, int exp try { action(); - eventsOccurred.WaitOne(new TimeSpan(0, 0, 15)); + eventsOccurred.WaitOne(WaitForExpectedEventTimeout_NoRetry); } finally {