diff --git a/test/dotnet-watch.FunctionalTests/FileWatcherTests.cs b/test/dotnet-watch.FunctionalTests/FileWatcherTests.cs index 60fd501ca8..7cd4bd15aa 100644 --- a/test/dotnet-watch.FunctionalTests/FileWatcherTests.cs +++ b/test/dotnet-watch.FunctionalTests/FileWatcherTests.cs @@ -286,98 +286,63 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests { var filesChanged = new HashSet(); - void Clear() - { - _output.WriteLine("Clear files changed list"); - filesChanged.Clear(); - } - UsingTempDirectory(dir => { - using (var changedEv = new AutoResetEvent(false)) using (var watcher = FileWatcherFactory.CreateWatcher(dir, usePolling)) { - - EventHandler handler = null; - handler = (_, f) => - { - _output.WriteLine("File changed: " + f); - filesChanged.Add(f); - try - { - changedEv.Set(); - } - catch (ObjectDisposedException) - { - // There's a known race condition here: - // even though we tell the watcher to stop raising events and we unsubscribe the handler - // there might be in-flight events that will still process. Since we dispose the reset - // event, this code will fail if the handler executes after Dispose happens. There's no - // better way to guard against it than catch because we cannot check if the object is - // disposed nor can we check if there are any in-flight events. - // This is actually a known issue in the corefx file watcher. It can trigger multiple - // times for the same item. - } - }; - - watcher.OnFileChange += handler; watcher.EnableRaisingEvents = true; - // On Unix the file write time is in 1s increments; - // if we don't wait, there's a chance that the polling - // watcher will not detect the change - Thread.Sleep(1000); - - var testFileFullPath = Path.Combine(dir, "foo1"); - File.WriteAllText(testFileFullPath, string.Empty); - Assert.True(changedEv.WaitOne(DefaultTimeout)); - var fileChanged = Assert.Single(filesChanged); - Assert.Equal(testFileFullPath, fileChanged); - Clear(); - changedEv.Reset(); - - // On Unix the file write time is in 1s increments; - // if we don't wait, there's a chance that the polling - // watcher will not detect the change - Thread.Sleep(1000); - - testFileFullPath = Path.Combine(dir, "foo2"); - File.WriteAllText(testFileFullPath, string.Empty); - Assert.True(changedEv.WaitOne(DefaultTimeout)); - fileChanged = Assert.Single(filesChanged); - Assert.Equal(testFileFullPath, fileChanged); - Clear(); - changedEv.Reset(); - - // On Unix the file write time is in 1s increments; - // if we don't wait, there's a chance that the polling - // watcher will not detect the change - Thread.Sleep(1000); - - testFileFullPath = Path.Combine(dir, "foo3"); - File.WriteAllText(testFileFullPath, string.Empty); - Assert.True(changedEv.WaitOne(DefaultTimeout)); - fileChanged = Assert.Single(filesChanged); - Assert.Equal(testFileFullPath, fileChanged); - Clear(); - changedEv.Reset(); - - // On Unix the file write time is in 1s increments; - // if we don't wait, there's a chance that the polling - // watcher will not detect the change - Thread.Sleep(1000); - - File.WriteAllText(testFileFullPath, string.Empty); - Assert.True(changedEv.WaitOne(DefaultTimeout)); - fileChanged = Assert.Single(filesChanged); - Assert.Equal(testFileFullPath, fileChanged); + for (var i = 0; i < 5; i++) + { + AssertFileChangeRaisesEvent(dir, watcher); + } watcher.EnableRaisingEvents = false; - watcher.OnFileChange -= handler; } }); } + private void AssertFileChangeRaisesEvent(string directory, IFileSystemWatcher watcher) + { + var semaphoreSlim = new SemaphoreSlim(0); + var expectedPath = Path.Combine(directory, Path.GetRandomFileName()); + EventHandler handler = (object _, string f) => + { + _output.WriteLine("File changed: " + f); + try + { + if (string.Equals(f, expectedPath, StringComparison.OrdinalIgnoreCase)) + { + semaphoreSlim.Release(); + } + } + catch (ObjectDisposedException) + { + // There's a known race condition here: + // even though we tell the watcher to stop raising events and we unsubscribe the handler + // there might be in-flight events that will still process. Since we dispose the reset + // event, this code will fail if the handler executes after Dispose happens. + } + }; + + File.AppendAllText(expectedPath, " "); + + watcher.OnFileChange += handler; + try + { + // On Unix the file write time is in 1s increments; + // if we don't wait, there's a chance that the polling + // watcher will not detect the change + Thread.Sleep(1000); + File.AppendAllText(expectedPath, " "); + Assert.True(semaphoreSlim.Wait(DefaultTimeout), "Expected a file change event for " + expectedPath); + } + finally + { + watcher.OnFileChange -= handler; + } + } + [Theory] [InlineData(true)] [InlineData(false)]