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 ;
45using System . Threading ;
6+ using System . Threading . Tasks ;
57using Xunit ;
68
79namespace 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