From 512a89be65b20f7a16282adf9873bf6494192edd Mon Sep 17 00:00:00 2001 From: Dan Moseley Date: Thu, 21 Apr 2022 21:02:02 -0600 Subject: [PATCH 1/5] Add retries to some FSW tests --- .../tests/TestUtilities/System/RetryHelper.cs | 14 +- .../FileSystemWatcher.Directory.Changed.cs | 18 +- ...ileSystemWatcher.Directory.NotifyFilter.cs | 41 ++-- .../tests/FileSystemWatcher.File.Create.cs | 31 ++-- .../tests/FileSystemWatcher.File.Delete.cs | 28 +-- .../FileSystemWatcher.File.NotifyFilter.cs | 33 ++-- .../tests/FileSystemWatcher.SymbolicLink.cs | 53 +++--- .../tests/FileSystemWatcher.cs | 24 ++- .../tests/FileSystemWatcher.unit.cs | 175 ++++++++++-------- .../tests/Utility/FileSystemWatcherTest.cs | 19 -- 10 files changed, 238 insertions(+), 198 deletions(-) diff --git a/src/libraries/Common/tests/TestUtilities/System/RetryHelper.cs b/src/libraries/Common/tests/TestUtilities/System/RetryHelper.cs index 8378d59a8b1a30..c347e2c292bc94 100644 --- a/src/libraries/Common/tests/TestUtilities/System/RetryHelper.cs +++ b/src/libraries/Common/tests/TestUtilities/System/RetryHelper.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; @@ -11,13 +13,14 @@ public static partial class RetryHelper { private static readonly Func s_defaultBackoffFunc = i => Math.Min(i * 100, 60_000); private static readonly Predicate s_defaultRetryWhenFunc = _ => true; + private static readonly bool s_debug = Environment.GetEnvironmentVariable("DEBUG_RETRYHELPER") == "1"; /// Executes the action up to a maximum of times. /// The maximum number of times to invoke . /// The test to invoke. /// After a failure, invoked to determine how many milliseconds to wait before the next attempt. It's passed the number of iterations attempted. /// Invoked to select the exceptions to retry on. If not set, any exception will trigger a retry. - public static void Execute(Action test, int maxAttempts = 5, Func backoffFunc = null, Predicate retryWhen = null) + public static void Execute(Action test, int maxAttempts = 5, Func backoffFunc = null, Predicate retryWhen = null, [CallerMemberName] string? testName = null) { // Validate arguments if (maxAttempts < 1) @@ -35,6 +38,7 @@ public static void Execute(Action test, int maxAttempts = 5, Func back var exceptions = new List(); for (int i = 1; i <= maxAttempts; i++) { + Exception lastException; try { test(); @@ -42,6 +46,7 @@ public static void Execute(Action test, int maxAttempts = 5, Func back } catch (Exception e) when (retryWhen(e)) { + lastException = e; exceptions.Add(e); if (i == maxAttempts) { @@ -49,6 +54,11 @@ public static void Execute(Action test, int maxAttempts = 5, Func back } } + if (s_debug) + { + Debug.WriteLine($"RetryHelper: retrying {testName} {i}th time of {maxAttempts}: got {lastException.Message}"); + } + Thread.Sleep((backoffFunc ?? s_defaultBackoffFunc)(i)); } } @@ -58,7 +68,7 @@ public static void Execute(Action test, int maxAttempts = 5, Func back /// The test to invoke. /// After a failure, invoked to determine how many milliseconds to wait before the next attempt. It's passed the number of iterations attempted. /// Invoked to select the exceptions to retry on. If not set, any exception will trigger a retry. - public static async Task ExecuteAsync(Func test, int maxAttempts = 5, Func backoffFunc = null, Predicate retryWhen = null) + public static async Task ExecuteAsync(Func test, int maxAttempts = 5, Func backoffFunc = null, Predicate retryWhen = null, [CallerMemberName] string? testName = null) { // Validate arguments if (maxAttempts < 1) diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Changed.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Changed.cs index 6aa7d80adcae36..fc371b9a0acd95 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Changed.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Changed.cs @@ -3,6 +3,7 @@ using System.Runtime.InteropServices; using Xunit; +using Xunit.Sdk; namespace System.IO.Tests { @@ -11,15 +12,18 @@ public class Directory_Changed_Tests : FileSystemWatcherTest [Fact] public void FileSystemWatcher_Directory_Changed_LastWrite() { - using (var testDirectory = new TempDirectory(GetTestFilePath())) - using (var dir = new TempDirectory(Path.Combine(testDirectory.Path, "dir"))) - using (var watcher = new FileSystemWatcher(testDirectory.Path, Path.GetFileName(dir.Path))) + RetryHelper.Execute(() => { - Action action = () => Directory.SetLastWriteTime(dir.Path, DateTime.Now + TimeSpan.FromSeconds(10)); + using (var testDirectory = new TempDirectory(GetTestFilePath())) + using (var dir = new TempDirectory(Path.Combine(testDirectory.Path, "dir"))) + using (var watcher = new FileSystemWatcher(testDirectory.Path, Path.GetFileName(dir.Path))) + { + Action action = () => Directory.SetLastWriteTime(dir.Path, DateTime.Now + TimeSpan.FromSeconds(10)); - WatcherChangeTypes expected = WatcherChangeTypes.Changed; - ExpectEvent(watcher, expected, action, expectedPath: dir.Path); - } + WatcherChangeTypes expected = WatcherChangeTypes.Changed; + ExpectEvent(watcher, expected, action, expectedPath: dir.Path); + } + }, maxAttempts: DefaultAttemptsForExpectedEvent, backoffFunc: (iteration) => RetryDelayMilliseconds, retryWhen: e => e is XunitException); } [Fact] diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.NotifyFilter.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.NotifyFilter.cs index ae3708dfb368da..445154ef4539f4 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.NotifyFilter.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.NotifyFilter.cs @@ -1,9 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using System.Collections.Generic; using System.Runtime.InteropServices; using Xunit; +using Xunit.Sdk; namespace System.IO.Tests { @@ -21,27 +23,30 @@ private static partial uint SetSecurityInfoByHandle( string name, uint objectTyp [MemberData(nameof(FilterTypes))] public void FileSystemWatcher_Directory_NotifyFilter_Attributes(NotifyFilters filter) { - using (var testDirectory = new TempDirectory(GetTestFilePath())) - using (var dir = new TempDirectory(Path.Combine(testDirectory.Path, "dir"))) - using (var watcher = new FileSystemWatcher(testDirectory.Path, Path.GetFileName(dir.Path))) + RetryHelper.Execute(() => { - watcher.NotifyFilter = filter; - var attributes = File.GetAttributes(dir.Path); + using (var testDirectory = new TempDirectory(GetTestFilePath())) + using (var dir = new TempDirectory(Path.Combine(testDirectory.Path, "dir"))) + using (var watcher = new FileSystemWatcher(testDirectory.Path, Path.GetFileName(dir.Path))) + { + watcher.NotifyFilter = filter; + var attributes = File.GetAttributes(dir.Path); - Action action = () => File.SetAttributes(dir.Path, attributes | FileAttributes.ReadOnly); - Action cleanup = () => File.SetAttributes(dir.Path, attributes); + Action action = () => File.SetAttributes(dir.Path, attributes | FileAttributes.ReadOnly); + Action cleanup = () => File.SetAttributes(dir.Path, attributes); - WatcherChangeTypes expected = 0; - if (filter == NotifyFilters.Attributes) - expected |= WatcherChangeTypes.Changed; - else if (OperatingSystem.IsLinux() && ((filter & LinuxFiltersForAttribute) > 0)) - expected |= WatcherChangeTypes.Changed; - else if (OperatingSystem.IsMacOS() && ((filter & OSXFiltersForModify) > 0)) - expected |= WatcherChangeTypes.Changed; - else if (OperatingSystem.IsMacOS() && ((filter & NotifyFilters.Security) > 0)) - expected |= WatcherChangeTypes.Changed; // Attribute change on OSX is a ChangeOwner operation which passes the Security NotifyFilter. - ExpectEvent(watcher, expected, action, cleanup, dir.Path); - } + WatcherChangeTypes expected = 0; + if (filter == NotifyFilters.Attributes) + expected |= WatcherChangeTypes.Changed; + else if (OperatingSystem.IsLinux() && ((filter & LinuxFiltersForAttribute) > 0)) + expected |= WatcherChangeTypes.Changed; + else if (OperatingSystem.IsMacOS() && ((filter & OSXFiltersForModify) > 0)) + expected |= WatcherChangeTypes.Changed; + else if (OperatingSystem.IsMacOS() && ((filter & NotifyFilters.Security) > 0)) + expected |= WatcherChangeTypes.Changed; // Attribute change on OSX is a ChangeOwner operation which passes the Security NotifyFilter. + ExpectEvent(watcher, expected, action, cleanup, dir.Path); + } + }, maxAttempts: DefaultAttemptsForExpectedEvent, backoffFunc: (iteration) => RetryDelayMilliseconds, retryWhen: e => e is XunitException); } [Theory] diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Create.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Create.cs index 8737fb3a8dace1..19ee821c3e4694 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Create.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Create.cs @@ -1,10 +1,12 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Xunit; +using Xunit.Sdk; namespace System.IO.Tests { @@ -30,7 +32,7 @@ public void FileSystemWatcher_File_Create() [OuterLoop] public void FileSystemWatcher_File_Create_EnablingDisablingNotAffectRaisingEvent() { - ExecuteWithRetry(() => + RetryHelper.Execute(() => { using (var testDirectory = new TempDirectory(GetTestFilePath())) using (var watcher = new FileSystemWatcher(testDirectory.Path)) @@ -62,7 +64,7 @@ public void FileSystemWatcher_File_Create_EnablingDisablingNotAffectRaisingEvent Assert.False(autoResetEvent.WaitOne(SubsequentExpectedWait)); Assert.True(numberOfRaisedEvents == 1); } - }); + }, DefaultAttemptsForExpectedEvent, (iteration) => RetryDelayMilliseconds); } [Fact] @@ -144,21 +146,24 @@ public void FileSystemWatcher_File_Create_SymLink() [Fact] public void FileSystemWatcher_File_Create_SynchronizingObject() { - using (var testDirectory = new TempDirectory(GetTestFilePath())) - using (var watcher = new FileSystemWatcher(testDirectory.Path)) + RetryHelper.Execute(() => { - TestISynchronizeInvoke invoker = new TestISynchronizeInvoke(); - watcher.SynchronizingObject = invoker; + using (var testDirectory = new TempDirectory(GetTestFilePath())) + using (var watcher = new FileSystemWatcher(testDirectory.Path)) + { + TestISynchronizeInvoke invoker = new TestISynchronizeInvoke(); + watcher.SynchronizingObject = invoker; - string fileName = Path.Combine(testDirectory.Path, "file"); - watcher.Filter = Path.GetFileName(fileName); + string fileName = Path.Combine(testDirectory.Path, "file"); + watcher.Filter = Path.GetFileName(fileName); - Action action = () => File.Create(fileName).Dispose(); - Action cleanup = () => File.Delete(fileName); + Action action = () => File.Create(fileName).Dispose(); + Action cleanup = () => File.Delete(fileName); - ExpectEvent(watcher, WatcherChangeTypes.Created, action, cleanup, fileName); - Assert.True(invoker.BeginInvoke_Called); - } + ExpectEvent(watcher, WatcherChangeTypes.Created, action, cleanup, fileName); + Assert.True(invoker.BeginInvoke_Called); + } + }, maxAttempts: DefaultAttemptsForExpectedEvent, backoffFunc: (iteration) => RetryDelayMilliseconds, retryWhen: e => e is XunitException); } } } diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Delete.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Delete.cs index 747c7b0548afd2..7b13293d04ca29 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Delete.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Delete.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Threading; using Xunit; +using Xunit.Sdk; namespace System.IO.Tests { @@ -91,19 +92,22 @@ public void FileSystemWatcher_File_Delete_DeepDirectoryStructure() [ConditionalFact(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))] public void FileSystemWatcher_File_Delete_SymLink() { - using (var testDirectory = new TempDirectory(GetTestFilePath())) - using (var dir = new TempDirectory(Path.Combine(testDirectory.Path, "dir"))) - using (var temp = new TempFile(GetTestFilePath())) - using (var watcher = new FileSystemWatcher(dir.Path, "*")) + RetryHelper.Execute(() => { - // Make the symlink in our path (to the temp file) and make sure an event is raised - string symLinkPath = Path.Combine(dir.Path, GetRandomLinkName()); - Action action = () => File.Delete(symLinkPath); - Action cleanup = () => Assert.True(MountHelper.CreateSymbolicLink(symLinkPath, temp.Path, false)); - cleanup(); - - ExpectEvent(watcher, WatcherChangeTypes.Deleted, action, cleanup, symLinkPath); - } + using (var testDirectory = new TempDirectory(GetTestFilePath())) + using (var dir = new TempDirectory(Path.Combine(testDirectory.Path, "dir"))) + using (var temp = new TempFile(GetTestFilePath())) + using (var watcher = new FileSystemWatcher(dir.Path, "*")) + { + // Make the symlink in our path (to the temp file) and make sure an event is raised + string symLinkPath = Path.Combine(dir.Path, GetRandomLinkName()); + Action action = () => File.Delete(symLinkPath); + Action cleanup = () => Assert.True(MountHelper.CreateSymbolicLink(symLinkPath, temp.Path, false)); + cleanup(); + + ExpectEvent(watcher, WatcherChangeTypes.Deleted, action, cleanup, symLinkPath); + } + }, maxAttempts: DefaultAttemptsForExpectedEvent, backoffFunc: (iteration) => RetryDelayMilliseconds, retryWhen: e => e is XunitException); } [Fact] diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.NotifyFilter.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.NotifyFilter.cs index 25538041f2afef..4d60c9ec1d063e 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.NotifyFilter.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.NotifyFilter.cs @@ -1,11 +1,13 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using Xunit; +using Xunit.Sdk; namespace System.IO.Tests { @@ -51,23 +53,26 @@ public void FileSystemWatcher_File_NotifyFilter_Attributes(NotifyFilters filter) [MemberData(nameof(FilterTypes))] public void FileSystemWatcher_File_NotifyFilter_CreationTime(NotifyFilters filter) { - using (var testDirectory = new TempDirectory(GetTestFilePath())) - using (var file = new TempFile(Path.Combine(testDirectory.Path, "file"))) - using (var watcher = new FileSystemWatcher(testDirectory.Path, Path.GetFileName(file.Path))) + RetryHelper.Execute(() => { - watcher.NotifyFilter = filter; - Action action = () => File.SetCreationTime(file.Path, DateTime.Now + TimeSpan.FromSeconds(10)); + using (var testDirectory = new TempDirectory(GetTestFilePath())) + using (var file = new TempFile(Path.Combine(testDirectory.Path, "file"))) + using (var watcher = new FileSystemWatcher(testDirectory.Path, Path.GetFileName(file.Path))) + { + watcher.NotifyFilter = filter; + Action action = () => File.SetCreationTime(file.Path, DateTime.Now + TimeSpan.FromSeconds(10)); - WatcherChangeTypes expected = 0; - if (filter == NotifyFilters.CreationTime) - expected |= WatcherChangeTypes.Changed; - else if (OperatingSystem.IsLinux() && ((filter & LinuxFiltersForAttribute) > 0)) - expected |= WatcherChangeTypes.Changed; - else if (OperatingSystem.IsMacOS() && ((filter & OSXFiltersForModify) > 0)) - expected |= WatcherChangeTypes.Changed; + WatcherChangeTypes expected = 0; + if (filter == NotifyFilters.CreationTime) + expected |= WatcherChangeTypes.Changed; + else if (OperatingSystem.IsLinux() && ((filter & LinuxFiltersForAttribute) > 0)) + expected |= WatcherChangeTypes.Changed; + else if (OperatingSystem.IsMacOS() && ((filter & OSXFiltersForModify) > 0)) + expected |= WatcherChangeTypes.Changed; - ExpectEvent(watcher, expected, action, expectedPath: file.Path); - } + ExpectEvent(watcher, expected, action, expectedPath: file.Path); + } + }, maxAttempts: DefaultAttemptsForExpectedEvent, backoffFunc: (iteration) => RetryDelayMilliseconds, retryWhen: e => e is XunitException); } [Theory] diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.SymbolicLink.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.SymbolicLink.cs index ae0e8fac596118..ef8020df60a2e6 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.SymbolicLink.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.SymbolicLink.cs @@ -1,7 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using Xunit; +using Xunit.Sdk; namespace System.IO.Tests { @@ -78,30 +80,33 @@ public void FileSystemWatcher_SymbolicLink_TargetsDirectory_Create() [Fact] public void FileSystemWatcher_SymbolicLink_TargetsDirectory_Create_IncludeSubdirectories() { - // Arrange - const string subDir = "subDir"; - const string subDirLv2 = "subDirLv2"; - using var tempDir = new TempDirectory(GetTestFilePath()); - using var tempSubDir = new TempDirectory(Path.Combine(tempDir.Path, subDir)); - - string linkPath = CreateSymbolicLinkToTarget(tempDir.Path, isDirectory: true); - using var watcher = new FileSystemWatcher(linkPath); - watcher.NotifyFilter = NotifyFilters.DirectoryName; - - string subDirLv2Path = Path.Combine(tempSubDir.Path, subDirLv2); - - // Act - Assert - ExpectNoEvent(watcher, WatcherChangeTypes.Created, - action: () => Directory.CreateDirectory(subDirLv2Path), - cleanup: () => Directory.Delete(subDirLv2Path)); - - // Turn include subdirectories on. - watcher.IncludeSubdirectories = true; - - ExpectEvent(watcher, WatcherChangeTypes.Created, - action: () => Directory.CreateDirectory(subDirLv2Path), - cleanup: () => Directory.Delete(subDirLv2Path), - expectedPath: Path.Combine(linkPath, subDir, subDirLv2)); + RetryHelper.Execute(() => + { + // Arrange + const string subDir = "subDir"; + const string subDirLv2 = "subDirLv2"; + using var tempDir = new TempDirectory(GetTestFilePath()); + using var tempSubDir = new TempDirectory(Path.Combine(tempDir.Path, subDir)); + + string linkPath = CreateSymbolicLinkToTarget(tempDir.Path, isDirectory: true); + using var watcher = new FileSystemWatcher(linkPath); + watcher.NotifyFilter = NotifyFilters.DirectoryName; + + string subDirLv2Path = Path.Combine(tempSubDir.Path, subDirLv2); + + // Act - Assert + ExpectNoEvent(watcher, WatcherChangeTypes.Created, + action: () => Directory.CreateDirectory(subDirLv2Path), + cleanup: () => Directory.Delete(subDirLv2Path)); + + // Turn include subdirectories on. + watcher.IncludeSubdirectories = true; + + ExpectEvent(watcher, WatcherChangeTypes.Created, + action: () => Directory.CreateDirectory(subDirLv2Path), + cleanup: () => Directory.Delete(subDirLv2Path), + expectedPath: Path.Combine(linkPath, subDir, subDirLv2)); + }, maxAttempts: DefaultAttemptsForExpectedEvent, backoffFunc: (iteration) => RetryDelayMilliseconds, retryWhen: e => e is XunitException); } [Fact] diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.cs index ffb3406aec1f6c..be9a10c98aab10 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.cs @@ -5,6 +5,7 @@ using System.Runtime.CompilerServices; using System.Threading; using Xunit; +using Xunit.Sdk; namespace System.IO.Tests { @@ -180,17 +181,20 @@ public void BeginInit_PausesEnableRaisingEvents() [InlineData(false)] public void EndInit_ResumesPausedEnableRaisingEvents(bool setBeforeBeginInit) { - using (var testDirectory = new TempDirectory(GetTestFilePath())) - using (var watcher = new TestFileSystemWatcher(testDirectory.Path, "*")) + RetryHelper.Execute(() => { - if (setBeforeBeginInit) - watcher.EnableRaisingEvents = true; - watcher.BeginInit(); - if (!setBeforeBeginInit) - watcher.EnableRaisingEvents = true; - watcher.EndInit(); - ExpectEvent(watcher, WatcherChangeTypes.Created | WatcherChangeTypes.Deleted, () => new TempFile(Path.Combine(testDirectory.Path, GetTestFileName())).Dispose(), null); - } + using (var testDirectory = new TempDirectory(GetTestFilePath())) + using (var watcher = new TestFileSystemWatcher(testDirectory.Path, "*")) + { + if (setBeforeBeginInit) + watcher.EnableRaisingEvents = true; + watcher.BeginInit(); + if (!setBeforeBeginInit) + watcher.EnableRaisingEvents = true; + watcher.EndInit(); + ExpectEvent(watcher, WatcherChangeTypes.Created | WatcherChangeTypes.Deleted, () => new TempFile(Path.Combine(testDirectory.Path, GetTestFileName())).Dispose(), null); + } + }, maxAttempts: DefaultAttemptsForExpectedEvent, backoffFunc: (iteration) => RetryDelayMilliseconds, retryWhen: e => e is XunitException); } /// 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 e64ea5a428817c..496e5db898abda 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.unit.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.unit.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using System.Collections.ObjectModel; using System.Linq; using System.Threading; @@ -11,6 +12,7 @@ using Xunit; using Xunit.Abstractions; +using Xunit.Sdk; namespace System.IO.Tests { @@ -952,66 +954,75 @@ public void SetAndGetFiltersProperty() [Fact] public void FileSystemWatcher_File_Delete_MultipleFilters() { - // Check delete events against multiple filters + RetryHelper.Execute(() => + { + // Check delete events against multiple filters - DirectoryInfo directory = Directory.CreateDirectory(GetTestFilePath()); - FileInfo fileOne = new FileInfo(Path.Combine(directory.FullName, GetTestFileName())); - FileInfo fileTwo = new FileInfo(Path.Combine(directory.FullName, GetTestFileName())); - FileInfo fileThree = new FileInfo(Path.Combine(directory.FullName, GetTestFileName())); - fileOne.Create().Dispose(); - fileTwo.Create().Dispose(); - fileThree.Create().Dispose(); + DirectoryInfo directory = Directory.CreateDirectory(GetTestFilePath()); + FileInfo fileOne = new FileInfo(Path.Combine(directory.FullName, GetTestFileName())); + FileInfo fileTwo = new FileInfo(Path.Combine(directory.FullName, GetTestFileName())); + FileInfo fileThree = new FileInfo(Path.Combine(directory.FullName, GetTestFileName())); + fileOne.Create().Dispose(); + fileTwo.Create().Dispose(); + fileThree.Create().Dispose(); - using (var watcher = new FileSystemWatcher(directory.FullName)) - { - watcher.Filters.Add(fileOne.Name); - watcher.Filters.Add(fileTwo.Name); + using (var watcher = new FileSystemWatcher(directory.FullName)) + { + watcher.Filters.Add(fileOne.Name); + watcher.Filters.Add(fileTwo.Name); - ExpectEvent(watcher, WatcherChangeTypes.Deleted, () => fileOne.Delete(), cleanup: null, expectedPath : fileOne.FullName); - ExpectEvent(watcher, WatcherChangeTypes.Deleted, () => fileTwo.Delete(), cleanup: null, expectedPath: fileTwo.FullName ); - ExpectNoEvent(watcher, WatcherChangeTypes.Deleted, () => fileThree.Delete(), cleanup: null, expectedPath: fileThree.FullName); - } + ExpectEvent(watcher, WatcherChangeTypes.Deleted, () => fileOne.Delete(), cleanup: null, expectedPath : fileOne.FullName); + ExpectEvent(watcher, WatcherChangeTypes.Deleted, () => fileTwo.Delete(), cleanup: null, expectedPath: fileTwo.FullName ); + ExpectNoEvent(watcher, WatcherChangeTypes.Deleted, () => fileThree.Delete(), cleanup: null, expectedPath: fileThree.FullName); + } + }, maxAttempts: DefaultAttemptsForExpectedEvent, backoffFunc: (iteration) => RetryDelayMilliseconds, retryWhen: e => e is XunitException); } [Fact] public void FileSystemWatcher_Directory_Create_MultipleFilters() { - // Check create events against multiple filters + RetryHelper.Execute(() => + { + // Check create events against multiple filters - DirectoryInfo directory = Directory.CreateDirectory(GetTestFilePath()); - string directoryOne = Path.Combine(directory.FullName, GetTestFileName()); - string directoryTwo = Path.Combine(directory.FullName, GetTestFileName()); - string directoryThree = Path.Combine(directory.FullName, GetTestFileName()); + DirectoryInfo directory = Directory.CreateDirectory(GetTestFilePath()); + string directoryOne = Path.Combine(directory.FullName, GetTestFileName()); + string directoryTwo = Path.Combine(directory.FullName, GetTestFileName()); + string directoryThree = Path.Combine(directory.FullName, GetTestFileName()); - using (var watcher = new FileSystemWatcher(directory.FullName)) - { - watcher.Filters.Add(Path.GetFileName(directoryOne)); - watcher.Filters.Add(Path.GetFileName(directoryTwo)); + using (var watcher = new FileSystemWatcher(directory.FullName)) + { + watcher.Filters.Add(Path.GetFileName(directoryOne)); + watcher.Filters.Add(Path.GetFileName(directoryTwo)); - ExpectEvent(watcher, WatcherChangeTypes.Created, () => Directory.CreateDirectory(directoryOne), cleanup: null, expectedPath: directoryOne); - ExpectEvent(watcher, WatcherChangeTypes.Created, () => Directory.CreateDirectory(directoryTwo), cleanup: null, expectedPath: directoryTwo); - ExpectNoEvent(watcher, WatcherChangeTypes.Created, () => Directory.CreateDirectory(directoryThree), cleanup: null, expectedPath: directoryThree); - } + ExpectEvent(watcher, WatcherChangeTypes.Created, () => Directory.CreateDirectory(directoryOne), cleanup: null, expectedPath: directoryOne); + ExpectEvent(watcher, WatcherChangeTypes.Created, () => Directory.CreateDirectory(directoryTwo), cleanup: null, expectedPath: directoryTwo); + ExpectNoEvent(watcher, WatcherChangeTypes.Created, () => Directory.CreateDirectory(directoryThree), cleanup: null, expectedPath: directoryThree); + } + }, maxAttempts: DefaultAttemptsForExpectedEvent, backoffFunc: (iteration) => RetryDelayMilliseconds, retryWhen: e => e is XunitException); } [Fact] public void FileSystemWatcher_Directory_Create_Filter_Ctor() { - // Check create events against multiple filters + RetryHelper.Execute(() => + { + // Check create events against multiple filters - DirectoryInfo directory = Directory.CreateDirectory(GetTestFilePath()); - string directoryOne = Path.Combine(directory.FullName, GetTestFileName()); - string directoryTwo = Path.Combine(directory.FullName, GetTestFileName()); - string directoryThree = Path.Combine(directory.FullName, GetTestFileName()); + DirectoryInfo directory = Directory.CreateDirectory(GetTestFilePath()); + string directoryOne = Path.Combine(directory.FullName, GetTestFileName()); + string directoryTwo = Path.Combine(directory.FullName, GetTestFileName()); + string directoryThree = Path.Combine(directory.FullName, GetTestFileName()); - using (var watcher = new FileSystemWatcher(directory.FullName, Path.GetFileName(directoryOne))) - { - watcher.Filters.Add(Path.GetFileName(directoryTwo)); + using (var watcher = new FileSystemWatcher(directory.FullName, Path.GetFileName(directoryOne))) + { + watcher.Filters.Add(Path.GetFileName(directoryTwo)); - ExpectEvent(watcher, WatcherChangeTypes.Created, () => Directory.CreateDirectory(directoryOne), cleanup: null, expectedPath: directoryOne); - ExpectEvent(watcher, WatcherChangeTypes.Created, () => Directory.CreateDirectory(directoryTwo), cleanup: null, expectedPath: directoryTwo); - ExpectNoEvent(watcher, WatcherChangeTypes.Created, () => Directory.CreateDirectory(directoryThree), cleanup: null, expectedPath: directoryThree); - } + ExpectEvent(watcher, WatcherChangeTypes.Created, () => Directory.CreateDirectory(directoryOne), cleanup: null, expectedPath: directoryOne); + ExpectEvent(watcher, WatcherChangeTypes.Created, () => Directory.CreateDirectory(directoryTwo), cleanup: null, expectedPath: directoryTwo); + ExpectNoEvent(watcher, WatcherChangeTypes.Created, () => Directory.CreateDirectory(directoryThree), cleanup: null, expectedPath: directoryThree); + } + }, maxAttempts: DefaultAttemptsForExpectedEvent, backoffFunc: (iteration) => RetryDelayMilliseconds, retryWhen: e => e is XunitException); } [Fact] @@ -1036,55 +1047,61 @@ public void FileSystemWatcher_Directory_Delete_MultipleFilters() [Fact] public void FileSystemWatcher_File_Create_MultipleFilters() { - DirectoryInfo directory = Directory.CreateDirectory(GetTestFilePath()); - FileInfo fileOne = new FileInfo(Path.Combine(directory.FullName, GetTestFileName())); - FileInfo fileTwo = new FileInfo(Path.Combine(directory.FullName, GetTestFileName())); - FileInfo fileThree = new FileInfo(Path.Combine(directory.FullName, GetTestFileName())); - - using (var watcher = new FileSystemWatcher(directory.FullName)) + RetryHelper.Execute(() => { - watcher.Filters.Add(fileOne.Name); - watcher.Filters.Add(fileTwo.Name); + DirectoryInfo directory = Directory.CreateDirectory(GetTestFilePath()); + FileInfo fileOne = new FileInfo(Path.Combine(directory.FullName, GetTestFileName())); + FileInfo fileTwo = new FileInfo(Path.Combine(directory.FullName, GetTestFileName())); + FileInfo fileThree = new FileInfo(Path.Combine(directory.FullName, GetTestFileName())); - ExpectEvent(watcher, WatcherChangeTypes.Created, () => fileOne.Create().Dispose(), cleanup: null, expectedPath: fileOne.FullName); - ExpectEvent(watcher, WatcherChangeTypes.Created, () => fileTwo.Create().Dispose(), cleanup: null, expectedPath: fileTwo.FullName); - ExpectNoEvent(watcher, WatcherChangeTypes.Created, () => fileThree.Create().Dispose(), cleanup: null, expectedPath: fileThree.FullName); - } + using (var watcher = new FileSystemWatcher(directory.FullName)) + { + watcher.Filters.Add(fileOne.Name); + watcher.Filters.Add(fileTwo.Name); + + ExpectEvent(watcher, WatcherChangeTypes.Created, () => fileOne.Create().Dispose(), cleanup: null, expectedPath: fileOne.FullName); + ExpectEvent(watcher, WatcherChangeTypes.Created, () => fileTwo.Create().Dispose(), cleanup: null, expectedPath: fileTwo.FullName); + ExpectNoEvent(watcher, WatcherChangeTypes.Created, () => fileThree.Create().Dispose(), cleanup: null, expectedPath: fileThree.FullName); + } + }, maxAttempts: DefaultAttemptsForExpectedEvent, backoffFunc: (iteration) => RetryDelayMilliseconds, retryWhen: e => e is XunitException); } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public void FileSystemWatcher_ModifyFiltersConcurrentWithEvents() { - DirectoryInfo directory = Directory.CreateDirectory(GetTestFilePath()); - FileInfo fileOne = new FileInfo(Path.Combine(directory.FullName, GetTestFileName())); - FileInfo fileTwo = new FileInfo(Path.Combine(directory.FullName, GetTestFileName())); - FileInfo fileThree = new FileInfo(Path.Combine(directory.FullName, GetTestFileName())); - - using (var watcher = new FileSystemWatcher(directory.FullName)) + RetryHelper.Execute(() => { - watcher.Filters.Add(fileOne.Name); - watcher.Filters.Add(fileTwo.Name); + DirectoryInfo directory = Directory.CreateDirectory(GetTestFilePath()); + FileInfo fileOne = new FileInfo(Path.Combine(directory.FullName, GetTestFileName())); + FileInfo fileTwo = new FileInfo(Path.Combine(directory.FullName, GetTestFileName())); + FileInfo fileThree = new FileInfo(Path.Combine(directory.FullName, GetTestFileName())); - var cts = new CancellationTokenSource(); - Thread thread = ThreadTestHelpers.CreateGuardedThread(out Action waitForThread, () => + using (var watcher = new FileSystemWatcher(directory.FullName)) { - string otherFilter = Guid.NewGuid().ToString("N"); - while (!cts.IsCancellationRequested) - { - watcher.Filters.Add(otherFilter); - watcher.Filters.RemoveAt(2); - } - }); - thread.IsBackground = true; - thread.Start(); + watcher.Filters.Add(fileOne.Name); + watcher.Filters.Add(fileTwo.Name); - ExpectEvent(watcher, WatcherChangeTypes.Created, () => fileOne.Create().Dispose(), cleanup: null, expectedPath: fileOne.FullName); - ExpectEvent(watcher, WatcherChangeTypes.Created, () => fileTwo.Create().Dispose(), cleanup: null, expectedPath: fileTwo.FullName); - ExpectNoEvent(watcher, WatcherChangeTypes.Created, () => fileThree.Create().Dispose(), cleanup: null, expectedPath: fileThree.FullName); - - cts.Cancel(); - waitForThread(); - } + var cts = new CancellationTokenSource(); + Thread thread = ThreadTestHelpers.CreateGuardedThread(out Action waitForThread, () => + { + string otherFilter = Guid.NewGuid().ToString("N"); + while (!cts.IsCancellationRequested) + { + watcher.Filters.Add(otherFilter); + watcher.Filters.RemoveAt(2); + } + }); + thread.IsBackground = true; + thread.Start(); + + ExpectEvent(watcher, WatcherChangeTypes.Created, () => fileOne.Create().Dispose(), cleanup: null, expectedPath: fileOne.FullName); + ExpectEvent(watcher, WatcherChangeTypes.Created, () => fileTwo.Create().Dispose(), cleanup: null, expectedPath: fileTwo.FullName); + ExpectNoEvent(watcher, WatcherChangeTypes.Created, () => fileThree.Create().Dispose(), cleanup: null, expectedPath: fileThree.FullName); + + cts.Cancel(); + waitForThread(); + } + }, maxAttempts: DefaultAttemptsForExpectedEvent, backoffFunc: (iteration) => RetryDelayMilliseconds, retryWhen: e => e is XunitException); } } 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 21fc7d4ec02b58..0d8e5e11d6edac 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/Utility/FileSystemWatcherTest.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/Utility/FileSystemWatcherTest.cs @@ -201,25 +201,6 @@ public static void ExpectEvent(FileSystemWatcher watcher, WatcherChangeTypes exp } } - /// Invokes the specified test action with retry on failure (other than assertion failure). - /// The test action. - /// The maximum number of times to attempt to run the test. - public static void ExecuteWithRetry(Action action, int maxAttempts = DefaultAttemptsForExpectedEvent) - { - for (int retry = 0; retry < maxAttempts; retry++) - { - try - { - action(); - return; - } - catch (Exception e) when (!(e is XunitException) && retry < maxAttempts - 1) - { - Thread.Sleep(RetryDelayMilliseconds); - } - } - } - /// /// Does verification that the given watcher will not throw exactly/only the events in "expectedEvents" when /// "action" is executed. From b125ece2ba4e51db3b70d3ad6cd6becc0c658138 Mon Sep 17 00:00:00 2001 From: Dan Moseley Date: Thu, 21 Apr 2022 21:05:05 -0600 Subject: [PATCH 2/5] another --- .../System.IO.FileSystem.Watcher/tests/FileSystemWatcher.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.cs index be9a10c98aab10..7fe42428daabff 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.cs @@ -175,7 +175,6 @@ public void BeginInit_PausesEnableRaisingEvents() /// /// EndInit will begin EnableRaisingEvents if we previously set EnableRaisingEvents=true /// - [ActiveIssue("https://github.com/dotnet/runtime/issues/24181", TestPlatforms.OSX)] [Theory] [InlineData(true)] [InlineData(false)] From 23f530ae644686d5b9aca88ca9e534454d68c0bc Mon Sep 17 00:00:00 2001 From: Dan Moseley Date: Thu, 21 Apr 2022 21:32:15 -0600 Subject: [PATCH 3/5] Add Helix logging to console --- .../tests/TestUtilities/System/PlatformDetection.cs | 3 +++ .../Common/tests/TestUtilities/System/RetryHelper.cs | 10 +++++++++- .../tests/DescriptionNameTests.cs | 4 +--- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs index 7732493fcb7db1..87e064b2ec4137 100644 --- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs +++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.IO; +using System.Linq; using System.Linq.Expressions; using System.Security; using System.Security.Authentication; @@ -22,6 +23,8 @@ public static partial class PlatformDetection // do it in a way that failures don't cascade. // + public static readonly bool IsInHelix = Environment.GetEnvironmentVariables().Keys.Cast().Where(key => key.StartsWith("HELIX")).Any(); + public static bool IsNetCore => Environment.Version.Major >= 5 || RuntimeInformation.FrameworkDescription.StartsWith(".NET Core", StringComparison.OrdinalIgnoreCase); public static bool IsMonoRuntime => Type.GetType("Mono.RuntimeStructs") != null; public static bool IsNotMonoRuntime => !IsMonoRuntime; diff --git a/src/libraries/Common/tests/TestUtilities/System/RetryHelper.cs b/src/libraries/Common/tests/TestUtilities/System/RetryHelper.cs index c347e2c292bc94..cdbd8c098e08a4 100644 --- a/src/libraries/Common/tests/TestUtilities/System/RetryHelper.cs +++ b/src/libraries/Common/tests/TestUtilities/System/RetryHelper.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.CompilerServices; @@ -54,9 +55,16 @@ public static void Execute(Action test, int maxAttempts = 5, Func back } } + string diagnostic = $"RetryHelper: retrying {testName} {i}th time of {maxAttempts}: got {lastException.Message}"; + if (PlatformDetection.IsInHelix && testName.Contains("FileSystemWatcher_")) + { + // Dump into the console output so we can mine it + Console.WriteLine(diagnostic); + } + if (s_debug) { - Debug.WriteLine($"RetryHelper: retrying {testName} {i}th time of {maxAttempts}: got {lastException.Message}"); + Debug.WriteLine(diagnostic); } Thread.Sleep((backoffFunc ?? s_defaultBackoffFunc)(i)); diff --git a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/DescriptionNameTests.cs b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/DescriptionNameTests.cs index c0069896dc49a9..f41c0b83345c3c 100644 --- a/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/DescriptionNameTests.cs +++ b/src/libraries/System.Runtime.InteropServices.RuntimeInformation/tests/DescriptionNameTests.cs @@ -16,13 +16,11 @@ public class DescriptionNameTests // When running both inner and outer loop together, dump only once private static bool s_dumpedRuntimeInfo = false; - private static readonly bool s_isInHelix = Environment.GetEnvironmentVariables().Keys.Cast().Where(key => key.StartsWith("HELIX")).Any(); - [Fact] [SkipOnPlatform(TestPlatforms.Browser, "throws PNSE when binariesLocation is not an empty string.")] public void DumpRuntimeInformationToConsole() { - if (s_dumpedRuntimeInfo || !s_isInHelix) + if (s_dumpedRuntimeInfo || !PlatformDetection.IsInHelix) return; s_dumpedRuntimeInfo = true; From d3c284bc0978d0fd8e203f16e79a4e6f1467f055 Mon Sep 17 00:00:00 2001 From: Dan Moseley Date: Fri, 22 Apr 2022 17:21:34 -0600 Subject: [PATCH 4/5] feedback --- .../tests/TestUtilities/System/RetryHelper.cs | 7 +-- .../FileSystemWatcher.Directory.Changed.cs | 2 +- ...ileSystemWatcher.Directory.NotifyFilter.cs | 2 +- .../tests/FileSystemWatcher.File.Create.cs | 4 +- .../tests/FileSystemWatcher.File.Delete.cs | 2 +- .../FileSystemWatcher.File.NotifyFilter.cs | 2 +- .../tests/FileSystemWatcher.SymbolicLink.cs | 2 +- .../tests/FileSystemWatcher.cs | 2 +- .../tests/FileSystemWatcher.unit.cs | 10 +-- .../tests/Utility/FileSystemWatcherTest.cs | 62 +++++++++++++++++++ 10 files changed, 78 insertions(+), 17 deletions(-) diff --git a/src/libraries/Common/tests/TestUtilities/System/RetryHelper.cs b/src/libraries/Common/tests/TestUtilities/System/RetryHelper.cs index cdbd8c098e08a4..15080c83ff2471 100644 --- a/src/libraries/Common/tests/TestUtilities/System/RetryHelper.cs +++ b/src/libraries/Common/tests/TestUtilities/System/RetryHelper.cs @@ -55,16 +55,15 @@ public static void Execute(Action test, int maxAttempts = 5, Func back } } - string diagnostic = $"RetryHelper: retrying {testName} {i}th time of {maxAttempts}: got {lastException.Message}"; - if (PlatformDetection.IsInHelix && testName.Contains("FileSystemWatcher_")) + if (PlatformDetection.IsInHelix) { // Dump into the console output so we can mine it - Console.WriteLine(diagnostic); + Console.WriteLine($"RetryHelper: retrying {testName} {i}th time of {maxAttempts}: got {lastException.Message}"); } if (s_debug) { - Debug.WriteLine(diagnostic); + Debug.WriteLine($"RetryHelper: retrying {testName} {i}th time of {maxAttempts}: got {lastException.Message}"); } Thread.Sleep((backoffFunc ?? s_defaultBackoffFunc)(i)); diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Changed.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Changed.cs index fc371b9a0acd95..42e8bba246c122 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Changed.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.Changed.cs @@ -12,7 +12,7 @@ public class Directory_Changed_Tests : FileSystemWatcherTest [Fact] public void FileSystemWatcher_Directory_Changed_LastWrite() { - RetryHelper.Execute(() => + FileSystemWatcherTest.Execute(() => { using (var testDirectory = new TempDirectory(GetTestFilePath())) using (var dir = new TempDirectory(Path.Combine(testDirectory.Path, "dir"))) diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.NotifyFilter.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.NotifyFilter.cs index 445154ef4539f4..bec7ca5499839e 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.NotifyFilter.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.Directory.NotifyFilter.cs @@ -23,7 +23,7 @@ private static partial uint SetSecurityInfoByHandle( string name, uint objectTyp [MemberData(nameof(FilterTypes))] public void FileSystemWatcher_Directory_NotifyFilter_Attributes(NotifyFilters filter) { - RetryHelper.Execute(() => + FileSystemWatcherTest.Execute(() => { using (var testDirectory = new TempDirectory(GetTestFilePath())) using (var dir = new TempDirectory(Path.Combine(testDirectory.Path, "dir"))) diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Create.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Create.cs index 19ee821c3e4694..860307e589befe 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Create.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Create.cs @@ -32,7 +32,7 @@ public void FileSystemWatcher_File_Create() [OuterLoop] public void FileSystemWatcher_File_Create_EnablingDisablingNotAffectRaisingEvent() { - RetryHelper.Execute(() => + FileSystemWatcherTest.Execute(() => { using (var testDirectory = new TempDirectory(GetTestFilePath())) using (var watcher = new FileSystemWatcher(testDirectory.Path)) @@ -146,7 +146,7 @@ public void FileSystemWatcher_File_Create_SymLink() [Fact] public void FileSystemWatcher_File_Create_SynchronizingObject() { - RetryHelper.Execute(() => + FileSystemWatcherTest.Execute(() => { using (var testDirectory = new TempDirectory(GetTestFilePath())) using (var watcher = new FileSystemWatcher(testDirectory.Path)) diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Delete.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Delete.cs index 7b13293d04ca29..234d377a630985 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Delete.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.Delete.cs @@ -92,7 +92,7 @@ public void FileSystemWatcher_File_Delete_DeepDirectoryStructure() [ConditionalFact(typeof(MountHelper), nameof(MountHelper.CanCreateSymbolicLinks))] public void FileSystemWatcher_File_Delete_SymLink() { - RetryHelper.Execute(() => + FileSystemWatcherTest.Execute(() => { using (var testDirectory = new TempDirectory(GetTestFilePath())) using (var dir = new TempDirectory(Path.Combine(testDirectory.Path, "dir"))) diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.NotifyFilter.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.NotifyFilter.cs index 4d60c9ec1d063e..d806086efc3018 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.NotifyFilter.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.File.NotifyFilter.cs @@ -53,7 +53,7 @@ public void FileSystemWatcher_File_NotifyFilter_Attributes(NotifyFilters filter) [MemberData(nameof(FilterTypes))] public void FileSystemWatcher_File_NotifyFilter_CreationTime(NotifyFilters filter) { - RetryHelper.Execute(() => + FileSystemWatcherTest.Execute(() => { using (var testDirectory = new TempDirectory(GetTestFilePath())) using (var file = new TempFile(Path.Combine(testDirectory.Path, "file"))) diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.SymbolicLink.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.SymbolicLink.cs index ef8020df60a2e6..f5309fd38e6d65 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.SymbolicLink.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.SymbolicLink.cs @@ -80,7 +80,7 @@ public void FileSystemWatcher_SymbolicLink_TargetsDirectory_Create() [Fact] public void FileSystemWatcher_SymbolicLink_TargetsDirectory_Create_IncludeSubdirectories() { - RetryHelper.Execute(() => + FileSystemWatcherTest.Execute(() => { // Arrange const string subDir = "subDir"; diff --git a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.cs b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.cs index 7fe42428daabff..acd36719d8055b 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.cs @@ -180,7 +180,7 @@ public void BeginInit_PausesEnableRaisingEvents() [InlineData(false)] public void EndInit_ResumesPausedEnableRaisingEvents(bool setBeforeBeginInit) { - RetryHelper.Execute(() => + FileSystemWatcherTest.Execute(() => { using (var testDirectory = new TempDirectory(GetTestFilePath())) using (var watcher = new TestFileSystemWatcher(testDirectory.Path, "*")) 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 496e5db898abda..42eecf4b56b500 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.unit.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/FileSystemWatcher.unit.cs @@ -954,7 +954,7 @@ public void SetAndGetFiltersProperty() [Fact] public void FileSystemWatcher_File_Delete_MultipleFilters() { - RetryHelper.Execute(() => + FileSystemWatcherTest.Execute(() => { // Check delete events against multiple filters @@ -981,7 +981,7 @@ public void FileSystemWatcher_File_Delete_MultipleFilters() [Fact] public void FileSystemWatcher_Directory_Create_MultipleFilters() { - RetryHelper.Execute(() => + FileSystemWatcherTest.Execute(() => { // Check create events against multiple filters @@ -1005,7 +1005,7 @@ public void FileSystemWatcher_Directory_Create_MultipleFilters() [Fact] public void FileSystemWatcher_Directory_Create_Filter_Ctor() { - RetryHelper.Execute(() => + FileSystemWatcherTest.Execute(() => { // Check create events against multiple filters @@ -1047,7 +1047,7 @@ public void FileSystemWatcher_Directory_Delete_MultipleFilters() [Fact] public void FileSystemWatcher_File_Create_MultipleFilters() { - RetryHelper.Execute(() => + FileSystemWatcherTest.Execute(() => { DirectoryInfo directory = Directory.CreateDirectory(GetTestFilePath()); FileInfo fileOne = new FileInfo(Path.Combine(directory.FullName, GetTestFileName())); @@ -1069,7 +1069,7 @@ public void FileSystemWatcher_File_Create_MultipleFilters() [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public void FileSystemWatcher_ModifyFiltersConcurrentWithEvents() { - RetryHelper.Execute(() => + FileSystemWatcherTest.Execute(() => { DirectoryInfo directory = Directory.CreateDirectory(GetTestFilePath()); FileInfo fileOne = new FileInfo(Path.Combine(directory.FullName, GetTestFileName())); 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 0d8e5e11d6edac..a18c8751f3f60c 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/Utility/FileSystemWatcherTest.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/Utility/FileSystemWatcherTest.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; +using System.Runtime.CompilerServices; using System.Threading; using Xunit; using Xunit.Sdk; @@ -201,6 +202,67 @@ public static void ExpectEvent(FileSystemWatcher watcher, WatcherChangeTypes exp } } + // Pasted from RetryHelper.cs in order to force FSW tests to log retries to the Helix console. + // We don't want to do that for tests in general. + // Once we've gotten enough data, delete this and go back to the regular RetryHelper. + private static readonly Func s_defaultBackoffFunc = i => Math.Min(i * 100, 60_000); + private static readonly Predicate s_defaultRetryWhenFunc = _ => true; + private static readonly bool s_debug = Environment.GetEnvironmentVariable("DEBUG_RETRYHELPER") == "1"; + + /// Executes the action up to a maximum of times. + /// The maximum number of times to invoke . + /// The test to invoke. + /// After a failure, invoked to determine how many milliseconds to wait before the next attempt. It's passed the number of iterations attempted. + /// Invoked to select the exceptions to retry on. If not set, any exception will trigger a retry. + public static void Execute(Action test, int maxAttempts = 5, Func backoffFunc = null, Predicate retryWhen = null, [CallerMemberName] string? testName = null) + { + // Validate arguments + if (maxAttempts < 1) + { + throw new ArgumentOutOfRangeException(nameof(maxAttempts)); + } + if (test == null) + { + throw new ArgumentNullException(nameof(test)); + } + + retryWhen ??= s_defaultRetryWhenFunc; + + // Execute the test until it either passes or we run it maxAttempts times + var exceptions = new List(); + for (int i = 1; i <= maxAttempts; i++) + { + Exception lastException; + try + { + test(); + return; + } + catch (Exception e) when (retryWhen(e)) + { + lastException = e; + exceptions.Add(e); + if (i == maxAttempts) + { + throw new AggregateException(exceptions); + } + } + + if (PlatformDetection.IsInHelix) + { + // Dump into the console output so we can mine it + Console.WriteLine($"RetryHelper: retrying {testName} {i}th time of {maxAttempts}: got {lastException.Message}"); + } + + if (s_debug) + { + Debug.WriteLine($"RetryHelper: retrying {testName} {i}th time of {maxAttempts}: got {lastException.Message}"); + } + + Thread.Sleep((backoffFunc ?? s_defaultBackoffFunc)(i)); + } + } + /// /// Does verification that the given watcher will not throw exactly/only the events in "expectedEvents" when /// "action" is executed. From 21609895b7cae3d00055e12fdb00fbbf315327b6 Mon Sep 17 00:00:00 2001 From: Dan Moseley Date: Fri, 22 Apr 2022 17:24:08 -0600 Subject: [PATCH 5/5] more --- .../Common/tests/TestUtilities/System/RetryHelper.cs | 10 +++------- .../tests/Utility/FileSystemWatcherTest.cs | 2 +- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/libraries/Common/tests/TestUtilities/System/RetryHelper.cs b/src/libraries/Common/tests/TestUtilities/System/RetryHelper.cs index 15080c83ff2471..3e0d7cbf1d39f2 100644 --- a/src/libraries/Common/tests/TestUtilities/System/RetryHelper.cs +++ b/src/libraries/Common/tests/TestUtilities/System/RetryHelper.cs @@ -55,15 +55,11 @@ public static void Execute(Action test, int maxAttempts = 5, Func back } } - if (PlatformDetection.IsInHelix) - { - // Dump into the console output so we can mine it - Console.WriteLine($"RetryHelper: retrying {testName} {i}th time of {maxAttempts}: got {lastException.Message}"); - } - if (s_debug) { - Debug.WriteLine($"RetryHelper: retrying {testName} {i}th time of {maxAttempts}: got {lastException.Message}"); + string diagnostic = $"RetryHelper: retrying {testName} {i}th time of {maxAttempts}: got {lastException.Message}"; + Console.WriteLine(diagnostic); + Debug.WriteLine(diagnostic); } Thread.Sleep((backoffFunc ?? s_defaultBackoffFunc)(i)); 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 a18c8751f3f60c..0cedd3954e793c 100644 --- a/src/libraries/System.IO.FileSystem.Watcher/tests/Utility/FileSystemWatcherTest.cs +++ b/src/libraries/System.IO.FileSystem.Watcher/tests/Utility/FileSystemWatcherTest.cs @@ -248,7 +248,7 @@ public static void Execute(Action test, int maxAttempts = 5, Func back } } - if (PlatformDetection.IsInHelix) + if (PlatformDetection.IsInHelix || s_debug) { // Dump into the console output so we can mine it Console.WriteLine($"RetryHelper: retrying {testName} {i}th time of {maxAttempts}: got {lastException.Message}");