Skip to content

Commit 3024b5a

Browse files
svickCopilot
andauthored
Skip flaky FileConfigurationProvider test on .NET Framework (#126874)
> [!NOTE] > This PR was AI/Copilot-generated. Fixes #126787 ## Problem The `ResolveFileProvider_WithMissingParentDirectory_WatchTokenFiresWhenFileCreated` test introduced in #126411 is flaky on the `net481-windows-Release-x86` CI leg, causing it to time out and block CI. ## Root cause `FileSystemWatcher` on .NET Framework drops directory creation events under I/O contention. The `PendingCreationWatcher` in `PhysicalFilesWatcher` relies on FSW to detect when a missing root directory is created. When the FSW event is lost, the change token never fires and the test hangs. This was verified by: 1. **Reproducing locally** — running the test in parallel (4-8 concurrent `dotnet test` processes) reproduces the hang on net481 x86 (~3% failure rate). 2. **Isolating to raw FSW** — a minimal test using only `FileSystemWatcher` (no `PendingCreationWatcher`) also fails under the same conditions on .NET Framework. 3. **Confirming .NET is unaffected** — 504 runs on net11.0 x86 with 8 concurrent threads produced 0 failures. All 7 CI failures in the issue report are on the same `net481-windows-Release-x86-NET481_Release-Windows.10.Amd64.Client.Open` configuration. ## Changes - **Skip on .NET Framework**: Changed `[Fact]` to `[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotNetFramework))]` since the test depends on FSW reliability that .NET Framework cannot guarantee under load. - **Improved timeout**: Replaced `CancellationTokenSource`-based timeout with `Task.WaitAsync(TimeSpan)` (via `TaskTimeoutExtensions` polyfill) and added `TaskCreationOptions.RunContinuationsAsynchronously` — matching the pattern used by all similar tests in `PhysicalFilesWatcherTests`. --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 1b31de7 commit 3024b5a

2 files changed

Lines changed: 7 additions & 7 deletions

File tree

src/libraries/Microsoft.Extensions.Configuration.FileExtensions/tests/FileConfigurationProviderTest.cs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using System;
55
using System.Collections.Generic;
66
using System.IO;
7-
using System.Threading;
87
using System.Threading.Tasks;
98
using Microsoft.Extensions.Configuration.Test;
109
using Microsoft.Extensions.FileProviders;
@@ -114,7 +113,8 @@ public void ProviderThrowsDirectoryNotFoundExceptionWhenNotFound(string physical
114113
Assert.Contains(physicalPath, exception.Message);
115114
}
116115

117-
[Fact]
116+
// FileSystemWatcher is unreliable under load on .NET Framework, making this test flaky
117+
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotNetFramework))]
118118
public async Task ResolveFileProvider_WithMissingParentDirectory_WatchTokenFiresWhenFileCreated()
119119
{
120120
// Verify the fix for https://github.com/dotnet/runtime/issues/116713:
@@ -150,18 +150,16 @@ public async Task ResolveFileProvider_WithMissingParentDirectory_WatchTokenFires
150150
Assert.NotNull(token);
151151

152152
// The token should fire only when the target file is created, not when just the directory appears.
153-
var tcs = new TaskCompletionSource<bool>();
154-
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(15));
155-
cts.Token.Register(() => tcs.TrySetCanceled());
156-
token.RegisterChangeCallback(_ => tcs.TrySetResult(true), null);
153+
var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
154+
using var changeCallbackRegistration = token.RegisterChangeCallback(_ => tcs.TrySetResult(true), null);
157155

158156
Directory.CreateDirectory(missingSubDir);
159157
await Task.Delay(500);
160158
Assert.False(tcs.Task.IsCompleted, "Token must not fire when only the directory is created.");
161159

162160
File.WriteAllText(configFilePath, "{}");
163161

164-
Assert.True(await tcs.Task, "Change token did not fire after the target file was created.");
162+
await tcs.Task.WaitAsync(TimeSpan.FromSeconds(30));
165163
}
166164

167165
public class FileInfoImpl : IFileInfo

src/libraries/Microsoft.Extensions.Configuration.FileExtensions/tests/Microsoft.Extensions.Configuration.FileExtensions.Tests.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
<ItemGroup>
99
<Compile Include="$(CommonTestPath)Extensions\ConfigurationRootTest.cs" Link="Common\Extensions\ConfigurationRootTest.cs" />
1010
<Compile Include="$(CommonTestPath)System\IO\TempDirectory.cs" Link="Common\System\IO\TempDirectory.cs" />
11+
<Compile Include="$(CommonTestPath)System\Threading\Tasks\TaskTimeoutExtensions.cs"
12+
Link="Common\System\Threading\Tasks\TaskTimeoutExtensions.cs" />
1113

1214
<TrimmerRootDescriptor Include="$(ILLinkDescriptorsPath)ILLink.Descriptors.Castle.xml" />
1315
</ItemGroup>

0 commit comments

Comments
 (0)