From 13281d988cfd2e1ed81f6dc42a28a55c90277b7d Mon Sep 17 00:00:00 2001 From: gstone Date: Tue, 6 Jan 2026 12:09:31 -0500 Subject: [PATCH 1/3] fix: additional NotifyFilterTests --- .../FileSystemWatcher/NotifyFiltersTests.cs | 186 ++++++++++++++++++ 1 file changed, 186 insertions(+) diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/NotifyFiltersTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/NotifyFiltersTests.cs index dd58e9cd..9e7c2881 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/NotifyFiltersTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/NotifyFiltersTests.cs @@ -524,6 +524,54 @@ public async Task NotifyFilter_MoveFile_DifferentDirectories_ShouldNotNotify_OnW await That(result).IsNull(); } + [Theory] + [InlineAutoData(true)] + [InlineAutoData(false)] + public async Task NotifyFilter_MoveFileOutOfTheWatchedDirectory_ShouldTriggerRenamed_OnLinuxOrMac( + bool includeSubdirectories, string sourcePath, string sourceName, + string destinationPath, string destinationName) + { + SkipIfLongRunningTestsShouldBeSkipped(); + Skip.If(Test.RunsOnWindows); + + FileSystem.Initialize() + .WithSubdirectory(sourcePath).Initialized(s => s + .WithFile(sourceName)) + .WithSubdirectory(destinationPath); + RenamedEventArgs? result = null; + using ManualResetEventSlim ms = new(); + using IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(sourcePath); + fileSystemWatcher.Renamed += (_, eventArgs) => + { + // ReSharper disable once AccessToDisposedClosure + try + { + result = eventArgs; + ms.Set(); + } + catch (ObjectDisposedException) + { + // Ignore any ObjectDisposedException + } + }; + + fileSystemWatcher.IncludeSubdirectories = includeSubdirectories; + fileSystemWatcher.EnableRaisingEvents = true; + + FileSystem.File.Move( + FileSystem.Path.Combine(sourcePath, sourceName), + FileSystem.Path.Combine(destinationPath, destinationName)); + + await That(ms.Wait(ExpectSuccess, TestContext.Current.CancellationToken)).IsTrue(); + await That(result).IsNotNull(); + await That(result!.ChangeType).IsEqualTo(WatcherChangeTypes.Renamed); + await That(result.FullPath).IsEqualTo(FileSystem.Path.Combine(destinationPath, destinationName)); + await That(result.Name).IsEqualTo(destinationName); + await That(result.OldFullPath).IsEqualTo(FileSystem.Path.Combine(sourcePath, sourceName)); + await That(result.OldName).IsEqualTo(sourceName); + } + [Theory] [InlineAutoData(true)] [InlineAutoData(false)] @@ -570,6 +618,54 @@ public async Task NotifyFilter_MoveFileOutOfTheWatchedDirectory_ShouldTriggerDel await That(result.Name).IsEqualTo(sourceName); } + [Theory] + [InlineAutoData(true)] + [InlineAutoData(false)] + public async Task NotifyFilter_MoveFileInToTheWatchedDirectory_ShouldTriggerRenamed_OnLinuxOrMac( + bool includeSubdirectories, string sourcePath, string sourceName, + string destinationPath, string destinationName) + { + SkipIfLongRunningTestsShouldBeSkipped(); + Skip.If(Test.RunsOnWindows); + + FileSystem.Initialize() + .WithSubdirectory(sourcePath).Initialized(s => s + .WithFile(sourceName)) + .WithSubdirectory(destinationPath); + RenamedEventArgs? result = null; + using ManualResetEventSlim ms = new(); + using IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New(destinationPath); + fileSystemWatcher.Renamed += (_, eventArgs) => + { + // ReSharper disable once AccessToDisposedClosure + try + { + result = eventArgs; + ms.Set(); + } + catch (ObjectDisposedException) + { + // Ignore any ObjectDisposedException + } + }; + + fileSystemWatcher.IncludeSubdirectories = includeSubdirectories; + fileSystemWatcher.EnableRaisingEvents = true; + + FileSystem.File.Move( + FileSystem.Path.Combine(sourcePath, sourceName), + FileSystem.Path.Combine(destinationPath, destinationName)); + + await That(ms.Wait(ExpectSuccess, TestContext.Current.CancellationToken)).IsTrue(); + await That(result).IsNotNull(); + await That(result!.ChangeType).IsEqualTo(WatcherChangeTypes.Renamed); + await That(result.FullPath).IsEqualTo(FileSystem.Path.Combine(destinationPath, destinationName)); + await That(result.Name).IsEqualTo(destinationName); + await That(result.OldFullPath).IsEqualTo(FileSystem.Path.Combine(sourcePath, sourceName)); + await That(result.OldName).IsEqualTo(sourceName); + } + [Theory] [InlineAutoData(true)] [InlineAutoData(false)] @@ -745,6 +841,51 @@ public async Task NotifyFilter_MoveDirectory_ShouldTriggerRenamedEventOnNotifyFi await That(result.OldName).IsEqualTo(FileSystem.Path.GetFileName(sourceName)); } + [Theory] + [InlineAutoData(NotifyFilters.DirectoryName, true)] + [InlineAutoData(NotifyFilters.DirectoryName, false)] + public async Task NotifyFilter_MoveDirectoryOutOfTheWatchedDirectory_ShouldTriggerRenamedEventOnNotifyFilters_OnLinuxOrMac( + NotifyFilters notifyFilter, bool includeSubdirectories, string sourceName, string destinationName) + { + SkipIfLongRunningTestsShouldBeSkipped(); + Skip.If(Test.RunsOnWindows); + + FileSystem.Initialize().WithSubdirectory("watched"); + var sourcePath = FileSystem.Path.Combine("watched", sourceName); + FileSystem.Directory.CreateDirectory(sourcePath); + RenamedEventArgs? result = null; + using ManualResetEventSlim ms = new(); + using IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New("watched"); + fileSystemWatcher.Renamed += (_, eventArgs) => + { + // ReSharper disable once AccessToDisposedClosure + try + { + result = eventArgs; + ms.Set(); + } + catch (ObjectDisposedException) + { + // Ignore any ObjectDisposedException + } + }; + + fileSystemWatcher.NotifyFilter = notifyFilter; + fileSystemWatcher.IncludeSubdirectories = includeSubdirectories; + fileSystemWatcher.EnableRaisingEvents = true; + + FileSystem.Directory.Move(sourcePath, destinationName); + + await That(ms.Wait(ExpectSuccess, TestContext.Current.CancellationToken)).IsTrue(); + await That(result).IsNotNull(); + await That(result!.ChangeType).IsEqualTo(WatcherChangeTypes.Renamed); + await That(result.FullPath).IsEqualTo(destinationName); + await That(result.Name).IsEqualTo(destinationName); + await That(result.OldFullPath).IsEqualTo(sourcePath); + await That(result.OldName).IsEqualTo(sourceName); + } + [Theory] [InlineAutoData(NotifyFilters.DirectoryName, true)] [InlineAutoData(NotifyFilters.DirectoryName, false)] @@ -788,6 +929,51 @@ public async Task NotifyFilter_MoveDirectoryOutOfTheWatchedDirectory_ShouldTrigg await That(result.Name).IsEqualTo(sourceName); } + [Theory] + [InlineAutoData(NotifyFilters.DirectoryName, true)] + [InlineAutoData(NotifyFilters.DirectoryName, false)] + public async Task NotifyFilter_MoveDirectoryInToTheWatchedDirectory_ShouldTriggerRenamedEventOnNotifyFilters_OnLinuxOrMac( + NotifyFilters notifyFilter, bool includeSubdirectories, string sourceName, string destinationName) + { + SkipIfLongRunningTestsShouldBeSkipped(); + Skip.If(Test.RunsOnWindows); + + FileSystem.Initialize().WithSubdirectory("watched"); + var destinationPath = FileSystem.Path.Combine("watched", destinationName); + FileSystem.Directory.CreateDirectory(sourceName); + RenamedEventArgs? result = null; + using ManualResetEventSlim ms = new(); + using IFileSystemWatcher fileSystemWatcher = + FileSystem.FileSystemWatcher.New("watched"); + fileSystemWatcher.Renamed += (_, eventArgs) => + { + // ReSharper disable once AccessToDisposedClosure + try + { + result = eventArgs; + ms.Set(); + } + catch (ObjectDisposedException) + { + // Ignore any ObjectDisposedException + } + }; + + fileSystemWatcher.NotifyFilter = notifyFilter; + fileSystemWatcher.IncludeSubdirectories = includeSubdirectories; + fileSystemWatcher.EnableRaisingEvents = true; + + FileSystem.Directory.Move(sourceName, destinationPath); + + await That(ms.Wait(ExpectSuccess, TestContext.Current.CancellationToken)).IsTrue(); + await That(result).IsNotNull(); + await That(result!.ChangeType).IsEqualTo(WatcherChangeTypes.Renamed); + await That(result.FullPath).IsEqualTo(destinationPath); + await That(result.Name).IsEqualTo(destinationName); + await That(result.OldFullPath).IsEqualTo(sourceName); + await That(result.OldName).IsEqualTo(sourceName); + } + [Theory] [InlineAutoData(NotifyFilters.DirectoryName, true)] [InlineAutoData(NotifyFilters.DirectoryName, false)] From 284d3b6c0b8fc77d2b453a6d45cd0ffbe2b3027c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valentin=20Breu=C3=9F?= Date: Wed, 7 Jan 2026 14:31:15 +0100 Subject: [PATCH 2/3] Adjust implementation so that tests succeed --- .../FileSystem/FileSystemWatcherMock.cs | 214 ++++++++++-------- .../IncludeSubdirectoriesTests.cs | 57 +---- 2 files changed, 134 insertions(+), 137 deletions(-) diff --git a/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherMock.cs index e1bff42a..53a55859 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherMock.cs @@ -24,11 +24,38 @@ internal sealed class FileSystemWatcherMock : Component, IFileSystemWatcher /// private const int BytesPerMessage = 128; + /// + /// Caches the full path of + /// + private string FullPath + { + get => _fullPath; + set + { + if (string.IsNullOrEmpty(value)) + { + _fullPath = value; + + return; + } + + string fullPath = _fileSystem.Path.GetFullPath(value); + + if (!fullPath.EndsWith(_fileSystem.Path.DirectorySeparatorChar)) + { + fullPath += _fileSystem.Path.DirectorySeparatorChar; + } + + _fullPath = fullPath; + } + } + private CancellationTokenSource? _cancellationTokenSource; private IDisposable? _changeHandler; private bool _enableRaisingEvents; private readonly MockFileSystem _fileSystem; private readonly Collection _filters = []; + private string _fullPath = string.Empty; private bool _includeSubdirectories; private int _internalBufferSize = 8192; private bool _isInitializing; @@ -38,7 +65,6 @@ internal sealed class FileSystemWatcherMock : Component, IFileSystemWatcher NotifyFilters.LastWrite; private string _path = string.Empty; - private string _fullPath = string.Empty; private ISynchronizeInvoke? _synchronizingObject; @@ -260,32 +286,6 @@ public ISynchronizeInvoke? SynchronizingObject } } - /// - /// Caches the full path of - /// - private string FullPath - { - get => _fullPath; - set - { - if (string.IsNullOrEmpty(value)) - { - _fullPath = value; - - return; - } - - string fullPath = _fileSystem.Path.GetFullPath(value); - - if (!fullPath.EndsWith(_fileSystem.Path.DirectorySeparatorChar)) - { - fullPath += _fileSystem.Path.DirectorySeparatorChar; - } - - _fullPath = fullPath; - } - } - /// public void BeginInit() { @@ -373,6 +373,25 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); } + private static string GetCommonDirectory(string path1, string path2, + StringComparison comparisonMode) + { + for (int i = 0; i < path1.Length; i++) + { + if (path2.Length <= i) + { + return path2; + } + + if (!string.Equals(path1[i].ToString(), path2[i].ToString(), comparisonMode)) + { + return path1.Substring(0, Math.Max(0, i - 1)); + } + } + + return path1; + } + private bool MatchesFilter(ChangeDescription changeDescription) { if (!MatchesWatcherPath(changeDescription.Path)) @@ -463,6 +482,46 @@ private void Restart() } } + private void SetFileSystemEventArgsFullPath(FileSystemEventArgs args, string fullPath) + { + if (_fileSystem.SimulationMode == SimulationMode.Native) + { + return; + } + + // 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 SetRenamedEventArgsOldFullPath(RenamedEventArgs args, string oldFullPath) + { + if (_fileSystem.SimulationMode == SimulationMode.Native) + { + return; + } + + // 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, oldFullPath); +#else + typeof(RenamedEventArgs) + .GetField("_oldFullPath", BindingFlags.Instance | BindingFlags.NonPublic)? + .SetValue(args, oldFullPath); +#endif + } + private void Start() { if (_isInitializing) @@ -530,6 +589,30 @@ private void Stop() _changeHandler?.Dispose(); } + private FileSystemEventArgs ToFileSystemEventArgs( + WatcherChangeTypes changeType, + string changePath) + { + string name = TransformPathAndName(changePath); + + FileSystemEventArgs eventArgs = new(changeType, Path, name); + + SetFileSystemEventArgsFullPath(eventArgs, _fileSystem.Path.Combine(Path, name)); + + return eventArgs; + } + + private string TransformPathAndName(string changeDescriptionPath) + { + if (changeDescriptionPath.StartsWith(FullPath, _fileSystem.Execute.StringComparisonMode)) + { + return changeDescriptionPath.Substring(FullPath.Length) + .TrimStart(_fileSystem.Path.DirectorySeparatorChar); + } + + return _fileSystem.Path.GetFileName(changeDescriptionPath); + } + private void TriggerRenameNotification(ChangeDescription item) { if (_fileSystem.Execute.IsWindows) @@ -583,8 +666,19 @@ private bool TryMakeRenamedEventArgs( eventArgs = new RenamedEventArgs(changeDescription.ChangeType, Path, name, oldName); - SetFileSystemEventArgsFullPath(eventArgs, name); - SetRenamedEventArgsFullPath(eventArgs, oldName); + string commonDirectory = GetCommonDirectory(changeDescription.Path, + changeDescription.OldPath, _fileSystem.Execute.StringComparisonMode); + + SetFileSystemEventArgsFullPath(eventArgs, + changeDescription.Path.StartsWith(FullPath, _fileSystem.Execute.StringComparisonMode) + ? _fileSystem.Path.Combine(Path, name) + : changeDescription.Path.Substring(commonDirectory.Length) + .TrimStart(_fileSystem.Path.DirectorySeparatorChar)); + SetRenamedEventArgsOldFullPath(eventArgs, + changeDescription.OldPath.StartsWith(FullPath, _fileSystem.Execute.StringComparisonMode) + ? _fileSystem.Path.Combine(Path, oldName) + : changeDescription.OldPath.Substring(commonDirectory.Length) + .TrimStart(_fileSystem.Path.DirectorySeparatorChar)); return _fileSystem.Execute.Path.GetDirectoryName(changeDescription.Path)?.Equals( _fileSystem.Execute.Path.GetDirectoryName(changeDescription.OldPath), @@ -593,68 +687,6 @@ private bool TryMakeRenamedEventArgs( ?? 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.Path.DirectorySeparatorChar); - } - - private void SetFileSystemEventArgsFullPath(FileSystemEventArgs args, string name) - { - if (_fileSystem.SimulationMode == SimulationMode.Native) - { - return; - } - - string fullPath = _fileSystem.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.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) { diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/IncludeSubdirectoriesTests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/IncludeSubdirectoriesTests.cs index 32999384..2b27c4ad 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/IncludeSubdirectoriesTests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/IncludeSubdirectoriesTests.cs @@ -133,7 +133,6 @@ string fileName { string subdirectoryName = "a"; string subSubdirectoryName = "b"; - // Arrange FileSystem.Initialize().WithSubdirectory(baseDirectory) .Initialized(s => s.WithSubdirectory(subdirectoryName) .Initialized(ss => ss.WithSubdirectory(subSubdirectoryName)) @@ -172,18 +171,14 @@ string fileName fileSystemWatcher.IncludeSubdirectories = true; fileSystemWatcher.EnableRaisingEvents = true; - - // Act FileSystem.File.Create(filePath).Dispose(); - - // Assert await That(createdMre.Wait(ExpectTimeout, TestContext.Current.CancellationToken)).IsTrue(); await That(createdArgs).IsNotNull().And - .Satisfies(args => string.Equals(args?.Name, expectedFileName, StringComparison.Ordinal) - ); + .Satisfies(args => string.Equals(args?.Name, expectedFileName, + StringComparison.Ordinal)); } [Theory] @@ -197,29 +192,22 @@ string fileName { string subdirectoryName = "a"; string subSubdirectoryName = "b"; - // Arrange FileSystem.Initialize().WithSubdirectory(baseDirectory) .Initialized(s => s.WithSubdirectory(subdirectoryName) .Initialized(ss => ss.WithSubdirectory(subSubdirectoryName)) ); - string filePath = FileSystem.Path.Combine( baseDirectory, subdirectoryName, subSubdirectoryName, fileName ); - string expectedFileName = FileSystem.Path.Combine( subdirectoryName, subSubdirectoryName, fileName ); - string watchPath = watchRootedPath ? FileSystem.Path.Combine(FileSystem.Directory.GetCurrentDirectory(), baseDirectory) : baseDirectory; - using ManualResetEventSlim changedMre = new(); FileSystemEventArgs? changedArgs = null; - using IFileSystemWatcher fileSystemWatcher = FileSystem.FileSystemWatcher.New(watchPath); - fileSystemWatcher.Changed += (_, eventArgs) => { // ReSharper disable once AccessToDisposedClosure @@ -239,22 +227,16 @@ string fileName // Ignore any ObjectDisposedException } }; - fileSystemWatcher.IncludeSubdirectories = true; fileSystemWatcher.EnableRaisingEvents = true; - - // Act FileSystem.File.Create(filePath).Dispose(); FileSystem.File.WriteAllText(filePath, "Hello World!"); - // Assert - await That(changedMre.Wait(ExpectTimeout, TestContext.Current.CancellationToken)).IsTrue(); - await That(changedArgs).IsNotNull().And - .Satisfies(args => string.Equals(args?.Name, expectedFileName, StringComparison.Ordinal) - ); + .Satisfies(args => string.Equals(args?.Name, expectedFileName, + StringComparison.Ordinal)); } [Theory] @@ -268,7 +250,6 @@ string fileName { string subdirectoryName = "a"; string subSubdirectoryName = "b"; - // Arrange FileSystem.Initialize().WithSubdirectory(baseDirectory) .Initialized(s => s.WithSubdirectory(subdirectoryName) .Initialized(ss => ss.WithSubdirectory(subSubdirectoryName)) @@ -312,23 +293,15 @@ string fileName fileSystemWatcher.IncludeSubdirectories = true; fileSystemWatcher.EnableRaisingEvents = true; - // Act - FileSystem.File.Create(filePath).Dispose(); FileSystem.File.Move(filePath, newFilePath); - // Assert - await That(renamedMre.Wait(ExpectTimeout, TestContext.Current.CancellationToken)).IsTrue(); - await That(renamedArgs).IsNotNull().And - .Satisfies(args => string.Equals( - args?.Name, expectedNewFileName, StringComparison.Ordinal - ) - ).And.Satisfies(args => string.Equals( - args?.OldName, expectedFileName, StringComparison.Ordinal - ) - ); + .Satisfies(args => string.Equals(args?.Name, expectedNewFileName, + StringComparison.Ordinal)).And + .Satisfies(args => string.Equals(args?.OldName, expectedFileName, + StringComparison.Ordinal)); } [Theory] @@ -342,7 +315,6 @@ string fileName { string subdirectoryName = "a"; string subSubdirectoryName = "b"; - // Arrange FileSystem.Initialize().WithSubdirectory(baseDirectory) .Initialized(s => s.WithSubdirectory(subdirectoryName) .Initialized(ss => ss.WithSubdirectory(subSubdirectoryName)) @@ -381,20 +353,13 @@ string fileName fileSystemWatcher.IncludeSubdirectories = true; fileSystemWatcher.EnableRaisingEvents = true; - - // Act FileSystem.File.Create(filePath).Dispose(); FileSystem.File.Delete(filePath); - - // Assert await That(deletedMre.Wait(ExpectTimeout, TestContext.Current.CancellationToken)).IsTrue(); - - await That(deletedArgs).IsNotNull().And.Satisfies(args => string.Equals( - args?.Name, expectedFileName, - StringComparison.Ordinal - ) - ); + await That(deletedArgs).IsNotNull().And + .Satisfies(args => string.Equals(args?.Name, expectedFileName, + StringComparison.Ordinal)); } } From bc135d212626dda50a8ced7a7ca0d617f2ee67ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valentin=20Breu=C3=9F?= Date: Sat, 10 Jan 2026 07:31:09 +0100 Subject: [PATCH 3/3] Next try --- .../FileSystem/FileSystemWatcherMock.cs | 48 +++++-------------- 1 file changed, 13 insertions(+), 35 deletions(-) diff --git a/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherMock.cs index 53a55859..c9ee9fed 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherMock.cs @@ -39,11 +39,11 @@ private string FullPath return; } - string fullPath = _fileSystem.Path.GetFullPath(value); + string fullPath = _fileSystem.Execute.Path.GetFullPath(value); - if (!fullPath.EndsWith(_fileSystem.Path.DirectorySeparatorChar)) + if (!fullPath.EndsWith(_fileSystem.Execute.Path.DirectorySeparatorChar)) { - fullPath += _fileSystem.Path.DirectorySeparatorChar; + fullPath += _fileSystem.Execute.Path.DirectorySeparatorChar; } _fullPath = fullPath; @@ -373,25 +373,6 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); } - private static string GetCommonDirectory(string path1, string path2, - StringComparison comparisonMode) - { - for (int i = 0; i < path1.Length; i++) - { - if (path2.Length <= i) - { - return path2; - } - - if (!string.Equals(path1[i].ToString(), path2[i].ToString(), comparisonMode)) - { - return path1.Substring(0, Math.Max(0, i - 1)); - } - } - - return path1; - } - private bool MatchesFilter(ChangeDescription changeDescription) { if (!MatchesWatcherPath(changeDescription.Path)) @@ -597,7 +578,7 @@ private FileSystemEventArgs ToFileSystemEventArgs( FileSystemEventArgs eventArgs = new(changeType, Path, name); - SetFileSystemEventArgsFullPath(eventArgs, _fileSystem.Path.Combine(Path, name)); + SetFileSystemEventArgsFullPath(eventArgs, _fileSystem.Execute.Path.Combine(Path, name)); return eventArgs; } @@ -607,10 +588,10 @@ private string TransformPathAndName(string changeDescriptionPath) if (changeDescriptionPath.StartsWith(FullPath, _fileSystem.Execute.StringComparisonMode)) { return changeDescriptionPath.Substring(FullPath.Length) - .TrimStart(_fileSystem.Path.DirectorySeparatorChar); + .TrimStart(_fileSystem.Execute.Path.DirectorySeparatorChar); } - return _fileSystem.Path.GetFileName(changeDescriptionPath); + return _fileSystem.Execute.Path.GetFileName(changeDescriptionPath); } private void TriggerRenameNotification(ChangeDescription item) @@ -664,21 +645,18 @@ private bool TryMakeRenamedEventArgs( string oldName = TransformPathAndName(changeDescription.OldPath); - eventArgs = new RenamedEventArgs(changeDescription.ChangeType, Path, name, oldName); - - string commonDirectory = GetCommonDirectory(changeDescription.Path, - changeDescription.OldPath, _fileSystem.Execute.StringComparisonMode); + eventArgs = new RenamedEventArgs(changeDescription.ChangeType, Path, + _fileSystem.Execute.Path.GetFileName(name), + _fileSystem.Execute.Path.GetFileName(oldName)); SetFileSystemEventArgsFullPath(eventArgs, changeDescription.Path.StartsWith(FullPath, _fileSystem.Execute.StringComparisonMode) - ? _fileSystem.Path.Combine(Path, name) - : changeDescription.Path.Substring(commonDirectory.Length) - .TrimStart(_fileSystem.Path.DirectorySeparatorChar)); + ? _fileSystem.Execute.Path.Combine(Path, name) + : name); SetRenamedEventArgsOldFullPath(eventArgs, changeDescription.OldPath.StartsWith(FullPath, _fileSystem.Execute.StringComparisonMode) - ? _fileSystem.Path.Combine(Path, oldName) - : changeDescription.OldPath.Substring(commonDirectory.Length) - .TrimStart(_fileSystem.Path.DirectorySeparatorChar)); + ? _fileSystem.Execute.Path.Combine(Path, oldName) + : oldName); return _fileSystem.Execute.Path.GetDirectoryName(changeDescription.Path)?.Equals( _fileSystem.Execute.Path.GetDirectoryName(changeDescription.OldPath),