diff --git a/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherMock.cs
index 5b5e70d3..33068dec 100644
--- a/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherMock.cs
+++ b/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherMock.cs
@@ -30,6 +30,27 @@ internal sealed class FileSystemWatcherMock : Component, IFileSystemWatcher
///
private const int BytesPerMessage = 128;
+ ///
+ /// Caches the full path of
+ ///
+ private string FullPath
+ {
+ get;
+ set
+ {
+ if (string.IsNullOrEmpty(value))
+ {
+ field = value;
+
+ return;
+ }
+
+ string fullPath = GetNormalizedFullPath(value);
+
+ field = fullPath;
+ }
+ } = string.Empty;
+
private CancellationTokenSource? _cancellationTokenSource;
private IDisposable? _changeHandler;
private readonly MockFileSystem _fileSystem;
@@ -258,27 +279,6 @@ public ISynchronizeInvoke? SynchronizingObject
}
}
- ///
- /// Caches the full path of
- ///
- private string FullPath
- {
- get;
- set
- {
- if (string.IsNullOrEmpty(value))
- {
- field = value;
-
- return;
- }
-
- string fullPath = GetNormalizedFullPath(value);
-
- field = fullPath;
- }
- } = string.Empty;
-
///
public void BeginInit()
{
@@ -355,6 +355,23 @@ public IWaitForChangedResult WaitForChanged(
internal static FileSystemWatcherMock New(MockFileSystem fileSystem)
=> new(fileSystem);
+ private static void CheckRenamePremise(RenamedContext context)
+ {
+ Debug.Assert(
+ context is not { ComesFromOutside: true, GoesToInside: true },
+ "The premise { ComesFromOutside: true, GoesToInside: true } should have been handled."
+ );
+
+ Debug.Assert(
+ context is not { ComesFromInside: true, GoesToInside: true },
+ "The premise { ComesFromInside: true, GoesToInside: true } should have been handled."
+ );
+
+ Debug.Assert(
+ !context.GoesToOutside, "The premise { GoesToOutside: true } should have been handled."
+ );
+ }
+
///
protected override void Dispose(bool disposing)
{
@@ -366,6 +383,70 @@ protected override void Dispose(bool disposing)
base.Dispose(disposing);
}
+ private string GetNormalizedFullPath(string path)
+ {
+ string normalized = _fileSystem.Execute.Path.GetFullPath(path);
+
+ return normalized.TrimEnd(_fileSystem.Execute.Path.DirectorySeparatorChar);
+ }
+
+ private string? GetNormalizedParent(string? path)
+ {
+ if (path == null)
+ {
+ return null;
+ }
+
+ string normalized = GetNormalizedFullPath(path);
+
+ return _fileSystem.Execute.Path.GetDirectoryName(normalized)
+ ?.TrimEnd(_fileSystem.Execute.Path.DirectorySeparatorChar);
+ }
+
+ ///
+ /// Counts the number of directory separators inside the relative path to
+ ///
+ ///
+ /// The number of directory separators inside the relative path to
+ /// Returns -1 if the path is outside the
+ private int GetSubDirectoryCount(string path)
+ {
+ string normalizedPath = GetNormalizedFullPath(path);
+
+ if (!normalizedPath.StartsWith(FullPath, _fileSystem.Execute.StringComparisonMode))
+ {
+ return -1;
+ }
+
+ return normalizedPath.Substring(FullPath.Length)
+ .TrimStart(_fileSystem.Execute.Path.DirectorySeparatorChar)
+ .Count(c => c == _fileSystem.Execute.Path.DirectorySeparatorChar);
+ }
+
+ private bool IsItemNameChange(ChangeDescription changeDescription)
+ {
+ string normalizedPath = GetNormalizedFullPath(changeDescription.Path);
+ string normalizedOldPath = GetNormalizedFullPath(changeDescription.OldPath!);
+
+ string name = _fileSystem.Execute.Path.GetFileName(normalizedPath);
+ string oldName = _fileSystem.Execute.Path.GetFileName(normalizedOldPath);
+
+ if (name.Equals(oldName, _fileSystem.Execute.StringComparisonMode))
+ {
+ return false;
+ }
+
+ if (name.Length == 0 || oldName.Length == 0)
+ {
+ return false;
+ }
+
+ string? parent = _fileSystem.Execute.Path.GetDirectoryName(normalizedPath);
+ string? oldParent = _fileSystem.Execute.Path.GetDirectoryName(normalizedOldPath);
+
+ return string.Equals(parent, oldParent, _fileSystem.Execute.StringComparisonMode);
+ }
+
private bool MatchesFilter(ChangeDescription changeDescription)
{
if (!MatchesWatcherPath(changeDescription.Path))
@@ -465,6 +546,50 @@ private void Restart()
}
}
+ private void SetFileSystemEventArgsFullPath(FileSystemEventArgs args, string name)
+ {
+ if (_fileSystem.SimulationMode == SimulationMode.Native)
+ {
+ return;
+ }
+
+ string fullPath = _fileSystem.Execute.Path.Combine(Path, name);
+
+ // FileSystemEventArgs implicitly combines the path in https://github.com/dotnet/runtime/blob/v8.0.4/src/libraries/System.IO.FileSystem.Watcher/src/System/IO/FileSystemEventArgs.cs
+ // HACK: The combination uses the system separator, so to simulate the behavior, we must override it using reflection!
+#if NETFRAMEWORK
+ typeof(FileSystemEventArgs)
+ .GetField("fullPath", BindingFlags.Instance | BindingFlags.NonPublic)?
+ .SetValue(args, fullPath);
+#else
+ typeof(FileSystemEventArgs)
+ .GetField("_fullPath", BindingFlags.Instance | BindingFlags.NonPublic)?
+ .SetValue(args, fullPath);
+#endif
+ }
+
+ private void SetRenamedEventArgsFullPath(RenamedEventArgs args, string oldName)
+ {
+ if (_fileSystem.SimulationMode == SimulationMode.Native)
+ {
+ return;
+ }
+
+ string fullPath = _fileSystem.Execute.Path.Combine(Path, oldName);
+
+ // FileSystemEventArgs implicitly combines the path in https://github.com/dotnet/runtime/blob/v8.0.4/src/libraries/System.IO.FileSystem.Watcher/src/System/IO/FileSystemEventArgs.cs
+ // HACK: The combination uses the system separator, so to simulate the behavior, we must override it using reflection!
+#if NETFRAMEWORK
+ typeof(RenamedEventArgs)
+ .GetField("oldFullPath", BindingFlags.Instance | BindingFlags.NonPublic)?
+ .SetValue(args, fullPath);
+#else
+ typeof(RenamedEventArgs)
+ .GetField("_oldFullPath", BindingFlags.Instance | BindingFlags.NonPublic)?
+ .SetValue(args, fullPath);
+#endif
+ }
+
private void Start()
{
if (_isInitializing)
@@ -492,34 +617,45 @@ private void Start()
InternalBufferSize, channelCapacity)));
}
});
- _ = Task.Run(() =>
+ _ = Task.Run(async () =>
+ {
+ try
{
- try
+ while (await reader.WaitToReadAsync(token).ConfigureAwait(false))
{
- while (!token.IsCancellationRequested)
+ while (reader.TryRead(out ChangeDescription? c))
{
- if (reader.TryRead(out ChangeDescription? c))
- {
- NotifyChange(c);
- }
+ NotifyChange(c);
}
}
- catch (Exception)
+ }
+ catch (OperationCanceledException) when (token.IsCancellationRequested)
+ {
+ while (reader.TryRead(out ChangeDescription? c))
{
- //Ignore any exception
+ NotifyChange(c);
}
- },
- token)
- .ContinueWith(__ =>
- {
- if (channel.Writer.TryComplete())
+ }
+ catch (Exception)
{
+ // Ignore any other exception
+ }
+ finally
+ {
+ channel.Writer.TryComplete();
+
+ while (reader.TryRead(out _))
+ {
+ // Drain and ignore any remaining items so that Reader.Completion can complete deterministically
+ }
+
_ = channel.Reader.Completion.ContinueWith(_ =>
{
cancellationTokenSource.Dispose();
}, CancellationToken.None);
}
- }, TaskScheduler.Default);
+ },
+ token);
}
private void Stop()
@@ -532,17 +668,37 @@ private void Stop()
_changeHandler?.Dispose();
}
- private void TriggerRenameNotification(ChangeDescription item)
+ private FileSystemEventArgs ToFileSystemEventArgs(
+ WatcherChangeTypes changeType,
+ string changePath)
+ {
+ string name = TransformPathAndName(changePath);
+
+ FileSystemEventArgs eventArgs = new(changeType, Path, name);
+
+ SetFileSystemEventArgsFullPath(eventArgs, name);
+
+ return eventArgs;
+ }
+
+ private string TransformPathAndName(string changeDescriptionPath)
{
- // Outside: Outside the FullPath
- // Inside: FullPath/
- // Nested: FullPath/*/
- // Deep Nested: FullPath/*/**/
+ return changeDescriptionPath.Substring(FullPath.Length)
+ .TrimStart(_fileSystem.Execute.Path.DirectorySeparatorChar);
+ }
+ ///
+ /// Triggers the appropriate , , or events
+ /// for a rename operation in the file system. Determines whether an item was moved into, out of,
+ /// or within the watched directory, and raises the correct event(s) based on the platform
+ /// (Windows, Linux, macOS) and the current watcher settings (such as ).
+ ///
+ /// The representing the rename operation.
+ private void TriggerRenameNotification(ChangeDescription item)
+ {
bool comesFromOutside = !MatchesWatcherPath(item.OldPath, true);
bool goesToInside = MatchesWatcherPath(item.Path, false);
- // Outside -> Inside
if (comesFromOutside && goesToInside)
{
Created?.Invoke(this, ToFileSystemEventArgs(WatcherChangeTypes.Created, item.Path));
@@ -553,7 +709,6 @@ private void TriggerRenameNotification(ChangeDescription item)
bool comesFromInside = MatchesWatcherPath(item.OldPath, false);
bool goesToOutside = !MatchesWatcherPath(item.Path, true);
- // ... -> Outside
if (goesToOutside && (comesFromInside || IncludeSubdirectories))
{
Deleted?.Invoke(this, ToFileSystemEventArgs(WatcherChangeTypes.Deleted, item.OldPath!));
@@ -561,7 +716,6 @@ private void TriggerRenameNotification(ChangeDescription item)
return;
}
- // Inside -> Inside
if (comesFromInside && goesToInside)
{
if (TryMakeRenamedEventArgs(item, out RenamedEventArgs? eventArgs))
@@ -598,32 +752,173 @@ private void TriggerRenameNotification(ChangeDescription item)
}
}
- #pragma warning disable S3776 // Cognitive Complexity of methods should not be too high
- private void TriggerWindowsRenameNotification(ChangeDescription item, RenamedContext context)
+ private bool TryMakeRenamedEventArgs(
+ ChangeDescription changeDescription,
+ [NotNullWhen(true)] out RenamedEventArgs? eventArgs
+ )
{
- CheckRenamePremise(context);
-
- if (context.ComesFromOutside)
+ if (changeDescription.OldPath == null)
{
- if (IncludeSubdirectories)
- {
- FireCreated();
- }
+ eventArgs = null;
+
+ return false;
}
- else if (context.ComesFromInside)
- {
- FireDeleted();
- if (IncludeSubdirectories)
+ string name = TransformPathAndName(changeDescription.Path);
+
+ string oldName = TransformPathAndName(changeDescription.OldPath);
+
+ eventArgs = new RenamedEventArgs(changeDescription.ChangeType, Path, name, oldName);
+
+ SetFileSystemEventArgsFullPath(eventArgs, name);
+ SetRenamedEventArgsFullPath(eventArgs, oldName);
+
+ return true;
+ }
+
+ private IWaitForChangedResult WaitForChangedInternal(
+ WatcherChangeTypes changeType, TimeSpan timeout)
+ {
+ TaskCompletionSource
+ tcs = new();
+
+ void EventHandler(object? _, ChangeDescriptionEventArgs c)
+ {
+ if ((c.ChangeDescription.ChangeType & changeType) != 0)
{
- FireCreated();
+ tcs.TrySetResult(new WaitForChangedResultMock(
+ c.ChangeDescription.ChangeType,
+ c.ChangeDescription.Name,
+ oldName: c.ChangeDescription.OldName,
+ timedOut: false));
}
}
- else if (context.ComesFromNested || context.ComesFromDeepNested)
+
+ InternalEvent += EventHandler;
+ try
{
- if (context.GoesToInside)
+ bool wasEnabled = EnableRaisingEvents;
+ if (!wasEnabled)
{
- if (IncludeSubdirectories)
+ EnableRaisingEvents = true;
+ }
+
+ #pragma warning disable MA0040
+ tcs.Task.Wait(timeout);
+ #pragma warning restore MA0040
+ EnableRaisingEvents = wasEnabled;
+ }
+ finally
+ {
+ InternalEvent -= EventHandler;
+ }
+
+#if NETFRAMEWORK
+ return tcs.Task.IsCompleted
+ ? tcs.Task.Result
+ : WaitForChangedResultMock.TimedOutResult;
+#else
+ return tcs.Task.IsCompletedSuccessfully
+ ? tcs.Task.Result
+ : WaitForChangedResultMock.TimedOutResult;
+#endif
+ }
+
+ private struct WaitForChangedResultMock : IWaitForChangedResult
+ {
+ public WaitForChangedResultMock(
+ WatcherChangeTypes changeType,
+ string? name,
+ string? oldName,
+ bool timedOut)
+ {
+ ChangeType = changeType;
+ Name = name;
+ OldName = oldName;
+ TimedOut = timedOut;
+ }
+
+ ///
+ /// The instance representing a timed out .
+ ///
+ public static readonly WaitForChangedResultMock TimedOutResult =
+ new(changeType: 0, name: null, oldName: null, timedOut: true);
+
+ ///
+ public WatcherChangeTypes ChangeType { get; }
+
+ ///
+ public string? Name { get; }
+
+ ///
+ public string? OldName { get; }
+
+ ///
+ public bool TimedOut { get; }
+ }
+
+ [StructLayout(LayoutKind.Auto)]
+ private readonly struct RenamedContext(
+ bool comesFromOutside,
+ bool comesFromInside,
+ bool goesToInside,
+ bool goesToOutside,
+ int oldSubDirectoryCount
+ )
+ {
+ private const int NestedLevelCount = 1;
+
+ public bool ComesFromOutside { get; } = comesFromOutside;
+
+ public bool ComesFromInside { get; } = comesFromInside;
+
+ public bool GoesToInside { get; } = goesToInside;
+
+ public bool GoesToOutside { get; } = goesToOutside;
+
+ ///
+ /// If this is then is
+ ///
+ public bool ComesFromNested { get; } = oldSubDirectoryCount == NestedLevelCount;
+
+ ///
+ /// If this is then is
+ ///
+ public bool ComesFromDeepNested { get; } = oldSubDirectoryCount > NestedLevelCount;
+ }
+
+ internal sealed class ChangeDescriptionEventArgs(ChangeDescription changeDescription)
+ : EventArgs
+ {
+ public ChangeDescription ChangeDescription { get; } = changeDescription;
+ }
+
+ #pragma warning disable S3776 // Cognitive Complexity of methods should not be too high
+ private void TriggerWindowsRenameNotification(ChangeDescription item, RenamedContext context)
+ {
+ CheckRenamePremise(context);
+
+ if (context.ComesFromOutside)
+ {
+ if (IncludeSubdirectories)
+ {
+ FireCreated();
+ }
+ }
+ else if (context.ComesFromInside)
+ {
+ FireDeleted();
+
+ if (IncludeSubdirectories)
+ {
+ FireCreated();
+ }
+ }
+ else if (context.ComesFromNested || context.ComesFromDeepNested)
+ {
+ if (context.GoesToInside)
+ {
+ if (IncludeSubdirectories)
{
FireDeleted();
}
@@ -719,289 +1014,4 @@ private void TriggerLinuxRenameNotification(ChangeDescription item, RenamedConte
}
}
#pragma warning restore S3776 // Cognitive Complexity of methods should not be too high
-
- private static void CheckRenamePremise(RenamedContext context)
- {
- Debug.Assert(
- context is not { ComesFromOutside: true, GoesToInside: true },
- "The premise { ComesFromOutside: true, GoesToInside: true } should have been handled."
- );
-
- Debug.Assert(
- context is not { ComesFromInside: true, GoesToInside: true },
- "The premise { ComesFromInside: true, GoesToInside: true } should have been handled."
- );
-
- Debug.Assert(
- !context.GoesToOutside, "The premise { GoesToOutside: true } should have been handled."
- );
- }
-
- private string? GetNormalizedParent(string? path)
- {
- if (path == null)
- {
- return null;
- }
-
- string normalized = GetNormalizedFullPath(path);
-
- return _fileSystem.Execute.Path.GetDirectoryName(normalized)
- ?.TrimEnd(_fileSystem.Execute.Path.DirectorySeparatorChar);
- }
-
- private string GetNormalizedFullPath(string path)
- {
- string normalized = _fileSystem.Execute.Path.GetFullPath(path);
-
- return normalized.TrimEnd(_fileSystem.Execute.Path.DirectorySeparatorChar);
- }
-
- ///
- /// Counts the number of directory separators inside the relative path to
- ///
- ///
- /// The number of directory separators inside the relative path to
- /// Returns -1 if the path is outside the
- private int GetSubDirectoryCount(string path)
- {
- string normalizedPath = GetNormalizedFullPath(path);
-
- if (!normalizedPath.StartsWith(FullPath, _fileSystem.Execute.StringComparisonMode))
- {
- return -1;
- }
-
- return normalizedPath.Substring(FullPath.Length)
- .TrimStart(_fileSystem.Execute.Path.DirectorySeparatorChar)
- .Count(c => c == _fileSystem.Execute.Path.DirectorySeparatorChar);
- }
-
- private bool IsItemNameChange(ChangeDescription changeDescription)
- {
- string normalizedPath = GetNormalizedFullPath(changeDescription.Path);
- string normalizedOldPath = GetNormalizedFullPath(changeDescription.OldPath!);
-
- string name = _fileSystem.Execute.Path.GetFileName(normalizedPath);
- string oldName = _fileSystem.Execute.Path.GetFileName(normalizedOldPath);
-
- if (name.Equals(oldName, _fileSystem.Execute.StringComparisonMode))
- {
- return false;
- }
-
- if (name.Length == 0 || oldName.Length == 0)
- {
- return false;
- }
-
- string? parent = _fileSystem.Execute.Path.GetDirectoryName(normalizedPath);
- string? oldParent = _fileSystem.Execute.Path.GetDirectoryName(normalizedOldPath);
-
- return string.Equals(parent, oldParent, _fileSystem.Execute.StringComparisonMode);
- }
-
- private bool TryMakeRenamedEventArgs(
- ChangeDescription changeDescription,
- [NotNullWhen(true)] out RenamedEventArgs? eventArgs
- )
- {
- if (changeDescription.OldPath == null)
- {
- eventArgs = null;
-
- return false;
- }
-
- string name = TransformPathAndName(changeDescription.Path);
-
- string oldName = TransformPathAndName(changeDescription.OldPath);
-
- eventArgs = new RenamedEventArgs(changeDescription.ChangeType, Path, name, oldName);
-
- SetFileSystemEventArgsFullPath(eventArgs, name);
- SetRenamedEventArgsFullPath(eventArgs, oldName);
-
- return true;
- }
-
- private FileSystemEventArgs ToFileSystemEventArgs(
- WatcherChangeTypes changeType,
- string changePath)
- {
- string name = TransformPathAndName(changePath);
-
- FileSystemEventArgs eventArgs = new(changeType, Path, name);
-
- SetFileSystemEventArgsFullPath(eventArgs, name);
-
- return eventArgs;
- }
-
- private string TransformPathAndName(string changeDescriptionPath)
- {
- return changeDescriptionPath.Substring(FullPath.Length)
- .TrimStart(_fileSystem.Execute.Path.DirectorySeparatorChar);
- }
-
- private void SetFileSystemEventArgsFullPath(FileSystemEventArgs args, string name)
- {
- if (_fileSystem.SimulationMode == SimulationMode.Native)
- {
- return;
- }
-
- string fullPath = _fileSystem.Execute.Path.Combine(Path, name);
-
- // FileSystemEventArgs implicitly combines the path in https://github.com/dotnet/runtime/blob/v8.0.4/src/libraries/System.IO.FileSystem.Watcher/src/System/IO/FileSystemEventArgs.cs
- // HACK: The combination uses the system separator, so to simulate the behavior, we must override it using reflection!
-#if NETFRAMEWORK
- typeof(FileSystemEventArgs)
- .GetField("fullPath", BindingFlags.Instance | BindingFlags.NonPublic)?
- .SetValue(args, fullPath);
-#else
- typeof(FileSystemEventArgs)
- .GetField("_fullPath", BindingFlags.Instance | BindingFlags.NonPublic)?
- .SetValue(args, fullPath);
-#endif
- }
-
- private void SetRenamedEventArgsFullPath(RenamedEventArgs args, string oldName)
- {
- if (_fileSystem.SimulationMode == SimulationMode.Native)
- {
- return;
- }
-
- string fullPath = _fileSystem.Execute.Path.Combine(Path, oldName);
-
- // FileSystemEventArgs implicitly combines the path in https://github.com/dotnet/runtime/blob/v8.0.4/src/libraries/System.IO.FileSystem.Watcher/src/System/IO/FileSystemEventArgs.cs
- // HACK: The combination uses the system separator, so to simulate the behavior, we must override it using reflection!
-#if NETFRAMEWORK
- typeof(RenamedEventArgs)
- .GetField("oldFullPath", BindingFlags.Instance | BindingFlags.NonPublic)?
- .SetValue(args, fullPath);
-#else
- typeof(RenamedEventArgs)
- .GetField("_oldFullPath", BindingFlags.Instance | BindingFlags.NonPublic)?
- .SetValue(args, fullPath);
-#endif
- }
-
- private IWaitForChangedResult WaitForChangedInternal(
- WatcherChangeTypes changeType, TimeSpan timeout)
- {
- TaskCompletionSource
- tcs = new();
-
- void EventHandler(object? _, ChangeDescriptionEventArgs c)
- {
- if ((c.ChangeDescription.ChangeType & changeType) != 0)
- {
- tcs.TrySetResult(new WaitForChangedResultMock(
- c.ChangeDescription.ChangeType,
- c.ChangeDescription.Name,
- oldName: c.ChangeDescription.OldName,
- timedOut: false));
- }
- }
-
- InternalEvent += EventHandler;
- try
- {
- bool wasEnabled = EnableRaisingEvents;
- if (!wasEnabled)
- {
- EnableRaisingEvents = true;
- }
-
- #pragma warning disable MA0040
- tcs.Task.Wait(timeout);
- #pragma warning restore MA0040
- EnableRaisingEvents = wasEnabled;
- }
- finally
- {
- InternalEvent -= EventHandler;
- }
-
-#if NETFRAMEWORK
- return tcs.Task.IsCompleted
- ? tcs.Task.Result
- : WaitForChangedResultMock.TimedOutResult;
-#else
- return tcs.Task.IsCompletedSuccessfully
- ? tcs.Task.Result
- : WaitForChangedResultMock.TimedOutResult;
-#endif
- }
-
- private struct WaitForChangedResultMock : IWaitForChangedResult
- {
- public WaitForChangedResultMock(
- WatcherChangeTypes changeType,
- string? name,
- string? oldName,
- bool timedOut)
- {
- ChangeType = changeType;
- Name = name;
- OldName = oldName;
- TimedOut = timedOut;
- }
-
- ///
- /// The instance representing a timed out .
- ///
- public static readonly WaitForChangedResultMock TimedOutResult =
- new(changeType: 0, name: null, oldName: null, timedOut: true);
-
- ///
- public WatcherChangeTypes ChangeType { get; }
-
- ///
- public string? Name { get; }
-
- ///
- public string? OldName { get; }
-
- ///
- public bool TimedOut { get; }
- }
-
- [StructLayout(LayoutKind.Auto)]
- private readonly struct RenamedContext(
- bool comesFromOutside,
- bool comesFromInside,
- bool goesToInside,
- bool goesToOutside,
- int oldSubDirectoryCount
- )
- {
- private const int NestedLevelCount = 1;
-
- public bool ComesFromOutside { get; } = comesFromOutside;
-
- public bool ComesFromInside { get; } = comesFromInside;
-
- public bool GoesToInside { get; } = goesToInside;
-
- public bool GoesToOutside { get; } = goesToOutside;
-
- ///
- /// If this is then is
- ///
- public bool ComesFromNested { get; } = oldSubDirectoryCount == NestedLevelCount;
-
- ///
- /// If this is then is
- ///
- public bool ComesFromDeepNested { get; } = oldSubDirectoryCount > NestedLevelCount;
- }
-
- internal sealed class ChangeDescriptionEventArgs(ChangeDescription changeDescription)
- : EventArgs
- {
- public ChangeDescription ChangeDescription { get; } = changeDescription;
- }
}
diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/EventTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/EventTests.cs
index 8ab470e3..265a5aee 100644
--- a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/EventTests.cs
+++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/EventTests.cs
@@ -7,7 +7,6 @@
namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcher;
[FileSystemTests]
-[NotInParallel(nameof(EventTests))]
public class EventTests(FileSystemTestData testData) : FileSystemTestBase(testData)
{
[Test]
diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/MoveTests.Unix.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/MoveTests.Unix.cs
index 96e23930..34a2b800 100644
--- a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/MoveTests.Unix.cs
+++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/MoveTests.Unix.cs
@@ -63,7 +63,7 @@ out ConcurrentBag createdBag
// Assert
- await That(createdMs.Wait(ExpectTimeout, CancellationToken))
+ await That(createdMs.Wait(includeSubdirectories ? ExpectSuccess : ExpectTimeout, CancellationToken))
.IsEqualTo(includeSubdirectories);
await That(deletedMs.Wait(ExpectTimeout, CancellationToken)).IsFalse();
@@ -147,10 +147,10 @@ out ConcurrentBag deletedBag
// Assert
- await That(deletedMs.Wait(ExpectTimeout, CancellationToken))
+ await That(deletedMs.Wait(!isRenamed ? ExpectSuccess : ExpectTimeout, CancellationToken))
.IsEqualTo(!isRenamed);
- await That(renamedMs.Wait(ExpectTimeout, CancellationToken))
+ await That(renamedMs.Wait(isRenamed ? ExpectSuccess : ExpectTimeout, CancellationToken))
.IsEqualTo(isRenamed);
await That(changedMs.Wait(ExpectTimeout, CancellationToken)).IsFalse();
@@ -255,10 +255,10 @@ out ConcurrentBag createdBag
// Assert
- await That(createdMs.Wait(ExpectTimeout, CancellationToken))
+ await That(createdMs.Wait(isCreated ? ExpectSuccess : ExpectTimeout, CancellationToken))
.IsEqualTo(isCreated);
- await That(renamedMs.Wait(ExpectTimeout, CancellationToken))
+ await That(renamedMs.Wait(includeSubdirectories ? ExpectSuccess : ExpectTimeout, CancellationToken))
.IsEqualTo(includeSubdirectories);
await That(changedMs.Wait(ExpectTimeout, CancellationToken)).IsFalse();
@@ -360,10 +360,10 @@ out ConcurrentBag createdBag
// Assert
- await That(createdMs.Wait(ExpectTimeout, CancellationToken))
+ await That(createdMs.Wait(isCreated ? ExpectSuccess : ExpectTimeout, CancellationToken))
.IsEqualTo(isCreated);
- await That(renamedMs.Wait(ExpectTimeout, CancellationToken))
+ await That(renamedMs.Wait(includeSubdirectories ? ExpectSuccess : ExpectTimeout, CancellationToken))
.IsEqualTo(includeSubdirectories);
await That(changedMs.Wait(ExpectTimeout, CancellationToken)).IsFalse();
diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/MoveTests.Windows.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/MoveTests.Windows.cs
index 74d2d796..cc555a7a 100644
--- a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/MoveTests.Windows.cs
+++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/MoveTests.Windows.cs
@@ -59,7 +59,7 @@ out ConcurrentBag createdBag
// Assert
- await That(createdMs.Wait(ExpectTimeout, CancellationToken))
+ await That(createdMs.Wait(includeSubdirectories ? ExpectSuccess : ExpectTimeout, CancellationToken))
.IsEqualTo(includeSubdirectories);
await That(deletedMs.Wait(ExpectTimeout, CancellationToken)).IsFalse();
@@ -133,9 +133,9 @@ out ConcurrentBag createdBag
// Assert
- await That(deletedMs.Wait(ExpectTimeout, CancellationToken)).IsTrue();
+ await That(deletedMs.Wait(ExpectSuccess, CancellationToken)).IsTrue();
- await That(createdMs.Wait(ExpectTimeout, CancellationToken))
+ await That(createdMs.Wait(includeSubdirectories ? ExpectSuccess : ExpectTimeout, CancellationToken))
.IsEqualTo(includeSubdirectories);
await That(renamedMs.Wait(ExpectTimeout, CancellationToken)).IsFalse();
@@ -229,10 +229,10 @@ out ConcurrentBag deletedBag
// Assert
- await That(createdMs.Wait(ExpectTimeout, CancellationToken))
+ await That(createdMs.Wait(isCreated ? ExpectSuccess : ExpectTimeout, CancellationToken))
.IsEqualTo(isCreated);
- await That(deletedMs.Wait(ExpectTimeout, CancellationToken))
+ await That(deletedMs.Wait(includeSubdirectories ? ExpectSuccess : ExpectTimeout, CancellationToken))
.IsEqualTo(includeSubdirectories);
await That(renamedMs.Wait(ExpectTimeout, CancellationToken)).IsFalse();
@@ -333,10 +333,10 @@ out ConcurrentBag deletedBag
// Assert
- await That(createdMs.Wait(ExpectTimeout, CancellationToken))
+ await That(createdMs.Wait(isCreated ? ExpectSuccess : ExpectTimeout, CancellationToken))
.IsEqualTo(isCreated);
- await That(deletedMs.Wait(ExpectTimeout, CancellationToken))
+ await That(deletedMs.Wait(includeSubdirectories ? ExpectSuccess : ExpectTimeout, CancellationToken))
.IsEqualTo(includeSubdirectories);
await That(renamedMs.Wait(ExpectTimeout, CancellationToken)).IsFalse();
diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/MoveTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/MoveTests.cs
index 7e7e7bf9..e59679be 100644
--- a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/MoveTests.cs
+++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/MoveTests.cs
@@ -6,8 +6,6 @@
namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcher;
[FileSystemTests]
-// TODO #956: Investigate, why these tests are not stable when run in parallel
-[NotInParallel(nameof(MoveTests))]
public partial class MoveTests(FileSystemTestData testData) : FileSystemTestBase(testData)
{
[Test]
@@ -51,7 +49,7 @@ out ConcurrentBag createdBag
// Assert
- await That(createdMs.Wait(ExpectTimeout, CancellationToken)).IsTrue();
+ await That(createdMs.Wait(ExpectSuccess, CancellationToken)).IsTrue();
await That(deletedMs.Wait(ExpectTimeout, CancellationToken)).IsFalse();
await That(renamedMs.Wait(ExpectTimeout, CancellationToken)).IsFalse();
@@ -122,7 +120,7 @@ out ConcurrentBag deletedBag
// Assert
- await That(deletedMs.Wait(ExpectTimeout, CancellationToken))
+ await That(deletedMs.Wait(shouldInvokeDeleted ? ExpectSuccess : ExpectTimeout, CancellationToken))
.IsEqualTo(shouldInvokeDeleted);
await That(createdMs.Wait(ExpectTimeout, CancellationToken)).IsFalse();
@@ -206,7 +204,7 @@ params string[] paths
// Assert
- await That(renamedMs.Wait(ExpectTimeout, CancellationToken))
+ await That(renamedMs.Wait(shouldInvokeRenamed ? ExpectSuccess : ExpectTimeout, CancellationToken))
.IsEqualTo(shouldInvokeRenamed);
await That(deletedMs.Wait(ExpectTimeout, CancellationToken)).IsFalse();