Skip to content

Commit d2eca3b

Browse files
authored
Fix flaky FSW test (#103873)
* Fix flaky FSW test * smaller buffer * typo
1 parent 74aaa6a commit d2eca3b

File tree

1 file changed

+28
-11
lines changed

1 file changed

+28
-11
lines changed

src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.InternalBufferSize.cs

+28-11
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Collections.Generic;
45
using System.Threading;
6+
using System.Threading.Tasks;
57
using Xunit;
68

79
namespace System.IO.Tests
@@ -32,26 +34,32 @@ public class InternalBufferSizeTests : FileSystemWatcherTest
3234
// it's internal buffer (up to some limit). Our docs say that limit is 64KB but testing on Win8.1
3335
// indicates that it is much higher than this: I could grow the buffer up to 128 MB and still see
3436
// that it had an effect. The size needed per operation is determined by the struct layout of
35-
// FILE_NOTIFY_INFORMATION. This works out to 16 + 2 * (Path.GetFileName(file.Path).Length + 1) bytes, where filePath
37+
// FILE_NOTIFY_INFORMATION. This works out to 12 + 2 * (Path.GetFileName(file.Path).Length + 1) bytes, where filePath
3638
// is the path to changed file relative to the path passed into ReadDirectoryChanges.
3739

3840
// At some point we might decide to improve how FSW handles this at which point we'll need
3941
// a better test for Error (perhaps just a mock), but for now there is some value in forcing this limit.
42+
private const int ExcessEventsMultiplier = 200; // 100 does not reliably trigger the error
43+
4044
[Theory]
4145
[InlineData(true)]
4246
[InlineData(false)]
4347
[PlatformSpecific(TestPlatforms.Windows)] // Uses P/Invokes
48+
[OuterLoop("A little slow")]
4449
public void FileSystemWatcher_InternalBufferSize(bool setToHigherCapacity)
4550
{
4651
ManualResetEvent unblockHandler = new ManualResetEvent(false);
4752
string file = CreateTestFile(TestDirectory, "file");
4853
using (FileSystemWatcher watcher = CreateWatcher(TestDirectory, file, unblockHandler))
4954
{
50-
int internalBufferOperationCapacity = CalculateInternalBufferOperationCapacity(watcher.InternalBufferSize, file);
55+
watcher.InternalBufferSize = 4096; // Minimum
56+
int internalBufferOperationCapacity = watcher.InternalBufferSize / (12 + 2 * (Path.GetFileName(file).Length + 1));
5157

52-
// Set the capacity high to ensure no error events arise.
5358
if (setToHigherCapacity)
54-
watcher.InternalBufferSize = watcher.InternalBufferSize * 12;
59+
{
60+
// Set the capacity high to ensure no error events arise.
61+
watcher.InternalBufferSize = watcher.InternalBufferSize * ExcessEventsMultiplier * 2;
62+
}
5563

5664
Action action = GetAction(unblockHandler, internalBufferOperationCapacity, file);
5765
Action cleanup = GetCleanup(unblockHandler);
@@ -65,6 +73,7 @@ public void FileSystemWatcher_InternalBufferSize(bool setToHigherCapacity)
6573

6674
[Fact]
6775
[PlatformSpecific(TestPlatforms.Windows)]
76+
[OuterLoop("A little slow")]
6877
public void FileSystemWatcher_InternalBufferSize_SynchronizingObject()
6978
{
7079
ManualResetEvent unblockHandler = new ManualResetEvent(false);
@@ -74,7 +83,8 @@ public void FileSystemWatcher_InternalBufferSize_SynchronizingObject()
7483
TestISynchronizeInvoke invoker = new TestISynchronizeInvoke();
7584
watcher.SynchronizingObject = invoker;
7685

77-
int internalBufferOperationCapacity = CalculateInternalBufferOperationCapacity(watcher.InternalBufferSize, file);
86+
watcher.InternalBufferSize = 4096; // Minimum
87+
int internalBufferOperationCapacity = watcher.InternalBufferSize / (12 + 2 * (Path.GetFileName(file).Length + 1));
7888

7989
Action action = GetAction(unblockHandler, internalBufferOperationCapacity, file);
8090
Action cleanup = GetCleanup(unblockHandler);
@@ -96,23 +106,30 @@ private FileSystemWatcher CreateWatcher(string testDirectoryPath, string filePat
96106
return watcher;
97107
}
98108

99-
private int CalculateInternalBufferOperationCapacity(int internalBufferSize, string filePath) =>
100-
internalBufferSize / (17 + Path.GetFileName(filePath).Length);
101-
102109
private Action GetAction(ManualResetEvent unblockHandler, int internalBufferOperationCapacity, string filePath)
103110
{
104111
return () =>
105112
{
106-
// generate enough file change events to overflow the default buffer
107-
for (int i = 1; i < internalBufferOperationCapacity * 10; i++)
113+
List<Task> tasks = new();
114+
115+
// Generate enough file change events to overflow the default buffer
116+
// For speed, do this on multiple threads
117+
for (int i = 1; i < internalBufferOperationCapacity * ExcessEventsMultiplier; i++)
108118
{
109-
File.SetLastWriteTime(filePath, DateTime.Now + TimeSpan.FromSeconds(i));
119+
tasks.Add(Task.Run(() =>
120+
{
121+
// Each sets to a different time to ensure it triggers an event
122+
File.SetLastWriteTime(filePath, DateTime.Now + TimeSpan.FromSeconds(i));
123+
}));
110124
}
111125

126+
Task.WaitAll(tasks);
127+
112128
unblockHandler.Set();
113129
};
114130
}
115131

132+
116133
private Action GetCleanup(ManualResetEvent unblockHandler) => () => unblockHandler.Reset();
117134

118135
#endregion

0 commit comments

Comments
 (0)