Skip to content

Commit

Permalink
fix: correctly set simulated EventArgs in FileSystemWatcherMock (#582)
Browse files Browse the repository at this point in the history
* Override implicit Combine in FileSystemEventArgs via Reflection, when simulation mode is not Native

* Execute FileSystemWatcher tests sequentially
  • Loading branch information
vbreuss committed Apr 29, 2024
1 parent 5479381 commit fb6c7fa
Show file tree
Hide file tree
Showing 9 changed files with 133 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
Expand Down Expand Up @@ -493,8 +494,18 @@ private void Stop()
changeName,
out string name);

return new FileSystemEventArgs(changeType,
path, name);
FileSystemEventArgs eventArgs = new(changeType, path, name);
if (_fileSystem.SimulationMode != SimulationMode.Native)
{
// 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: Have to resort to Reflection to override this behavior!
string fullPath = _fileSystem.Execute.Path.Combine(path, name);
typeof(FileSystemEventArgs)
.GetField("_fullPath", BindingFlags.Instance | BindingFlags.NonPublic)?
.SetValue(eventArgs, fullPath);
}

return eventArgs;
}

private string TransformPathAndName(
Expand Down Expand Up @@ -571,8 +582,21 @@ private void TriggerRenameNotification(ChangeDescription item)
path,
name,
oldName);
return System.IO.Path.GetDirectoryName(changeDescription.Path)?
.Equals(System.IO.Path.GetDirectoryName(changeDescription.OldPath),
if (_fileSystem.SimulationMode != SimulationMode.Native)
{
// RenamedEventArgs implicitly combines the path in https://github.com/dotnet/runtime/blob/v8.0.4/src/libraries/System.IO.FileSystem.Watcher/src/System/IO/RenamedEventArgs.cs
// HACK: Have to resort to Reflection to override this behavior!
string fullPath = _fileSystem.Execute.Path.Combine(path, name);
typeof(FileSystemEventArgs)
.GetField("_fullPath", BindingFlags.Instance | BindingFlags.NonPublic)?
.SetValue(eventArgs, fullPath);
string oldFullPath = _fileSystem.Execute.Path.Combine(path, oldName);
typeof(RenamedEventArgs)
.GetField("_oldFullPath", BindingFlags.Instance | BindingFlags.NonPublic)?
.SetValue(eventArgs, oldFullPath);
}
return _fileSystem.Execute.Path.GetDirectoryName(changeDescription.Path)?
.Equals(_fileSystem.Execute.Path.GetDirectoryName(changeDescription.OldPath),
_fileSystem.Execute.StringComparisonMode) ?? true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public void Dispose()
string path)
{
FileSystem.Directory.CreateDirectory(path);
IFileSystemWatcher fileSystemWatcher =
using IFileSystemWatcher fileSystemWatcher =
FileSystem.FileSystemWatcher.New(BasePath);
using ManualResetEventSlim block1 = new();
using ManualResetEventSlim block2 = new();
Expand Down Expand Up @@ -83,7 +83,6 @@ public void Dispose()
}

block2.Wait(10000).Should().BeTrue();
fileSystemWatcher.Dispose();
result.Should().NotBeNull();
result!.GetException().Should().BeOfType<InternalBufferOverflowException>();
}
Expand All @@ -96,7 +95,7 @@ public void Dispose()
{
int maxMessages = internalBufferSize / 128;
FileSystem.Directory.CreateDirectory(path);
IFileSystemWatcher fileSystemWatcher =
using IFileSystemWatcher fileSystemWatcher =
FileSystem.FileSystemWatcher.New(BasePath);
using ManualResetEventSlim block1 = new();
using ManualResetEventSlim block2 = new();
Expand Down Expand Up @@ -142,7 +141,6 @@ public void Dispose()
}

