Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
194 changes: 102 additions & 92 deletions Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherMock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,38 @@ internal sealed class FileSystemWatcherMock : Component, IFileSystemWatcher
/// </summary>
private const int BytesPerMessage = 128;

/// <summary>
/// Caches the full path of <see cref="Path" />
/// </summary>
private string FullPath
{
get => _fullPath;
set
{
if (string.IsNullOrEmpty(value))
{
_fullPath = value;

return;
}

string fullPath = _fileSystem.Execute.Path.GetFullPath(value);

if (!fullPath.EndsWith(_fileSystem.Execute.Path.DirectorySeparatorChar))
{
fullPath += _fileSystem.Execute.Path.DirectorySeparatorChar;
}

_fullPath = fullPath;
}
}

private CancellationTokenSource? _cancellationTokenSource;
private IDisposable? _changeHandler;
private bool _enableRaisingEvents;
private readonly MockFileSystem _fileSystem;
private readonly Collection<string> _filters = [];
private string _fullPath = string.Empty;
private bool _includeSubdirectories;
private int _internalBufferSize = 8192;
private bool _isInitializing;
Expand All @@ -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;

Expand Down Expand Up @@ -260,32 +286,6 @@ public ISynchronizeInvoke? SynchronizingObject
}
}

/// <summary>
/// Caches the full path of <see cref="Path"/>
/// </summary>
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;
}
}

/// <inheritdoc cref="IFileSystemWatcher.BeginInit()" />
public void BeginInit()
{
Expand Down Expand Up @@ -463,6 +463,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)
Expand Down Expand Up @@ -530,6 +570,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.Execute.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.Execute.Path.DirectorySeparatorChar);
}

return _fileSystem.Execute.Path.GetFileName(changeDescriptionPath);
}

private void TriggerRenameNotification(ChangeDescription item)
{
if (_fileSystem.Execute.IsWindows)
Expand Down Expand Up @@ -581,10 +645,18 @@ private bool TryMakeRenamedEventArgs(

string oldName = TransformPathAndName(changeDescription.OldPath);

eventArgs = new RenamedEventArgs(changeDescription.ChangeType, Path, name, oldName);
eventArgs = new RenamedEventArgs(changeDescription.ChangeType, Path,
_fileSystem.Execute.Path.GetFileName(name),
_fileSystem.Execute.Path.GetFileName(oldName));

SetFileSystemEventArgsFullPath(eventArgs, name);
SetRenamedEventArgsFullPath(eventArgs, oldName);
SetFileSystemEventArgsFullPath(eventArgs,
changeDescription.Path.StartsWith(FullPath, _fileSystem.Execute.StringComparisonMode)
? _fileSystem.Execute.Path.Combine(Path, name)
: name);
SetRenamedEventArgsOldFullPath(eventArgs,
changeDescription.OldPath.StartsWith(FullPath, _fileSystem.Execute.StringComparisonMode)
? _fileSystem.Execute.Path.Combine(Path, oldName)
: oldName);

return _fileSystem.Execute.Path.GetDirectoryName(changeDescription.Path)?.Equals(
_fileSystem.Execute.Path.GetDirectoryName(changeDescription.OldPath),
Expand All @@ -593,68 +665,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)
{
Expand Down
Loading
Loading