From 10becfb62b92e342cffb7567ead9281d42d5e108 Mon Sep 17 00:00:00 2001 From: Tomas Weinfurt Date: Wed, 25 Sep 2019 14:30:10 -0700 Subject: [PATCH] improve stability of FileSystemWatcher tests (dotnet/corefx#40543) * improve stability of watcher tests * feedback from review * more feedback * disable parallelization for dangerous tests * enable FileSystemWatcher_File_Create_ForceLoopRestart again * feedback from review Commit migrated from https://github.com/dotnet/corefx/commit/e8fd8931d488d2e06a25a49c0d722b67f35e10fa --- .../tests/FileSystemWatcher.MultipleWatchers.cs | 465 +++++++++++---------- .../tests/FileSystemWatcher.unit.cs | 78 ++-- .../tests/Utility/FileSystemWatcherTest.cs | 3 + 3 files changed, 286 insertions(+), 260 deletions(-) diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.MultipleWatchers.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.MultipleWatchers.cs index 37ed4c1..a322cd3 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.MultipleWatchers.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.MultipleWatchers.cs @@ -5,303 +5,318 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; + using Xunit; +using Xunit.Abstractions; namespace System.IO.Tests { public class FileSystemWatcher_Multiple_Test : FileSystemWatcherTest { + private readonly ITestOutputHelper _output; + + private void OnError(object source, ErrorEventArgs e) + { + string msg = $"Watcher failed: {e.GetException()} source={source} {source.GetHashCode()}"; + _output.WriteLine(msg); + // Repeat on Console so it easier to triage in CI. + Console.WriteLine(msg); + } + + public FileSystemWatcher_Multiple_Test(ITestOutputHelper output) + { + _output = output; + } + [OuterLoop] [Fact] public void FileSystemWatcher_File_Create_ExecutionContextFlowed() { - ExecuteWithRetry(() => + using (var watcher1 = new FileSystemWatcher(TestDirectory)) + using (var watcher2 = new FileSystemWatcher(TestDirectory)) { - using (var watcher1 = new FileSystemWatcher(TestDirectory)) - using (var watcher2 = new FileSystemWatcher(TestDirectory)) - { - string fileName = Path.Combine(TestDirectory, "file"); - watcher1.Filter = Path.GetFileName(fileName); - watcher2.Filter = Path.GetFileName(fileName); + string fileName = Path.Combine(TestDirectory, "file"); + watcher1.Filter = Path.GetFileName(fileName); + watcher2.Filter = Path.GetFileName(fileName); - var local = new AsyncLocal(); + var local = new AsyncLocal(); - var tcs1 = new TaskCompletionSource(); - var tcs2 = new TaskCompletionSource(); - watcher1.Created += (s, e) => tcs1.SetResult(local.Value); - watcher2.Created += (s, e) => tcs2.SetResult(local.Value); + var tcs1 = new TaskCompletionSource(); + var tcs2 = new TaskCompletionSource(); + watcher1.Created += (s, e) => tcs1.SetResult(local.Value); + watcher2.Created += (s, e) => tcs2.SetResult(local.Value); - local.Value = 42; - watcher1.EnableRaisingEvents = true; - local.Value = 84; - watcher2.EnableRaisingEvents = true; - local.Value = 168; + watcher1.Error += OnError; + watcher2.Error += OnError; - File.Create(fileName).Dispose(); - Task.WaitAll(new[] { tcs1.Task, tcs2.Task }, WaitForExpectedEventTimeout); + local.Value = 42; + watcher1.EnableRaisingEvents = true; + local.Value = 84; + watcher2.EnableRaisingEvents = true; + local.Value = 168; - Assert.Equal(42, tcs1.Task.Result); - Assert.Equal(84, tcs2.Task.Result); - } - }); + File.Create(fileName).Dispose(); + Task.WaitAll(new[] { tcs1.Task, tcs2.Task }, WaitForExpectedEventTimeout); + + Assert.Equal(42, tcs1.Task.Result); + Assert.Equal(84, tcs2.Task.Result); + } } [OuterLoop] [Fact] public void FileSystemWatcher_File_Create_SuppressedExecutionContextHandled() { - ExecuteWithRetry(() => + using (var watcher1 = new FileSystemWatcher(TestDirectory)) { - using (var watcher1 = new FileSystemWatcher(TestDirectory)) - { - string fileName = Path.Combine(TestDirectory, "file"); - watcher1.Filter = Path.GetFileName(fileName); + string fileName = Path.Combine(TestDirectory, "FileSystemWatcher_File_Create_SuppressedExecutionContextHandled"); + watcher1.Filter = Path.GetFileName(fileName); + watcher1.Error += OnError; - var local = new AsyncLocal(); + var local = new AsyncLocal(); - var tcs1 = new TaskCompletionSource(); - watcher1.Created += (s, e) => tcs1.SetResult(local.Value); + var tcs1 = new TaskCompletionSource(); + watcher1.Created += (s, e) => tcs1.SetResult(local.Value); - local.Value = 42; + local.Value = 42; - ExecutionContext.SuppressFlow(); - try - { - watcher1.EnableRaisingEvents = true; - } - finally - { - ExecutionContext.RestoreFlow(); - } + ExecutionContext.SuppressFlow(); + try + { + watcher1.EnableRaisingEvents = true; + } + finally + { + ExecutionContext.RestoreFlow(); + } File.Create(fileName).Dispose(); tcs1.Task.Wait(WaitForExpectedEventTimeout); Assert.Equal(0, tcs1.Task.Result); - } - }); + } } [OuterLoop] [Fact] public void FileSystemWatcher_File_Create_NotAffectEachOther() { - ExecuteWithRetry(() => + using (var watcher1 = new FileSystemWatcher(TestDirectory)) + using (var watcher2 = new FileSystemWatcher(TestDirectory)) + using (var watcher3 = new FileSystemWatcher(TestDirectory)) { - using (var watcher1 = new FileSystemWatcher(TestDirectory)) - using (var watcher2 = new FileSystemWatcher(TestDirectory)) - using (var watcher3 = new FileSystemWatcher(TestDirectory)) - { - string fileName = Path.Combine(TestDirectory, "file"); - watcher1.Filter = Path.GetFileName(fileName); - watcher2.Filter = Path.GetFileName(fileName); - watcher3.Filter = Path.GetFileName(fileName); + string fileName = Path.Combine(TestDirectory, "FileSystemWatcher_File_Create_NotAffectEachOther"); + watcher1.Filter = Path.GetFileName(fileName); + watcher2.Filter = Path.GetFileName(fileName); + watcher3.Filter = Path.GetFileName(fileName); - AutoResetEvent autoResetEvent1 = WatchCreated(watcher1, new[] { fileName }).EventOccured; - AutoResetEvent autoResetEvent2 = WatchCreated(watcher2, new[] { fileName }).EventOccured; - AutoResetEvent autoResetEvent3 = WatchCreated(watcher3, new[] { fileName }).EventOccured; + watcher1.Error += OnError; + watcher2.Error += OnError; + watcher3.Error += OnError; - watcher1.EnableRaisingEvents = true; - watcher2.EnableRaisingEvents = true; - watcher3.EnableRaisingEvents = true; + AutoResetEvent autoResetEvent1 = WatchCreated(watcher1, new[] { fileName }).EventOccured; + AutoResetEvent autoResetEvent2 = WatchCreated(watcher2, new[] { fileName }).EventOccured; + AutoResetEvent autoResetEvent3 = WatchCreated(watcher3, new[] { fileName }).EventOccured; - File.Create(fileName).Dispose(); - Assert.True(WaitHandle.WaitAll(new[] { autoResetEvent1, autoResetEvent2, autoResetEvent3 }, WaitForExpectedEventTimeout_NoRetry)); + watcher1.EnableRaisingEvents = true; + watcher2.EnableRaisingEvents = true; + watcher3.EnableRaisingEvents = true; - File.Delete(fileName); - watcher1.EnableRaisingEvents = false; + File.Create(fileName).Dispose(); + Assert.True(WaitHandle.WaitAll(new[] { autoResetEvent1, autoResetEvent2, autoResetEvent3 }, WaitForExpectedEventTimeout_NoRetry)); - File.Create(fileName).Dispose(); - Assert.False(autoResetEvent1.WaitOne(WaitForUnexpectedEventTimeout)); - Assert.True(WaitHandle.WaitAll(new[] { autoResetEvent2, autoResetEvent3 }, WaitForExpectedEventTimeout_NoRetry)); - } - }); + File.Delete(fileName); + watcher1.EnableRaisingEvents = false; + + File.Create(fileName).Dispose(); + Assert.False(autoResetEvent1.WaitOne(WaitForUnexpectedEventTimeout)); + Assert.True(WaitHandle.WaitAll(new[] { autoResetEvent2, autoResetEvent3 }, WaitForExpectedEventTimeout_NoRetry)); + } } [OuterLoop] [Fact] public void FileSystemWatcher_File_Create_WatchOwnPath() { - ExecuteWithRetry(() => + using (var dir = new TempDirectory(GetTestFilePath())) + using (var dir1 = new TempDirectory(Path.Combine(dir.Path, "dir1"))) + using (var dir2 = new TempDirectory(Path.Combine(dir.Path, "dir2"))) + using (var watcher1 = new FileSystemWatcher(dir1.Path, "*")) + using (var watcher2 = new FileSystemWatcher(dir2.Path, "*")) { - using (var dir = new TempDirectory(GetTestFilePath())) - using (var dir1 = new TempDirectory(Path.Combine(dir.Path, "dir1"))) - using (var dir2 = new TempDirectory(Path.Combine(dir.Path, "dir2"))) - using (var watcher1 = new FileSystemWatcher(dir1.Path, "*")) - using (var watcher2 = new FileSystemWatcher(dir2.Path, "*")) - { - string fileName1 = Path.Combine(dir1.Path, "file"); - string fileName2 = Path.Combine(dir2.Path, "file"); + watcher1.Error += OnError; + watcher2.Error += OnError; - AutoResetEvent autoResetEvent1 = WatchCreated(watcher1, new[] { fileName1 }).EventOccured; - AutoResetEvent autoResetEvent2 = WatchCreated(watcher2, new[] { fileName2 }).EventOccured; + string fileName1 = Path.Combine(dir1.Path, "file"); + string fileName2 = Path.Combine(dir2.Path, "file"); - watcher1.EnableRaisingEvents = true; - watcher2.EnableRaisingEvents = true; + AutoResetEvent autoResetEvent1 = WatchCreated(watcher1, new[] { fileName1 }).EventOccured; + AutoResetEvent autoResetEvent2 = WatchCreated(watcher2, new[] { fileName2 }).EventOccured; - File.Create(fileName1).Dispose(); - Assert.True(autoResetEvent1.WaitOne(WaitForExpectedEventTimeout_NoRetry)); - Assert.False(autoResetEvent2.WaitOne(WaitForUnexpectedEventTimeout)); + watcher1.EnableRaisingEvents = true; + watcher2.EnableRaisingEvents = true; - File.Create(fileName2).Dispose(); - Assert.True(autoResetEvent2.WaitOne(WaitForExpectedEventTimeout_NoRetry)); - Assert.False(autoResetEvent1.WaitOne(WaitForUnexpectedEventTimeout)); - } - }); + File.Create(fileName1).Dispose(); + Assert.True(autoResetEvent1.WaitOne(WaitForExpectedEventTimeout_NoRetry)); + Assert.False(autoResetEvent2.WaitOne(WaitForUnexpectedEventTimeout)); + + File.Create(fileName2).Dispose(); + + Assert.True(autoResetEvent2.WaitOne(WaitForExpectedEventTimeout_NoRetry)); + Assert.False(autoResetEvent1.WaitOne(WaitForUnexpectedEventTimeout)); + } } - [ActiveIssue(34361, TestPlatforms.Linux)] [OuterLoop] [Theory] [InlineData(true)] [InlineData(false)] public void FileSystemWatcher_File_Create_ForceLoopRestart(bool useExistingWatchers) { - ExecuteWithRetry(() => + FileSystemWatcher[] watchers = new FileSystemWatcher[64]; + FileSystemWatcher[] watchers1 = new FileSystemWatcher[64]; + + try { - FileSystemWatcher[] watchers = new FileSystemWatcher[64]; - FileSystemWatcher[] watchers1 = new FileSystemWatcher[64]; + string fileName = Path.Combine(TestDirectory, "FileSystemWatcher_File_Create_ForceLoopRestart"); + AutoResetEvent[] autoResetEvents = new AutoResetEvent[64]; + for (var i = 0; i < watchers.Length; i++) + { + watchers[i] = new FileSystemWatcher(TestDirectory); + watchers[i].Filter = Path.GetFileName(fileName); + autoResetEvents[i] = WatchCreated(watchers[i], new[] { fileName }).EventOccured; + watchers[i].EnableRaisingEvents = true; + } - try + File.Create(fileName).Dispose(); + Assert.True(WaitHandle.WaitAll(autoResetEvents, WaitForExpectedEventTimeout_NoRetry)); + + File.Delete(fileName); + for (var i = 0; i < watchers.Length; i++) + { + watchers[i].EnableRaisingEvents = false; + } + + File.Create(fileName).Dispose(); + Assert.False(WaitHandle.WaitAll(autoResetEvents, WaitForUnexpectedEventTimeout)); + + File.Delete(fileName); + + if (useExistingWatchers) { - string fileName = Path.Combine(TestDirectory, "file"); - AutoResetEvent[] autoResetEvents = new AutoResetEvent[64]; for (var i = 0; i < watchers.Length; i++) { - watchers[i] = new FileSystemWatcher(TestDirectory); - watchers[i].Filter = Path.GetFileName(fileName); - autoResetEvents[i] = WatchCreated(watchers[i], new[] { fileName }).EventOccured; watchers[i].EnableRaisingEvents = true; } File.Create(fileName).Dispose(); Assert.True(WaitHandle.WaitAll(autoResetEvents, WaitForExpectedEventTimeout_NoRetry)); - - File.Delete(fileName); - for (var i = 0; i < watchers.Length; i++) + } + else + { + AutoResetEvent[] autoResetEvents1 = new AutoResetEvent[64]; + for (var i = 0; i < watchers1.Length; i++) { - watchers[i].EnableRaisingEvents = false; + watchers1[i] = new FileSystemWatcher(TestDirectory); + watchers1[i].Filter = Path.GetFileName(fileName); + autoResetEvents1[i] = WatchCreated(watchers1[i], new[] { fileName }).EventOccured; + watchers1[i].EnableRaisingEvents = true; } File.Create(fileName).Dispose(); - Assert.False(WaitHandle.WaitAll(autoResetEvents, WaitForUnexpectedEventTimeout)); - - File.Delete(fileName); - - if (useExistingWatchers) - { - for (var i = 0; i < watchers.Length; i++) - { - watchers[i].EnableRaisingEvents = true; - } - - File.Create(fileName).Dispose(); - Assert.True(WaitHandle.WaitAll(autoResetEvents, WaitForExpectedEventTimeout_NoRetry)); - } - else - { - AutoResetEvent[] autoResetEvents1 = new AutoResetEvent[64]; - for (var i = 0; i < watchers1.Length; i++) - { - watchers1[i] = new FileSystemWatcher(TestDirectory); - watchers1[i].Filter = Path.GetFileName(fileName); - autoResetEvents1[i] = WatchCreated(watchers1[i], new[] { fileName }).EventOccured; - watchers1[i].EnableRaisingEvents = true; - } - - File.Create(fileName).Dispose(); - Assert.True(WaitHandle.WaitAll(autoResetEvents1, WaitForExpectedEventTimeout_NoRetry)); - } + Assert.True(WaitHandle.WaitAll(autoResetEvents1, WaitForExpectedEventTimeout_NoRetry)); } - finally + } + finally + { + for (var i = 0; i < watchers.Length; i++) { - for (var i = 0; i < watchers.Length; i++) - { - watchers[i]?.Dispose(); - watchers1[i]?.Dispose(); - } + watchers[i]?.Dispose(); + watchers1[i]?.Dispose(); } - }); + } } [OuterLoop] [Fact] public void FileSystemWatcher_File_Changed_NotAffectEachOther() { - ExecuteWithRetry(() => + using (var testDirectory = new TempDirectory(GetTestFilePath())) + using (var file = new TempFile(Path.Combine(testDirectory.Path, "file"))) + using (var otherFile = new TempFile(Path.Combine(testDirectory.Path, "otherfile"))) + using (var watcher1 = new FileSystemWatcher(testDirectory.Path, Path.GetFileName(file.Path))) + using (var watcher2 = new FileSystemWatcher(testDirectory.Path, Path.GetFileName(file.Path))) + using (var watcher3 = new FileSystemWatcher(testDirectory.Path, Path.GetFileName(otherFile.Path))) { - using (var testDirectory = new TempDirectory(GetTestFilePath())) - using (var file = new TempFile(Path.Combine(testDirectory.Path, "file"))) - using (var otherFile = new TempFile(Path.Combine(testDirectory.Path, "otherfile"))) - using (var watcher1 = new FileSystemWatcher(testDirectory.Path, Path.GetFileName(file.Path))) - using (var watcher2 = new FileSystemWatcher(testDirectory.Path, Path.GetFileName(file.Path))) - using (var watcher3 = new FileSystemWatcher(testDirectory.Path, Path.GetFileName(otherFile.Path))) - { - AutoResetEvent autoResetEvent1 = WatchChanged(watcher1, new[] { Path.Combine(testDirectory.Path, "file") }).EventOccured; - AutoResetEvent autoResetEvent2 = WatchChanged(watcher2, new[] { Path.Combine(testDirectory.Path, "file") }).EventOccured; - AutoResetEvent autoResetEvent3 = WatchChanged(watcher3, new[] { Path.Combine(testDirectory.Path, "otherfile") }).EventOccured; + AutoResetEvent autoResetEvent1 = WatchChanged(watcher1, new[] { Path.Combine(testDirectory.Path, "file") }).EventOccured; + AutoResetEvent autoResetEvent2 = WatchChanged(watcher2, new[] { Path.Combine(testDirectory.Path, "file") }).EventOccured; + AutoResetEvent autoResetEvent3 = WatchChanged(watcher3, new[] { Path.Combine(testDirectory.Path, "otherfile") }).EventOccured; - watcher1.EnableRaisingEvents = true; - watcher2.EnableRaisingEvents = true; - watcher3.EnableRaisingEvents = true; + watcher1.Error += OnError; + watcher2.Error += OnError; + watcher3.Error += OnError; - Directory.SetLastWriteTime(file.Path, DateTime.Now + TimeSpan.FromSeconds(10)); - Assert.True(WaitHandle.WaitAll(new[] { autoResetEvent1, autoResetEvent2 }, WaitForExpectedEventTimeout_NoRetry)); - Assert.False(autoResetEvent3.WaitOne(WaitForUnexpectedEventTimeout)); + watcher1.EnableRaisingEvents = true; + watcher2.EnableRaisingEvents = true; + watcher3.EnableRaisingEvents = true; - Directory.SetLastWriteTime(otherFile.Path, DateTime.Now + TimeSpan.FromSeconds(10)); - Assert.False(WaitHandle.WaitAll(new[] { autoResetEvent1, autoResetEvent2 }, WaitForUnexpectedEventTimeout)); - Assert.True(autoResetEvent3.WaitOne(WaitForExpectedEventTimeout_NoRetry)); + Directory.SetLastWriteTime(file.Path, DateTime.Now + TimeSpan.FromSeconds(10)); + Assert.True(WaitHandle.WaitAll(new[] { autoResetEvent1, autoResetEvent2 }, WaitForExpectedEventTimeout_NoRetry)); + Assert.False(autoResetEvent3.WaitOne(WaitForUnexpectedEventTimeout)); - watcher1.EnableRaisingEvents = false; + Directory.SetLastWriteTime(otherFile.Path, DateTime.Now + TimeSpan.FromSeconds(10)); + Assert.False(WaitHandle.WaitAll(new[] { autoResetEvent1, autoResetEvent2 }, WaitForUnexpectedEventTimeout)); + Assert.True(autoResetEvent3.WaitOne(WaitForExpectedEventTimeout_NoRetry)); - Directory.SetLastWriteTime(file.Path, DateTime.Now + TimeSpan.FromSeconds(10)); - Assert.False(WaitHandle.WaitAll(new[] { autoResetEvent1, autoResetEvent3 }, WaitForUnexpectedEventTimeout)); - Assert.True(autoResetEvent2.WaitOne(WaitForExpectedEventTimeout_NoRetry)); + watcher1.EnableRaisingEvents = false; - Directory.SetLastWriteTime(otherFile.Path, DateTime.Now + TimeSpan.FromSeconds(10)); - Assert.False(WaitHandle.WaitAll(new[] { autoResetEvent1, autoResetEvent2 }, WaitForUnexpectedEventTimeout)); - Assert.True(autoResetEvent3.WaitOne(WaitForExpectedEventTimeout_NoRetry)); - } - }); + Directory.SetLastWriteTime(file.Path, DateTime.Now + TimeSpan.FromSeconds(10)); + Assert.False(WaitHandle.WaitAll(new[] { autoResetEvent1, autoResetEvent3 }, WaitForUnexpectedEventTimeout)); + Assert.True(autoResetEvent2.WaitOne(WaitForExpectedEventTimeout_NoRetry)); + + Directory.SetLastWriteTime(otherFile.Path, DateTime.Now + TimeSpan.FromSeconds(10)); + Assert.False(WaitHandle.WaitAll(new[] { autoResetEvent1, autoResetEvent2 }, WaitForUnexpectedEventTimeout)); + Assert.True(autoResetEvent3.WaitOne(WaitForExpectedEventTimeout_NoRetry)); + } } [OuterLoop] [Fact] public void FileSystemWatcher_File_Delet_NotAffectEachOther() { - ExecuteWithRetry(() => + using (var watcher1 = new FileSystemWatcher(TestDirectory)) + using (var watcher2 = new FileSystemWatcher(TestDirectory)) + using (var watcher3 = new FileSystemWatcher(TestDirectory)) { - using (var watcher1 = new FileSystemWatcher(TestDirectory)) - using (var watcher2 = new FileSystemWatcher(TestDirectory)) - using (var watcher3 = new FileSystemWatcher(TestDirectory)) - { - string fileName = Path.Combine(TestDirectory, "file"); - File.Create(fileName).Dispose(); + string fileName = Path.Combine(TestDirectory, "file"); + File.Create(fileName).Dispose(); - watcher1.Filter = Path.GetFileName(fileName); - watcher2.Filter = Path.GetFileName(fileName); - watcher3.Filter = Path.GetFileName(fileName); + watcher1.Filter = Path.GetFileName(fileName); + watcher2.Filter = Path.GetFileName(fileName); + watcher3.Filter = Path.GetFileName(fileName); - AutoResetEvent autoResetEvent1 = WatchDeleted(watcher1, new[] { fileName }).EventOccured; - AutoResetEvent autoResetEvent2 = WatchDeleted(watcher2, new[] { fileName }).EventOccured; - AutoResetEvent autoResetEvent3 = WatchDeleted(watcher3, new[] { fileName }).EventOccured; + watcher1.Error += OnError; + watcher2.Error += OnError; + watcher3.Error += OnError; - watcher1.EnableRaisingEvents = true; - watcher2.EnableRaisingEvents = true; - watcher3.EnableRaisingEvents = true; + AutoResetEvent autoResetEvent1 = WatchDeleted(watcher1, new[] { fileName }).EventOccured; + AutoResetEvent autoResetEvent2 = WatchDeleted(watcher2, new[] { fileName }).EventOccured; + AutoResetEvent autoResetEvent3 = WatchDeleted(watcher3, new[] { fileName }).EventOccured; - File.Delete(fileName); - Assert.True(WaitHandle.WaitAll(new[] { autoResetEvent1, autoResetEvent2, autoResetEvent3 }, WaitForExpectedEventTimeout_NoRetry)); + watcher1.EnableRaisingEvents = true; + watcher2.EnableRaisingEvents = true; + watcher3.EnableRaisingEvents = true; - File.Create(fileName).Dispose(); - watcher1.EnableRaisingEvents = false; + File.Delete(fileName); + Assert.True(WaitHandle.WaitAll(new[] { autoResetEvent1, autoResetEvent2, autoResetEvent3 }, WaitForExpectedEventTimeout_NoRetry)); - File.Delete(fileName); - Assert.False(autoResetEvent1.WaitOne(WaitForUnexpectedEventTimeout)); - Assert.True(WaitHandle.WaitAll(new[] { autoResetEvent2, autoResetEvent3 }, WaitForExpectedEventTimeout_NoRetry)); - } - }); + File.Create(fileName).Dispose(); + watcher1.EnableRaisingEvents = false; + + File.Delete(fileName); + Assert.False(autoResetEvent1.WaitOne(WaitForUnexpectedEventTimeout)); + Assert.True(WaitHandle.WaitAll(new[] { autoResetEvent2, autoResetEvent3 }, WaitForExpectedEventTimeout_NoRetry)); + } } [OuterLoop] @@ -309,41 +324,41 @@ namespace System.IO.Tests [PlatformSpecific(TestPlatforms.OSX)] public void FileSystemWatcher_File_Rename_NotAffectEachOther() { - ExecuteWithRetry(() => + using (var testDirectory = new TempDirectory(GetTestFilePath())) + using (var file = new TempFile(Path.Combine(testDirectory.Path, "file"))) + using (var watcher1 = new FileSystemWatcher(testDirectory.Path, Path.GetFileName(file.Path))) + using (var watcher2 = new FileSystemWatcher(testDirectory.Path, Path.GetFileName(file.Path))) { - using (var testDirectory = new TempDirectory(GetTestFilePath())) - using (var file = new TempFile(Path.Combine(testDirectory.Path, "file"))) - using (var watcher1 = new FileSystemWatcher(testDirectory.Path, Path.GetFileName(file.Path))) - using (var watcher2 = new FileSystemWatcher(testDirectory.Path, Path.GetFileName(file.Path))) - { - AutoResetEvent autoResetEvent1_created = WatchCreated(watcher1, new[] { Path.Combine(testDirectory.Path, "file") }).EventOccured; - AutoResetEvent autoResetEvent1_deleted = WatchDeleted(watcher1, new[] { Path.Combine(testDirectory.Path, "file") }).EventOccured; - AutoResetEvent autoResetEvent2_created = WatchCreated(watcher2, new[] { Path.Combine(testDirectory.Path, "file") }).EventOccured; - AutoResetEvent autoResetEvent2_deleted = WatchDeleted(watcher2, new[] { Path.Combine(testDirectory.Path, "file") }).EventOccured; - - watcher1.EnableRaisingEvents = true; - watcher2.EnableRaisingEvents = true; - - string filePath = file.Path; - string filePathRenamed = file.Path + "_renamed"; - - File.Move(filePath, filePathRenamed); - Assert.True(WaitHandle.WaitAll( - new[] { autoResetEvent1_created, autoResetEvent1_deleted, autoResetEvent2_created, autoResetEvent2_deleted }, - WaitForExpectedEventTimeout_NoRetry)); - - File.Move(filePathRenamed, filePath); - watcher1.EnableRaisingEvents = false; - - File.Move(filePath, filePathRenamed); - Assert.False(WaitHandle.WaitAll( - new[] { autoResetEvent1_created, autoResetEvent1_deleted }, - WaitForUnexpectedEventTimeout)); - Assert.True(WaitHandle.WaitAll( - new[] { autoResetEvent2_created, autoResetEvent2_deleted }, - WaitForExpectedEventTimeout_NoRetry)); - } - }); + AutoResetEvent autoResetEvent1_created = WatchCreated(watcher1, new[] { Path.Combine(testDirectory.Path, "file") }).EventOccured; + AutoResetEvent autoResetEvent1_deleted = WatchDeleted(watcher1, new[] { Path.Combine(testDirectory.Path, "file") }).EventOccured; + AutoResetEvent autoResetEvent2_created = WatchCreated(watcher2, new[] { Path.Combine(testDirectory.Path, "file") }).EventOccured; + AutoResetEvent autoResetEvent2_deleted = WatchDeleted(watcher2, new[] { Path.Combine(testDirectory.Path, "file") }).EventOccured; + + watcher1.Error += OnError; + watcher2.Error += OnError; + + watcher1.EnableRaisingEvents = true; + watcher2.EnableRaisingEvents = true; + + string filePath = file.Path; + string filePathRenamed = file.Path + "_renamed"; + + File.Move(filePath, filePathRenamed); + Assert.True(WaitHandle.WaitAll( + new[] { autoResetEvent1_created, autoResetEvent1_deleted, autoResetEvent2_created, autoResetEvent2_deleted }, + WaitForExpectedEventTimeout_NoRetry)); + + File.Move(filePathRenamed, filePath); + watcher1.EnableRaisingEvents = false; + + File.Move(filePath, filePathRenamed); + Assert.False(WaitHandle.WaitAll( + new[] { autoResetEvent1_created, autoResetEvent1_deleted }, + WaitForUnexpectedEventTimeout)); + Assert.True(WaitHandle.WaitAll( + new[] { autoResetEvent2_created, autoResetEvent2_deleted }, + WaitForExpectedEventTimeout_NoRetry)); + } } } } 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 0e9b25c..2dd0613 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.unit.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.unit.cs @@ -7,7 +7,11 @@ using System.Linq; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; + +using Microsoft.DotNet.XUnitExtensions; + using Xunit; +using Xunit.Abstractions; namespace System.IO.Tests { @@ -550,41 +554,6 @@ namespace System.IO.Tests } [Fact] - [PlatformSpecific(TestPlatforms.Linux)] // Reads MaxUsersWatches from Linux OS files - [OuterLoop("This test has high system resource demands and may cause failures in other concurrent tests")] - public void FileSystemWatcher_CreateManyConcurrentWatches() - { - int maxUserWatches = int.Parse(File.ReadAllText("/proc/sys/fs/inotify/max_user_watches")); - - using (var dir = new TempDirectory(GetTestFilePath())) - using (var watcher = new FileSystemWatcher(dir.Path) { IncludeSubdirectories = true, NotifyFilter = NotifyFilters.FileName }) - { - Action action = () => - { - // Create enough directories to exceed the number of allowed watches - for (int i = 0; i <= maxUserWatches; i++) - { - Directory.CreateDirectory(Path.Combine(dir.Path, i.ToString())); - } - }; - Action cleanup = () => - { - for (int i = 0; i <= maxUserWatches; i++) - { - Directory.Delete(Path.Combine(dir.Path, i.ToString())); - } - }; - - ExpectError(watcher, action, cleanup); - - // Make sure existing watches still work even after we've had one or more failures - Action createAction = () => File.WriteAllText(Path.Combine(dir.Path, Path.GetRandomFileName()), "text"); - Action createCleanup = () => File.Delete(Path.Combine(dir.Path, Path.GetRandomFileName())); - ExpectEvent(watcher, WatcherChangeTypes.Created, createAction, createCleanup); - } - } - - [Fact] public void FileSystemWatcher_StopCalledOnBackgroundThreadDoesNotDeadlock() { // Check the case where Stop or Dispose (they do the same thing) is called from @@ -1117,4 +1086,43 @@ namespace System.IO.Tests } } } + + [Collection("NoParallelTests")] + public class DangerousFileSystemWatcherTests : FileSystemWatcherTest + { + [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] + public void FileSystemWatcher_CreateManyConcurrentWatches() + { + int maxUserWatches = int.Parse(File.ReadAllText("/proc/sys/fs/inotify/max_user_watches")); + + using (var dir = new TempDirectory(GetTestFilePath())) + using (var watcher = new FileSystemWatcher(dir.Path) { IncludeSubdirectories = true, NotifyFilter = NotifyFilters.FileName }) + { + Action action = () => + { + // Create enough directories to exceed the number of allowed watches + for (int i = 0; i <= maxUserWatches; i++) + { + Directory.CreateDirectory(Path.Combine(dir.Path, i.ToString())); + } + }; + Action cleanup = () => + { + for (int i = 0; i <= maxUserWatches; i++) + { + Directory.Delete(Path.Combine(dir.Path, i.ToString())); + } + }; + + ExpectError(watcher, action, cleanup); + + // Make sure existing watches still work even after we've had one or more failures + Action createAction = () => File.WriteAllText(Path.Combine(dir.Path, Path.GetRandomFileName()), "text"); + Action createCleanup = () => File.Delete(Path.Combine(dir.Path, Path.GetRandomFileName())); + ExpectEvent(watcher, WatcherChangeTypes.Created, createAction, createCleanup); + } + } + } } 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 eaa6f19..4f100b8 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/Utility/FileSystemWatcherTest.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/Utility/FileSystemWatcherTest.cs @@ -11,6 +11,9 @@ using Xunit.Sdk; namespace System.IO.Tests { + [CollectionDefinition("NoParallelTests", DisableParallelization = true)] + public partial class NoParallelTests { } + public abstract partial class FileSystemWatcherTest : FileCleanupTestBase { // Events are reported asynchronously by the OS, so allow an amount of time for -- 2.7.4