block2.Wait(5000).Should().BeTrue();
fileSystemWatcher.Dispose();
result.Should().NotBeNull();
result!.GetException().Should().BeOfType<InternalBufferOverflowException>();
}
Expand All @@ -153,7 +151,7 @@ public void Dispose()
public void Filter_ShouldResetFiltersToOnlyContainASingleValue(
string[] filters, string expectedFilter)
{
IFileSystemWatcher fileSystemWatcher =
using IFileSystemWatcher fileSystemWatcher =
FileSystem.FileSystemWatcher.New(BasePath);
foreach (string filter in filters)
{
Expand Down Expand Up @@ -235,4 +233,99 @@ public void InternalBufferSize_ShouldResetQueue(string path1, string path2)
block2.Wait(100).Should().BeFalse();
result.Should().BeNull();
}

#if !NETFRAMEWORK
public sealed class EventArgsTests
{
[SkippableTheory]
[InlineAutoData(SimulationMode.Linux)]
[InlineAutoData(SimulationMode.MacOS)]
[InlineAutoData(SimulationMode.Windows)]
public void FileSystemEventArgs_ShouldUseDirectorySeparatorFromSimulatedFileSystem(
SimulationMode simulationMode, string parentDirectory, string directoryName)
{
MockFileSystem fileSystem =
new MockFileSystem(s => s.SimulatingOperatingSystem(simulationMode));
fileSystem.Directory.CreateDirectory(parentDirectory);
FileSystemEventArgs? result = null;
string expectedFullPath = fileSystem.Path.GetFullPath(
fileSystem.Path.Combine(parentDirectory, directoryName));
string expectedName = fileSystem.Path.Combine(parentDirectory, directoryName);

using IFileSystemWatcher fileSystemWatcher =
fileSystem.FileSystemWatcher.New(parentDirectory);
using ManualResetEventSlim ms = new();
fileSystemWatcher.Created += (_, eventArgs) =>
{
result = eventArgs;
// ReSharper disable once AccessToDisposedClosure
try
{
ms.Set();
}
catch (ObjectDisposedException)
{
// Ignore any ObjectDisposedException
}
};
fileSystemWatcher.NotifyFilter = NotifyFilters.DirectoryName;
fileSystemWatcher.EnableRaisingEvents = true;
fileSystem.Directory.CreateDirectory(expectedName);
ms.Wait(5000);

result.Should().NotBeNull();
result!.FullPath.Should().Be(expectedFullPath);
result.Name.Should().Be(expectedName);
result.ChangeType.Should().Be(WatcherChangeTypes.Created);
}

[SkippableTheory]
[InlineAutoData(SimulationMode.Linux)]
[InlineAutoData(SimulationMode.MacOS)]
[InlineAutoData(SimulationMode.Windows)]
public void RenamedEventArgs_ShouldUseDirectorySeparatorFromSimulatedFileSystem(
SimulationMode simulationMode, string parentDirectory,
string sourceName, string destinationName)
{
MockFileSystem fileSystem =
new MockFileSystem(s => s.SimulatingOperatingSystem(simulationMode));
fileSystem.Directory.CreateDirectory(parentDirectory);
RenamedEventArgs? result = null;
string expectedOldFullPath = fileSystem.Path.GetFullPath(
fileSystem.Path.Combine(parentDirectory, sourceName));
string expectedFullPath = fileSystem.Path.GetFullPath(
fileSystem.Path.Combine(parentDirectory, destinationName));
fileSystem.Directory.CreateDirectory(parentDirectory);
fileSystem.File.WriteAllText(expectedOldFullPath, "foo");

using IFileSystemWatcher fileSystemWatcher =
fileSystem.FileSystemWatcher.New(parentDirectory);
using ManualResetEventSlim ms = new();
fileSystemWatcher.Renamed += (_, eventArgs) =>
{
result = eventArgs;
// ReSharper disable once AccessToDisposedClosure
try
{
ms.Set();
}
catch (ObjectDisposedException)
{
// Ignore any ObjectDisposedException
}
};
fileSystemWatcher.NotifyFilter = NotifyFilters.FileName;
fileSystemWatcher.EnableRaisingEvents = true;
fileSystem.File.Move(expectedOldFullPath, expectedFullPath);
ms.Wait(5000);

result.Should().NotBeNull();
result!.FullPath.Should().Be(expectedFullPath);
result.Name.Should().Be(destinationName);
result!.OldFullPath.Should().Be(expectedOldFullPath);
result.OldName.Should().Be(sourceName);
result.ChangeType.Should().Be(WatcherChangeTypes.Renamed);
}
}
#endif
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcher;

// ReSharper disable once PartialTypeWithSinglePart
[Collection("RealFileSystemTests")]
public abstract partial class EnableRaisingEventsTests<TFileSystem>
: FileSystemTestBase<TFileSystem>
where TFileSystem : IFileSystem
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcher;

// ReSharper disable once PartialTypeWithSinglePart
[Collection("RealFileSystemTests")]
public abstract partial class EventTests<TFileSystem>
: FileSystemTestBase<TFileSystem>
where TFileSystem : IFileSystem
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcher;

// ReSharper disable once PartialTypeWithSinglePart
[Collection("RealFileSystemTests")]
public abstract partial class FilterTests<TFileSystem>
: FileSystemTestBase<TFileSystem>
where TFileSystem : IFileSystem
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcher;

// ReSharper disable once PartialTypeWithSinglePart
[Collection("RealFileSystemTests")]
public abstract partial class IncludeSubdirectoriesTests<TFileSystem>
: FileSystemTestBase<TFileSystem>
where TFileSystem : IFileSystem
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcher;

// ReSharper disable once PartialTypeWithSinglePart
[Collection("RealFileSystemTests")]
public abstract partial class NotifyFiltersTests<TFileSystem>
: FileSystemTestBase<TFileSystem>
where TFileSystem : IFileSystem
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcher;

// ReSharper disable once PartialTypeWithSinglePart
[Collection("RealFileSystemTests")]
public abstract partial class Tests<TFileSystem>
: FileSystemTestBase<TFileSystem>
where TFileSystem : IFileSystem
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Testably.Abstractions.Tests.FileSystem.FileSystemWatcher;

// ReSharper disable once PartialTypeWithSinglePart
[Collection("RealFileSystemTests")]
public abstract partial class WaitForChangedTests<TFileSystem>
: FileSystemTestBase<TFileSystem>
where TFileSystem : IFileSystem
Expand Down

0 comments on commit fb6c7fa

Please sign in to comment.