From 82ada1b1ecb1a92d7809247ed9c6285d1723af40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valentin=20Breu=C3=9F?= Date: Sun, 10 Mar 2024 03:14:33 +0100 Subject: [PATCH] feat: add statistics (#474) From #476 Add a `Statistics` property to the `MockFileSystem` so that it is possible to check which method calls were made on it. --- .../Helpers/PathSystemBase.cs | 86 +- .../FileSystem/DirectoryInfoFactoryMock.cs | 21 +- .../FileSystem/DirectoryInfoMock.cs | 207 ++- .../FileSystem/DirectoryMock.cs | 354 ++++- .../FileSystem/DriveInfoFactoryMock.cs | 28 +- .../FileSystem/FileInfoFactoryMock.cs | 22 +- .../FileSystem/FileInfoMock.cs | 104 +- .../FileSystem/FileMock.cs | 509 +++++- .../FileSystem/FileStreamFactoryMock.cs | 103 +- .../FileSystem/FileStreamMock.cs | 130 +- .../FileSystem/FileSystemInfoMock.cs | 18 +- .../FileSystemWatcherFactoryMock.cs | 28 +- .../FileSystem/FileSystemWatcherMock.cs | 38 +- .../FileSystem/PathMock.cs | 500 +++++- .../FileSystemInitializer/FileInitializer.cs | 2 + .../FileSystemInitializer.cs | 7 +- .../FileSystemInitializerExtensions.cs | 5 +- .../Helpers/FileSystemExtensions.cs | 23 + .../Helpers/NoOpDisposable.cs | 12 + .../MockFileSystem.cs | 9 + .../Statistics/CallStatistics.cs | 37 + .../Statistics/FileSystemEntryStatistics.cs | 37 + .../Statistics/FileSystemStatistics.cs | 99 ++ .../Statistics/IFileSystemStatistics.cs | 47 + .../Statistics/IPathStatistics.cs | 18 + .../Statistics/IStatistics.cs | 14 + .../Statistics/IStatisticsGate.cs | 9 + .../Statistics/MethodStatistic.cs | 29 + .../Statistics/ParameterDescription.cs | 159 ++ .../Storage/NullContainer.cs | 2 +- .../Internal/AccessControlHelperTests.cs | 1 - .../Helpers/PathHelperTests.cs | 73 +- .../DirectoryInfoFactoryStatisticsTests.cs | 31 + .../DirectoryInfoStatisticsTests.cs | 416 +++++ .../FileSystem/DirectoryStatisticsTests.cs | 688 +++++++++ .../DriveInfoFactoryStatisticsTests.cs | 42 + .../FileSystem/DriveInfoStatisticsTests.cs | 8 + .../FileInfoFactoryStatisticsTests.cs | 31 + .../FileSystem/FileInfoStatisticsTests.cs | 285 ++++ .../FileSystem/FileStatisticsTests.cs | 1372 +++++++++++++++++ .../FileStreamFactoryStatisticsTests.cs | 195 +++ .../FileSystem/FileStreamStatisticsTests.cs | 315 ++++ ...FileSystemWatcherFactoryStatisticsTests.cs | 57 + .../FileSystemWatcherStatisticsTests.cs | 107 ++ .../FileSystem/PathStatisticsTests.cs | 571 +++++++ .../Statistics/StatisticsTests.cs | 377 +++++ .../TestHelpers/StatisticsTestHelpers.cs | 209 +++ .../FileSystem/FileSystemWatcher/Tests.cs | 10 +- 48 files changed, 7156 insertions(+), 289 deletions(-) create mode 100644 Source/Testably.Abstractions.Testing/Helpers/NoOpDisposable.cs create mode 100644 Source/Testably.Abstractions.Testing/Statistics/CallStatistics.cs create mode 100644 Source/Testably.Abstractions.Testing/Statistics/FileSystemEntryStatistics.cs create mode 100644 Source/Testably.Abstractions.Testing/Statistics/FileSystemStatistics.cs create mode 100644 Source/Testably.Abstractions.Testing/Statistics/IFileSystemStatistics.cs create mode 100644 Source/Testably.Abstractions.Testing/Statistics/IPathStatistics.cs create mode 100644 Source/Testably.Abstractions.Testing/Statistics/IStatistics.cs create mode 100644 Source/Testably.Abstractions.Testing/Statistics/IStatisticsGate.cs create mode 100644 Source/Testably.Abstractions.Testing/Statistics/MethodStatistic.cs create mode 100644 Source/Testably.Abstractions.Testing/Statistics/ParameterDescription.cs create mode 100644 Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/DirectoryInfoFactoryStatisticsTests.cs create mode 100644 Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/DirectoryInfoStatisticsTests.cs create mode 100644 Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/DirectoryStatisticsTests.cs create mode 100644 Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/DriveInfoFactoryStatisticsTests.cs create mode 100644 Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/DriveInfoStatisticsTests.cs create mode 100644 Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/FileInfoFactoryStatisticsTests.cs create mode 100644 Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/FileInfoStatisticsTests.cs create mode 100644 Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/FileStatisticsTests.cs create mode 100644 Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/FileStreamFactoryStatisticsTests.cs create mode 100644 Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/FileStreamStatisticsTests.cs create mode 100644 Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/FileSystemWatcherFactoryStatisticsTests.cs create mode 100644 Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/FileSystemWatcherStatisticsTests.cs create mode 100644 Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/PathStatisticsTests.cs create mode 100644 Tests/Testably.Abstractions.Testing.Tests/Statistics/StatisticsTests.cs create mode 100644 Tests/Testably.Abstractions.Testing.Tests/TestHelpers/StatisticsTestHelpers.cs diff --git a/Source/Testably.Abstractions.Interface/Helpers/PathSystemBase.cs b/Source/Testably.Abstractions.Interface/Helpers/PathSystemBase.cs index e6e280b70..8b57d1dfb 100644 --- a/Source/Testably.Abstractions.Interface/Helpers/PathSystemBase.cs +++ b/Source/Testably.Abstractions.Interface/Helpers/PathSystemBase.cs @@ -25,43 +25,43 @@ protected PathSystemBase(IFileSystem fileSystem) #region IPath Members /// - public char AltDirectorySeparatorChar + public virtual char AltDirectorySeparatorChar => Path.AltDirectorySeparatorChar; /// - public char DirectorySeparatorChar + public virtual char DirectorySeparatorChar => Path.DirectorySeparatorChar; /// public IFileSystem FileSystem { get; } /// - public char PathSeparator + public virtual char PathSeparator => Path.PathSeparator; /// - public char VolumeSeparatorChar + public virtual char VolumeSeparatorChar => Path.VolumeSeparatorChar; /// [return: NotNullIfNotNull("path")] - public string? ChangeExtension(string? path, string? extension) + public virtual string? ChangeExtension(string? path, string? extension) => Path.ChangeExtension(path, extension); /// - public string Combine(string path1, string path2) + public virtual string Combine(string path1, string path2) => Path.Combine(path1, path2); /// - public string Combine(string path1, string path2, string path3) + public virtual string Combine(string path1, string path2, string path3) => Path.Combine(path1, path2, path3); /// - public string Combine(string path1, string path2, string path3, string path4) + public virtual string Combine(string path1, string path2, string path3, string path4) => Path.Combine(path1, path2, path3, path4); /// - public string Combine(params string[] paths) + public virtual string Combine(params string[] paths) => Path.Combine(paths); #if FEATURE_FILESYSTEM_NET7 @@ -71,45 +71,45 @@ public string Combine(params string[] paths) #if FEATURE_SPAN /// - public ReadOnlySpan GetDirectoryName(ReadOnlySpan path) + public virtual ReadOnlySpan GetDirectoryName(ReadOnlySpan path) => Path.GetDirectoryName(path); #endif /// - public string? GetDirectoryName(string? path) + public virtual string? GetDirectoryName(string? path) => Path.GetDirectoryName(path); #if FEATURE_SPAN /// - public ReadOnlySpan GetExtension(ReadOnlySpan path) + public virtual ReadOnlySpan GetExtension(ReadOnlySpan path) => Path.GetExtension(path); #endif /// [return: NotNullIfNotNull("path")] - public string? GetExtension(string? path) + public virtual string? GetExtension(string? path) => Path.GetExtension(path); #if FEATURE_SPAN /// - public ReadOnlySpan GetFileName(ReadOnlySpan path) + public virtual ReadOnlySpan GetFileName(ReadOnlySpan path) => Path.GetFileName(path); #endif /// [return: NotNullIfNotNull("path")] - public string? GetFileName(string? path) + public virtual string? GetFileName(string? path) => Path.GetFileName(path); #if FEATURE_SPAN /// - public ReadOnlySpan GetFileNameWithoutExtension(ReadOnlySpan path) + public virtual ReadOnlySpan GetFileNameWithoutExtension(ReadOnlySpan path) => Path.GetFileNameWithoutExtension(path); #endif /// [return: NotNullIfNotNull("path")] - public string? GetFileNameWithoutExtension(string? path) + public virtual string? GetFileNameWithoutExtension(string? path) => Path.GetFileNameWithoutExtension(path); /// @@ -123,25 +123,25 @@ public virtual string GetFullPath(string path, string basePath) #endif /// - public char[] GetInvalidFileNameChars() + public virtual char[] GetInvalidFileNameChars() => Path.GetInvalidFileNameChars(); /// - public char[] GetInvalidPathChars() + public virtual char[] GetInvalidPathChars() => Path.GetInvalidPathChars(); #if FEATURE_SPAN /// - public ReadOnlySpan GetPathRoot(ReadOnlySpan path) + public virtual ReadOnlySpan GetPathRoot(ReadOnlySpan path) => Path.GetPathRoot(path); #endif /// - public string? GetPathRoot(string? path) + public virtual string? GetPathRoot(string? path) => Path.GetPathRoot(path); /// - public string GetRandomFileName() + public virtual string GetRandomFileName() => Path.GetRandomFileName(); #if FEATURE_PATH_RELATIVE @@ -155,64 +155,64 @@ public virtual string GetRelativePath(string relativeTo, string path) [Obsolete( "Insecure temporary file creation methods should not be used. Use `Path.Combine(Path.GetTempPath(), Path.GetRandomFileName())` instead.")] #endif - public string GetTempFileName() + public virtual string GetTempFileName() => Path.GetTempFileName(); /// - public string GetTempPath() + public virtual string GetTempPath() => Path.GetTempPath(); #if FEATURE_SPAN /// - public bool HasExtension(ReadOnlySpan path) + public virtual bool HasExtension(ReadOnlySpan path) => Path.HasExtension(path); #endif /// - public bool HasExtension([NotNullWhen(true)] string? path) + public virtual bool HasExtension([NotNullWhen(true)] string? path) => Path.HasExtension(path); #if FEATURE_SPAN /// - public bool IsPathFullyQualified(ReadOnlySpan path) + public virtual bool IsPathFullyQualified(ReadOnlySpan path) => Path.IsPathFullyQualified(path); #endif #if FEATURE_PATH_RELATIVE /// - public bool IsPathFullyQualified(string path) + public virtual bool IsPathFullyQualified(string path) => Path.IsPathFullyQualified(path); #endif #if FEATURE_SPAN /// - public bool IsPathRooted(ReadOnlySpan path) + public virtual bool IsPathRooted(ReadOnlySpan path) => Path.IsPathRooted(path); #endif /// - public bool IsPathRooted(string? path) + public virtual bool IsPathRooted(string? path) => Path.IsPathRooted(path); #endregion #if FEATURE_PATH_ADVANCED /// - public bool EndsInDirectorySeparator(ReadOnlySpan path) + public virtual bool EndsInDirectorySeparator(ReadOnlySpan path) => Path.EndsInDirectorySeparator(path); /// - public bool EndsInDirectorySeparator(string path) + public virtual bool EndsInDirectorySeparator(string path) => Path.EndsInDirectorySeparator(path); #endif #if FEATURE_PATH_JOIN /// - public string Join(ReadOnlySpan path1, ReadOnlySpan path2) + public virtual string Join(ReadOnlySpan path1, ReadOnlySpan path2) => Path.Join(path1, path2); /// - public string Join(ReadOnlySpan path1, + public virtual string Join(ReadOnlySpan path1, ReadOnlySpan path2, ReadOnlySpan path3) => Path.Join(path1, path2, path3); @@ -220,49 +220,49 @@ public string Join(ReadOnlySpan path1, #if FEATURE_PATH_ADVANCED /// - public string Join(ReadOnlySpan path1, + public virtual string Join(ReadOnlySpan path1, ReadOnlySpan path2, ReadOnlySpan path3, ReadOnlySpan path4) => Path.Join(path1, path2, path3, path4); /// - public string Join(string? path1, string? path2) + public virtual string Join(string? path1, string? path2) => Path.Join(path1, path2); /// - public string Join(string? path1, string? path2, string? path3) + public virtual string Join(string? path1, string? path2, string? path3) => Path.Join(path1, path2, path3); /// - public string Join(string? path1, string? path2, string? path3, string? path4) + public virtual string Join(string? path1, string? path2, string? path3, string? path4) => Path.Join(path1, path2, path3, path4); /// - public string Join(params string?[] paths) + public virtual string Join(params string?[] paths) => Path.Join(paths); #endif #if FEATURE_PATH_ADVANCED /// - public ReadOnlySpan TrimEndingDirectorySeparator(ReadOnlySpan path) + public virtual ReadOnlySpan TrimEndingDirectorySeparator(ReadOnlySpan path) => Path.TrimEndingDirectorySeparator(path); /// - public string TrimEndingDirectorySeparator(string path) + public virtual string TrimEndingDirectorySeparator(string path) => Path.TrimEndingDirectorySeparator(path); #endif #if FEATURE_PATH_JOIN /// - public bool TryJoin(ReadOnlySpan path1, + public virtual bool TryJoin(ReadOnlySpan path1, ReadOnlySpan path2, Span destination, out int charsWritten) => Path.TryJoin(path1, path2, destination, out charsWritten); /// - public bool TryJoin(ReadOnlySpan path1, + public virtual bool TryJoin(ReadOnlySpan path1, ReadOnlySpan path2, ReadOnlySpan path3, Span destination, diff --git a/Source/Testably.Abstractions.Testing/FileSystem/DirectoryInfoFactoryMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/DirectoryInfoFactoryMock.cs index 3286f7709..3072ba4d7 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/DirectoryInfoFactoryMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/DirectoryInfoFactoryMock.cs @@ -2,6 +2,7 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using Testably.Abstractions.Testing.Helpers; +using Testably.Abstractions.Testing.Statistics; namespace Testably.Abstractions.Testing.FileSystem; @@ -23,11 +24,19 @@ public IFileSystem FileSystem /// [Obsolete("Use `IDirectoryInfoFactory.New(string)` instead")] public IDirectoryInfo FromDirectoryName(string directoryName) - => New(directoryName); + { + using IDisposable registration = Register(nameof(FromDirectoryName), + directoryName); + + return New(directoryName); + } /// public IDirectoryInfo New(string path) { + using IDisposable registration = Register(nameof(New), + path); + return DirectoryInfoMock.New( _fileSystem.Storage.GetLocation(path .EnsureValidArgument(_fileSystem, nameof(path))), @@ -37,11 +46,19 @@ public IDirectoryInfo New(string path) /// [return: NotNullIfNotNull("directoryInfo")] public IDirectoryInfo? Wrap(DirectoryInfo? directoryInfo) - => DirectoryInfoMock.New( + { + using IDisposable registration = Register(nameof(Wrap), + directoryInfo); + + return DirectoryInfoMock.New( _fileSystem.Storage.GetLocation( directoryInfo?.FullName, directoryInfo?.ToString()), _fileSystem); + } #endregion + + private IDisposable Register(string name, T1 parameter1) + => _fileSystem.StatisticsRegistration.DirectoryInfo.Register(name, ParameterDescription.FromParameter(parameter1)); } diff --git a/Source/Testably.Abstractions.Testing/FileSystem/DirectoryInfoMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/DirectoryInfoMock.cs index 4fd2d7ed6..c6a208497 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/DirectoryInfoMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/DirectoryInfoMock.cs @@ -1,8 +1,10 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using Testably.Abstractions.Testing.Helpers; +using Testably.Abstractions.Testing.Statistics; using Testably.Abstractions.Testing.Storage; namespace Testably.Abstractions.Testing.FileSystem; @@ -40,6 +42,8 @@ public IDirectoryInfo Root /// public void Create() { + using IDisposable registration = Register(nameof(Create)); + FullName.EnsureValidFormat(_fileSystem); Container = _fileSystem.Storage.GetOrCreateContainer(Location, @@ -57,6 +61,9 @@ public void Create() /// public IDirectoryInfo CreateSubdirectory(string path) { + using IDisposable registration = Register(nameof(CreateSubdirectory), + path); + DirectoryInfoMock directory = New( _fileSystem.Storage.GetLocation( _fileSystem.Path.Combine(FullName, path @@ -67,9 +74,11 @@ public IDirectoryInfo CreateSubdirectory(string path) return directory; } - /// + /// public override void Delete() { + using IDisposable registration = Register(nameof(Delete)); + if (!_fileSystem.Storage.DeleteContainer(Location)) { throw ExceptionFactory.DirectoryNotFound(Location.FullPath); @@ -81,6 +90,9 @@ public override void Delete() /// public void Delete(bool recursive) { + using IDisposable registration = Register(nameof(Delete), + recursive); + if (!_fileSystem.Storage.DeleteContainer( _fileSystem.Storage.GetLocation(FullName), recursive)) { @@ -92,168 +104,287 @@ public void Delete(bool recursive) /// public IEnumerable EnumerateDirectories() - => EnumerateDirectories( + { + using IDisposable registration = Register(nameof(EnumerateDirectories)); + + return EnumerateDirectories( EnumerationOptionsHelper.DefaultSearchPattern, SearchOption.TopDirectoryOnly); + } /// public IEnumerable EnumerateDirectories(string searchPattern) - => EnumerateDirectories(searchPattern, SearchOption.TopDirectoryOnly); + { + using IDisposable registration = Register(nameof(EnumerateDirectories), + searchPattern); + + return EnumerateDirectories(searchPattern, SearchOption.TopDirectoryOnly); + } /// public IEnumerable EnumerateDirectories( string searchPattern, SearchOption searchOption) - => EnumerateInternal(FileSystemTypes.Directory, + { + using IDisposable registration = Register(nameof(EnumerateDirectories), + searchPattern, searchOption); + + return EnumerateInternal(FileSystemTypes.Directory, FullName, searchPattern, EnumerationOptionsHelper.FromSearchOption(searchOption)) .Select(location => New(location, _fileSystem)); + } #if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS /// public IEnumerable EnumerateDirectories( string searchPattern, EnumerationOptions enumerationOptions) - => EnumerateInternal(FileSystemTypes.Directory, + { + using IDisposable registration = Register(nameof(EnumerateDirectories), + searchPattern, enumerationOptions); + + return EnumerateInternal(FileSystemTypes.Directory, FullName, searchPattern, enumerationOptions) .Select(location => New(location, _fileSystem)); + } #endif /// public IEnumerable EnumerateFiles() - => EnumerateFiles( + { + using IDisposable registration = Register(nameof(EnumerateFiles)); + + return EnumerateFiles( EnumerationOptionsHelper.DefaultSearchPattern, SearchOption.TopDirectoryOnly); + } /// public IEnumerable EnumerateFiles(string searchPattern) - => EnumerateFiles(searchPattern, SearchOption.TopDirectoryOnly); + { + using IDisposable registration = Register(nameof(EnumerateFiles), + searchPattern); + + return EnumerateFiles(searchPattern, SearchOption.TopDirectoryOnly); + } /// public IEnumerable EnumerateFiles( string searchPattern, SearchOption searchOption) - => EnumerateInternal(FileSystemTypes.File, + { + using IDisposable registration = Register(nameof(EnumerateFiles), + searchPattern, searchOption); + + return EnumerateInternal(FileSystemTypes.File, FullName, searchPattern, EnumerationOptionsHelper.FromSearchOption(searchOption)) .Select(location => FileInfoMock.New(location, _fileSystem)); + } #if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS /// public IEnumerable EnumerateFiles( string searchPattern, EnumerationOptions enumerationOptions) - => EnumerateInternal(FileSystemTypes.File, + { + using IDisposable registration = Register(nameof(EnumerateFiles), + searchPattern, enumerationOptions); + + return EnumerateInternal(FileSystemTypes.File, FullName, searchPattern, enumerationOptions) .Select(location => FileInfoMock.New(location, _fileSystem)); + } #endif /// public IEnumerable EnumerateFileSystemInfos() - => EnumerateFileSystemInfos( + { + using IDisposable registration = Register(nameof(EnumerateFileSystemInfos)); + + return EnumerateFileSystemInfos( EnumerationOptionsHelper.DefaultSearchPattern, SearchOption.TopDirectoryOnly); + } /// public IEnumerable EnumerateFileSystemInfos(string searchPattern) - => EnumerateFileSystemInfos(searchPattern, SearchOption.TopDirectoryOnly); + { + using IDisposable registration = Register(nameof(EnumerateFileSystemInfos), + searchPattern); + + return EnumerateFileSystemInfos(searchPattern, SearchOption.TopDirectoryOnly); + } /// public IEnumerable EnumerateFileSystemInfos( string searchPattern, SearchOption searchOption) - => EnumerateInternal(FileSystemTypes.DirectoryOrFile, + { + using IDisposable registration = Register(nameof(EnumerateFileSystemInfos), + searchPattern, searchOption); + + return EnumerateInternal(FileSystemTypes.DirectoryOrFile, FullName, searchPattern, EnumerationOptionsHelper.FromSearchOption(searchOption)) .Select(location => FileSystemInfoMock.New(location, _fileSystem)); + } #if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS /// public IEnumerable EnumerateFileSystemInfos( string searchPattern, EnumerationOptions enumerationOptions) - => EnumerateInternal(FileSystemTypes.DirectoryOrFile, + { + using IDisposable registration = Register(nameof(EnumerateFileSystemInfos), + searchPattern, enumerationOptions); + + return EnumerateInternal(FileSystemTypes.DirectoryOrFile, FullName, searchPattern, enumerationOptions) .Select(location => FileSystemInfoMock.New(location, _fileSystem)); + } #endif /// public IDirectoryInfo[] GetDirectories() - => EnumerateDirectories().ToArray(); + { + using IDisposable registration = Register(nameof(GetDirectories)); + + return EnumerateDirectories().ToArray(); + } /// public IDirectoryInfo[] GetDirectories(string searchPattern) - => EnumerateDirectories(searchPattern).ToArray(); + { + using IDisposable registration = Register(nameof(GetDirectories), + searchPattern); + + return EnumerateDirectories(searchPattern).ToArray(); + } /// public IDirectoryInfo[] GetDirectories( string searchPattern, SearchOption searchOption) - => EnumerateDirectories(searchPattern, searchOption).ToArray(); + { + using IDisposable registration = Register(nameof(GetDirectories), + searchPattern, searchOption); + + return EnumerateDirectories(searchPattern, searchOption).ToArray(); + } #if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS /// public IDirectoryInfo[] GetDirectories( string searchPattern, EnumerationOptions enumerationOptions) - => EnumerateDirectories(searchPattern, enumerationOptions).ToArray(); + { + using IDisposable registration = Register(nameof(GetDirectories), + searchPattern, enumerationOptions); + + return EnumerateDirectories(searchPattern, enumerationOptions).ToArray(); + } #endif /// public IFileInfo[] GetFiles() - => EnumerateFiles().ToArray(); + { + using IDisposable registration = Register(nameof(GetFiles)); + + return EnumerateFiles().ToArray(); + } /// public IFileInfo[] GetFiles(string searchPattern) - => EnumerateFiles(searchPattern).ToArray(); + { + using IDisposable registration = Register(nameof(GetFiles), + searchPattern); + + return EnumerateFiles(searchPattern).ToArray(); + } /// public IFileInfo[] GetFiles(string searchPattern, SearchOption searchOption) - => EnumerateFiles(searchPattern, searchOption).ToArray(); + { + using IDisposable registration = Register(nameof(GetFiles), + searchPattern, searchOption); + + return EnumerateFiles(searchPattern, searchOption).ToArray(); + } #if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS /// public IFileInfo[] GetFiles(string searchPattern, EnumerationOptions enumerationOptions) - => EnumerateFiles(searchPattern, enumerationOptions).ToArray(); + { + using IDisposable registration = Register(nameof(GetFiles), + searchPattern, enumerationOptions); + + return EnumerateFiles(searchPattern, enumerationOptions).ToArray(); + } #endif /// public IFileSystemInfo[] GetFileSystemInfos() - => EnumerateFileSystemInfos().ToArray(); + { + using IDisposable registration = Register(nameof(GetFileSystemInfos)); + + return EnumerateFileSystemInfos().ToArray(); + } /// public IFileSystemInfo[] GetFileSystemInfos(string searchPattern) - => EnumerateFileSystemInfos(searchPattern).ToArray(); + { + using IDisposable registration = Register(nameof(GetFileSystemInfos), + searchPattern); + + return EnumerateFileSystemInfos(searchPattern).ToArray(); + } /// public IFileSystemInfo[] GetFileSystemInfos( string searchPattern, SearchOption searchOption) - => EnumerateFileSystemInfos(searchPattern, searchOption).ToArray(); + { + using IDisposable registration = Register(nameof(GetFileSystemInfos), + searchPattern, searchOption); + + return EnumerateFileSystemInfos(searchPattern, searchOption).ToArray(); + } #if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS /// public IFileSystemInfo[] GetFileSystemInfos( string searchPattern, EnumerationOptions enumerationOptions) - => EnumerateFileSystemInfos(searchPattern, enumerationOptions).ToArray(); + { + using IDisposable registration = Register(nameof(GetFileSystemInfos), + searchPattern, enumerationOptions); + + return EnumerateFileSystemInfos(searchPattern, enumerationOptions).ToArray(); + } #endif /// public void MoveTo(string destDirName) - => Location = _fileSystem.Storage.Move( - _fileSystem.Storage.GetLocation(FullName), - _fileSystem.Storage.GetLocation(destDirName - .EnsureValidFormat(_fileSystem, nameof(destDirName))), - recursive: true) - ?? throw ExceptionFactory.DirectoryNotFound(FullName); + { + using IDisposable registration = Register(nameof(MoveTo), + destDirName); + + Location = _fileSystem.Storage.Move( + _fileSystem.Storage.GetLocation(FullName), + _fileSystem.Storage.GetLocation(destDirName + .EnsureValidFormat(_fileSystem, nameof(destDirName))), + recursive: true) + ?? throw ExceptionFactory.DirectoryNotFound(FullName); + } #endregion @@ -285,4 +416,16 @@ private IEnumerable EnumerateInternal( adjustedLocation.SearchPattern, enumerationOptions); } + + protected override IDisposable Register(string name) + => _fileSystem.StatisticsRegistration.DirectoryInfo.Register(Location.FullPath, name); + + protected override IDisposable Register(string name, T1 parameter1) + => _fileSystem.StatisticsRegistration.DirectoryInfo.Register(Location.FullPath, name, + ParameterDescription.FromParameter(parameter1)); + + private IDisposable Register(string name, T1 parameter1, T2 parameter2) + => _fileSystem.StatisticsRegistration.DirectoryInfo.Register(Location.FullPath, name, + ParameterDescription.FromParameter(parameter1), + ParameterDescription.FromParameter(parameter2)); } diff --git a/Source/Testably.Abstractions.Testing/FileSystem/DirectoryMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/DirectoryMock.cs index 99515b78c..e18e2bc46 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/DirectoryMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/DirectoryMock.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using Testably.Abstractions.Testing.Helpers; +using Testably.Abstractions.Testing.Statistics; using Testably.Abstractions.Testing.Storage; namespace Testably.Abstractions.Testing.FileSystem; @@ -26,6 +27,9 @@ public IFileSystem FileSystem /// public IDirectoryInfo CreateDirectory(string path) { + using IDisposable registration = Register(nameof(CreateDirectory), + path); + path.EnsureValidFormat(_fileSystem); DirectoryInfoMock directory = DirectoryInfoMock.New( @@ -40,6 +44,9 @@ public IDirectoryInfo CreateDirectory(string path) /// public IDirectoryInfo CreateDirectory(string path, UnixFileMode unixCreateMode) { + using IDisposable registration = Register(nameof(CreateDirectory), + path, unixCreateMode); + IDirectoryInfo directoryInfo = CreateDirectory(path); #pragma warning disable CA1416 directoryInfo.UnixFileMode = unixCreateMode; @@ -53,6 +60,9 @@ public IDirectoryInfo CreateDirectory(string path, UnixFileMode unixCreateMode) public IFileSystemInfo CreateSymbolicLink( string path, string pathToTarget) { + using IDisposable registration = Register(nameof(CreateSymbolicLink), + path, pathToTarget); + path.EnsureValidFormat(_fileSystem); IDirectoryInfo fileSystemInfo = _fileSystem.DirectoryInfo.New(path); @@ -65,6 +75,9 @@ public IFileSystemInfo CreateSymbolicLink( /// public IDirectoryInfo CreateTempSubdirectory(string? prefix = null) { + using IDisposable registration = Register(nameof(CreateTempSubdirectory), + prefix); + string basePath; do @@ -84,258 +97,447 @@ public IDirectoryInfo CreateTempSubdirectory(string? prefix = null) /// public void Delete(string path) - => _fileSystem.DirectoryInfo + { + using IDisposable registration = Register(nameof(Delete), + path); + + _fileSystem.DirectoryInfo .New(path.EnsureValidFormat(_fileSystem)) .Delete(); + } /// public void Delete(string path, bool recursive) - => _fileSystem.DirectoryInfo + { + using IDisposable registration = Register(nameof(Delete), + path, recursive); + + _fileSystem.DirectoryInfo .New(path.EnsureValidFormat(_fileSystem)) .Delete(recursive); + } /// public IEnumerable EnumerateDirectories(string path) - => EnumerateDirectories(path, + { + using IDisposable registration = Register(nameof(EnumerateDirectories), + path); + + return EnumerateDirectories(path, EnumerationOptionsHelper.DefaultSearchPattern, SearchOption.TopDirectoryOnly); + } /// public IEnumerable EnumerateDirectories(string path, string searchPattern) - => EnumerateDirectories(path, searchPattern, SearchOption.TopDirectoryOnly); + { + using IDisposable registration = Register(nameof(EnumerateDirectories), + path, searchPattern); + + return EnumerateDirectories(path, searchPattern, SearchOption.TopDirectoryOnly); + } /// public IEnumerable EnumerateDirectories(string path, string searchPattern, SearchOption searchOption) - => EnumerateInternal(FileSystemTypes.Directory, + { + using IDisposable registration = Register(nameof(EnumerateDirectories), + path, searchPattern, searchOption); + + return EnumerateInternal(FileSystemTypes.Directory, path, searchPattern, EnumerationOptionsHelper.FromSearchOption(searchOption)); + } #if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS /// public IEnumerable EnumerateDirectories(string path, string searchPattern, - EnumerationOptions - enumerationOptions) - => EnumerateInternal(FileSystemTypes.Directory, + EnumerationOptions enumerationOptions) + { + using IDisposable registration = Register(nameof(EnumerateDirectories), + path, searchPattern, enumerationOptions); + + return EnumerateInternal(FileSystemTypes.Directory, path, searchPattern, enumerationOptions); + } #endif /// public IEnumerable EnumerateFiles(string path) - => EnumerateFiles(path, + { + using IDisposable registration = Register(nameof(EnumerateFiles), + path); + + return EnumerateFiles(path, EnumerationOptionsHelper.DefaultSearchPattern, SearchOption.TopDirectoryOnly); + } /// public IEnumerable EnumerateFiles(string path, string searchPattern) - => EnumerateFiles(path, searchPattern, SearchOption.TopDirectoryOnly); + { + using IDisposable registration = Register(nameof(EnumerateFiles), + path, searchPattern); + + return EnumerateFiles(path, searchPattern, SearchOption.TopDirectoryOnly); + } /// public IEnumerable EnumerateFiles(string path, string searchPattern, SearchOption searchOption) - => EnumerateInternal(FileSystemTypes.File, + { + using IDisposable registration = Register(nameof(EnumerateFiles), + path, searchPattern, searchOption); + + return EnumerateInternal(FileSystemTypes.File, path, searchPattern, EnumerationOptionsHelper.FromSearchOption(searchOption)); + } #if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS /// public IEnumerable EnumerateFiles(string path, string searchPattern, EnumerationOptions enumerationOptions) - => EnumerateInternal(FileSystemTypes.File, + { + using IDisposable registration = Register(nameof(EnumerateFiles), + path, searchPattern, enumerationOptions); + + return EnumerateInternal(FileSystemTypes.File, path, searchPattern, enumerationOptions); + } #endif /// public IEnumerable EnumerateFileSystemEntries(string path) - => EnumerateFileSystemEntries(path, + { + using IDisposable registration = Register(nameof(EnumerateFileSystemEntries), + path); + + return EnumerateFileSystemEntries(path, EnumerationOptionsHelper.DefaultSearchPattern, SearchOption.TopDirectoryOnly); + } /// public IEnumerable EnumerateFileSystemEntries( string path, string searchPattern) - => EnumerateFileSystemEntries(path, + { + using IDisposable registration = Register(nameof(EnumerateFileSystemEntries), + path, searchPattern); + + return EnumerateFileSystemEntries(path, searchPattern, SearchOption.TopDirectoryOnly); + } /// public IEnumerable EnumerateFileSystemEntries(string path, string searchPattern, SearchOption searchOption) - => EnumerateInternal(FileSystemTypes.DirectoryOrFile, + { + using IDisposable registration = Register(nameof(EnumerateFileSystemEntries), + path, searchPattern, searchOption); + + return EnumerateInternal(FileSystemTypes.DirectoryOrFile, path, searchPattern, EnumerationOptionsHelper.FromSearchOption(searchOption)); + } #if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS /// public IEnumerable EnumerateFileSystemEntries(string path, string searchPattern, - EnumerationOptions - enumerationOptions) - => EnumerateInternal(FileSystemTypes.DirectoryOrFile, + EnumerationOptions enumerationOptions) + { + using IDisposable registration = Register(nameof(EnumerateFileSystemEntries), + path, searchPattern, enumerationOptions); + + return EnumerateInternal(FileSystemTypes.DirectoryOrFile, path, searchPattern, enumerationOptions); + } #endif /// public bool Exists([NotNullWhen(true)] string? path) - => DirectoryInfoMock.New( + { + using IDisposable registration = Register(nameof(Exists), + path); + + return DirectoryInfoMock.New( _fileSystem.Storage.GetLocation(path), _fileSystem)?.Exists ?? false; + } /// public DateTime GetCreationTime(string path) - => _fileSystem.Storage.GetContainer( + { + using IDisposable registration = Register(nameof(GetCreationTime), + path); + + return _fileSystem.Storage.GetContainer( _fileSystem.Storage.GetLocation( path.EnsureValidFormat(_fileSystem))) .CreationTime.Get(DateTimeKind.Local); + } /// public DateTime GetCreationTimeUtc(string path) - => _fileSystem.Storage.GetContainer( + { + using IDisposable registration = Register(nameof(GetCreationTimeUtc), + path); + + return _fileSystem.Storage.GetContainer( _fileSystem.Storage.GetLocation( path.EnsureValidFormat(_fileSystem))) .CreationTime.Get(DateTimeKind.Utc); + } /// public string GetCurrentDirectory() - => _fileSystem.Storage.CurrentDirectory; + { + using IDisposable registration = Register(nameof(GetCurrentDirectory)); + + return _fileSystem.Storage.CurrentDirectory; + } /// public string[] GetDirectories(string path) - => EnumerateDirectories(path).ToArray(); + { + using IDisposable registration = Register(nameof(GetDirectories), + path); + + return EnumerateDirectories(path).ToArray(); + } /// public string[] GetDirectories(string path, string searchPattern) - => EnumerateDirectories(path, searchPattern).ToArray(); + { + using IDisposable registration = Register(nameof(GetDirectories), + path, searchPattern); + + return EnumerateDirectories(path, searchPattern).ToArray(); + } /// public string[] GetDirectories(string path, string searchPattern, SearchOption searchOption) - => EnumerateDirectories(path, searchPattern, searchOption).ToArray(); + { + using IDisposable registration = Register(nameof(GetDirectories), + path, searchPattern, searchOption); + + return EnumerateDirectories(path, searchPattern, searchOption).ToArray(); + } #if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS /// public string[] GetDirectories(string path, string searchPattern, EnumerationOptions enumerationOptions) - => EnumerateDirectories(path, searchPattern, enumerationOptions).ToArray(); + { + using IDisposable registration = Register(nameof(GetDirectories), + path, searchPattern, enumerationOptions); + + return EnumerateDirectories(path, searchPattern, enumerationOptions).ToArray(); + } #endif /// public string GetDirectoryRoot(string path) - => _fileSystem.Path.GetPathRoot( - _fileSystem.Path.GetFullPath(path)) ?? - throw ExceptionFactory.PathIsEmpty(nameof(path)); + { + using IDisposable registration = Register(nameof(GetDirectoryRoot), + path); + + return _fileSystem.Path.GetPathRoot( + _fileSystem.Path.GetFullPath(path)) ?? + throw ExceptionFactory.PathIsEmpty(nameof(path)); + } /// public string[] GetFiles(string path) - => EnumerateFiles(path).ToArray(); + { + using IDisposable registration = Register(nameof(GetFiles), + path); + + return EnumerateFiles(path).ToArray(); + } /// public string[] GetFiles(string path, string searchPattern) - => EnumerateFiles(path, searchPattern).ToArray(); + { + using IDisposable registration = Register(nameof(GetFiles), + path, searchPattern); + + return EnumerateFiles(path, searchPattern).ToArray(); + } /// public string[] GetFiles(string path, string searchPattern, SearchOption searchOption) - => EnumerateFiles(path, searchPattern, searchOption).ToArray(); + { + using IDisposable registration = Register(nameof(GetFiles), + path, searchPattern, searchOption); + + return EnumerateFiles(path, searchPattern, searchOption).ToArray(); + } #if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS /// public string[] GetFiles(string path, string searchPattern, EnumerationOptions enumerationOptions) - => EnumerateFiles(path, searchPattern, enumerationOptions).ToArray(); + { + using IDisposable registration = Register(nameof(GetFiles), + path, searchPattern, enumerationOptions); + + return EnumerateFiles(path, searchPattern, enumerationOptions).ToArray(); + } #endif /// public string[] GetFileSystemEntries(string path) - => EnumerateFileSystemEntries(path).ToArray(); + { + using IDisposable registration = Register(nameof(GetFileSystemEntries), + path); + + return EnumerateFileSystemEntries(path).ToArray(); + } /// public string[] GetFileSystemEntries(string path, string searchPattern) - => EnumerateFileSystemEntries(path, searchPattern).ToArray(); + { + using IDisposable registration = Register(nameof(GetFileSystemEntries), + path, searchPattern); + + return EnumerateFileSystemEntries(path, searchPattern).ToArray(); + } /// public string[] GetFileSystemEntries(string path, string searchPattern, SearchOption searchOption) - => EnumerateFileSystemEntries(path, searchPattern, searchOption).ToArray(); + { + using IDisposable registration = Register(nameof(GetFileSystemEntries), + path, searchPattern, searchOption); + + return EnumerateFileSystemEntries(path, searchPattern, searchOption).ToArray(); + } #if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS /// public string[] GetFileSystemEntries(string path, string searchPattern, EnumerationOptions enumerationOptions) - => EnumerateFileSystemEntries(path, searchPattern, enumerationOptions) + { + using IDisposable registration = Register(nameof(GetFileSystemEntries), + path, searchPattern, enumerationOptions); + + return EnumerateFileSystemEntries(path, searchPattern, enumerationOptions) .ToArray(); + } #endif /// public DateTime GetLastAccessTime(string path) - => _fileSystem.Storage.GetContainer( + { + using IDisposable registration = Register(nameof(GetLastAccessTime), + path); + + return _fileSystem.Storage.GetContainer( _fileSystem.Storage.GetLocation( path.EnsureValidFormat(_fileSystem))) .LastAccessTime.Get(DateTimeKind.Local); + } /// public DateTime GetLastAccessTimeUtc(string path) - => _fileSystem.Storage.GetContainer( + { + using IDisposable registration = Register(nameof(GetLastAccessTimeUtc), + path); + + return _fileSystem.Storage.GetContainer( _fileSystem.Storage.GetLocation( path.EnsureValidFormat(_fileSystem))) .LastAccessTime.Get(DateTimeKind.Utc); + } /// public DateTime GetLastWriteTime(string path) - => _fileSystem.Storage.GetContainer( + { + using IDisposable registration = Register(nameof(GetLastWriteTime), + path); + + return _fileSystem.Storage.GetContainer( _fileSystem.Storage.GetLocation( path.EnsureValidFormat(_fileSystem))) .LastWriteTime.Get(DateTimeKind.Local); + } /// public DateTime GetLastWriteTimeUtc(string path) - => _fileSystem.Storage.GetContainer( + { + using IDisposable registration = Register(nameof(GetLastWriteTimeUtc), + path); + + return _fileSystem.Storage.GetContainer( _fileSystem.Storage.GetLocation( path.EnsureValidFormat(_fileSystem))) .LastWriteTime.Get(DateTimeKind.Utc); + } /// public string[] GetLogicalDrives() - => _fileSystem.DriveInfo.GetDrives().Select(x => x.Name).ToArray(); + { + using IDisposable registration = Register(nameof(GetLogicalDrives)); + + return _fileSystem.DriveInfo.GetDrives().Select(x => x.Name).ToArray(); + } /// public IDirectoryInfo? GetParent(string path) - => _fileSystem.DirectoryInfo + { + using IDisposable registration = Register(nameof(GetParent), + path); + + return _fileSystem.DirectoryInfo .New(path.EnsureValidArgument(_fileSystem, nameof(path))) .Parent; + } /// public void Move(string sourceDirName, string destDirName) - => _fileSystem.DirectoryInfo.New(sourceDirName + { + using IDisposable registration = Register(nameof(Move), + sourceDirName, destDirName); + + _fileSystem.DirectoryInfo.New(sourceDirName .EnsureValidFormat(_fileSystem, nameof(sourceDirName))) .MoveTo(destDirName .EnsureValidFormat(_fileSystem, nameof(destDirName))); + } #if FEATURE_FILESYSTEM_LINK /// public IFileSystemInfo? ResolveLinkTarget( string linkPath, bool returnFinalTarget) { + using IDisposable registration = Register(nameof(ResolveLinkTarget), + linkPath, returnFinalTarget); + try { return _fileSystem.DirectoryInfo.New(linkPath @@ -353,17 +555,31 @@ public void Move(string sourceDirName, string destDirName) /// public void SetCreationTime(string path, DateTime creationTime) - => LoadDirectoryInfoOrThrowNotFoundException(path, ThrowMissingFileCreatedTimeException) + { + using IDisposable registration = Register(nameof(SetCreationTime), + path, creationTime); + + LoadDirectoryInfoOrThrowNotFoundException(path, ThrowMissingFileCreatedTimeException) .CreationTime = creationTime; + } /// public void SetCreationTimeUtc(string path, DateTime creationTimeUtc) - => LoadDirectoryInfoOrThrowNotFoundException(path, ThrowMissingFileCreatedTimeException) + { + using IDisposable registration = Register(nameof(SetCreationTimeUtc), + path, creationTimeUtc); + + LoadDirectoryInfoOrThrowNotFoundException(path, ThrowMissingFileCreatedTimeException) .CreationTimeUtc = creationTimeUtc; + } /// public void SetCurrentDirectory(string path) { + using IDisposable registration = Register(nameof(SetCurrentDirectory), + path); + + IDirectoryInfo directoryInfo = _fileSystem.DirectoryInfo.New(path); if (!directoryInfo.Exists) @@ -377,27 +593,47 @@ public void SetCurrentDirectory(string path) /// public void SetLastAccessTime(string path, DateTime lastAccessTime) - => LoadDirectoryInfoOrThrowNotFoundException(path, + { + using IDisposable registration = Register(nameof(SetLastAccessTime), + path, lastAccessTime); + + LoadDirectoryInfoOrThrowNotFoundException(path, ThrowMissingFileLastAccessOrLastWriteTimeException) .LastAccessTime = lastAccessTime; + } /// public void SetLastAccessTimeUtc(string path, DateTime lastAccessTimeUtc) - => LoadDirectoryInfoOrThrowNotFoundException(path, + { + using IDisposable registration = Register(nameof(SetLastAccessTimeUtc), + path, lastAccessTimeUtc); + + LoadDirectoryInfoOrThrowNotFoundException(path, ThrowMissingFileLastAccessOrLastWriteTimeException) .LastAccessTimeUtc = lastAccessTimeUtc; + } /// public void SetLastWriteTime(string path, DateTime lastWriteTime) - => LoadDirectoryInfoOrThrowNotFoundException(path, + { + using IDisposable registration = Register(nameof(SetLastWriteTime), + path, lastWriteTime); + + LoadDirectoryInfoOrThrowNotFoundException(path, ThrowMissingFileLastAccessOrLastWriteTimeException) .LastWriteTime = lastWriteTime; + } /// public void SetLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc) - => LoadDirectoryInfoOrThrowNotFoundException(path, + { + using IDisposable registration = Register(nameof(SetLastWriteTimeUtc), + path, lastWriteTimeUtc); + + LoadDirectoryInfoOrThrowNotFoundException(path, ThrowMissingFileLastAccessOrLastWriteTimeException) .LastWriteTimeUtc = lastWriteTimeUtc; + } #endregion @@ -469,4 +705,22 @@ private IEnumerable EnumerateInternal(FileSystemTypes fileSystemTypes, .Select(x => _fileSystem .GetSubdirectoryPath(x.FullPath, adjustedLocation.GivenPath)); } + + private IDisposable Register(string name) + => _fileSystem.StatisticsRegistration.Directory.Register(name); + + private IDisposable Register(string name, T1 parameter1) + => _fileSystem.StatisticsRegistration.Directory.Register(name, + ParameterDescription.FromParameter(parameter1)); + + private IDisposable Register(string name, T1 parameter1, T2 parameter2) + => _fileSystem.StatisticsRegistration.Directory.Register(name, + ParameterDescription.FromParameter(parameter1), + ParameterDescription.FromParameter(parameter2)); + + private IDisposable Register(string name, T1 parameter1, T2 parameter2, T3 parameter3) + => _fileSystem.StatisticsRegistration.Directory.Register(name, + ParameterDescription.FromParameter(parameter1), + ParameterDescription.FromParameter(parameter2), + ParameterDescription.FromParameter(parameter3)); } diff --git a/Source/Testably.Abstractions.Testing/FileSystem/DriveInfoFactoryMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/DriveInfoFactoryMock.cs index 052aef285..912ae35a0 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/DriveInfoFactoryMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/DriveInfoFactoryMock.cs @@ -2,6 +2,8 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; +using System.Runtime.InteropServices.ComTypes; +using Testably.Abstractions.Testing.Statistics; using Testably.Abstractions.Testing.Storage; namespace Testably.Abstractions.Testing.FileSystem; @@ -24,18 +26,30 @@ public IFileSystem FileSystem /// [Obsolete("Use `IDriveInfoFactory.New(string)` instead")] public IDriveInfo FromDriveName(string driveName) - => New(driveName); + { + using IDisposable registration = Register(nameof(FromDriveName), + driveName); + + return New(driveName); + } /// public IDriveInfo[] GetDrives() - => _fileSystem.Storage.GetDrives() + { + using IDisposable registration = Register(nameof(GetDrives)); + + return _fileSystem.Storage.GetDrives() .Where(x => !x.IsUncPath) .Cast() .ToArray(); + } /// public IDriveInfo New(string driveName) { + using IDisposable registration = Register(nameof(New), + driveName); + if (driveName == null) { throw new ArgumentNullException(nameof(driveName)); @@ -50,6 +64,9 @@ public IDriveInfo New(string driveName) [return: NotNullIfNotNull("driveInfo")] public IDriveInfo? Wrap(DriveInfo? driveInfo) { + using IDisposable registration = Register(nameof(Wrap), + driveInfo); + if (driveInfo?.Name == null) { return null; @@ -59,4 +76,11 @@ public IDriveInfo New(string driveName) } #endregion + + private IDisposable Register(string name) + => _fileSystem.StatisticsRegistration.DriveInfo.Register(name); + + private IDisposable Register(string name, T1 parameter1) + => _fileSystem.StatisticsRegistration.DriveInfo.Register(name, + ParameterDescription.FromParameter(parameter1)); } diff --git a/Source/Testably.Abstractions.Testing/FileSystem/FileInfoFactoryMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/FileInfoFactoryMock.cs index cee9cd4f2..a5e944edd 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/FileInfoFactoryMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/FileInfoFactoryMock.cs @@ -2,6 +2,7 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using Testably.Abstractions.Testing.Helpers; +using Testably.Abstractions.Testing.Statistics; namespace Testably.Abstractions.Testing.FileSystem; @@ -23,11 +24,19 @@ public IFileSystem FileSystem /// [Obsolete("Use `IFileInfoFactory.New(string)` instead")] public IFileInfo FromFileName(string fileName) - => New(fileName); + { + using IDisposable registration = Register(nameof(FromFileName), + fileName); + + return New(fileName); + } /// public IFileInfo New(string fileName) { + using IDisposable registration = Register(nameof(New), + fileName); + if (fileName == null) { throw new ArgumentNullException(nameof(fileName)); @@ -46,11 +55,20 @@ public IFileInfo New(string fileName) /// [return: NotNullIfNotNull("fileInfo")] public IFileInfo? Wrap(FileInfo? fileInfo) - => FileInfoMock.New( + { + using IDisposable registration = Register(nameof(Wrap), + fileInfo); + + return FileInfoMock.New( _fileSystem.Storage.GetLocation( fileInfo?.FullName, fileInfo?.ToString()), _fileSystem); + } #endregion + + private IDisposable Register(string name, T1 parameter1) + => _fileSystem.StatisticsRegistration.FileInfo.Register(name, + ParameterDescription.FromParameter(parameter1)); } diff --git a/Source/Testably.Abstractions.Testing/FileSystem/FileInfoMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/FileInfoMock.cs index a5ab2309d..695cf2c29 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/FileInfoMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/FileInfoMock.cs @@ -1,6 +1,8 @@ -using System.Diagnostics.CodeAnalysis; +using System; +using System.Diagnostics.CodeAnalysis; using System.IO; using Testably.Abstractions.Testing.Helpers; +using Testably.Abstractions.Testing.Statistics; using Testably.Abstractions.Testing.Storage; namespace Testably.Abstractions.Testing.FileSystem; @@ -86,11 +88,18 @@ public override string Name /// public StreamWriter AppendText() - => new(Open(FileMode.Append, FileAccess.Write)); + { + using IDisposable registration = Register(nameof(AppendText)); + + return new StreamWriter(Open(FileMode.Append, FileAccess.Write)); + } /// public IFileInfo CopyTo(string destFileName) { + using IDisposable registration = Register(nameof(CopyTo), + destFileName); + destFileName.EnsureValidArgument(_fileSystem, nameof(destFileName)); IStorageLocation destinationLocation = _fileSystem.Storage.GetLocation(destFileName); Location.ThrowExceptionIfNotFound(_fileSystem); @@ -103,6 +112,9 @@ public IFileInfo CopyTo(string destFileName) /// public IFileInfo CopyTo(string destFileName, bool overwrite) { + using IDisposable registration = Register(nameof(CopyTo), + destFileName, overwrite); + destFileName.EnsureValidArgument(_fileSystem, nameof(destFileName)); IStorageLocation location = _fileSystem.Storage.Copy( Location, @@ -115,6 +127,8 @@ public IFileInfo CopyTo(string destFileName, bool overwrite) /// public FileSystemStream Create() { + using IDisposable registration = Register(nameof(Create)); + _fileSystem.Execute.NotOnNetFramework(Refresh); return _fileSystem.File.Create(FullName); } @@ -122,6 +136,8 @@ public FileSystemStream Create() /// public StreamWriter CreateText() { + using IDisposable registration = Register(nameof(CreateText)); + StreamWriter streamWriter = new(_fileSystem.File.Create(FullName)); #if NET8_0_OR_GREATER Refresh(); @@ -132,16 +148,27 @@ public StreamWriter CreateText() /// [SupportedOSPlatform("windows")] public void Decrypt() - => Container.Decrypt(); + { + using IDisposable registration = Register(nameof(Decrypt)); + + Container.Decrypt(); + } /// [SupportedOSPlatform("windows")] public void Encrypt() - => Container.Encrypt(); + { + using IDisposable registration = Register(nameof(Encrypt)); + + Container.Encrypt(); + } /// public void MoveTo(string destFileName) { + using IDisposable registration = Register(nameof(MoveTo), + destFileName); + Location = _fileSystem.Storage.Move( Location, _fileSystem.Storage.GetLocation(destFileName @@ -153,6 +180,9 @@ public void MoveTo(string destFileName) /// public void MoveTo(string destFileName, bool overwrite) { + using IDisposable registration = Register(nameof(MoveTo), + destFileName, overwrite); + Location = _fileSystem.Storage.Move( Location, _fileSystem.Storage.GetLocation(destFileName @@ -165,6 +195,9 @@ public void MoveTo(string destFileName, bool overwrite) /// public FileSystemStream Open(FileMode mode) { + using IDisposable registration = Register(nameof(Open), + mode); + _fileSystem.Execute.OnNetFrameworkIf(mode == FileMode.Append, () => throw ExceptionFactory.AppendAccessOnlyInWriteOnlyMode()); @@ -178,53 +211,83 @@ public FileSystemStream Open(FileMode mode) /// public FileSystemStream Open(FileMode mode, FileAccess access) - => new FileStreamMock( + { + using IDisposable registration = Register(nameof(Open), + mode, access); + + return new FileStreamMock( _fileSystem, FullName, mode, access, FileShare.None); + } /// public FileSystemStream Open(FileMode mode, FileAccess access, FileShare share) - => new FileStreamMock( + { + using IDisposable registration = Register(nameof(Open), + mode, access, share); + + return new FileStreamMock( _fileSystem, FullName, mode, access, share); + } #if FEATURE_FILESYSTEM_STREAM_OPTIONS /// public FileSystemStream Open(FileStreamOptions options) - => _fileSystem.File.Open(FullName, options); + { + using IDisposable registration = Register(nameof(Open), + options); + + return _fileSystem.File.Open(FullName, options); + } #endif /// public FileSystemStream OpenRead() - => new FileStreamMock( + { + using IDisposable registration = Register(nameof(OpenRead)); + + return new FileStreamMock( _fileSystem, FullName, FileMode.Open, FileAccess.Read); + } /// public StreamReader OpenText() - => new(OpenRead()); + { + using IDisposable registration = Register(nameof(OpenText)); + + return new StreamReader(OpenRead()); + } /// public FileSystemStream OpenWrite() - => new FileStreamMock( + { + using IDisposable registration = Register(nameof(OpenWrite)); + + return new FileStreamMock( _fileSystem, FullName, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None); + } /// public IFileInfo Replace(string destinationFileName, string? destinationBackupFileName) { + using IDisposable registration = Register(nameof(Replace), + destinationFileName, destinationBackupFileName); + IStorageLocation location = _fileSystem .Storage @@ -277,6 +340,9 @@ public IFileInfo Replace(string destinationFileName, string? destinationBackupFileName, bool ignoreMetadataErrors) { + using IDisposable registration = Register(nameof(Replace), + destinationFileName, destinationBackupFileName, ignoreMetadataErrors); + IStorageLocation location = _fileSystem.Storage.Replace( Location, _fileSystem.Storage.GetLocation( @@ -303,4 +369,22 @@ public IFileInfo Replace(string destinationFileName, return new FileInfoMock(location, fileSystem); } + + protected override IDisposable Register(string name) + => _fileSystem.StatisticsRegistration.FileInfo.Register(Location.FullPath, name); + + protected override IDisposable Register(string name, T1 parameter1) + => _fileSystem.StatisticsRegistration.FileInfo.Register(Location.FullPath, name, + ParameterDescription.FromParameter(parameter1)); + + private IDisposable Register(string name, T1 parameter1, T2 parameter2) + => _fileSystem.StatisticsRegistration.FileInfo.Register(Location.FullPath, name, + ParameterDescription.FromParameter(parameter1), + ParameterDescription.FromParameter(parameter2)); + + private IDisposable Register(string name, T1 parameter1, T2 parameter2, T3 parameter3) + => _fileSystem.StatisticsRegistration.FileInfo.Register(Location.FullPath, name, + ParameterDescription.FromParameter(parameter1), + ParameterDescription.FromParameter(parameter2), + ParameterDescription.FromParameter(parameter3)); } diff --git a/Source/Testably.Abstractions.Testing/FileSystem/FileMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/FileMock.cs index ee32d03bc..688fabb26 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/FileMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/FileMock.cs @@ -8,11 +8,11 @@ using System.Linq; using System.Text; using Testably.Abstractions.Testing.Helpers; +using Testably.Abstractions.Testing.Statistics; using Testably.Abstractions.Testing.Storage; #if FEATURE_FILESYSTEM_ASYNC using System.Threading; using System.Threading.Tasks; - #endif namespace Testably.Abstractions.Testing.FileSystem; @@ -34,7 +34,12 @@ public IFileSystem FileSystem /// public void AppendAllLines(string path, IEnumerable contents) - => AppendAllLines(path, contents, Encoding.Default); + { + using IDisposable registration = Register(nameof(AppendAllLines), + path, contents); + + AppendAllLines(path, contents, Encoding.Default); + } /// public void AppendAllLines( @@ -42,6 +47,9 @@ public void AppendAllLines( IEnumerable contents, Encoding encoding) { + using IDisposable registration = Register(nameof(AppendAllLines), + path, contents, encoding); + _ = contents ?? throw new ArgumentNullException(nameof(contents)); _ = encoding ?? throw new ArgumentNullException(nameof(encoding)); AppendAllText( @@ -54,13 +62,21 @@ public void AppendAllLines( /// public Task AppendAllLinesAsync(string path, IEnumerable contents, CancellationToken cancellationToken = default) - => AppendAllLinesAsync(path, contents, Encoding.Default, cancellationToken); + { + using IDisposable registration = Register(nameof(AppendAllLinesAsync), + path, contents, cancellationToken); + + return AppendAllLinesAsync(path, contents, Encoding.Default, cancellationToken); + } /// public Task AppendAllLinesAsync(string path, IEnumerable contents, Encoding encoding, CancellationToken cancellationToken = default) { + using IDisposable registration = Register(nameof(AppendAllLinesAsync), + path, contents, encoding, cancellationToken); + ThrowIfCancelled(cancellationToken); AppendAllLines(path, contents, encoding); return Task.CompletedTask; @@ -69,11 +85,19 @@ public Task AppendAllLinesAsync(string path, IEnumerable contents, /// public void AppendAllText(string path, string? contents) - => AppendAllText(path, contents, Encoding.Default); + { + using IDisposable registration = Register(nameof(AppendAllText), + path, contents); + + AppendAllText(path, contents, Encoding.Default); + } /// public void AppendAllText(string path, string? contents, Encoding encoding) { + using IDisposable registration = Register(nameof(AppendAllText), + path, contents, encoding); + IStorageContainer container = _fileSystem.Storage.GetOrCreateContainer( _fileSystem.Storage.GetLocation( @@ -105,12 +129,20 @@ public void AppendAllText(string path, string? contents, Encoding encoding) /// public Task AppendAllTextAsync(string path, string? contents, CancellationToken cancellationToken = default) - => AppendAllTextAsync(path, contents, Encoding.Default, cancellationToken); + { + using IDisposable registration = Register(nameof(AppendAllTextAsync), + path, contents, cancellationToken); + + return AppendAllTextAsync(path, contents, Encoding.Default, cancellationToken); + } /// public Task AppendAllTextAsync(string path, string? contents, Encoding encoding, CancellationToken cancellationToken = default) { + using IDisposable registration = Register(nameof(AppendAllTextAsync), + path, contents, encoding, cancellationToken); + ThrowIfCancelled(cancellationToken); AppendAllText(path, contents, encoding); return Task.CompletedTask; @@ -119,13 +151,21 @@ public Task AppendAllTextAsync(string path, string? contents, Encoding encoding, /// public StreamWriter AppendText(string path) - => FileSystem.FileInfo + { + using IDisposable registration = Register(nameof(AppendText), + path); + + return FileSystem.FileInfo .New(path.EnsureValidFormat(_fileSystem)) .AppendText(); + } /// public void Copy(string sourceFileName, string destFileName) { + using IDisposable registration = Register(nameof(Copy), + sourceFileName, destFileName); + sourceFileName.EnsureValidFormat(_fileSystem, nameof(sourceFileName)); destFileName.EnsureValidFormat(_fileSystem, nameof(destFileName)); try @@ -145,7 +185,11 @@ public void Copy(string sourceFileName, string destFileName) /// public void Copy(string sourceFileName, string destFileName, bool overwrite) - => _fileSystem.Execute.OnNetFramework( + { + using IDisposable registration = Register(nameof(Copy), + sourceFileName, destFileName, overwrite); + + _fileSystem.Execute.OnNetFramework( () => { try @@ -168,29 +212,44 @@ public void Copy(string sourceFileName, string destFileName, bool overwrite) .CopyTo(destFileName .EnsureValidFormat(_fileSystem, nameof(destFileName)), overwrite); }); + } /// public FileSystemStream Create(string path) - => new FileStreamMock( + { + using IDisposable registration = Register(nameof(Create), + path); + + return new FileStreamMock( _fileSystem, path, FileMode.Create, FileAccess.ReadWrite, FileShare.None); + } /// public FileSystemStream Create(string path, int bufferSize) - => new FileStreamMock( + { + using IDisposable registration = Register(nameof(Create), + path, bufferSize); + + return new FileStreamMock( _fileSystem, path, FileMode.Create, FileAccess.ReadWrite, FileShare.None, bufferSize); + } /// public FileSystemStream Create(string path, int bufferSize, FileOptions options) - => new FileStreamMock( + { + using IDisposable registration = Register(nameof(Create), + path, bufferSize, options); + + return new FileStreamMock( _fileSystem, path, FileMode.Create, @@ -198,12 +257,16 @@ public FileSystemStream Create(string path, int bufferSize, FileOptions options) FileShare.None, bufferSize, options); + } #if FEATURE_FILESYSTEM_LINK /// public IFileSystemInfo CreateSymbolicLink( string path, string pathToTarget) { + using IDisposable registration = Register(nameof(CreateSymbolicLink), + path, pathToTarget); + path.EnsureValidFormat(_fileSystem); IFileInfo fileSystemInfo = _fileSystem.FileInfo.New(path); @@ -214,28 +277,44 @@ public IFileSystemInfo CreateSymbolicLink( /// public StreamWriter CreateText(string path) - => FileSystem.FileInfo + { + using IDisposable registration = Register(nameof(CreateText), + path); + + return FileSystem.FileInfo .New(path.EnsureValidFormat(_fileSystem)) .CreateText(); + } /// [SupportedOSPlatform("windows")] public void Decrypt(string path) { + using IDisposable registration = Register(nameof(Decrypt), + path); + IStorageContainer container = GetContainerFromPath(path); container.Decrypt(); } /// public void Delete(string path) - => _fileSystem.Storage.DeleteContainer( + { + using IDisposable registration = Register(nameof(Delete), + path); + + _fileSystem.Storage.DeleteContainer( _fileSystem.Storage.GetLocation( path.EnsureValidFormat(_fileSystem))); + } /// [SupportedOSPlatform("windows")] public void Encrypt(string path) { + using IDisposable registration = Register(nameof(Encrypt), + path); + IStorageContainer container = GetContainerFromPath(path); container.Encrypt(); } @@ -243,6 +322,9 @@ public void Encrypt(string path) /// public bool Exists([NotNullWhen(true)] string? path) { + using IDisposable registration = Register(nameof(Exists), + path); + if (string.IsNullOrEmpty(path)) { return false; @@ -257,6 +339,9 @@ public bool Exists([NotNullWhen(true)] string? path) /// public FileAttributes GetAttributes(string path) { + using IDisposable registration = Register(nameof(GetAttributes), + path); + IStorageContainer container = _fileSystem.Storage .GetContainer(_fileSystem.Storage.GetLocation( path.EnsureValidFormat(_fileSystem)) @@ -269,6 +354,9 @@ public FileAttributes GetAttributes(string path) /// public FileAttributes GetAttributes(SafeFileHandle fileHandle) { + using IDisposable registration = Register(nameof(GetAttributes), + fileHandle); + IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); return container.Attributes; } @@ -276,144 +364,234 @@ public FileAttributes GetAttributes(SafeFileHandle fileHandle) /// public DateTime GetCreationTime(string path) - => _fileSystem.Storage.GetContainer( + { + using IDisposable registration = Register(nameof(GetCreationTime), + path); + + return _fileSystem.Storage.GetContainer( _fileSystem.Storage.GetLocation( path.EnsureValidFormat(_fileSystem))) .CreationTime.Get(DateTimeKind.Local); + } #if FEATURE_FILESYSTEM_SAFEFILEHANDLE /// public DateTime GetCreationTime(SafeFileHandle fileHandle) - => GetContainerFromSafeFileHandle(fileHandle) + { + using IDisposable registration = Register(nameof(GetCreationTime), + fileHandle); + + return GetContainerFromSafeFileHandle(fileHandle) .CreationTime.Get(DateTimeKind.Local); + } #endif /// public DateTime GetCreationTimeUtc(string path) - => _fileSystem.Storage.GetContainer( + { + using IDisposable registration = Register(nameof(GetCreationTimeUtc), + path); + + return _fileSystem.Storage.GetContainer( _fileSystem.Storage.GetLocation( path.EnsureValidFormat(_fileSystem))) .CreationTime.Get(DateTimeKind.Utc); + } #if FEATURE_FILESYSTEM_SAFEFILEHANDLE /// public DateTime GetCreationTimeUtc(SafeFileHandle fileHandle) - => GetContainerFromSafeFileHandle(fileHandle) + { + using IDisposable registration = Register(nameof(GetCreationTimeUtc), + fileHandle); + + return GetContainerFromSafeFileHandle(fileHandle) .CreationTime.Get(DateTimeKind.Utc); + } #endif /// public DateTime GetLastAccessTime(string path) - => _fileSystem.Storage.GetContainer( + { + using IDisposable registration = Register(nameof(GetLastAccessTime), + path); + + return _fileSystem.Storage.GetContainer( _fileSystem.Storage.GetLocation( path.EnsureValidFormat(_fileSystem))) .LastAccessTime.Get(DateTimeKind.Local); + } #if FEATURE_FILESYSTEM_SAFEFILEHANDLE /// public DateTime GetLastAccessTime(SafeFileHandle fileHandle) - => GetContainerFromSafeFileHandle(fileHandle) + { + using IDisposable registration = Register(nameof(GetLastAccessTime), + fileHandle); + + return GetContainerFromSafeFileHandle(fileHandle) .LastAccessTime.Get(DateTimeKind.Local); + } #endif /// public DateTime GetLastAccessTimeUtc(string path) - => _fileSystem.Storage.GetContainer( + { + using IDisposable registration = Register(nameof(GetLastAccessTimeUtc), + path); + + return _fileSystem.Storage.GetContainer( _fileSystem.Storage.GetLocation( path.EnsureValidFormat(_fileSystem))) .LastAccessTime.Get(DateTimeKind.Utc); + } #if FEATURE_FILESYSTEM_SAFEFILEHANDLE /// public DateTime GetLastAccessTimeUtc(SafeFileHandle fileHandle) - => GetContainerFromSafeFileHandle(fileHandle) + { + using IDisposable registration = Register(nameof(GetLastAccessTimeUtc), + fileHandle); + + return GetContainerFromSafeFileHandle(fileHandle) .LastAccessTime.Get(DateTimeKind.Utc); + } #endif /// public DateTime GetLastWriteTime(string path) - => _fileSystem.Storage.GetContainer( + { + using IDisposable registration = Register(nameof(GetLastWriteTime), + path); + + return _fileSystem.Storage.GetContainer( _fileSystem.Storage.GetLocation( path.EnsureValidFormat(_fileSystem))) .LastWriteTime.Get(DateTimeKind.Local); + } #if FEATURE_FILESYSTEM_SAFEFILEHANDLE /// public DateTime GetLastWriteTime(SafeFileHandle fileHandle) - => GetContainerFromSafeFileHandle(fileHandle) + { + using IDisposable registration = Register(nameof(GetLastWriteTime), + fileHandle); + + return GetContainerFromSafeFileHandle(fileHandle) .LastWriteTime.Get(DateTimeKind.Local); + } #endif /// public DateTime GetLastWriteTimeUtc(string path) - => _fileSystem.Storage.GetContainer( + { + using IDisposable registration = Register(nameof(GetLastWriteTimeUtc), + path); + + return _fileSystem.Storage.GetContainer( _fileSystem.Storage.GetLocation( path.EnsureValidFormat(_fileSystem))) .LastWriteTime.Get(DateTimeKind.Utc); + } #if FEATURE_FILESYSTEM_SAFEFILEHANDLE /// public DateTime GetLastWriteTimeUtc(SafeFileHandle fileHandle) - => GetContainerFromSafeFileHandle(fileHandle) + { + using IDisposable registration = Register(nameof(GetLastWriteTimeUtc), + fileHandle); + + return GetContainerFromSafeFileHandle(fileHandle) .LastWriteTime.Get(DateTimeKind.Utc); + } #endif #if FEATURE_FILESYSTEM_UNIXFILEMODE /// [UnsupportedOSPlatform("windows")] public UnixFileMode GetUnixFileMode(string path) - => _fileSystem.Execute.OnWindows( + { + using IDisposable registration = Register(nameof(GetUnixFileMode), + path); + + return _fileSystem.Execute.OnWindows( () => throw ExceptionFactory.UnixFileModeNotSupportedOnThisPlatform(), () => _fileSystem.Storage.GetContainer( _fileSystem.Storage.GetLocation( path.EnsureValidFormat(_fileSystem)) .ThrowExceptionIfNotFound(_fileSystem)) .UnixFileMode); + } #endif #if FEATURE_FILESYSTEM_SAFEFILEHANDLE /// [UnsupportedOSPlatform("windows")] public UnixFileMode GetUnixFileMode(SafeFileHandle fileHandle) - => _fileSystem.Execute.OnWindows( + { + using IDisposable registration = Register(nameof(GetUnixFileMode), + fileHandle); + + return _fileSystem.Execute.OnWindows( () => throw ExceptionFactory.UnixFileModeNotSupportedOnThisPlatform(), () => GetContainerFromSafeFileHandle(fileHandle) .UnixFileMode); + } #endif /// public void Move(string sourceFileName, string destFileName) - => _fileSystem.FileInfo.New(sourceFileName + { + using IDisposable registration = Register(nameof(Move), + sourceFileName, destFileName); + + _fileSystem.FileInfo.New(sourceFileName .EnsureValidFormat(_fileSystem, nameof(sourceFileName))) .MoveTo(destFileName .EnsureValidFormat(_fileSystem, nameof(destFileName))); + } #if FEATURE_FILE_MOVETO_OVERWRITE /// public void Move(string sourceFileName, string destFileName, bool overwrite) - => _fileSystem.FileInfo.New(sourceFileName + { + using IDisposable registration = Register(nameof(Move), + sourceFileName, destFileName, overwrite); + + _fileSystem.FileInfo.New(sourceFileName .EnsureValidFormat(_fileSystem, nameof(sourceFileName))) .MoveTo(destFileName .EnsureValidFormat(_fileSystem, nameof(destFileName)), overwrite); + } #endif /// public FileSystemStream Open(string path, FileMode mode) - => new FileStreamMock( + { + using IDisposable registration = Register(nameof(Open), + path, mode); + + return new FileStreamMock( _fileSystem, path, mode, mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite, FileShare.None); + } /// public FileSystemStream Open(string path, FileMode mode, FileAccess access) - => new FileStreamMock( + { + using IDisposable registration = Register(nameof(Open), + path, mode, access); + + return new FileStreamMock( _fileSystem, path, mode, access, FileShare.None); + } /// public FileSystemStream Open( @@ -421,17 +599,26 @@ public FileSystemStream Open( FileMode mode, FileAccess access, FileShare share) - => new FileStreamMock( + { + using IDisposable registration = Register(nameof(Open), + path, mode, access, share); + + return new FileStreamMock( _fileSystem, path, mode, access, share); + } #if FEATURE_FILESYSTEM_STREAM_OPTIONS /// public FileSystemStream Open(string path, FileStreamOptions options) - => new FileStreamMock( + { + using IDisposable registration = Register(nameof(Open), + path, options); + + return new FileStreamMock( _fileSystem, path, options.Mode, @@ -439,34 +626,53 @@ public FileSystemStream Open(string path, FileStreamOptions options) options.Share, options.BufferSize, options.Options); + } #endif /// public FileSystemStream OpenRead(string path) - => new FileStreamMock( + { + using IDisposable registration = Register(nameof(OpenRead), + path); + + return new FileStreamMock( _fileSystem, path, FileMode.Open, FileAccess.Read); + } /// public StreamReader OpenText(string path) - => FileSystem.FileInfo + { + using IDisposable registration = Register(nameof(OpenText), + path); + + return FileSystem.FileInfo .New(path.EnsureValidFormat(_fileSystem)) .OpenText(); + } /// public FileSystemStream OpenWrite(string path) - => new FileStreamMock( + { + using IDisposable registration = Register(nameof(OpenWrite), + path); + + return new FileStreamMock( _fileSystem, path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None); + } /// public byte[] ReadAllBytes(string path) { + using IDisposable registration = Register(nameof(ReadAllBytes), + path); + IStorageContainer container = GetContainerFromPath(path); using (container.RequestAccess( FileAccess.Read, @@ -482,9 +688,11 @@ public byte[] ReadAllBytes(string path) #if FEATURE_FILESYSTEM_ASYNC /// public Task ReadAllBytesAsync(string path, - CancellationToken cancellationToken = - default) + CancellationToken cancellationToken = default) { + using IDisposable registration = Register(nameof(ReadAllBytesAsync), + path, cancellationToken); + ThrowIfCancelled(cancellationToken); return Task.FromResult(ReadAllBytes(path)); } @@ -492,18 +700,33 @@ public Task ReadAllBytesAsync(string path, /// public string[] ReadAllLines(string path) - => ReadAllLines(path, Encoding.Default); + { + using IDisposable registration = Register(nameof(ReadAllLines), + path); + + return ReadAllLines(path, Encoding.Default); + } /// public string[] ReadAllLines(string path, Encoding encoding) - => ReadLines(path, encoding).ToArray(); + { + using IDisposable registration = Register(nameof(ReadAllLines), + path, encoding); + + return ReadLines(path, encoding).ToArray(); + } #if FEATURE_FILESYSTEM_ASYNC /// public Task ReadAllLinesAsync( string path, CancellationToken cancellationToken = default) - => ReadAllLinesAsync(path, Encoding.Default, cancellationToken); + { + using IDisposable registration = Register(nameof(ReadAllLinesAsync), + path, cancellationToken); + + return ReadAllLinesAsync(path, Encoding.Default, cancellationToken); + } /// public Task ReadAllLinesAsync( @@ -511,6 +734,9 @@ public Task ReadAllLinesAsync( Encoding encoding, CancellationToken cancellationToken = default) { + using IDisposable registration = Register(nameof(ReadAllLinesAsync), + path, encoding, cancellationToken); + ThrowIfCancelled(cancellationToken); return Task.FromResult(ReadAllLines(path, encoding)); } @@ -518,11 +744,19 @@ public Task ReadAllLinesAsync( /// public string ReadAllText(string path) - => ReadAllText(path, Encoding.Default); + { + using IDisposable registration = Register(nameof(ReadAllText), + path); + + return ReadAllText(path, Encoding.Default); + } /// public string ReadAllText(string path, Encoding encoding) { + using IDisposable registration = Register(nameof(ReadAllText), + path, encoding); + IStorageContainer container = GetContainerFromPath(path); using (container.RequestAccess( FileAccess.Read, @@ -544,7 +778,12 @@ public string ReadAllText(string path, Encoding encoding) public Task ReadAllTextAsync( string path, CancellationToken cancellationToken = default) - => ReadAllTextAsync(path, Encoding.Default, cancellationToken); + { + using IDisposable registration = Register(nameof(ReadAllTextAsync), + path, cancellationToken); + + return ReadAllTextAsync(path, Encoding.Default, cancellationToken); + } /// public Task ReadAllTextAsync( @@ -552,6 +791,9 @@ public Task ReadAllTextAsync( Encoding encoding, CancellationToken cancellationToken = default) { + using IDisposable registration = Register(nameof(ReadAllTextAsync), + path, encoding, cancellationToken); + ThrowIfCancelled(cancellationToken); return Task.FromResult(ReadAllText(path, encoding)); } @@ -559,27 +801,41 @@ public Task ReadAllTextAsync( /// public IEnumerable ReadLines(string path) - => ReadLines(path, Encoding.Default); + { + using IDisposable registration = Register(nameof(ReadLines), + path); + + return ReadLines(path, Encoding.Default); + } /// public IEnumerable ReadLines(string path, Encoding encoding) - => EnumerateLines(ReadAllText(path, encoding)); + { + using IDisposable registration = Register(nameof(ReadLines), + path, encoding); + + return EnumerateLines(ReadAllText(path, encoding)); + } #if FEATURE_FILESYSTEM_NET7 /// public IAsyncEnumerable ReadLinesAsync(string path, - CancellationToken cancellationToken = - default) + CancellationToken cancellationToken = default) { + using IDisposable registration = Register(nameof(ReadLinesAsync), + path, cancellationToken); + ThrowIfCancelled(cancellationToken); return ReadAllLines(path).ToAsyncEnumerable(); } /// public IAsyncEnumerable ReadLinesAsync(string path, Encoding encoding, - CancellationToken cancellationToken = - default) + CancellationToken cancellationToken = default) { + using IDisposable registration = Register(nameof(ReadLinesAsync), + path, encoding, cancellationToken); + ThrowIfCancelled(cancellationToken); return ReadAllLines(path, encoding).ToAsyncEnumerable(); } @@ -589,29 +845,42 @@ public IAsyncEnumerable ReadLinesAsync(string path, Encoding encoding, public void Replace(string sourceFileName, string destinationFileName, string? destinationBackupFileName) - => _fileSystem.FileInfo.New(sourceFileName + { + using IDisposable registration = Register(nameof(Replace), + sourceFileName, destinationFileName, destinationBackupFileName); + + _fileSystem.FileInfo.New(sourceFileName .EnsureValidFormat(_fileSystem, nameof(sourceFileName))) .Replace(destinationFileName .EnsureValidFormat(_fileSystem, nameof(destinationFileName)), destinationBackupFileName); + } /// public void Replace(string sourceFileName, string destinationFileName, string? destinationBackupFileName, bool ignoreMetadataErrors) - => _fileSystem.FileInfo.New(sourceFileName + { + using IDisposable registration = Register(nameof(Replace), + sourceFileName, destinationFileName, destinationBackupFileName, ignoreMetadataErrors); + + _fileSystem.FileInfo.New(sourceFileName .EnsureValidFormat(_fileSystem, nameof(sourceFileName))) .Replace(destinationFileName .EnsureValidFormat(_fileSystem, nameof(destinationFileName)), destinationBackupFileName, ignoreMetadataErrors); + } #if FEATURE_FILESYSTEM_LINK /// public IFileSystemInfo? ResolveLinkTarget( string linkPath, bool returnFinalTarget) { + using IDisposable registration = Register(nameof(ResolveLinkTarget), + linkPath, returnFinalTarget); + IStorageLocation location = _fileSystem.Storage.GetLocation(linkPath .EnsureValidFormat(_fileSystem, nameof(linkPath))); @@ -642,6 +911,9 @@ public void Replace(string sourceFileName, /// public void SetAttributes(string path, FileAttributes fileAttributes) { + using IDisposable registration = Register(nameof(SetAttributes), + path, fileAttributes); + IStorageContainer container = GetContainerFromPath(path); container.Attributes = fileAttributes; } @@ -650,6 +922,9 @@ public void SetAttributes(string path, FileAttributes fileAttributes) /// public void SetAttributes(SafeFileHandle fileHandle, FileAttributes fileAttributes) { + using IDisposable registration = Register(nameof(SetAttributes), + fileHandle, fileAttributes); + IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); container.Attributes = fileAttributes; } @@ -658,6 +933,9 @@ public void SetAttributes(SafeFileHandle fileHandle, FileAttributes fileAttribut /// public void SetCreationTime(string path, DateTime creationTime) { + using IDisposable registration = Register(nameof(SetCreationTime), + path, creationTime); + IStorageContainer container = GetContainerFromPath(path, ExceptionMode.FileNotFoundExceptionOnLinuxAndMac); container.CreationTime.Set(creationTime, DateTimeKind.Local); @@ -667,6 +945,9 @@ public void SetCreationTime(string path, DateTime creationTime) /// public void SetCreationTime(SafeFileHandle fileHandle, DateTime creationTime) { + using IDisposable registration = Register(nameof(SetCreationTime), + fileHandle, creationTime); + IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); container.CreationTime.Set(creationTime, DateTimeKind.Local); } @@ -675,6 +956,9 @@ public void SetCreationTime(SafeFileHandle fileHandle, DateTime creationTime) /// public void SetCreationTimeUtc(string path, DateTime creationTimeUtc) { + using IDisposable registration = Register(nameof(SetCreationTimeUtc), + path, creationTimeUtc); + IStorageContainer container = GetContainerFromPath(path, ExceptionMode.FileNotFoundExceptionOnLinuxAndMac); container.CreationTime.Set(creationTimeUtc, DateTimeKind.Utc); @@ -684,6 +968,9 @@ public void SetCreationTimeUtc(string path, DateTime creationTimeUtc) /// public void SetCreationTimeUtc(SafeFileHandle fileHandle, DateTime creationTimeUtc) { + using IDisposable registration = Register(nameof(SetCreationTimeUtc), + fileHandle, creationTimeUtc); + IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); container.CreationTime.Set(creationTimeUtc, DateTimeKind.Utc); } @@ -692,6 +979,9 @@ public void SetCreationTimeUtc(SafeFileHandle fileHandle, DateTime creationTimeU /// public void SetLastAccessTime(string path, DateTime lastAccessTime) { + using IDisposable registration = Register(nameof(SetLastAccessTime), + path, lastAccessTime); + IStorageContainer container = GetContainerFromPath(path, ExceptionMode.FileNotFoundExceptionOnLinuxAndMac); container.LastAccessTime.Set(lastAccessTime, DateTimeKind.Local); @@ -701,6 +991,9 @@ public void SetLastAccessTime(string path, DateTime lastAccessTime) /// public void SetLastAccessTime(SafeFileHandle fileHandle, DateTime lastAccessTime) { + using IDisposable registration = Register(nameof(SetLastAccessTime), + fileHandle, lastAccessTime); + IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); container.LastAccessTime.Set(lastAccessTime, DateTimeKind.Local); } @@ -709,6 +1002,9 @@ public void SetLastAccessTime(SafeFileHandle fileHandle, DateTime lastAccessTime /// public void SetLastAccessTimeUtc(string path, DateTime lastAccessTimeUtc) { + using IDisposable registration = Register(nameof(SetLastAccessTimeUtc), + path, lastAccessTimeUtc); + IStorageContainer container = GetContainerFromPath(path, ExceptionMode.FileNotFoundExceptionOnLinuxAndMac); container.LastAccessTime.Set(lastAccessTimeUtc, DateTimeKind.Utc); @@ -716,9 +1012,11 @@ public void SetLastAccessTimeUtc(string path, DateTime lastAccessTimeUtc) #if FEATURE_FILESYSTEM_SAFEFILEHANDLE /// - public void SetLastAccessTimeUtc(SafeFileHandle fileHandle, - DateTime lastAccessTimeUtc) + public void SetLastAccessTimeUtc(SafeFileHandle fileHandle, DateTime lastAccessTimeUtc) { + using IDisposable registration = Register(nameof(SetLastAccessTimeUtc), + fileHandle, lastAccessTimeUtc); + IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); container.LastAccessTime.Set(lastAccessTimeUtc, DateTimeKind.Utc); } @@ -727,6 +1025,9 @@ public void SetLastAccessTimeUtc(SafeFileHandle fileHandle, /// public void SetLastWriteTime(string path, DateTime lastWriteTime) { + using IDisposable registration = Register(nameof(SetLastWriteTime), + path, lastWriteTime); + IStorageContainer container = GetContainerFromPath(path, ExceptionMode.FileNotFoundExceptionOnLinuxAndMac); container.LastWriteTime.Set(lastWriteTime, DateTimeKind.Local); @@ -736,6 +1037,9 @@ public void SetLastWriteTime(string path, DateTime lastWriteTime) /// public void SetLastWriteTime(SafeFileHandle fileHandle, DateTime lastWriteTime) { + using IDisposable registration = Register(nameof(SetLastWriteTime), + fileHandle, lastWriteTime); + IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); container.LastWriteTime.Set(lastWriteTime, DateTimeKind.Local); } @@ -744,6 +1048,9 @@ public void SetLastWriteTime(SafeFileHandle fileHandle, DateTime lastWriteTime) /// public void SetLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc) { + using IDisposable registration = Register(nameof(SetLastWriteTimeUtc), + path, lastWriteTimeUtc); + IStorageContainer container = GetContainerFromPath(path, ExceptionMode.FileNotFoundExceptionOnLinuxAndMac); container.LastWriteTime.Set(lastWriteTimeUtc, DateTimeKind.Utc); @@ -753,6 +1060,9 @@ public void SetLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc) /// public void SetLastWriteTimeUtc(SafeFileHandle fileHandle, DateTime lastWriteTimeUtc) { + using IDisposable registration = Register(nameof(SetLastWriteTimeUtc), + fileHandle, lastWriteTimeUtc); + IStorageContainer container = GetContainerFromSafeFileHandle(fileHandle); container.LastWriteTime.Set(lastWriteTimeUtc, DateTimeKind.Utc); } @@ -763,6 +1073,9 @@ public void SetLastWriteTimeUtc(SafeFileHandle fileHandle, DateTime lastWriteTim [UnsupportedOSPlatform("windows")] public void SetUnixFileMode(string path, UnixFileMode mode) { + using IDisposable registration = Register(nameof(SetUnixFileMode), + path, mode); + _fileSystem.Execute.OnWindows( () => throw ExceptionFactory.UnixFileModeNotSupportedOnThisPlatform()); @@ -776,6 +1089,9 @@ public void SetUnixFileMode(string path, UnixFileMode mode) [UnsupportedOSPlatform("windows")] public void SetUnixFileMode(SafeFileHandle fileHandle, UnixFileMode mode) { + using IDisposable registration = Register(nameof(SetUnixFileMode), + fileHandle, mode); + _fileSystem.Execute.OnWindows( () => throw ExceptionFactory.UnixFileModeNotSupportedOnThisPlatform()); @@ -787,6 +1103,9 @@ public void SetUnixFileMode(SafeFileHandle fileHandle, UnixFileMode mode) /// public void WriteAllBytes(string path, byte[] bytes) { + using IDisposable registration = Register(nameof(WriteAllBytes), + path, bytes); + _ = bytes ?? throw new ArgumentNullException(nameof(bytes)); IStorageContainer container = _fileSystem.Storage.GetOrCreateContainer( @@ -820,6 +1139,9 @@ public void WriteAllBytes(string path, byte[] bytes) public Task WriteAllBytesAsync(string path, byte[] bytes, CancellationToken cancellationToken = default) { + using IDisposable registration = Register(nameof(WriteAllBytesAsync), + path, bytes, cancellationToken); + ThrowIfCancelled(cancellationToken); WriteAllBytes(path, bytes); return Task.CompletedTask; @@ -828,28 +1150,48 @@ public Task WriteAllBytesAsync(string path, byte[] bytes, /// public void WriteAllLines(string path, string[] contents) - => WriteAllLines(path, contents, Encoding.Default); + { + using IDisposable registration = Register(nameof(WriteAllLines), + path, contents); + + WriteAllLines(path, contents, Encoding.Default); + } /// public void WriteAllLines(string path, IEnumerable contents) - => WriteAllLines(path, contents, Encoding.Default); + { + using IDisposable registration = Register(nameof(WriteAllLines), + path, contents); + + WriteAllLines(path, contents, Encoding.Default); + } /// public void WriteAllLines( string path, string[] contents, Encoding encoding) - => WriteAllLines(path, contents.AsEnumerable(), encoding); + { + using IDisposable registration = Register(nameof(WriteAllLines), + path, contents, encoding); + + WriteAllLines(path, contents.AsEnumerable(), encoding); + } /// public void WriteAllLines( string path, IEnumerable contents, Encoding encoding) - => WriteAllText( + { + using IDisposable registration = Register(nameof(WriteAllLines), + path, contents, encoding); + + WriteAllText( path, contents.Aggregate(string.Empty, (a, b) => a + b + Environment.NewLine), encoding); + } #if FEATURE_FILESYSTEM_ASYNC /// @@ -857,7 +1199,12 @@ public Task WriteAllLinesAsync( string path, IEnumerable contents, CancellationToken cancellationToken = default) - => WriteAllLinesAsync(path, contents, Encoding.Default, cancellationToken); + { + using IDisposable registration = Register(nameof(WriteAllLinesAsync), + path, contents, cancellationToken); + + return WriteAllLinesAsync(path, contents, Encoding.Default, cancellationToken); + } /// public Task WriteAllLinesAsync( @@ -866,6 +1213,9 @@ public Task WriteAllLinesAsync( Encoding encoding, CancellationToken cancellationToken = default) { + using IDisposable registration = Register(nameof(WriteAllLinesAsync), + path, contents, encoding, cancellationToken); + ThrowIfCancelled(cancellationToken); WriteAllLines(path, contents, encoding); return Task.CompletedTask; @@ -874,11 +1224,19 @@ public Task WriteAllLinesAsync( /// public void WriteAllText(string path, string? contents) - => WriteAllText(path, contents, Encoding.Default); + { + using IDisposable registration = Register(nameof(WriteAllText), + path, contents); + + WriteAllText(path, contents, Encoding.Default); + } /// public void WriteAllText(string path, string? contents, Encoding encoding) { + using IDisposable registration = Register(nameof(WriteAllText), + path, contents, encoding); + IStorageContainer container = _fileSystem.Storage.GetOrCreateContainer( _fileSystem.Storage.GetLocation( @@ -913,12 +1271,20 @@ public void WriteAllText(string path, string? contents, Encoding encoding) /// public Task WriteAllTextAsync(string path, string? contents, CancellationToken cancellationToken = default) - => WriteAllTextAsync(path, contents, Encoding.Default, cancellationToken); + { + using IDisposable registration = Register(nameof(WriteAllTextAsync), + path, contents, cancellationToken); + + return WriteAllTextAsync(path, contents, Encoding.Default, cancellationToken); + } /// public Task WriteAllTextAsync(string path, string? contents, Encoding encoding, CancellationToken cancellationToken = default) { + using IDisposable registration = Register(nameof(WriteAllTextAsync), + path, contents, encoding, cancellationToken); + ThrowIfCancelled(cancellationToken); WriteAllText(path, contents, encoding); return Task.CompletedTask; @@ -992,4 +1358,29 @@ private static void ThrowIfCancelled(CancellationToken cancellationToken) } } #endif + + private IDisposable Register(string name) + => _fileSystem.StatisticsRegistration.File.Register(name); + + private IDisposable Register(string name, T1 parameter1) + => _fileSystem.StatisticsRegistration.File.Register(name, + ParameterDescription.FromParameter(parameter1)); + + private IDisposable Register(string name, T1 parameter1, T2 parameter2) + => _fileSystem.StatisticsRegistration.File.Register(name, + ParameterDescription.FromParameter(parameter1), + ParameterDescription.FromParameter(parameter2)); + + private IDisposable Register(string name, T1 parameter1, T2 parameter2, T3 parameter3) + => _fileSystem.StatisticsRegistration.File.Register(name, + ParameterDescription.FromParameter(parameter1), + ParameterDescription.FromParameter(parameter2), + ParameterDescription.FromParameter(parameter3)); + + private IDisposable Register(string name, T1 parameter1, T2 parameter2, T3 parameter3, T4 parameter4) + => _fileSystem.StatisticsRegistration.File.Register(name, + ParameterDescription.FromParameter(parameter1), + ParameterDescription.FromParameter(parameter2), + ParameterDescription.FromParameter(parameter3), + ParameterDescription.FromParameter(parameter4)); } diff --git a/Source/Testably.Abstractions.Testing/FileSystem/FileStreamFactoryMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/FileStreamFactoryMock.cs index e4b64b7da..0ad2a8608 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/FileStreamFactoryMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/FileStreamFactoryMock.cs @@ -2,6 +2,7 @@ using System; using System.IO; using Testably.Abstractions.Testing.Helpers; +using Testably.Abstractions.Testing.Statistics; #if NET6_0_OR_GREATER using System.Diagnostics.CodeAnalysis; #endif @@ -105,23 +106,38 @@ public Stream Create(IntPtr handle, FileAccess access, bool ownsHandle, int buff /// public FileSystemStream New(string path, FileMode mode) - => New(path, + { + using IDisposable registration = Register(nameof(New), + path, mode); + + return New(path, mode, mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite, DefaultShare, DefaultBufferSize, DefaultUseAsync); + } /// public FileSystemStream New(string path, FileMode mode, FileAccess access) - => New(path, mode, access, DefaultShare, DefaultBufferSize, DefaultUseAsync); + { + using IDisposable registration = Register(nameof(New), + path, mode, access); + + return New(path, mode, access, DefaultShare, DefaultBufferSize, DefaultUseAsync); + } /// public FileSystemStream New(string path, FileMode mode, FileAccess access, FileShare share) - => New(path, mode, access, share, DefaultBufferSize, DefaultUseAsync); + { + using IDisposable registration = Register(nameof(New), + path, mode, access, share); + + return New(path, mode, access, share, DefaultBufferSize, DefaultUseAsync); + } /// public FileSystemStream New(string path, @@ -129,7 +145,12 @@ public FileSystemStream New(string path, FileAccess access, FileShare share, int bufferSize) - => New(path, mode, access, share, bufferSize, DefaultUseAsync); + { + using IDisposable registration = Register(nameof(New), + path, mode, access, share, bufferSize); + + return New(path, mode, access, share, bufferSize, DefaultUseAsync); + } /// public FileSystemStream New(string path, @@ -138,12 +159,17 @@ public FileSystemStream New(string path, FileShare share, int bufferSize, bool useAsync) - => New(path, + { + using IDisposable registration = Register(nameof(New), + path, mode, access, share, bufferSize, useAsync); + + return New(path, mode, access, share, bufferSize, useAsync ? FileOptions.Asynchronous : FileOptions.None); + } /// public FileSystemStream New(string path, @@ -152,13 +178,18 @@ public FileSystemStream New(string path, FileShare share, int bufferSize, FileOptions options) - => new FileStreamMock(_fileSystem, + { + using IDisposable registration = Register(nameof(New), + path, mode, access, share, bufferSize, options); + + return new FileStreamMock(_fileSystem, path, mode, access, share, bufferSize, options); + } /// #if NET6_0_OR_GREATER @@ -166,6 +197,9 @@ public FileSystemStream New(string path, #endif public FileSystemStream New(SafeFileHandle handle, FileAccess access) { + using IDisposable registration = Register(nameof(New), + handle, access); + SafeFileHandleMock safeFileHandleMock = _fileSystem .SafeFileHandleStrategy.MapSafeFileHandle(handle); return New( @@ -181,6 +215,9 @@ public FileSystemStream New(SafeFileHandle handle, FileAccess access) #endif public FileSystemStream New(SafeFileHandle handle, FileAccess access, int bufferSize) { + using IDisposable registration = Register(nameof(New), + handle, access, bufferSize); + SafeFileHandleMock safeFileHandleMock = _fileSystem .SafeFileHandleStrategy.MapSafeFileHandle(handle); return New( @@ -198,6 +235,9 @@ public FileSystemStream New(SafeFileHandle handle, FileAccess access, int buffer public FileSystemStream New(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) { + using IDisposable registration = Register(nameof(New), + handle, access, bufferSize, isAsync); + SafeFileHandleMock safeFileHandleMock = _fileSystem .SafeFileHandleStrategy.MapSafeFileHandle(handle); return New( @@ -212,17 +252,64 @@ public FileSystemStream New(SafeFileHandle handle, FileAccess access, int buffer #if FEATURE_FILESYSTEM_STREAM_OPTIONS /// public FileSystemStream New(string path, FileStreamOptions options) - => New(path, + { + using IDisposable registration = Register(nameof(New), + path, options); + + return New(path, options.Mode, options.Access, options.Share, options.BufferSize, options.Options); + } #endif /// public FileSystemStream Wrap(FileStream fileStream) - => throw ExceptionFactory.NotSupportedFileStreamWrapping(); + { + Register(nameof(Wrap), fileStream); + throw ExceptionFactory.NotSupportedFileStreamWrapping(); + } #endregion + + private void Register(string name, T1 parameter1) + => _fileSystem.StatisticsRegistration.FileStream.Register(name, + ParameterDescription.FromParameter(parameter1)); + + private IDisposable Register(string name, T1 parameter1, T2 parameter2) + => _fileSystem.StatisticsRegistration.FileStream.Register(name, + ParameterDescription.FromParameter(parameter1), + ParameterDescription.FromParameter(parameter2)); + + private IDisposable Register(string name, T1 parameter1, T2 parameter2, T3 parameter3) + => _fileSystem.StatisticsRegistration.FileStream.Register(name, + ParameterDescription.FromParameter(parameter1), + ParameterDescription.FromParameter(parameter2), + ParameterDescription.FromParameter(parameter3)); + + private IDisposable Register(string name, T1 parameter1, T2 parameter2, T3 parameter3, T4 parameter4) + => _fileSystem.StatisticsRegistration.FileStream.Register(name, + ParameterDescription.FromParameter(parameter1), + ParameterDescription.FromParameter(parameter2), + ParameterDescription.FromParameter(parameter3), + ParameterDescription.FromParameter(parameter4)); + + private IDisposable Register(string name, T1 parameter1, T2 parameter2, T3 parameter3, T4 parameter4, T5 parameter5) + => _fileSystem.StatisticsRegistration.FileStream.Register(name, + ParameterDescription.FromParameter(parameter1), + ParameterDescription.FromParameter(parameter2), + ParameterDescription.FromParameter(parameter3), + ParameterDescription.FromParameter(parameter4), + ParameterDescription.FromParameter(parameter5)); + + private IDisposable Register(string name, T1 parameter1, T2 parameter2, T3 parameter3, T4 parameter4, T5 parameter5, T6 parameter6) + => _fileSystem.StatisticsRegistration.FileStream.Register(name, + ParameterDescription.FromParameter(parameter1), + ParameterDescription.FromParameter(parameter2), + ParameterDescription.FromParameter(parameter3), + ParameterDescription.FromParameter(parameter4), + ParameterDescription.FromParameter(parameter5), + ParameterDescription.FromParameter(parameter6)); } diff --git a/Source/Testably.Abstractions.Testing/FileSystem/FileStreamMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/FileStreamMock.cs index 276161c6a..bf20e4f3d 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/FileStreamMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/FileStreamMock.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using Testably.Abstractions.Helpers; using Testably.Abstractions.Testing.Helpers; +using Testably.Abstractions.Testing.Statistics; using Testably.Abstractions.Testing.Storage; namespace Testably.Abstractions.Testing.FileSystem; @@ -32,6 +33,7 @@ public override bool CanWrite private readonly FileMode _mode; private readonly FileOptions _options; private readonly MemoryStream _stream; + private readonly IStorageLocation _location; internal FileStreamMock(MockFileSystem fileSystem, string? path, @@ -74,9 +76,9 @@ private FileStreamMock(MemoryStream stream, _options = options; _initialPosition = Position; - IStorageLocation location = _fileSystem.Storage.GetLocation(Name); - location.ThrowExceptionIfNotFound(_fileSystem, true); - IStorageContainer file = _fileSystem.Storage.GetContainer(location); + _location = _fileSystem.Storage.GetLocation(Name); + _location.ThrowExceptionIfNotFound(_fileSystem, true); + IStorageContainer file = _fileSystem.Storage.GetContainer(_location); if (file is NullContainer) { if (_mode.Equals(FileMode.Open) || @@ -86,7 +88,7 @@ private FileStreamMock(MemoryStream stream, _fileSystem.Path.GetFullPath(Name)); } - file = _fileSystem.Storage.GetOrCreateContainer(location, + file = _fileSystem.Storage.GetOrCreateContainer(_location, InMemoryContainer.NewFile, this); } @@ -110,7 +112,7 @@ private FileStreamMock(MemoryStream stream, if (file.Attributes.HasFlag(FileAttributes.ReadOnly) && access.HasFlag(FileAccess.Write)) { - throw ExceptionFactory.AccessToPathDenied(location.FullPath); + throw ExceptionFactory.AccessToPathDenied(_location.FullPath); } _accessLock = file.RequestAccess(access, share); @@ -127,6 +129,9 @@ public override IAsyncResult BeginRead(byte[] buffer, AsyncCallback? callback, object? state) { + using IDisposable registration = Register(nameof(BeginRead), + buffer, offset, count, callback, state); + ThrowIfDisposed(); if (!CanRead) { @@ -143,6 +148,9 @@ public override IAsyncResult BeginWrite(byte[] buffer, AsyncCallback? callback, object? state) { + using IDisposable registration = Register(nameof(BeginWrite), + buffer, offset, count, callback, state); + ThrowIfDisposed(); if (!CanWrite) { @@ -155,6 +163,9 @@ public override IAsyncResult BeginWrite(byte[] buffer, /// public override void CopyTo(Stream destination, int bufferSize) { + using IDisposable registration = Register(nameof(CopyTo), + destination, bufferSize); + _fileSystem.Execute.NotOnWindows(() => _container.AdjustTimes(TimeAdjustments.LastAccessTime)); base.CopyTo(destination, bufferSize); @@ -164,6 +175,9 @@ public override void CopyTo(Stream destination, int bufferSize) public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) { + using IDisposable registration = Register(nameof(CopyToAsync), + destination, bufferSize, cancellationToken); + _container.AdjustTimes(TimeAdjustments.LastAccessTime); return base.CopyToAsync(destination, bufferSize, cancellationToken); } @@ -171,6 +185,9 @@ public override Task CopyToAsync(Stream destination, int bufferSize, /// public override int EndRead(IAsyncResult asyncResult) { + using IDisposable registration = Register(nameof(EndRead), + asyncResult); + _container.AdjustTimes(TimeAdjustments.LastAccessTime); return base.EndRead(asyncResult); } @@ -178,6 +195,9 @@ public override int EndRead(IAsyncResult asyncResult) /// public override void EndWrite(IAsyncResult asyncResult) { + using IDisposable registration = Register(nameof(EndWrite), + asyncResult); + _isContentChanged = true; base.EndWrite(asyncResult); } @@ -185,17 +205,27 @@ public override void EndWrite(IAsyncResult asyncResult) /// public override void Flush() { + using IDisposable registration = Register(nameof(Flush)); + ThrowIfDisposed(); InternalFlush(); } /// public override void Flush(bool flushToDisk) - => Flush(); + { + using IDisposable registration = Register(nameof(Flush), + flushToDisk); + + Flush(); + } /// public override Task FlushAsync(CancellationToken cancellationToken) { + using IDisposable registration = Register(nameof(FlushAsync), + cancellationToken); + if (cancellationToken.IsCancellationRequested) { throw ExceptionFactory.TaskWasCanceled(); @@ -208,6 +238,9 @@ public override Task FlushAsync(CancellationToken cancellationToken) /// public override int Read(byte[] buffer, int offset, int count) { + using IDisposable registration = Register(nameof(Read), + buffer, offset, count); + if (!CanRead) { throw ExceptionFactory.StreamDoesNotSupportReading(); @@ -222,6 +255,9 @@ public override int Read(byte[] buffer, int offset, int count) /// public override int Read(Span buffer) { + using IDisposable registration = Register(nameof(Read), + buffer); + if (!CanRead) { throw ExceptionFactory.StreamDoesNotSupportReading(); @@ -237,6 +273,9 @@ public override int Read(Span buffer) public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { + using IDisposable registration = Register(nameof(ReadAsync), + buffer, offset, count, cancellationToken); + if (!CanRead) { throw ExceptionFactory.StreamDoesNotSupportReading(); @@ -250,9 +289,11 @@ public override Task ReadAsync(byte[] buffer, int offset, int count, #if FEATURE_SPAN /// public override ValueTask ReadAsync(Memory buffer, - CancellationToken cancellationToken = - new()) + CancellationToken cancellationToken = new()) { + using IDisposable registration = Register(nameof(ReadAsync), + buffer, cancellationToken); + if (!CanRead) { throw ExceptionFactory.StreamDoesNotSupportReading(); @@ -267,6 +308,8 @@ public override ValueTask ReadAsync(Memory buffer, /// public override int ReadByte() { + using IDisposable registration = Register(nameof(ReadByte)); + if (!CanRead) { throw ExceptionFactory.StreamDoesNotSupportReading(); @@ -280,6 +323,9 @@ public override int ReadByte() /// public override long Seek(long offset, SeekOrigin origin) { + using IDisposable registration = Register(nameof(Seek), + offset, origin); + if (_mode == FileMode.Append && offset <= _initialPosition) { throw ExceptionFactory.SeekBackwardNotPossibleInAppendMode(); @@ -291,6 +337,9 @@ public override long Seek(long offset, SeekOrigin origin) /// public override void SetLength(long value) { + using IDisposable registration = Register(nameof(SetLength), + value); + ThrowIfDisposed(); if (!CanWrite) { @@ -303,6 +352,9 @@ public override void SetLength(long value) /// public override void Write(byte[] buffer, int offset, int count) { + using IDisposable registration = Register(nameof(Write), + buffer, offset, count); + if (!CanWrite) { throw ExceptionFactory.StreamDoesNotSupportWriting(); @@ -316,6 +368,9 @@ public override void Write(byte[] buffer, int offset, int count) /// public override void Write(ReadOnlySpan buffer) { + using IDisposable registration = Register(nameof(Write), + buffer); + if (!CanWrite) { throw ExceptionFactory.StreamDoesNotSupportWriting(); @@ -330,6 +385,9 @@ public override void Write(ReadOnlySpan buffer) public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { + using IDisposable registration = Register(nameof(WriteAsync), + buffer, offset, count, cancellationToken); + if (!CanWrite) { throw ExceptionFactory.StreamDoesNotSupportWriting(); @@ -344,6 +402,9 @@ public override Task WriteAsync(byte[] buffer, int offset, int count, public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = new()) { + using IDisposable registration = Register(nameof(WriteAsync), + buffer, cancellationToken); + if (!CanWrite) { throw ExceptionFactory.StreamDoesNotSupportWriting(); @@ -357,6 +418,9 @@ public override ValueTask WriteAsync(ReadOnlyMemory buffer, /// public override void WriteByte(byte value) { + using IDisposable registration = Register(nameof(WriteByte), + value); + if (!CanWrite) { throw ExceptionFactory.StreamDoesNotSupportWriting(); @@ -366,6 +430,14 @@ public override void WriteByte(byte value) base.WriteByte(value); } + /// + public override string? ToString() + { + using IDisposable registration = Register(nameof(ToString)); + + return base.ToString(); + } + /// protected override void Dispose(bool disposing) { @@ -465,4 +537,46 @@ public void StoreMetadata(string key, T? value) /// public T? RetrieveMetadata(string key) => _container.Extensibility.RetrieveMetadata(key); + + private IDisposable Register(string name) + => _fileSystem.StatisticsRegistration.FileStream.Register(_location.FullPath, name); + + private IDisposable Register(string name, T1 parameter1) + => _fileSystem.StatisticsRegistration.FileStream.Register(_location.FullPath, name, + ParameterDescription.FromParameter(parameter1)); + +#if FEATURE_SPAN + private IDisposable Register(string name, Span parameter1) + => _fileSystem.StatisticsRegistration.FileStream.Register(_location.FullPath, name, + ParameterDescription.FromParameter(parameter1)); + private IDisposable Register(string name, ReadOnlySpan parameter1) + => _fileSystem.StatisticsRegistration.FileStream.Register(_location.FullPath, name, + ParameterDescription.FromParameter(parameter1)); +#endif + + private IDisposable Register(string name, T1 parameter1, T2 parameter2) + => _fileSystem.StatisticsRegistration.FileStream.Register(_location.FullPath, name, + ParameterDescription.FromParameter(parameter1), + ParameterDescription.FromParameter(parameter2)); + + private IDisposable Register(string name, T1 parameter1, T2 parameter2, T3 parameter3) + => _fileSystem.StatisticsRegistration.FileStream.Register(_location.FullPath, name, + ParameterDescription.FromParameter(parameter1), + ParameterDescription.FromParameter(parameter2), + ParameterDescription.FromParameter(parameter3)); + + private IDisposable Register(string name, T1 parameter1, T2 parameter2, T3 parameter3, T4 parameter4) + => _fileSystem.StatisticsRegistration.FileStream.Register(_location.FullPath, name, + ParameterDescription.FromParameter(parameter1), + ParameterDescription.FromParameter(parameter2), + ParameterDescription.FromParameter(parameter3), + ParameterDescription.FromParameter(parameter4)); + + private IDisposable Register(string name, T1 parameter1, T2 parameter2, T3 parameter3, T4 parameter4, T5 parameter5) + => _fileSystem.StatisticsRegistration.FileStream.Register(_location.FullPath, name, + ParameterDescription.FromParameter(parameter1), + ParameterDescription.FromParameter(parameter2), + ParameterDescription.FromParameter(parameter3), + ParameterDescription.FromParameter(parameter4), + ParameterDescription.FromParameter(parameter5)); } diff --git a/Source/Testably.Abstractions.Testing/FileSystem/FileSystemInfoMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/FileSystemInfoMock.cs index bc384d336..34921a74c 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/FileSystemInfoMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/FileSystemInfoMock.cs @@ -55,6 +55,9 @@ public FileAttributes Attributes /// public void CreateAsSymbolicLink(string pathToTarget) { + using IDisposable registration = Register(nameof(CreateAsSymbolicLink), + pathToTarget); + if (!_fileSystem.Execute.IsWindows && string.IsNullOrWhiteSpace(FullName)) { return; @@ -71,7 +74,7 @@ public void CreateAsSymbolicLink(string pathToTarget) else { throw ExceptionFactory.CannotCreateFileAsAlreadyExists( - _fileSystem.Execute, + _fileSystem.Execute, Location.FriendlyName); } } @@ -94,6 +97,8 @@ public DateTime CreationTimeUtc /// public virtual void Delete() { + using IDisposable registration = Register(nameof(Delete)); + _fileSystem.Storage.DeleteContainer(Location); ResetCache(!_fileSystem.Execute.IsNetFramework); } @@ -193,6 +198,8 @@ public UnixFileMode UnixFileMode /// public void Refresh() { + using IDisposable registration = Register(nameof(Refresh)); + ResetCache(true); } @@ -200,6 +207,9 @@ public void Refresh() /// public IFileSystemInfo? ResolveLinkTarget(bool returnFinalTarget) { + using IDisposable registration = Register(nameof(ResolveLinkTarget), + returnFinalTarget); + try { IStorageLocation? targetLocation = @@ -281,4 +291,10 @@ private void RefreshInternal() Container = _fileSystem.Storage.GetContainer(Location); _isInitialized = true; } + + protected virtual IDisposable Register(string name) + => new NoOpDisposable(); + + protected virtual IDisposable Register(string name, T1 parameter1) + => new NoOpDisposable(); } diff --git a/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherFactoryMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherFactoryMock.cs index 48e6c3537..21ac70591 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherFactoryMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherFactoryMock.cs @@ -2,6 +2,7 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using Testably.Abstractions.Testing.Helpers; +using Testably.Abstractions.Testing.Statistics; namespace Testably.Abstractions.Testing.FileSystem; @@ -38,11 +39,18 @@ public IFileSystemWatcher CreateNew(string path, string filter) /// public IFileSystemWatcher New() - => FileSystemWatcherMock.New(_fileSystem); + { + using IDisposable registration = Register(nameof(New)); + + return FileSystemWatcherMock.New(_fileSystem); + } /// public IFileSystemWatcher New(string path) { + using IDisposable registration = Register(nameof(New), + path); + FileSystemWatcherMock fileSystemWatcherMock = FileSystemWatcherMock.New(_fileSystem); fileSystemWatcherMock.Path = path.EnsureValidArgument(_fileSystem); @@ -52,6 +60,9 @@ public IFileSystemWatcher New(string path) /// public IFileSystemWatcher New(string path, string filter) { + using IDisposable registration = Register(nameof(New), + path, filter); + FileSystemWatcherMock fileSystemWatcherMock = FileSystemWatcherMock.New(_fileSystem); fileSystemWatcherMock.Path = path.EnsureValidArgument(_fileSystem); @@ -64,6 +75,9 @@ public IFileSystemWatcher New(string path, string filter) // ReSharper disable once ReturnTypeCanBeNotNullable public IFileSystemWatcher? Wrap(FileSystemWatcher? fileSystemWatcher) { + using IDisposable registration = Register(nameof(Wrap), + fileSystemWatcher); + if (fileSystemWatcher == null) { return null; @@ -91,4 +105,16 @@ public IFileSystemWatcher New(string path, string filter) } #endregion + + private IDisposable Register(string name) + => _fileSystem.StatisticsRegistration.FileSystemWatcher.Register(name); + + private IDisposable Register(string name, T1 parameter1) + => _fileSystem.StatisticsRegistration.FileSystemWatcher.Register(name, + ParameterDescription.FromParameter(parameter1)); + + private IDisposable Register(string name, T1 parameter1, T2 parameter2) + => _fileSystem.StatisticsRegistration.FileSystemWatcher.Register(name, + ParameterDescription.FromParameter(parameter1), + ParameterDescription.FromParameter(parameter2)); } diff --git a/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherMock.cs index 8249bac36..f299829f1 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/FileSystemWatcherMock.cs @@ -8,6 +8,7 @@ using System.Threading.Channels; using System.Threading.Tasks; using Testably.Abstractions.Testing.Helpers; +using Testably.Abstractions.Testing.Statistics; namespace Testably.Abstractions.Testing.FileSystem; @@ -135,6 +136,8 @@ public string Path /// public void BeginInit() { + using IDisposable registration = Register(nameof(BeginInit)); + _isInitializing = true; Stop(); } @@ -151,6 +154,8 @@ public void BeginInit() /// public void EndInit() { + using IDisposable registration = Register(nameof(EndInit)); + _isInitializing = false; Restart(); } @@ -164,18 +169,33 @@ public void EndInit() /// public IWaitForChangedResult WaitForChanged( WatcherChangeTypes changeType) - => WaitForChanged(changeType, Timeout.Infinite); + { + using IDisposable registration = Register(nameof(WaitForChanged), + changeType); + + return WaitForChanged(changeType, Timeout.Infinite); + } /// public IWaitForChangedResult WaitForChanged( WatcherChangeTypes changeType, int timeout) - => WaitForChangedInternal(changeType, TimeSpan.FromMilliseconds(timeout)); + { + using IDisposable registration = Register(nameof(WaitForChanged), + changeType, timeout); + + return WaitForChangedInternal(changeType, TimeSpan.FromMilliseconds(timeout)); + } #if FEATURE_FILESYSTEM_NET7 /// public IWaitForChangedResult WaitForChanged( WatcherChangeTypes changeType, TimeSpan timeout) - => WaitForChangedInternal(changeType, timeout); + { + using IDisposable registration = Register(nameof(WaitForChanged), + changeType, timeout); + + return WaitForChangedInternal(changeType, timeout); + } #endif #endregion @@ -506,4 +526,16 @@ public WaitForChangedResultMock( /// public bool TimedOut { get; } } + + private IDisposable Register(string name) + => _fileSystem.StatisticsRegistration.FileSystemWatcher.Register(_path, name); + + private IDisposable Register(string name, T1 parameter1) + => _fileSystem.StatisticsRegistration.FileSystemWatcher.Register(_path, name, + ParameterDescription.FromParameter(parameter1)); + + private IDisposable Register(string name, T1 parameter1, T2 parameter2) + => _fileSystem.StatisticsRegistration.FileSystemWatcher.Register(_path, name, + ParameterDescription.FromParameter(parameter1), + ParameterDescription.FromParameter(parameter2)); } diff --git a/Source/Testably.Abstractions.Testing/FileSystem/PathMock.cs b/Source/Testably.Abstractions.Testing/FileSystem/PathMock.cs index 00ac8af0a..62a83c4dc 100644 --- a/Source/Testably.Abstractions.Testing/FileSystem/PathMock.cs +++ b/Source/Testably.Abstractions.Testing/FileSystem/PathMock.cs @@ -1,8 +1,10 @@ -using System.IO; +using System; +using System.Diagnostics.CodeAnalysis; +using System.IO; using Testably.Abstractions.Helpers; using Testably.Abstractions.Testing.Helpers; +using Testably.Abstractions.Testing.Statistics; #if FEATURE_FILESYSTEM_NET7 -using System.Diagnostics.CodeAnalysis; using Testably.Abstractions.Testing.Storage; #endif @@ -18,10 +20,79 @@ internal PathMock(MockFileSystem fileSystem) _fileSystem = fileSystem; } + /// + [return: NotNullIfNotNull("path")] + public override string? ChangeExtension(string? path, string? extension) + { + using IDisposable register = Register(nameof(ChangeExtension), + path, extension); + + return base.ChangeExtension(path, extension); + } + + /// + public override string Combine(string path1, string path2) + { + using IDisposable register = Register(nameof(Combine), + path1, path2); + + return base.Combine(path1, path2); + } + + /// + public override string Combine(string path1, string path2, string path3) + { + using IDisposable register = Register(nameof(Combine), + path1, path2, path3); + + return base.Combine(path1, path2, path3); + } + + /// + public override string Combine(string path1, string path2, string path3, string path4) + { + using IDisposable register = Register(nameof(Combine), + path1, path2, path3, path4); + + return base.Combine(path1, path2, path3, path4); + } + + /// + public override string Combine(params string[] paths) + { + using IDisposable register = Register(nameof(Combine), + paths); + + return base.Combine(paths); + } + +#if FEATURE_PATH_ADVANCED + /// + public override bool EndsInDirectorySeparator(ReadOnlySpan path) + { + using IDisposable register = Register(nameof(EndsInDirectorySeparator), + path); + + return base.EndsInDirectorySeparator(path); + } + + /// + public override bool EndsInDirectorySeparator(string path) + { + using IDisposable register = Register(nameof(EndsInDirectorySeparator), + path); + + return base.EndsInDirectorySeparator(path); + } +#endif + #if FEATURE_FILESYSTEM_NET7 /// public override bool Exists([NotNullWhen(true)] string? path) { + using IDisposable register = Register(nameof(Exists), + path); + if (string.IsNullOrEmpty(path)) { return false; @@ -32,9 +103,95 @@ public override bool Exists([NotNullWhen(true)] string? path) } #endif +#if FEATURE_SPAN + /// + public override ReadOnlySpan GetDirectoryName(ReadOnlySpan path) + { + using IDisposable register = Register(nameof(GetDirectoryName), + path); + + return base.GetDirectoryName(path); + } +#endif + + /// + public override string? GetDirectoryName(string? path) + { + using IDisposable register = Register(nameof(GetDirectoryName), + path); + + return base.GetDirectoryName(path); + } + +#if FEATURE_SPAN + /// + public override ReadOnlySpan GetExtension(ReadOnlySpan path) + { + using IDisposable register = Register(nameof(GetExtension), + path); + + return base.GetExtension(path); + } +#endif + + /// + [return: NotNullIfNotNull("path")] + public override string? GetExtension(string? path) + { + using IDisposable register = Register(nameof(GetExtension), + path); + + return base.GetExtension(path); + } + +#if FEATURE_SPAN + /// + public override ReadOnlySpan GetFileName(ReadOnlySpan path) + { + using IDisposable register = Register(nameof(GetFileName), + path); + + return base.GetFileName(path); + } +#endif + + /// + [return: NotNullIfNotNull("path")] + public override string? GetFileName(string? path) + { + using IDisposable register = Register(nameof(GetFileName), + path); + + return base.GetFileName(path); + } + +#if FEATURE_SPAN + /// + public override ReadOnlySpan GetFileNameWithoutExtension(ReadOnlySpan path) + { + using IDisposable register = Register(nameof(GetFileNameWithoutExtension), + path); + + return base.GetFileNameWithoutExtension(path); + } +#endif + + /// + [return: NotNullIfNotNull("path")] + public override string? GetFileNameWithoutExtension(string? path) + { + using IDisposable register = Register(nameof(GetFileNameWithoutExtension), + path); + + return base.GetFileNameWithoutExtension(path); + } + /// public override string GetFullPath(string path) { + using IDisposable register = Register(nameof(GetFullPath), + path); + path.EnsureValidArgument(_fileSystem, nameof(path)); string? pathRoot = Path.GetPathRoot(path); @@ -57,10 +214,68 @@ public override string GetFullPath(string path) path)); } +#if FEATURE_PATH_RELATIVE + /// + public override string GetFullPath(string path, string basePath) + { + using IDisposable register = Register(nameof(GetFullPath), + path, basePath); + + return base.GetFullPath(path, basePath); + } +#endif + + /// + public override char[] GetInvalidFileNameChars() + { + using IDisposable register = Register(nameof(GetInvalidFileNameChars)); + + return base.GetInvalidFileNameChars(); + } + + /// + public override char[] GetInvalidPathChars() + { + using IDisposable register = Register(nameof(GetInvalidPathChars)); + + return base.GetInvalidPathChars(); + } + +#if FEATURE_SPAN + /// + public override ReadOnlySpan GetPathRoot(ReadOnlySpan path) + { + using IDisposable register = Register(nameof(GetPathRoot), + path); + + return base.GetPathRoot(path); + } +#endif + + /// + public override string? GetPathRoot(string? path) + { + using IDisposable register = Register(nameof(GetPathRoot), + path); + + return base.GetPathRoot(path); + } + + /// + public override string GetRandomFileName() + { + using IDisposable register = Register(nameof(GetRandomFileName)); + + return base.GetRandomFileName(); + } + #if FEATURE_PATH_RELATIVE /// public override string GetRelativePath(string relativeTo, string path) { + using IDisposable register = Register(nameof(GetRelativePath), + relativeTo, path); + relativeTo.EnsureValidArgument(_fileSystem, nameof(relativeTo)); path.EnsureValidArgument(_fileSystem, nameof(path)); @@ -70,4 +285,285 @@ public override string GetRelativePath(string relativeTo, string path) return Path.GetRelativePath(relativeTo, path); } #endif + + /// +#if !NETSTANDARD2_0 + [Obsolete( + "Insecure temporary file creation methods should not be used. Use `Path.Combine(Path.GetTempPath(), Path.GetRandomFileName())` instead.")] +#endif + public override string GetTempFileName() + { + using IDisposable register = Register(nameof(GetTempFileName)); + + return base.GetTempFileName(); + } + + /// + public override string GetTempPath() + { + using IDisposable register = Register(nameof(GetTempPath)); + + return base.GetTempPath(); + } + +#if FEATURE_SPAN + /// + public override bool HasExtension(ReadOnlySpan path) + { + using IDisposable register = Register(nameof(HasExtension), + path); + + return base.HasExtension(path); + } +#endif + + /// + public override bool HasExtension([NotNullWhen(true)] string? path) + { + using IDisposable register = Register(nameof(HasExtension), + path); + + return base.HasExtension(path); + } + +#if FEATURE_SPAN + /// + public override bool IsPathFullyQualified(ReadOnlySpan path) + { + using IDisposable register = Register(nameof(IsPathFullyQualified), + path); + + return base.IsPathFullyQualified(path); + } +#endif + +#if FEATURE_PATH_RELATIVE + /// + public override bool IsPathFullyQualified(string path) + { + using IDisposable register = Register(nameof(IsPathFullyQualified), + path); + + return base.IsPathFullyQualified(path); + } +#endif + +#if FEATURE_SPAN + /// + public override bool IsPathRooted(ReadOnlySpan path) + { + using IDisposable register = Register(nameof(IsPathRooted), + path); + + return base.IsPathRooted(path); + } +#endif + + /// + public override bool IsPathRooted(string? path) + { + using IDisposable register = Register(nameof(IsPathRooted), + path); + + return base.IsPathRooted(path); + } + +#if FEATURE_PATH_ADVANCED + /// + public override string Join(ReadOnlySpan path1, ReadOnlySpan path2) + { + using IDisposable register = Register(nameof(Join), + path1, path2); + + return base.Join(path1, path2); + } + + /// + public override string Join(ReadOnlySpan path1, + ReadOnlySpan path2, + ReadOnlySpan path3) + { + using IDisposable register = Register(nameof(Join), + path1, + path2, + path3); + + return base.Join(path1, path2, path3); + } + + /// + public override string Join(ReadOnlySpan path1, + ReadOnlySpan path2, + ReadOnlySpan path3, + ReadOnlySpan path4) + { + using IDisposable register = Register(nameof(Join), + path1, + path2, + path3, + path4); + + return base.Join(path1, path2, path3, path4); + } + + /// + public override string Join(string? path1, string? path2) + { + using IDisposable register = Register(nameof(Join), + path1, path2); + + return base.Join(path1, path2); + } + + /// + public override string Join(string? path1, string? path2, string? path3) + { + using IDisposable register = Register(nameof(Join), + path1, path2, path3); + + return base.Join(path1, path2, path3); + } + + /// + public override string Join(string? path1, string? path2, string? path3, string? path4) + { + using IDisposable register = Register(nameof(Join), + path1, path2, path3, path4); + + return base.Join(path1, path2, path3, path4); + } + + /// + public override string Join(params string?[] paths) + { + using IDisposable register = Register(nameof(Join), + paths); + + return base.Join(paths); + } +#endif + +#if FEATURE_PATH_ADVANCED + /// + public override ReadOnlySpan TrimEndingDirectorySeparator(ReadOnlySpan path) + { + using IDisposable register = Register(nameof(TrimEndingDirectorySeparator), + path); + + return base.TrimEndingDirectorySeparator(path); + } + + /// + public override string TrimEndingDirectorySeparator(string path) + { + using IDisposable register = Register(nameof(TrimEndingDirectorySeparator), + path); + + return base.TrimEndingDirectorySeparator(path); + } +#endif + +#if FEATURE_PATH_JOIN + /// + public override bool TryJoin(ReadOnlySpan path1, + ReadOnlySpan path2, + Span destination, + out int charsWritten) + { + int registerCharsWritten = 0; + try + { + bool result = base.TryJoin(path1, path2, destination, out charsWritten); + registerCharsWritten = charsWritten; + return result; + } + finally + { + _fileSystem.StatisticsRegistration.Path.Register(nameof(TryJoin), + ParameterDescription.FromParameter(path1), + ParameterDescription.FromParameter(path2), + ParameterDescription.FromParameter(destination), + ParameterDescription.FromOutParameter(registerCharsWritten)); + } + } + + /// + public override bool TryJoin(ReadOnlySpan path1, + ReadOnlySpan path2, + ReadOnlySpan path3, + Span destination, + out int charsWritten) + { + int registerCharsWritten = 0; + try + { + bool result = base.TryJoin(path1, path2, path3, destination, out charsWritten); + registerCharsWritten = charsWritten; + return result; + } + finally + { + _fileSystem.StatisticsRegistration.Path.Register(nameof(TryJoin), + ParameterDescription.FromParameter(path1), + ParameterDescription.FromParameter(path2), + ParameterDescription.FromParameter(path3), + ParameterDescription.FromParameter(destination), + ParameterDescription.FromOutParameter(registerCharsWritten)); + } + } +#endif + + private IDisposable Register(string name) + => _fileSystem.StatisticsRegistration.Path.Register(name); + + private IDisposable Register(string name, T1 parameter1) + => _fileSystem.StatisticsRegistration.Path.Register(name, + ParameterDescription.FromParameter(parameter1)); + +#if FEATURE_SPAN + private IDisposable Register(string name, ReadOnlySpan parameter1) + => _fileSystem.StatisticsRegistration.Path.Register(name, + ParameterDescription.FromParameter(parameter1)); + + private IDisposable Register(string name, ReadOnlySpan parameter1, + ReadOnlySpan parameter2) + => _fileSystem.StatisticsRegistration.Path.Register(name, + ParameterDescription.FromParameter(parameter1), + ParameterDescription.FromParameter(parameter2)); + + private IDisposable Register(string name, ReadOnlySpan parameter1, + ReadOnlySpan parameter2, ReadOnlySpan parameter3) + => _fileSystem.StatisticsRegistration.Path.Register(name, + ParameterDescription.FromParameter(parameter1), + ParameterDescription.FromParameter(parameter2), + ParameterDescription.FromParameter(parameter3)); + + private IDisposable Register(string name, ReadOnlySpan parameter1, + ReadOnlySpan parameter2, ReadOnlySpan parameter3, ReadOnlySpan parameter4) + => _fileSystem.StatisticsRegistration.Path.Register(name, + ParameterDescription.FromParameter(parameter1), + ParameterDescription.FromParameter(parameter2), + ParameterDescription.FromParameter(parameter3), + ParameterDescription.FromParameter(parameter4)); +#endif + + private IDisposable Register(string name, T1 parameter1, T2 parameter2) + => _fileSystem.StatisticsRegistration.Path.Register(name, + ParameterDescription.FromParameter(parameter1), + ParameterDescription.FromParameter(parameter2)); + + private IDisposable Register(string name, T1 parameter1, T2 parameter2, + T3 parameter3) + => _fileSystem.StatisticsRegistration.Path.Register(name, + ParameterDescription.FromParameter(parameter1), + ParameterDescription.FromParameter(parameter2), + ParameterDescription.FromParameter(parameter3)); + + private IDisposable Register(string name, T1 parameter1, T2 parameter2, + T3 parameter3, T4 parameter4) + => _fileSystem.StatisticsRegistration.Path.Register(name, + ParameterDescription.FromParameter(parameter1), + ParameterDescription.FromParameter(parameter2), + ParameterDescription.FromParameter(parameter3), + ParameterDescription.FromParameter(parameter4)); } diff --git a/Source/Testably.Abstractions.Testing/FileSystemInitializer/FileInitializer.cs b/Source/Testably.Abstractions.Testing/FileSystemInitializer/FileInitializer.cs index 67d19daa0..eb24275f4 100644 --- a/Source/Testably.Abstractions.Testing/FileSystemInitializer/FileInitializer.cs +++ b/Source/Testably.Abstractions.Testing/FileSystemInitializer/FileInitializer.cs @@ -1,4 +1,5 @@ using System; +using Testably.Abstractions.Testing.Helpers; namespace Testably.Abstractions.Testing.FileSystemInitializer; @@ -23,6 +24,7 @@ public FileInitializer(FileSystemInitializer initializer, public IFileSystemFileInitializer Which( Action fileManipulation) { + using IDisposable release = FileSystem.IgnoreStatistics(); FileManipulator fileManipulator = new(FileSystem, File); fileManipulation.Invoke(fileManipulator); return this; diff --git a/Source/Testably.Abstractions.Testing/FileSystemInitializer/FileSystemInitializer.cs b/Source/Testably.Abstractions.Testing/FileSystemInitializer/FileSystemInitializer.cs index 6592e6ff7..5f522b63b 100644 --- a/Source/Testably.Abstractions.Testing/FileSystemInitializer/FileSystemInitializer.cs +++ b/Source/Testably.Abstractions.Testing/FileSystemInitializer/FileSystemInitializer.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using Testably.Abstractions.RandomSystem; using Testably.Abstractions.Testing.Helpers; @@ -68,6 +69,7 @@ public IFileSystemInitializer With( /// public IFileSystemFileInitializer WithAFile(string? extension = null) { + using IDisposable release = FileSystem.IgnoreStatistics(); IRandom random = (FileSystem as MockFileSystem)? .RandomSystem.Random.Shared ?? RandomFactory.Shared; string fileName; @@ -84,6 +86,7 @@ public IFileSystemFileInitializer WithAFile(string? extension = nul /// public IFileSystemDirectoryInitializer WithASubdirectory() { + using IDisposable release = FileSystem.IgnoreStatistics(); IRandom random = (FileSystem as MockFileSystem)? .RandomSystem.Random.Shared ?? RandomFactory.Shared; string directoryName; @@ -127,6 +130,7 @@ public IFileSystemDirectoryInitializer WithSubdirectory( private IDirectoryInfo WithDirectory(DirectoryDescription directory) { + using IDisposable release = FileSystem.IgnoreStatistics(); IDirectoryInfo directoryInfo = FileSystem.DirectoryInfo.New( FileSystem.Path.Combine(_basePath, directory.Name)); if (directoryInfo.Exists) @@ -162,6 +166,7 @@ private IDirectoryInfo WithDirectory(DirectoryDescription directory) private IFileInfo WithFile(FileDescription file) { + using IDisposable release = FileSystem.IgnoreStatistics(); IFileInfo fileInfo = FileSystem.FileInfo.New( FileSystem.Path.Combine(_basePath, file.Name)); if (fileInfo.Exists) diff --git a/Source/Testably.Abstractions.Testing/FileSystemInitializerExtensions.cs b/Source/Testably.Abstractions.Testing/FileSystemInitializerExtensions.cs index b015e3e14..c78fe2317 100644 --- a/Source/Testably.Abstractions.Testing/FileSystemInitializerExtensions.cs +++ b/Source/Testably.Abstractions.Testing/FileSystemInitializerExtensions.cs @@ -29,10 +29,11 @@ public static IFileSystemInitializer InitializeIn( Action? options = null) where TFileSystem : IFileSystem { - if (Path.IsPathRooted(basePath) && + using IDisposable release = fileSystem.IgnoreStatistics(); + if (fileSystem.Path.IsPathRooted(basePath) && fileSystem is MockFileSystem mockFileSystem) { - string? drive = Path.GetPathRoot(basePath); + string? drive = fileSystem.Path.GetPathRoot(basePath); mockFileSystem.WithDrive(drive); } diff --git a/Source/Testably.Abstractions.Testing/Helpers/FileSystemExtensions.cs b/Source/Testably.Abstractions.Testing/Helpers/FileSystemExtensions.cs index 24feb4f43..8c5556f18 100644 --- a/Source/Testably.Abstractions.Testing/Helpers/FileSystemExtensions.cs +++ b/Source/Testably.Abstractions.Testing/Helpers/FileSystemExtensions.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using Testably.Abstractions.Testing.Statistics; using Testably.Abstractions.Testing.Storage; namespace Testably.Abstractions.Testing.Helpers; @@ -81,4 +82,26 @@ internal static string GetSubdirectoryPath(this MockFileSystem fileSystem, return fullFilePath; } + + /// + /// Ignores all registrations on the until the return value is disposed. + /// + internal static IDisposable Ignore(this IStatisticsGate statisticsGate) + { + statisticsGate.TryGetLock(out IDisposable? release); + return release; + } + + /// + /// Ignores all registrations on the until the return value is disposed. + /// + internal static IDisposable IgnoreStatistics(this IFileSystem fileSystem) + { + if (fileSystem is MockFileSystem mockFileSystem) + { + return mockFileSystem.StatisticsRegistration.Ignore(); + } + + return new NoOpDisposable(); + } } diff --git a/Source/Testably.Abstractions.Testing/Helpers/NoOpDisposable.cs b/Source/Testably.Abstractions.Testing/Helpers/NoOpDisposable.cs new file mode 100644 index 000000000..53206f6bb --- /dev/null +++ b/Source/Testably.Abstractions.Testing/Helpers/NoOpDisposable.cs @@ -0,0 +1,12 @@ +using System; + +namespace Testably.Abstractions.Testing.Helpers; + +internal sealed class NoOpDisposable : IDisposable +{ + /// + public void Dispose() + { + // Do nothing + } +} diff --git a/Source/Testably.Abstractions.Testing/MockFileSystem.cs b/Source/Testably.Abstractions.Testing/MockFileSystem.cs index 3fd4ff06d..66100f0cd 100644 --- a/Source/Testably.Abstractions.Testing/MockFileSystem.cs +++ b/Source/Testably.Abstractions.Testing/MockFileSystem.cs @@ -4,6 +4,7 @@ using System.IO; using Testably.Abstractions.Testing.FileSystem; using Testably.Abstractions.Testing.Helpers; +using Testably.Abstractions.Testing.Statistics; using Testably.Abstractions.Testing.Storage; namespace Testably.Abstractions.Testing; @@ -54,10 +55,16 @@ internal IReadOnlyList StorageContainers /// internal Execute Execute { get; } + /// + /// Contains statistical information about the file system usage. + /// + public IFileSystemStatistics Statistics => StatisticsRegistration; + private readonly DirectoryMock _directoryMock; private readonly FileMock _fileMock; private readonly PathMock _pathMock; private readonly InMemoryStorage _storage; + internal readonly FileSystemStatistics StatisticsRegistration; internal IAccessControlStrategy AccessControlStrategy { @@ -76,6 +83,8 @@ internal ISafeFileHandleStrategy SafeFileHandleStrategy /// public MockFileSystem() { + StatisticsRegistration = new FileSystemStatistics(this); + using IDisposable release = StatisticsRegistration.Ignore(); Execute = Execute.Default; RandomSystem = new MockRandomSystem(); TimeSystem = new MockTimeSystem(TimeProvider.Now()); diff --git a/Source/Testably.Abstractions.Testing/Statistics/CallStatistics.cs b/Source/Testably.Abstractions.Testing/Statistics/CallStatistics.cs new file mode 100644 index 000000000..aa84a2c6a --- /dev/null +++ b/Source/Testably.Abstractions.Testing/Statistics/CallStatistics.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; + +namespace Testably.Abstractions.Testing.Statistics; + +internal class CallStatistics : IStatistics +{ + private readonly IStatisticsGate _statisticsGate; + private readonly ConcurrentDictionary _calls = new(); + public IReadOnlyDictionary Methods +#if NET7_0_OR_GREATER + => _calls.AsReadOnly(); +#else + => _calls; +#endif + + public CallStatistics(IStatisticsGate statisticsGate) + { + _statisticsGate = statisticsGate; + } + + /// + /// Registers the callback with . + /// + /// A disposable which ignores all registrations, until it is disposed. + internal IDisposable Register(string name, params ParameterDescription[] parameters) + { + if (_statisticsGate.TryGetLock(out IDisposable release)) + { + int counter = _statisticsGate.GetCounter(); + _calls[counter] = new MethodStatistic(name, parameters); + } + + return release; + } +} diff --git a/Source/Testably.Abstractions.Testing/Statistics/FileSystemEntryStatistics.cs b/Source/Testably.Abstractions.Testing/Statistics/FileSystemEntryStatistics.cs new file mode 100644 index 000000000..43243ecc8 --- /dev/null +++ b/Source/Testably.Abstractions.Testing/Statistics/FileSystemEntryStatistics.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Concurrent; + +namespace Testably.Abstractions.Testing.Statistics; + +internal class FileSystemEntryStatistics : CallStatistics, IPathStatistics +{ + private readonly IStatisticsGate _statisticsGate; + private readonly MockFileSystem _fileSystem; + + public FileSystemEntryStatistics( + IStatisticsGate statisticsGate, + MockFileSystem fileSystem) + : base(statisticsGate) + { + _statisticsGate = statisticsGate; + _fileSystem = fileSystem; + } + + private readonly ConcurrentDictionary _statistics = new(); + + /// + public IStatistics this[string path] + => _statistics.GetOrAdd(_fileSystem.Path.GetFullPath(path), + _ => new CallStatistics(_statisticsGate)); + + /// + /// Registers the callback with under . + /// + /// A disposable which ignores all registrations, until it is disposed. + internal IDisposable Register(string path, string name, params ParameterDescription[] parameters) + { + CallStatistics callStatistics = _statistics.GetOrAdd(_fileSystem.Path.GetFullPath(path), + _ => new CallStatistics(_statisticsGate)); + return callStatistics.Register(name, parameters); + } +} diff --git a/Source/Testably.Abstractions.Testing/Statistics/FileSystemStatistics.cs b/Source/Testably.Abstractions.Testing/Statistics/FileSystemStatistics.cs new file mode 100644 index 000000000..dc32d92f1 --- /dev/null +++ b/Source/Testably.Abstractions.Testing/Statistics/FileSystemStatistics.cs @@ -0,0 +1,99 @@ +using System; +using System.Threading; +using Testably.Abstractions.Testing.Helpers; + +namespace Testably.Abstractions.Testing.Statistics; + +internal sealed class FileSystemStatistics : IFileSystemStatistics, IStatisticsGate +{ + internal readonly FileSystemEntryStatistics DirectoryInfo; + internal readonly CallStatistics Directory; + internal readonly FileSystemEntryStatistics DriveInfo; + internal readonly FileSystemEntryStatistics FileInfo; + internal readonly CallStatistics File; + internal readonly FileSystemEntryStatistics FileStream; + internal readonly FileSystemEntryStatistics FileSystemWatcher; + internal readonly CallStatistics Path; + private int _counter; + + private static readonly AsyncLocal IsDisabled = new(); + + public FileSystemStatistics(MockFileSystem fileSystem) + { + DirectoryInfo = new FileSystemEntryStatistics(this, fileSystem); + DriveInfo = new FileSystemEntryStatistics(this, fileSystem); + FileInfo = new FileSystemEntryStatistics(this, fileSystem); + FileStream = new FileSystemEntryStatistics(this, fileSystem); + FileSystemWatcher = new FileSystemEntryStatistics(this, fileSystem); + File = new CallStatistics(this); + Directory = new CallStatistics(this); + Path = new CallStatistics(this); + } + + #region IFileSystemStatistics Members + + /// + IStatistics IFileSystemStatistics.Directory => Directory; + + /// + IPathStatistics IFileSystemStatistics.DirectoryInfo => DirectoryInfo; + + /// + IPathStatistics IFileSystemStatistics.DriveInfo => DriveInfo; + + /// + IStatistics IFileSystemStatistics.File => File; + + /// + IPathStatistics IFileSystemStatistics.FileInfo => FileInfo; + + /// + IPathStatistics IFileSystemStatistics.FileStream => FileStream; + + /// + IPathStatistics IFileSystemStatistics.FileSystemWatcher => FileSystemWatcher; + + /// + IStatistics IFileSystemStatistics.Path => Path; + + #endregion + + #region IStatisticsGate Members + + /// + public bool TryGetLock(out IDisposable release) + { + if (IsDisabled.Value) + { + release = TemporaryDisable.None; + return false; + } + + IsDisabled.Value = true; + release = new TemporaryDisable(() => IsDisabled.Value = false); + return true; + } + + /// + public int GetCounter() + { + return Interlocked.Increment(ref _counter); + } + + #endregion + + private class TemporaryDisable : IDisposable + { + public static IDisposable None { get; } = new NoOpDisposable(); + + private readonly Action _onDispose; + + public TemporaryDisable(Action onDispose) + { + _onDispose = onDispose; + } + + /// + public void Dispose() => _onDispose(); + } +} diff --git a/Source/Testably.Abstractions.Testing/Statistics/IFileSystemStatistics.cs b/Source/Testably.Abstractions.Testing/Statistics/IFileSystemStatistics.cs new file mode 100644 index 000000000..693982fa9 --- /dev/null +++ b/Source/Testably.Abstractions.Testing/Statistics/IFileSystemStatistics.cs @@ -0,0 +1,47 @@ +namespace Testably.Abstractions.Testing.Statistics; + +/// +/// Contains statistical information about the usage of the . +/// +public interface IFileSystemStatistics +{ + /// + /// Statistical information about calls to . + /// + IStatistics Directory { get; } + + /// + /// Statistical information about calls to . + /// + IPathStatistics DirectoryInfo { get; } + + /// + /// Statistical information about calls to . + /// + IPathStatistics DriveInfo { get; } + + /// + /// Statistical information about calls to . + /// + IStatistics File { get; } + + /// + /// Statistical information about calls to . + /// + IPathStatistics FileInfo { get; } + + /// + /// Statistical information about calls to . + /// + IPathStatistics FileStream { get; } + + /// + /// Statistical information about calls to . + /// + IPathStatistics FileSystemWatcher { get; } + + /// + /// Statistical information about calls to . + /// + IStatistics Path { get; } +} diff --git a/Source/Testably.Abstractions.Testing/Statistics/IPathStatistics.cs b/Source/Testably.Abstractions.Testing/Statistics/IPathStatistics.cs new file mode 100644 index 000000000..9547efb19 --- /dev/null +++ b/Source/Testably.Abstractions.Testing/Statistics/IPathStatistics.cs @@ -0,0 +1,18 @@ +namespace Testably.Abstractions.Testing.Statistics; + +/// +/// Contains statistical information about the mock on a given path. +/// +/// +/// See also . +/// +public interface IPathStatistics : IStatistics +{ + /// + /// Returns the underlying under . + /// + IStatistics this[string path] + { + get; + } +} diff --git a/Source/Testably.Abstractions.Testing/Statistics/IStatistics.cs b/Source/Testably.Abstractions.Testing/Statistics/IStatistics.cs new file mode 100644 index 000000000..10955e85a --- /dev/null +++ b/Source/Testably.Abstractions.Testing/Statistics/IStatistics.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace Testably.Abstractions.Testing.Statistics; + +/// +/// Contains statistical information about the mock usage. +/// +public interface IStatistics +{ + /// + /// Lists all called mocked methods. + /// + IReadOnlyDictionary Methods { get; } +} diff --git a/Source/Testably.Abstractions.Testing/Statistics/IStatisticsGate.cs b/Source/Testably.Abstractions.Testing/Statistics/IStatisticsGate.cs new file mode 100644 index 000000000..f63035b67 --- /dev/null +++ b/Source/Testably.Abstractions.Testing/Statistics/IStatisticsGate.cs @@ -0,0 +1,9 @@ +using System; + +namespace Testably.Abstractions.Testing.Statistics; + +internal interface IStatisticsGate +{ + bool TryGetLock(out IDisposable release); + int GetCounter(); +} diff --git a/Source/Testably.Abstractions.Testing/Statistics/MethodStatistic.cs b/Source/Testably.Abstractions.Testing/Statistics/MethodStatistic.cs new file mode 100644 index 000000000..653151911 --- /dev/null +++ b/Source/Testably.Abstractions.Testing/Statistics/MethodStatistic.cs @@ -0,0 +1,29 @@ +using System.Linq; + +namespace Testably.Abstractions.Testing.Statistics; + +/// +/// Describes a call to a mocked method. +/// +public sealed class MethodStatistic +{ + /// + /// The name of the called method. + /// + public string Name { get; } + + /// + /// The parameters of the called method. + /// + public ParameterDescription[] Parameters { get; } + + internal MethodStatistic(string name, ParameterDescription[] parameters) + { + Name = name; + Parameters = parameters; + } + + /// + public override string ToString() + => $"{Name}({string.Join(",", Parameters.Select(p => p.ToString()))})"; +} diff --git a/Source/Testably.Abstractions.Testing/Statistics/ParameterDescription.cs b/Source/Testably.Abstractions.Testing/Statistics/ParameterDescription.cs new file mode 100644 index 000000000..e6d92c17f --- /dev/null +++ b/Source/Testably.Abstractions.Testing/Statistics/ParameterDescription.cs @@ -0,0 +1,159 @@ +using System; +using System.Linq; + +namespace Testably.Abstractions.Testing.Statistics; + +/// +/// The description of a parameter in the statistic. +/// +public abstract class ParameterDescription +{ + /// + /// Initializes a new instance of . + /// + /// + protected ParameterDescription(bool isOutParameter) + { + IsOutParameter = isOutParameter; + } + + /// + /// Specifies, if the parameter was used as an out parameter. + /// + public bool IsOutParameter { get; } + + /// + /// Checks, if the value of the parameter equals . + /// + /// When the types match, uses . + public bool Is(T value) + => this is GenericParameterDescription d && + IsEqual(value, d.Value); + + /// + /// Checks, if the sequence of values of the parameter equals . + /// + /// + /// When the types match, uses + /// + /// . + /// + public bool Is(T[] value) + { + return this is GenericParameterDescription d && + value.SequenceEqual(d.Value); + } + + private static bool IsEqual(T value1, T value2) + { + if (value1 is null) + { + return value2 is null; + } + + return value1.Equals(value2); + } +#if FEATURE_SPAN + /// + /// Checks, if the span value of the parameter equals . + /// + public bool Is(Span value) + => this is SpanParameterDescription { IsReadOnly: false } d && + d.Value.SequenceEqual(value.ToArray()); + + /// + /// Checks, if the read-only span value of the parameter equals . + /// + public bool Is(ReadOnlySpan value) + => this is SpanParameterDescription { IsReadOnly: true } d && + d.Value.SequenceEqual(value.ToArray()); +#endif + + /// + /// Checks, if the span value of the parameter matches the . + /// + public bool Is(Func comparer) + => this is GenericParameterDescription d && + comparer(d.Value); + + /// + /// Creates a from the . + /// + public static ParameterDescription FromParameter(T value) + { + return new GenericParameterDescription(value, false); + } + +#if FEATURE_SPAN + /// + /// Creates a from the span . + /// + public static ParameterDescription FromParameter(Span value) + { + return new SpanParameterDescription(value); + } + + /// + /// Creates a from the read-only span . + /// + public static ParameterDescription FromParameter(ReadOnlySpan value) + { + return new SpanParameterDescription(value); + } +#endif + + /// + /// Creates a from the used as an out parameter. + /// + public static ParameterDescription FromOutParameter(T value) + { + return new GenericParameterDescription(value, true); + } + + private class GenericParameterDescription : ParameterDescription + { + public T Value { get; } + + public GenericParameterDescription(T value, bool isOutParameter) : base(isOutParameter) + { + Value = value; + } + + /// + public override string? ToString() + { + if (Value is string) + { + return $"\"{Value}\""; + } + + return Value?.ToString(); + } + } + +#if FEATURE_SPAN + private class SpanParameterDescription : ParameterDescription + { + public T[] Value { get; } + + public bool IsReadOnly { get; } + + public SpanParameterDescription(Span value) : base(false) + { + Value = value.ToArray(); + IsReadOnly = false; + } + + public SpanParameterDescription(ReadOnlySpan value) : base(false) + { + Value = value.ToArray(); + IsReadOnly = true; + } + + /// + public override string ToString() + => $"[{string.Join(",", Value)}]"; + } +#endif +} diff --git a/Source/Testably.Abstractions.Testing/Storage/NullContainer.cs b/Source/Testably.Abstractions.Testing/Storage/NullContainer.cs index dc38bb48e..b8c6cee20 100644 --- a/Source/Testably.Abstractions.Testing/Storage/NullContainer.cs +++ b/Source/Testably.Abstractions.Testing/Storage/NullContainer.cs @@ -192,7 +192,7 @@ private sealed class CreationNullTime : NullTime { public CreationNullTime(MockFileSystem fileSystem) : base(fileSystem) { } - /// + /// public override void Set(DateTime time, DateTimeKind kind) { #if NET7_0_OR_GREATER diff --git a/Tests/Testably.Abstractions.AccessControl.Tests/Internal/AccessControlHelperTests.cs b/Tests/Testably.Abstractions.AccessControl.Tests/Internal/AccessControlHelperTests.cs index 83168ce0e..4a384374a 100644 --- a/Tests/Testably.Abstractions.AccessControl.Tests/Internal/AccessControlHelperTests.cs +++ b/Tests/Testably.Abstractions.AccessControl.Tests/Internal/AccessControlHelperTests.cs @@ -118,7 +118,6 @@ public void GetExtensibilityOrThrow_CustomFileInfo_ShouldThrowNotSupportedExcept private class CustomFileSystemStream : FileSystemStream { - /// public CustomFileSystemStream() : base(Null, ".", false) { } diff --git a/Tests/Testably.Abstractions.Testing.Tests/Helpers/PathHelperTests.cs b/Tests/Testably.Abstractions.Testing.Tests/Helpers/PathHelperTests.cs index 2be685f2b..f5c6049c4 100644 --- a/Tests/Testably.Abstractions.Testing.Tests/Helpers/PathHelperTests.cs +++ b/Tests/Testably.Abstractions.Testing.Tests/Helpers/PathHelperTests.cs @@ -170,35 +170,27 @@ public FileSystemMockForPath(char[] invalidChars) #region IFileSystem Members - /// public IDirectory Directory => throw new NotSupportedException(); - /// public IDirectoryInfoFactory DirectoryInfo => throw new NotSupportedException(); - /// public IDriveInfoFactory DriveInfo => throw new NotSupportedException(); - /// public IFile File => throw new NotSupportedException(); - /// public IFileInfoFactory FileInfo => throw new NotSupportedException(); - /// public IFileStreamFactory FileStream => throw new NotSupportedException(); - /// public IFileSystemWatcherFactory FileSystemWatcher => throw new NotSupportedException(); - /// public IPath Path { get; } #endregion @@ -214,231 +206,200 @@ public PathMockWithInvalidChars(char[] invalidChars) #region IPath Members - /// public char AltDirectorySeparatorChar => throw new NotSupportedException(); - /// public char DirectorySeparatorChar => throw new NotSupportedException(); - /// public IFileSystem FileSystem => throw new NotSupportedException(); - /// public char PathSeparator => throw new NotSupportedException(); - /// public char VolumeSeparatorChar => throw new NotSupportedException(); - /// public string ChangeExtension(string? path, string? extension) => throw new NotSupportedException(); - /// public string Combine(string path1, string path2) => throw new NotSupportedException(); - /// public string Combine(string path1, string path2, string path3) => throw new NotSupportedException(); - /// public string Combine(string path1, string path2, string path3, string path4) => throw new NotSupportedException(); - /// public string Combine(params string[] paths) => throw new NotSupportedException(); #if FEATURE_PATH_ADVANCED - /// + public bool EndsInDirectorySeparator(ReadOnlySpan path) => throw new NotSupportedException(); - /// public bool EndsInDirectorySeparator(string path) => throw new NotSupportedException(); #endif #if FEATURE_FILESYSTEM_NET7 - /// + public bool Exists(string? path) => throw new NotSupportedException(); #endif #if FEATURE_SPAN - /// + public ReadOnlySpan GetDirectoryName(ReadOnlySpan path) => throw new NotSupportedException(); #endif - /// public string GetDirectoryName(string? path) => throw new NotSupportedException(); #if FEATURE_SPAN - /// + public ReadOnlySpan GetExtension(ReadOnlySpan path) => throw new NotSupportedException(); #endif - /// public string GetExtension(string? path) => throw new NotSupportedException(); #if FEATURE_SPAN - /// + public ReadOnlySpan GetFileName(ReadOnlySpan path) => throw new NotSupportedException(); #endif - /// public string GetFileName(string? path) => throw new NotSupportedException(); #if FEATURE_SPAN - /// + public ReadOnlySpan GetFileNameWithoutExtension(ReadOnlySpan path) => throw new NotSupportedException(); #endif - /// public string GetFileNameWithoutExtension(string? path) => throw new NotSupportedException(); - /// public string GetFullPath(string path) => path; #if FEATURE_PATH_RELATIVE - /// + public string GetFullPath(string path, string basePath) => throw new NotSupportedException(); #endif - /// public char[] GetInvalidFileNameChars() => throw new NotSupportedException(); - /// public char[] GetInvalidPathChars() => _invalidChars; #if FEATURE_SPAN - /// + public ReadOnlySpan GetPathRoot(ReadOnlySpan path) => throw new NotSupportedException(); #endif - /// public string GetPathRoot(string? path) => throw new NotSupportedException(); - /// public string GetRandomFileName() => throw new NotSupportedException(); #if FEATURE_PATH_RELATIVE - /// + public string GetRelativePath(string relativeTo, string path) => throw new NotSupportedException(); #endif - /// public string GetTempFileName() => throw new NotSupportedException(); - /// public string GetTempPath() => throw new NotSupportedException(); #if FEATURE_SPAN - /// + public bool HasExtension(ReadOnlySpan path) => throw new NotSupportedException(); #endif - /// public bool HasExtension(string? path) => throw new NotSupportedException(); #if FEATURE_SPAN - /// + public bool IsPathFullyQualified(ReadOnlySpan path) => throw new NotSupportedException(); #endif #if FEATURE_PATH_RELATIVE - /// + public bool IsPathFullyQualified(string path) => throw new NotSupportedException(); #endif #if FEATURE_SPAN - /// + public bool IsPathRooted(ReadOnlySpan path) => throw new NotSupportedException(); #endif - /// public bool IsPathRooted(string? path) => throw new NotSupportedException(); #if FEATURE_PATH_JOIN - /// + public string Join(ReadOnlySpan path1, ReadOnlySpan path2) => throw new NotSupportedException(); - /// public string Join(ReadOnlySpan path1, ReadOnlySpan path2, ReadOnlySpan path3) => throw new NotSupportedException(); #endif #if FEATURE_PATH_ADVANCED - /// + public string Join(string? path1, string? path2) => throw new NotSupportedException(); - /// public string Join(string? path1, string? path2, string? path3) => throw new NotSupportedException(); - /// public string Join(params string?[] paths) => throw new NotSupportedException(); - /// public string Join(ReadOnlySpan path1, ReadOnlySpan path2, ReadOnlySpan path3, ReadOnlySpan path4) => throw new NotSupportedException(); - /// public string Join(string? path1, string? path2, string? path3, string? path4) => throw new NotSupportedException(); #endif #if FEATURE_PATH_ADVANCED - /// + public ReadOnlySpan TrimEndingDirectorySeparator(ReadOnlySpan path) => throw new NotSupportedException(); - /// public string TrimEndingDirectorySeparator(string path) => throw new NotSupportedException(); #endif #if FEATURE_PATH_JOIN - /// + public bool TryJoin(ReadOnlySpan path1, ReadOnlySpan path2, Span destination, out int charsWritten) => throw new NotSupportedException(); - /// public bool TryJoin(ReadOnlySpan path1, ReadOnlySpan path2, ReadOnlySpan path3, Span destination, out int charsWritten) diff --git a/Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/DirectoryInfoFactoryStatisticsTests.cs b/Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/DirectoryInfoFactoryStatisticsTests.cs new file mode 100644 index 000000000..5d735450a --- /dev/null +++ b/Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/DirectoryInfoFactoryStatisticsTests.cs @@ -0,0 +1,31 @@ +using System.IO; +using Testably.Abstractions.Testing.Tests.TestHelpers; + +namespace Testably.Abstractions.Testing.Tests.Statistics.FileSystem; + +public sealed class DirectoryInfoFactoryStatisticsTests +{ + [SkippableFact] + public void New_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + + sut.DirectoryInfo.New(path); + + sut.Statistics.DirectoryInfo.ShouldOnlyContain(nameof(IDirectoryInfoFactory.New), + path); + } + + [SkippableFact] + public void Wrap_DirectoryInfo_ShouldRegisterCall() + { + MockFileSystem sut = new(); + DirectoryInfo directoryInfo = new("."); + + sut.DirectoryInfo.Wrap(directoryInfo); + + sut.Statistics.DirectoryInfo.ShouldOnlyContain(nameof(IDirectoryInfoFactory.Wrap), + directoryInfo); + } +} diff --git a/Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/DirectoryInfoStatisticsTests.cs b/Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/DirectoryInfoStatisticsTests.cs new file mode 100644 index 000000000..784ab83f0 --- /dev/null +++ b/Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/DirectoryInfoStatisticsTests.cs @@ -0,0 +1,416 @@ +using System.IO; +using Testably.Abstractions.Testing.Tests.TestHelpers; + +namespace Testably.Abstractions.Testing.Tests.Statistics.FileSystem; + +public sealed class DirectoryInfoStatisticsTests +{ + [SkippableFact] + public void Create_ShouldRegisterCall() + { + MockFileSystem sut = new(); + + sut.DirectoryInfo.New("foo").Create(); + + sut.Statistics.DirectoryInfo["foo"].ShouldOnlyContain(nameof(IDirectoryInfo.Create)); + } + +#if FEATURE_FILESYSTEM_LINK + [SkippableFact] + public void CreateAsSymbolicLink_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string pathToTarget = "foo"; + + sut.DirectoryInfo.New("foo").CreateAsSymbolicLink(pathToTarget); + + sut.Statistics.DirectoryInfo["foo"].ShouldOnlyContain(nameof(IDirectoryInfo.CreateAsSymbolicLink), + pathToTarget); + } +#endif + + [SkippableFact] + public void CreateSubdirectory_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + + sut.DirectoryInfo.New("foo").CreateSubdirectory(path); + + sut.Statistics.DirectoryInfo["foo"].ShouldOnlyContain(nameof(IDirectoryInfo.CreateSubdirectory), + path); + } + + [SkippableFact] + public void Delete_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + + sut.DirectoryInfo.New("foo").Delete(); + + sut.Statistics.DirectoryInfo["foo"].ShouldOnlyContain(nameof(IDirectoryInfo.Delete)); + } + + [SkippableFact] + public void Delete_Bool_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + bool recursive = true; + + sut.DirectoryInfo.New("foo").Delete(recursive); + + sut.Statistics.DirectoryInfo["foo"].ShouldOnlyContain(nameof(IDirectoryInfo.Delete), + recursive); + } + + [SkippableFact] + public void EnumerateDirectories_ShouldRegisterCall() + { + MockFileSystem sut = new(); + + sut.DirectoryInfo.New("foo").EnumerateDirectories(); + + sut.Statistics.DirectoryInfo["foo"].ShouldOnlyContain(nameof(IDirectoryInfo.EnumerateDirectories)); + } + + [SkippableFact] + public void EnumerateDirectories_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string searchPattern = "foo"; + + sut.DirectoryInfo.New("foo").EnumerateDirectories(searchPattern); + + sut.Statistics.DirectoryInfo["foo"].ShouldOnlyContain(nameof(IDirectoryInfo.EnumerateDirectories), + searchPattern); + } + + [SkippableFact] + public void EnumerateDirectories_String_SearchOption_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string searchPattern = "foo"; + SearchOption searchOption = SearchOption.AllDirectories; + + sut.DirectoryInfo.New("foo").EnumerateDirectories(searchPattern, searchOption); + + sut.Statistics.DirectoryInfo["foo"].ShouldOnlyContain(nameof(IDirectoryInfo.EnumerateDirectories), + searchPattern, searchOption); + } + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + [SkippableFact] + public void EnumerateDirectories_String_EnumerationOptions_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string searchPattern = "foo"; + EnumerationOptions enumerationOptions = new(); + + sut.DirectoryInfo.New("foo").EnumerateDirectories(searchPattern, enumerationOptions); + + sut.Statistics.DirectoryInfo["foo"].ShouldOnlyContain(nameof(IDirectoryInfo.EnumerateDirectories), + searchPattern, enumerationOptions); + } +#endif + + [SkippableFact] + public void EnumerateFiles_ShouldRegisterCall() + { + MockFileSystem sut = new(); + + sut.DirectoryInfo.New("foo").EnumerateFiles(); + + sut.Statistics.DirectoryInfo["foo"].ShouldOnlyContain(nameof(IDirectoryInfo.EnumerateFiles)); + } + + [SkippableFact] + public void EnumerateFiles_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string searchPattern = "foo"; + + sut.DirectoryInfo.New("foo").EnumerateFiles(searchPattern); + + sut.Statistics.DirectoryInfo["foo"].ShouldOnlyContain(nameof(IDirectoryInfo.EnumerateFiles), + searchPattern); + } + + [SkippableFact] + public void EnumerateFiles_String_SearchOption_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string searchPattern = "foo"; + SearchOption searchOption = SearchOption.AllDirectories; + + sut.DirectoryInfo.New("foo").EnumerateFiles(searchPattern, searchOption); + + sut.Statistics.DirectoryInfo["foo"].ShouldOnlyContain(nameof(IDirectoryInfo.EnumerateFiles), + searchPattern, searchOption); + } + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + [SkippableFact] + public void EnumerateFiles_String_EnumerationOptions_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string searchPattern = "foo"; + EnumerationOptions enumerationOptions = new(); + + sut.DirectoryInfo.New("foo").EnumerateFiles(searchPattern, enumerationOptions); + + sut.Statistics.DirectoryInfo["foo"].ShouldOnlyContain(nameof(IDirectoryInfo.EnumerateFiles), + searchPattern, enumerationOptions); + } +#endif + + [SkippableFact] + public void EnumerateFileSystemInfos_ShouldRegisterCall() + { + MockFileSystem sut = new(); + + sut.DirectoryInfo.New("foo").EnumerateFileSystemInfos(); + + sut.Statistics.DirectoryInfo["foo"].ShouldOnlyContain(nameof(IDirectoryInfo.EnumerateFileSystemInfos)); + } + + [SkippableFact] + public void EnumerateFileSystemInfos_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string searchPattern = "foo"; + + sut.DirectoryInfo.New("foo").EnumerateFileSystemInfos(searchPattern); + + sut.Statistics.DirectoryInfo["foo"].ShouldOnlyContain(nameof(IDirectoryInfo.EnumerateFileSystemInfos), + searchPattern); + } + + [SkippableFact] + public void EnumerateFileSystemInfos_String_SearchOption_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string searchPattern = "foo"; + SearchOption searchOption = SearchOption.AllDirectories; + + sut.DirectoryInfo.New("foo").EnumerateFileSystemInfos(searchPattern, searchOption); + + sut.Statistics.DirectoryInfo["foo"].ShouldOnlyContain(nameof(IDirectoryInfo.EnumerateFileSystemInfos), + searchPattern, searchOption); + } + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + [SkippableFact] + public void EnumerateFileSystemInfos_String_EnumerationOptions_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string searchPattern = "foo"; + EnumerationOptions enumerationOptions = new(); + + sut.DirectoryInfo.New("foo").EnumerateFileSystemInfos(searchPattern, enumerationOptions); + + sut.Statistics.DirectoryInfo["foo"].ShouldOnlyContain(nameof(IDirectoryInfo.EnumerateFileSystemInfos), + searchPattern, enumerationOptions); + } +#endif + + [SkippableFact] + public void GetDirectories_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + + sut.DirectoryInfo.New("foo").GetDirectories(); + + sut.Statistics.DirectoryInfo["foo"].ShouldOnlyContain(nameof(IDirectoryInfo.GetDirectories)); + } + + [SkippableFact] + public void GetDirectories_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + string searchPattern = "foo"; + + sut.DirectoryInfo.New("foo").GetDirectories(searchPattern); + + sut.Statistics.DirectoryInfo["foo"].ShouldOnlyContain(nameof(IDirectoryInfo.GetDirectories), + searchPattern); + } + + [SkippableFact] + public void GetDirectories_String_SearchOption_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + string searchPattern = "foo"; + SearchOption searchOption = SearchOption.AllDirectories; + + sut.DirectoryInfo.New("foo").GetDirectories(searchPattern, searchOption); + + sut.Statistics.DirectoryInfo["foo"].ShouldOnlyContain(nameof(IDirectoryInfo.GetDirectories), + searchPattern, searchOption); + } + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + [SkippableFact] + public void GetDirectories_String_EnumerationOptions_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + string searchPattern = "foo"; + EnumerationOptions enumerationOptions = new(); + + sut.DirectoryInfo.New("foo").GetDirectories(searchPattern, enumerationOptions); + + sut.Statistics.DirectoryInfo["foo"].ShouldOnlyContain(nameof(IDirectoryInfo.GetDirectories), + searchPattern, enumerationOptions); + } +#endif + + [SkippableFact] + public void GetFiles_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + + sut.DirectoryInfo.New("foo").GetFiles(); + + sut.Statistics.DirectoryInfo["foo"].ShouldOnlyContain(nameof(IDirectoryInfo.GetFiles)); + } + + [SkippableFact] + public void GetFiles_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + string searchPattern = "foo"; + + sut.DirectoryInfo.New("foo").GetFiles(searchPattern); + + sut.Statistics.DirectoryInfo["foo"].ShouldOnlyContain(nameof(IDirectoryInfo.GetFiles), + searchPattern); + } + + [SkippableFact] + public void GetFiles_String_SearchOption_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + string searchPattern = "foo"; + SearchOption searchOption = SearchOption.AllDirectories; + + sut.DirectoryInfo.New("foo").GetFiles(searchPattern, searchOption); + + sut.Statistics.DirectoryInfo["foo"].ShouldOnlyContain(nameof(IDirectoryInfo.GetFiles), + searchPattern, searchOption); + } + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + [SkippableFact] + public void GetFiles_String_EnumerationOptions_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + string searchPattern = "foo"; + EnumerationOptions enumerationOptions = new(); + + sut.DirectoryInfo.New("foo").GetFiles(searchPattern, enumerationOptions); + + sut.Statistics.DirectoryInfo["foo"].ShouldOnlyContain(nameof(IDirectoryInfo.GetFiles), + searchPattern, enumerationOptions); + } +#endif + + [SkippableFact] + public void GetFileSystemInfos_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + + sut.DirectoryInfo.New("foo").GetFileSystemInfos(); + + sut.Statistics.DirectoryInfo["foo"].ShouldOnlyContain(nameof(IDirectoryInfo.GetFileSystemInfos)); + } + + [SkippableFact] + public void GetFileSystemInfos_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + string searchPattern = "foo"; + + sut.DirectoryInfo.New("foo").GetFileSystemInfos(searchPattern); + + sut.Statistics.DirectoryInfo["foo"].ShouldOnlyContain(nameof(IDirectoryInfo.GetFileSystemInfos), + searchPattern); + } + + [SkippableFact] + public void GetFileSystemInfos_String_SearchOption_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + string searchPattern = "foo"; + SearchOption searchOption = SearchOption.AllDirectories; + + sut.DirectoryInfo.New("foo").GetFileSystemInfos(searchPattern, searchOption); + + sut.Statistics.DirectoryInfo["foo"].ShouldOnlyContain(nameof(IDirectoryInfo.GetFileSystemInfos), + searchPattern, searchOption); + } + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + [SkippableFact] + public void GetFileSystemInfos_String_EnumerationOptions_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + string searchPattern = "foo"; + EnumerationOptions enumerationOptions = new(); + + sut.DirectoryInfo.New("foo").GetFileSystemInfos(searchPattern, enumerationOptions); + + sut.Statistics.DirectoryInfo["foo"].ShouldOnlyContain(nameof(IDirectoryInfo.GetFileSystemInfos), + searchPattern, enumerationOptions); + } +#endif + + [SkippableFact] + public void MoveTo_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + string destDirName = "bar"; + + sut.DirectoryInfo.New("foo").MoveTo(destDirName); + + sut.Statistics.DirectoryInfo["foo"].ShouldOnlyContain(nameof(IDirectoryInfo.MoveTo), + destDirName); + } + + [SkippableFact] + public void Refresh_ShouldRegisterCall() + { + MockFileSystem sut = new(); + + sut.DirectoryInfo.New("foo").Refresh(); + + sut.Statistics.DirectoryInfo["foo"].ShouldOnlyContain(nameof(IDirectoryInfo.Refresh)); + } + +#if FEATURE_FILESYSTEM_LINK + [SkippableFact] + public void ResolveLinkTarget_Bool_ShouldRegisterCall() + { + MockFileSystem sut = new(); + bool returnFinalTarget = true; + + sut.DirectoryInfo.New("foo").ResolveLinkTarget(returnFinalTarget); + + sut.Statistics.DirectoryInfo["foo"].ShouldOnlyContain(nameof(IDirectoryInfo.ResolveLinkTarget), + returnFinalTarget); + } +#endif +} diff --git a/Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/DirectoryStatisticsTests.cs b/Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/DirectoryStatisticsTests.cs new file mode 100644 index 000000000..3f31e681d --- /dev/null +++ b/Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/DirectoryStatisticsTests.cs @@ -0,0 +1,688 @@ +using System.IO; +using Testably.Abstractions.Testing.Tests.TestHelpers; + +namespace Testably.Abstractions.Testing.Tests.Statistics.FileSystem; + +public sealed class DirectoryStatisticsTests +{ + [Fact] + public void CreateDirectory_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + + sut.Directory.CreateDirectory(path); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.CreateDirectory), + path); + } + +#if FEATURE_FILESYSTEM_UNIXFILEMODE + [SkippableFact] + public void CreateDirectory_String_UnixFileMode_ShouldRegisterCall() + { + Skip.If(!Test.RunsOnLinux); + + MockFileSystem sut = new(); + string path = "foo"; + UnixFileMode unixCreateMode = UnixFileMode.None; + + sut.Directory.CreateDirectory(path, unixCreateMode); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.CreateDirectory), + path, unixCreateMode); + } +#endif + +#if FEATURE_FILESYSTEM_LINK + [SkippableFact] + public void CreateSymbolicLink_String_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + string pathToTarget = "foo"; + + sut.Directory.CreateSymbolicLink(path, pathToTarget); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.CreateSymbolicLink), + path, pathToTarget); + } +#endif + +#if FEATURE_FILESYSTEM_NET7 + [SkippableFact] + public void CreateTempSubdirectory_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string prefix = "foo"; + + sut.Directory.CreateTempSubdirectory(prefix); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.CreateTempSubdirectory), + prefix); + } +#endif + + [SkippableFact] + public void Delete_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + string path = "foo"; + + sut.Directory.Delete(path); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.Delete), + path); + } + + [SkippableFact] + public void Delete_String_Bool_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + string path = "foo"; + bool recursive = true; + + sut.Directory.Delete(path, recursive); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.Delete), + path, recursive); + } + + [SkippableFact] + public void EnumerateDirectories_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + + sut.Directory.EnumerateDirectories(path); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.EnumerateDirectories), + path); + } + + [SkippableFact] + public void EnumerateDirectories_String_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + string searchPattern = "foo"; + + sut.Directory.EnumerateDirectories(path, searchPattern); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.EnumerateDirectories), + path, searchPattern); + } + + [SkippableFact] + public void EnumerateDirectories_String_String_SearchOption_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + string searchPattern = "foo"; + SearchOption searchOption = SearchOption.AllDirectories; + + sut.Directory.EnumerateDirectories(path, searchPattern, searchOption); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.EnumerateDirectories), + path, searchPattern, searchOption); + } + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + [SkippableFact] + public void EnumerateDirectories_String_String_EnumerationOptions_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + string searchPattern = "foo"; + EnumerationOptions enumerationOptions = new(); + + sut.Directory.EnumerateDirectories(path, searchPattern, enumerationOptions); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.EnumerateDirectories), + path, searchPattern, enumerationOptions); + } +#endif + + [SkippableFact] + public void EnumerateFiles_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + + sut.Directory.EnumerateFiles(path); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.EnumerateFiles), + path); + } + + [SkippableFact] + public void EnumerateFiles_String_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + string searchPattern = "foo"; + + sut.Directory.EnumerateFiles(path, searchPattern); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.EnumerateFiles), + path, searchPattern); + } + + [SkippableFact] + public void EnumerateFiles_String_String_SearchOption_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + string searchPattern = "foo"; + SearchOption searchOption = SearchOption.AllDirectories; + + sut.Directory.EnumerateFiles(path, searchPattern, searchOption); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.EnumerateFiles), + path, searchPattern, searchOption); + } + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + [SkippableFact] + public void EnumerateFiles_String_String_EnumerationOptions_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + string searchPattern = "foo"; + EnumerationOptions enumerationOptions = new(); + + sut.Directory.EnumerateFiles(path, searchPattern, enumerationOptions); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.EnumerateFiles), + path, searchPattern, enumerationOptions); + } +#endif + + [SkippableFact] + public void EnumerateFileSystemEntries_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + + sut.Directory.EnumerateFileSystemEntries(path); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.EnumerateFileSystemEntries), + path); + } + + [SkippableFact] + public void EnumerateFileSystemEntries_String_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + string searchPattern = "foo"; + + sut.Directory.EnumerateFileSystemEntries(path, searchPattern); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.EnumerateFileSystemEntries), + path, searchPattern); + } + + [SkippableFact] + public void EnumerateFileSystemEntries_String_String_SearchOption_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + string searchPattern = "foo"; + SearchOption searchOption = SearchOption.AllDirectories; + + sut.Directory.EnumerateFileSystemEntries(path, searchPattern, searchOption); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.EnumerateFileSystemEntries), + path, searchPattern, searchOption); + } + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + [SkippableFact] + public void EnumerateFileSystemEntries_String_String_EnumerationOptions_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + string searchPattern = "foo"; + EnumerationOptions enumerationOptions = new(); + + sut.Directory.EnumerateFileSystemEntries(path, searchPattern, enumerationOptions); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.EnumerateFileSystemEntries), + path, searchPattern, enumerationOptions); + } +#endif + + [SkippableFact] + public void Exists_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + + sut.Directory.Exists(path); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.Exists), + path); + } + + [SkippableFact] + public void GetCreationTime_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + + sut.Directory.GetCreationTime(path); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.GetCreationTime), + path); + } + + [SkippableFact] + public void GetCreationTimeUtc_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + + sut.Directory.GetCreationTimeUtc(path); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.GetCreationTimeUtc), + path); + } + + [SkippableFact] + public void GetCurrentDirectory_ShouldRegisterCall() + { + MockFileSystem sut = new(); + + sut.Directory.GetCurrentDirectory(); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.GetCurrentDirectory)); + } + + [SkippableFact] + public void GetDirectories_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + string path = "foo"; + + sut.Directory.GetDirectories(path); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.GetDirectories), + path); + } + + [SkippableFact] + public void GetDirectories_String_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + string path = "foo"; + string searchPattern = "foo"; + + sut.Directory.GetDirectories(path, searchPattern); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.GetDirectories), + path, searchPattern); + } + + [SkippableFact] + public void GetDirectories_String_String_SearchOption_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + string path = "foo"; + string searchPattern = "foo"; + SearchOption searchOption = SearchOption.AllDirectories; + + sut.Directory.GetDirectories(path, searchPattern, searchOption); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.GetDirectories), + path, searchPattern, searchOption); + } + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + [SkippableFact] + public void GetDirectories_String_String_EnumerationOptions_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + string path = "foo"; + string searchPattern = "foo"; + EnumerationOptions enumerationOptions = new(); + + sut.Directory.GetDirectories(path, searchPattern, enumerationOptions); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.GetDirectories), + path, searchPattern, enumerationOptions); + } +#endif + + [SkippableFact] + public void GetDirectoryRoot_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + + sut.Directory.GetDirectoryRoot(path); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.GetDirectoryRoot), + path); + } + + [SkippableFact] + public void GetFiles_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + string path = "foo"; + + sut.Directory.GetFiles(path); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.GetFiles), + path); + } + + [SkippableFact] + public void GetFiles_String_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + string path = "foo"; + string searchPattern = "foo"; + + sut.Directory.GetFiles(path, searchPattern); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.GetFiles), + path, searchPattern); + } + + [SkippableFact] + public void GetFiles_String_String_SearchOption_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + string path = "foo"; + string searchPattern = "foo"; + SearchOption searchOption = SearchOption.AllDirectories; + + sut.Directory.GetFiles(path, searchPattern, searchOption); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.GetFiles), + path, searchPattern, searchOption); + } + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + [SkippableFact] + public void GetFiles_String_String_EnumerationOptions_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + string path = "foo"; + string searchPattern = "foo"; + EnumerationOptions enumerationOptions = new(); + + sut.Directory.GetFiles(path, searchPattern, enumerationOptions); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.GetFiles), + path, searchPattern, enumerationOptions); + } +#endif + + [SkippableFact] + public void GetFileSystemEntries_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + string path = "foo"; + + sut.Directory.GetFileSystemEntries(path); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.GetFileSystemEntries), + path); + } + + [SkippableFact] + public void GetFileSystemEntries_String_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + string path = "foo"; + string searchPattern = "foo"; + + sut.Directory.GetFileSystemEntries(path, searchPattern); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.GetFileSystemEntries), + path, searchPattern); + } + + [SkippableFact] + public void GetFileSystemEntries_String_String_SearchOption_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + string path = "foo"; + string searchPattern = "foo"; + SearchOption searchOption = SearchOption.AllDirectories; + + sut.Directory.GetFileSystemEntries(path, searchPattern, searchOption); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.GetFileSystemEntries), + path, searchPattern, searchOption); + } + +#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS + [SkippableFact] + public void GetFileSystemEntries_String_String_EnumerationOptions_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + string path = "foo"; + string searchPattern = "foo"; + EnumerationOptions enumerationOptions = new(); + + sut.Directory.GetFileSystemEntries(path, searchPattern, enumerationOptions); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.GetFileSystemEntries), + path, searchPattern, enumerationOptions); + } +#endif + + [SkippableFact] + public void GetLastAccessTime_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + + sut.Directory.GetLastAccessTime(path); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.GetLastAccessTime), + path); + } + + [SkippableFact] + public void GetLastAccessTimeUtc_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + + sut.Directory.GetLastAccessTimeUtc(path); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.GetLastAccessTimeUtc), + path); + } + + [SkippableFact] + public void GetLastWriteTime_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + + sut.Directory.GetLastWriteTime(path); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.GetLastWriteTime), + path); + } + + [SkippableFact] + public void GetLastWriteTimeUtc_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + + sut.Directory.GetLastWriteTimeUtc(path); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.GetLastWriteTimeUtc), + path); + } + + [SkippableFact] + public void GetLogicalDrives_ShouldRegisterCall() + { + MockFileSystem sut = new(); + + sut.Directory.GetLogicalDrives(); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.GetLogicalDrives)); + } + + [SkippableFact] + public void GetParent_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + + sut.Directory.GetParent(path); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.GetParent), + path); + } + + [SkippableFact] + public void Move_String_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + string sourceDirName = "foo"; + string destDirName = "bar"; + + sut.Directory.Move(sourceDirName, destDirName); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.Move), + sourceDirName, destDirName); + } + +#if FEATURE_FILESYSTEM_LINK + [SkippableFact] + public void ResolveLinkTarget_String_Bool_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string linkPath = "foo"; + bool returnFinalTarget = true; + + sut.Directory.ResolveLinkTarget(linkPath, returnFinalTarget); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.ResolveLinkTarget), + linkPath, returnFinalTarget); + } +#endif + + [SkippableFact] + public void SetCreationTime_String_DateTime_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + string path = "foo"; + DateTime creationTime = new(); + + sut.Directory.SetCreationTime(path, creationTime); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.SetCreationTime), + path, creationTime); + } + + [SkippableFact] + public void SetCreationTimeUtc_String_DateTime_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + string path = "foo"; + DateTime creationTimeUtc = new(); + + sut.Directory.SetCreationTimeUtc(path, creationTimeUtc); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.SetCreationTimeUtc), + path, creationTimeUtc); + } + + [SkippableFact] + public void SetCurrentDirectory_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + string path = "foo"; + + sut.Directory.SetCurrentDirectory(path); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.SetCurrentDirectory), + path); + } + + [SkippableFact] + public void SetLastAccessTime_String_DateTime_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + string path = "foo"; + DateTime lastAccessTime = new(); + + sut.Directory.SetLastAccessTime(path, lastAccessTime); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.SetLastAccessTime), + path, lastAccessTime); + } + + [SkippableFact] + public void SetLastAccessTimeUtc_String_DateTime_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + string path = "foo"; + DateTime lastAccessTimeUtc = new(); + + sut.Directory.SetLastAccessTimeUtc(path, lastAccessTimeUtc); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.SetLastAccessTimeUtc), + path, lastAccessTimeUtc); + } + + [SkippableFact] + public void SetLastWriteTime_String_DateTime_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + string path = "foo"; + DateTime lastWriteTime = new(); + + sut.Directory.SetLastWriteTime(path, lastWriteTime); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.SetLastWriteTime), + path, lastWriteTime); + } + + [SkippableFact] + public void SetLastWriteTimeUtc_String_DateTime_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + string path = "foo"; + DateTime lastWriteTimeUtc = new(); + + sut.Directory.SetLastWriteTimeUtc(path, lastWriteTimeUtc); + + sut.Statistics.Directory.ShouldOnlyContain(nameof(IDirectory.SetLastWriteTimeUtc), + path, lastWriteTimeUtc); + } +} diff --git a/Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/DriveInfoFactoryStatisticsTests.cs b/Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/DriveInfoFactoryStatisticsTests.cs new file mode 100644 index 000000000..5db723257 --- /dev/null +++ b/Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/DriveInfoFactoryStatisticsTests.cs @@ -0,0 +1,42 @@ +using System.IO; +using System.Linq; +using Testably.Abstractions.Testing.Tests.TestHelpers; + +namespace Testably.Abstractions.Testing.Tests.Statistics.FileSystem; + +public sealed class DriveInfoFactoryStatisticsTests +{ + [SkippableFact] + public void GetDrives_ShouldRegisterCall() + { + MockFileSystem sut = new(); + + sut.DriveInfo.GetDrives(); + + sut.Statistics.DriveInfo.ShouldOnlyContain(nameof(IDriveInfoFactory.GetDrives)); + } + + [SkippableFact] + public void New_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string driveName = "X"; + + sut.DriveInfo.New(driveName); + + sut.Statistics.DriveInfo.ShouldOnlyContain(nameof(IDriveInfoFactory.New), + driveName); + } + + [SkippableFact] + public void Wrap_DriveInfo_ShouldRegisterCall() + { + MockFileSystem sut = new(); + DriveInfo driveInfo = DriveInfo.GetDrives().First(); + + sut.DriveInfo.Wrap(driveInfo); + + sut.Statistics.DriveInfo.ShouldOnlyContain(nameof(IDriveInfoFactory.Wrap), + driveInfo); + } +} diff --git a/Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/DriveInfoStatisticsTests.cs b/Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/DriveInfoStatisticsTests.cs new file mode 100644 index 000000000..c9aa95182 --- /dev/null +++ b/Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/DriveInfoStatisticsTests.cs @@ -0,0 +1,8 @@ +using System.IO; +using Testably.Abstractions.Testing.Tests.TestHelpers; + +namespace Testably.Abstractions.Testing.Tests.Statistics.FileSystem; + +public sealed class DriveInfoStatisticsTests +{ +} diff --git a/Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/FileInfoFactoryStatisticsTests.cs b/Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/FileInfoFactoryStatisticsTests.cs new file mode 100644 index 000000000..69803c025 --- /dev/null +++ b/Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/FileInfoFactoryStatisticsTests.cs @@ -0,0 +1,31 @@ +using System.IO; +using Testably.Abstractions.Testing.Tests.TestHelpers; + +namespace Testably.Abstractions.Testing.Tests.Statistics.FileSystem; + +public class FileInfoFactoryStatisticsTests +{ + [SkippableFact] + public void New_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string fileName = "foo"; + + sut.FileInfo.New(fileName); + + sut.Statistics.FileInfo.ShouldOnlyContain(nameof(IFileInfoFactory.New), + fileName); + } + + [SkippableFact] + public void Wrap_FileInfo_ShouldRegisterCall() + { + MockFileSystem sut = new(); + FileInfo fileInfo = new("foo"); + + sut.FileInfo.Wrap(fileInfo); + + sut.Statistics.FileInfo.ShouldOnlyContain(nameof(IFileInfoFactory.Wrap), + fileInfo); + } +} diff --git a/Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/FileInfoStatisticsTests.cs b/Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/FileInfoStatisticsTests.cs new file mode 100644 index 000000000..1f58a9f57 --- /dev/null +++ b/Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/FileInfoStatisticsTests.cs @@ -0,0 +1,285 @@ +using System.IO; +using Testably.Abstractions.Testing.Tests.TestHelpers; + +namespace Testably.Abstractions.Testing.Tests.Statistics.FileSystem; + +public class FileInfoStatisticsTests +{ + [SkippableFact] + public void AppendText_ShouldRegisterCall() + { + MockFileSystem sut = new(); + + sut.FileInfo.New("foo").AppendText(); + + sut.Statistics.FileInfo["foo"].ShouldOnlyContain(nameof(IFileInfo.AppendText)); + } + + [SkippableFact] + public void CopyTo_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string destFileName = "bar"; + + sut.FileInfo.New("foo").CopyTo(destFileName); + + sut.Statistics.FileInfo["foo"].ShouldOnlyContain(nameof(IFileInfo.CopyTo), + destFileName); + } + + [SkippableFact] + public void CopyTo_String_Bool_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string destFileName = "bar"; + bool overwrite = true; + + sut.FileInfo.New("foo").CopyTo(destFileName, overwrite); + + sut.Statistics.FileInfo["foo"].ShouldOnlyContain(nameof(IFileInfo.CopyTo), + destFileName, overwrite); + } + + [SkippableFact] + public void Create_ShouldRegisterCall() + { + MockFileSystem sut = new(); + + sut.FileInfo.New("foo").Create(); + + sut.Statistics.FileInfo["foo"].ShouldOnlyContain(nameof(IFileInfo.Create)); + } + +#if FEATURE_FILESYSTEM_LINK + [SkippableFact] + public void CreateAsSymbolicLink_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string pathToTarget = "foo"; + + sut.FileInfo.New("foo").CreateAsSymbolicLink(pathToTarget); + + sut.Statistics.FileInfo["foo"].ShouldOnlyContain(nameof(IFileInfo.CreateAsSymbolicLink), + pathToTarget); + } +#endif + + [SkippableFact] + public void CreateText_ShouldRegisterCall() + { + MockFileSystem sut = new(); + + sut.FileInfo.New("foo").CreateText(); + + sut.Statistics.FileInfo["foo"].ShouldOnlyContain(nameof(IFileInfo.CreateText)); + } + + [SkippableFact] + public void Decrypt_ShouldRegisterCall() + { + Skip.If(!Test.RunsOnWindows); + + MockFileSystem sut = new(); + + #pragma warning disable CA1416 + sut.FileInfo.New("foo").Decrypt(); + #pragma warning restore CA1416 + + sut.Statistics.FileInfo["foo"].ShouldOnlyContain(nameof(IFileInfo.Decrypt)); + } + + [SkippableFact] + public void Delete_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + + sut.FileInfo.New("foo").Delete(); + + sut.Statistics.FileInfo["foo"].ShouldOnlyContain(nameof(IFileInfo.Delete)); + } + + [SkippableFact] + public void Encrypt_ShouldRegisterCall() + { + Skip.If(!Test.RunsOnWindows); + + MockFileSystem sut = new(); + + #pragma warning disable CA1416 + sut.FileInfo.New("foo").Encrypt(); + #pragma warning restore CA1416 + + sut.Statistics.FileInfo["foo"].ShouldOnlyContain(nameof(IFileInfo.Encrypt)); + } + + [SkippableFact] + public void MoveTo_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string destFileName = "bar"; + + sut.FileInfo.New("foo").MoveTo(destFileName); + + sut.Statistics.FileInfo["foo"].ShouldOnlyContain(nameof(IFileInfo.MoveTo), + destFileName); + } + +#if FEATURE_FILE_MOVETO_OVERWRITE + [SkippableFact] + public void MoveTo_String_Bool_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string destFileName = "bar"; + bool overwrite = true; + + sut.FileInfo.New("foo").MoveTo(destFileName, overwrite); + + sut.Statistics.FileInfo["foo"].ShouldOnlyContain(nameof(IFileInfo.MoveTo), + destFileName, overwrite); + } +#endif + + [SkippableFact] + public void Open_FileMode_ShouldRegisterCall() + { + MockFileSystem sut = new(); + FileMode mode = new(); + + sut.FileInfo.New("foo").Open(mode); + + sut.Statistics.FileInfo["foo"].ShouldOnlyContain(nameof(IFileInfo.Open), + mode); + } + +#if FEATURE_FILESYSTEM_STREAM_OPTIONS + [SkippableFact] + public void Open_FileStreamOptions_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + FileStreamOptions options = new(); + + sut.FileInfo.New("foo").Open(options); + + sut.Statistics.FileInfo["foo"].ShouldOnlyContain(nameof(IFileInfo.Open), + options); + } +#endif + + [SkippableFact] + public void Open_FileMode_FileAccess_ShouldRegisterCall() + { + MockFileSystem sut = new(); + FileMode mode = new(); + FileAccess access = new(); + + sut.FileInfo.New("foo").Open(mode, access); + + sut.Statistics.FileInfo["foo"].ShouldOnlyContain(nameof(IFileInfo.Open), + mode, access); + } + + [SkippableFact] + public void Open_FileMode_FileAccess_FileShare_ShouldRegisterCall() + { + MockFileSystem sut = new(); + FileMode mode = new(); + FileAccess access = new(); + FileShare share = new(); + + sut.FileInfo.New("foo").Open(mode, access, share); + + sut.Statistics.FileInfo["foo"].ShouldOnlyContain(nameof(IFileInfo.Open), + mode, access, share); + } + + [SkippableFact] + public void OpenRead_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + + sut.FileInfo.New("foo").OpenRead(); + + sut.Statistics.FileInfo["foo"].ShouldOnlyContain(nameof(IFileInfo.OpenRead)); + } + + [SkippableFact] + public void OpenText_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + + sut.FileInfo.New("foo").OpenText(); + + sut.Statistics.FileInfo["foo"].ShouldOnlyContain(nameof(IFileInfo.OpenText)); + } + + [SkippableFact] + public void OpenWrite_ShouldRegisterCall() + { + MockFileSystem sut = new(); + + sut.FileInfo.New("foo").OpenWrite(); + + sut.Statistics.FileInfo["foo"].ShouldOnlyContain(nameof(IFileInfo.OpenWrite)); + } + + [SkippableFact] + public void Refresh_ShouldRegisterCall() + { + MockFileSystem sut = new(); + + sut.FileInfo.New("foo").Refresh(); + + sut.Statistics.FileInfo["foo"].ShouldOnlyContain(nameof(IFileInfo.Refresh)); + } + + [SkippableFact] + public void Replace_String_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo").WithFile("bar"); + string destinationFileName = "bar"; + string destinationBackupFileName = "xyz"; + + sut.FileInfo.New("foo").Replace(destinationFileName, destinationBackupFileName); + + sut.Statistics.FileInfo["foo"].ShouldOnlyContain(nameof(IFileInfo.Replace), + destinationFileName, destinationBackupFileName); + } + + [SkippableFact] + public void Replace_String_String_Bool_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo").WithFile("bar"); + string destinationFileName = "bar"; + string destinationBackupFileName = "xyz"; + bool ignoreMetadataErrors = true; + + sut.FileInfo.New("foo").Replace(destinationFileName, destinationBackupFileName, ignoreMetadataErrors); + + sut.Statistics.FileInfo["foo"].ShouldOnlyContain(nameof(IFileInfo.Replace), + destinationFileName, destinationBackupFileName, ignoreMetadataErrors); + } + +#if FEATURE_FILESYSTEM_LINK + [SkippableFact] + public void ResolveLinkTarget_Bool_ShouldRegisterCall() + { + MockFileSystem sut = new(); + bool returnFinalTarget = true; + + sut.FileInfo.New("foo").ResolveLinkTarget(returnFinalTarget); + + sut.Statistics.FileInfo["foo"].ShouldOnlyContain(nameof(IFileInfo.ResolveLinkTarget), + returnFinalTarget); + } +#endif +} diff --git a/Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/FileStatisticsTests.cs b/Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/FileStatisticsTests.cs new file mode 100644 index 000000000..0b1c393de --- /dev/null +++ b/Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/FileStatisticsTests.cs @@ -0,0 +1,1372 @@ +using System.Collections.Generic; +using System.IO; +using System.Text; +using Testably.Abstractions.Testing.Tests.TestHelpers; +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE +using Testably.Abstractions.Testing.FileSystem; +using Microsoft.Win32.SafeHandles; +#endif +#if FEATURE_FILESYSTEM_ASYNC +using System.Threading; +using System.Threading.Tasks; +#endif +// ReSharper disable PossibleMultipleEnumeration + +namespace Testably.Abstractions.Testing.Tests.Statistics.FileSystem; + +public sealed class FileStatisticsTests +{ + [SkippableFact] + public void AppendAllLines_String_IEnumerableString_Encoding_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + IEnumerable contents = ["foo", "bar"]; + Encoding encoding = Encoding.UTF8; + + sut.File.AppendAllLines(path, contents, encoding); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.AppendAllLines), + path, contents, encoding); + } + + [SkippableFact] + public void AppendAllLines_String_IEnumerableString_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + IEnumerable contents = ["foo", "bar"]; + + sut.File.AppendAllLines(path, contents); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.AppendAllLines), + path, contents); + } + +#if FEATURE_FILESYSTEM_ASYNC + [SkippableFact] + public async Task + AppendAllLinesAsync_String_IEnumerableString_CancellationToken_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + IEnumerable contents = ["foo", "bar"]; + CancellationToken cancellationToken = CancellationToken.None; + + await sut.File.AppendAllLinesAsync(path, contents, cancellationToken); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.AppendAllLinesAsync), + path, contents, cancellationToken); + } + + [SkippableFact] + public async Task + AppendAllLinesAsync_String_IEnumerableString_Encoding_CancellationToken_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + IEnumerable contents = ["foo", "bar"]; + Encoding encoding = Encoding.UTF8; + CancellationToken cancellationToken = CancellationToken.None; + + await sut.File.AppendAllLinesAsync(path, contents, encoding, cancellationToken); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.AppendAllLinesAsync), + path, contents, encoding, cancellationToken); + } +#endif + + [SkippableFact] + public void AppendAllText_String_String_Encoding_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + string contents = "foo"; + Encoding encoding = Encoding.UTF8; + + sut.File.AppendAllText(path, contents, encoding); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.AppendAllText), + path, contents, encoding); + } + + [SkippableFact] + public void AppendAllText_String_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + string contents = "foo"; + + sut.File.AppendAllText(path, contents); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.AppendAllText), + path, contents); + } + +#if FEATURE_FILESYSTEM_ASYNC + [SkippableFact] + public async Task AppendAllTextAsync_String_String_CancellationToken_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + string contents = "foo"; + CancellationToken cancellationToken = CancellationToken.None; + + await sut.File.AppendAllTextAsync(path, contents, cancellationToken); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.AppendAllTextAsync), + path, contents, cancellationToken); + } + + [SkippableFact] + public async Task + AppendAllTextAsync_String_String_Encoding_CancellationToken_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + string contents = "foo"; + Encoding encoding = Encoding.UTF8; + CancellationToken cancellationToken = CancellationToken.None; + + await sut.File.AppendAllTextAsync(path, contents, encoding, cancellationToken); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.AppendAllTextAsync), + path, contents, encoding, cancellationToken); + } +#endif + + [SkippableFact] + public void AppendText_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + + sut.File.AppendText(path); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.AppendText), + path); + } + + [SkippableFact] + public void Copy_String_String_Bool_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string sourceFileName = "foo"; + string destFileName = "bar"; + bool overwrite = true; + + sut.File.Copy(sourceFileName, destFileName, overwrite); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.Copy), + sourceFileName, destFileName, overwrite); + } + + [SkippableFact] + public void Copy_String_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string sourceFileName = "foo"; + string destFileName = "bar"; + + sut.File.Copy(sourceFileName, destFileName); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.Copy), + sourceFileName, destFileName); + } + + [SkippableFact] + public void Create_String_Int_FileOptions_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + int bufferSize = 42; + FileOptions options = new(); + + sut.File.Create(path, bufferSize, options); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.Create), + path, bufferSize, options); + } + + [SkippableFact] + public void Create_String_Int_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + int bufferSize = 42; + + sut.File.Create(path, bufferSize); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.Create), + path, bufferSize); + } + + [SkippableFact] + public void Create_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + + sut.File.Create(path); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.Create), + path); + } + +#if FEATURE_FILESYSTEM_LINK + [SkippableFact] + public void CreateSymbolicLink_String_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + string pathToTarget = "foo"; + + sut.File.CreateSymbolicLink(path, pathToTarget); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.CreateSymbolicLink), + path, pathToTarget); + } +#endif + + [SkippableFact] + public void CreateText_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + + sut.File.CreateText(path); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.CreateText), + path); + } + + [SkippableFact] + public void Decrypt_String_ShouldRegisterCall() + { + Skip.If(!Test.RunsOnWindows); + + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string path = "foo"; + + #pragma warning disable CA1416 + sut.File.Decrypt(path); + #pragma warning restore CA1416 + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.Decrypt), + path); + } + + [SkippableFact] + public void Delete_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string path = "foo"; + + sut.File.Delete(path); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.Delete), + path); + } + + [SkippableFact] + public void Encrypt_String_ShouldRegisterCall() + { + Skip.If(!Test.RunsOnWindows); + + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string path = "foo"; + + #pragma warning disable CA1416 + sut.File.Encrypt(path); + #pragma warning restore CA1416 + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.Encrypt), + path); + } + + [SkippableFact] + public void Exists_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + + sut.File.Exists(path); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.Exists), + path); + } + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + [SkippableFact] + public void GetAttributes_SafeFileHandle_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.WithSafeFileHandleStrategy( + new DefaultSafeFileHandleStrategy(_ => new SafeFileHandleMock("foo"))) + .Initialize().WithFile("foo"); + SafeFileHandle fileHandle = new(); + + sut.File.GetAttributes(fileHandle); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.GetAttributes), + fileHandle); + } +#endif + + [SkippableFact] + public void GetAttributes_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string path = "foo"; + + sut.File.GetAttributes(path); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.GetAttributes), + path); + } + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + [SkippableFact] + public void GetCreationTime_SafeFileHandle_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.WithSafeFileHandleStrategy( + new DefaultSafeFileHandleStrategy(_ => new SafeFileHandleMock("foo"))) + .Initialize().WithFile("foo"); + SafeFileHandle fileHandle = new(); + + sut.File.GetCreationTime(fileHandle); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.GetCreationTime), + fileHandle); + } +#endif + + [SkippableFact] + public void GetCreationTime_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string path = "foo"; + + sut.File.GetCreationTime(path); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.GetCreationTime), + path); + } + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + [SkippableFact] + public void GetCreationTimeUtc_SafeFileHandle_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.WithSafeFileHandleStrategy( + new DefaultSafeFileHandleStrategy(_ => new SafeFileHandleMock("foo"))) + .Initialize().WithFile("foo"); + SafeFileHandle fileHandle = new(); + + sut.File.GetCreationTimeUtc(fileHandle); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.GetCreationTimeUtc), + fileHandle); + } +#endif + + [SkippableFact] + public void GetCreationTimeUtc_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string path = "foo"; + + sut.File.GetCreationTimeUtc(path); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.GetCreationTimeUtc), + path); + } + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + [SkippableFact] + public void GetLastAccessTime_SafeFileHandle_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.WithSafeFileHandleStrategy( + new DefaultSafeFileHandleStrategy(_ => new SafeFileHandleMock("foo"))) + .Initialize().WithFile("foo"); + SafeFileHandle fileHandle = new(); + + sut.File.GetLastAccessTime(fileHandle); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.GetLastAccessTime), + fileHandle); + } +#endif + + [SkippableFact] + public void GetLastAccessTime_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string path = "foo"; + + sut.File.GetLastAccessTime(path); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.GetLastAccessTime), + path); + } + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + [SkippableFact] + public void GetLastAccessTimeUtc_SafeFileHandle_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.WithSafeFileHandleStrategy( + new DefaultSafeFileHandleStrategy(_ => new SafeFileHandleMock("foo"))) + .Initialize().WithFile("foo"); + SafeFileHandle fileHandle = new(); + + sut.File.GetLastAccessTimeUtc(fileHandle); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.GetLastAccessTimeUtc), + fileHandle); + } +#endif + + [SkippableFact] + public void GetLastAccessTimeUtc_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string path = "foo"; + + sut.File.GetLastAccessTimeUtc(path); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.GetLastAccessTimeUtc), + path); + } + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + [SkippableFact] + public void GetLastWriteTime_SafeFileHandle_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.WithSafeFileHandleStrategy( + new DefaultSafeFileHandleStrategy(_ => new SafeFileHandleMock("foo"))) + .Initialize().WithFile("foo"); + SafeFileHandle fileHandle = new(); + + sut.File.GetLastWriteTime(fileHandle); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.GetLastWriteTime), + fileHandle); + } +#endif + + [SkippableFact] + public void GetLastWriteTime_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string path = "foo"; + + sut.File.GetLastWriteTime(path); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.GetLastWriteTime), + path); + } + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + [SkippableFact] + public void GetLastWriteTimeUtc_SafeFileHandle_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.WithSafeFileHandleStrategy( + new DefaultSafeFileHandleStrategy(_ => new SafeFileHandleMock("foo"))) + .Initialize().WithFile("foo"); + SafeFileHandle fileHandle = new(); + + sut.File.GetLastWriteTimeUtc(fileHandle); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.GetLastWriteTimeUtc), + fileHandle); + } +#endif + + [SkippableFact] + public void GetLastWriteTimeUtc_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string path = "foo"; + + sut.File.GetLastWriteTimeUtc(path); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.GetLastWriteTimeUtc), + path); + } + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + [SkippableFact] + public void GetUnixFileMode_SafeFileHandle_ShouldRegisterCall() + { + Skip.If(!Test.RunsOnLinux); + + MockFileSystem sut = new(); + sut.WithSafeFileHandleStrategy( + new DefaultSafeFileHandleStrategy(_ => new SafeFileHandleMock("foo"))) + .Initialize().WithFile("foo"); + SafeFileHandle fileHandle = new(); + + #pragma warning disable CA1416 + sut.File.GetUnixFileMode(fileHandle); + #pragma warning restore CA1416 + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.GetUnixFileMode), + fileHandle); + } +#endif + +#if FEATURE_FILESYSTEM_UNIXFILEMODE + [SkippableFact] + public void GetUnixFileMode_String_ShouldRegisterCall() + { + Skip.If(!Test.RunsOnLinux); + + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string path = "foo"; + + #pragma warning disable CA1416 + sut.File.GetUnixFileMode(path); + #pragma warning restore CA1416 + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.GetUnixFileMode), + path); + } +#endif + +#if FEATURE_FILE_MOVETO_OVERWRITE + [SkippableFact] + public void Move_String_String_Bool_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string sourceFileName = "foo"; + string destFileName = "bar"; + bool overwrite = true; + + sut.File.Move(sourceFileName, destFileName, overwrite); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.Move), + sourceFileName, destFileName, overwrite); + } +#endif + + [SkippableFact] + public void Move_String_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string sourceFileName = "foo"; + string destFileName = "bar"; + + sut.File.Move(sourceFileName, destFileName); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.Move), + sourceFileName, destFileName); + } + + [SkippableFact] + public void Open_String_FileMode_FileAccess_FileShare_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + FileMode mode = new(); + FileAccess access = new(); + FileShare share = new(); + + sut.File.Open(path, mode, access, share); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.Open), + path, mode, access, share); + } + + [SkippableFact] + public void Open_String_FileMode_FileAccess_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + FileMode mode = new(); + FileAccess access = new(); + + sut.File.Open(path, mode, access); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.Open), + path, mode, access); + } + + [SkippableFact] + public void Open_String_FileMode_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + FileMode mode = new(); + + sut.File.Open(path, mode); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.Open), + path, mode); + } + +#if FEATURE_FILESYSTEM_STREAM_OPTIONS + [SkippableFact] + public void Open_String_FileStreamOptions_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string path = "foo"; + FileStreamOptions options = new(); + + sut.File.Open(path, options); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.Open), + path, options); + } +#endif + + [SkippableFact] + public void OpenRead_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string path = "foo"; + + sut.File.OpenRead(path); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.OpenRead), + path); + } + + [SkippableFact] + public void OpenText_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string path = "foo"; + + sut.File.OpenText(path); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.OpenText), + path); + } + + [SkippableFact] + public void OpenWrite_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + + sut.File.OpenWrite(path); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.OpenWrite), + path); + } + + [SkippableFact] + public void ReadAllBytes_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string path = "foo"; + + sut.File.ReadAllBytes(path); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.ReadAllBytes), + path); + } + +#if FEATURE_FILESYSTEM_ASYNC + [SkippableFact] + public async Task ReadAllBytesAsync_String_CancellationToken_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string path = "foo"; + CancellationToken cancellationToken = CancellationToken.None; + + await sut.File.ReadAllBytesAsync(path, cancellationToken); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.ReadAllBytesAsync), + path, cancellationToken); + } +#endif + + [SkippableFact] + public void ReadAllLines_String_Encoding_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string path = "foo"; + Encoding encoding = Encoding.UTF8; + + sut.File.ReadAllLines(path, encoding); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.ReadAllLines), + path, encoding); + } + + [SkippableFact] + public void ReadAllLines_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string path = "foo"; + + sut.File.ReadAllLines(path); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.ReadAllLines), + path); + } + +#if FEATURE_FILESYSTEM_ASYNC + [SkippableFact] + public async Task ReadAllLinesAsync_String_CancellationToken_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string path = "foo"; + CancellationToken cancellationToken = CancellationToken.None; + + await sut.File.ReadAllLinesAsync(path, cancellationToken); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.ReadAllLinesAsync), + path, cancellationToken); + } + + [SkippableFact] + public async Task ReadAllLinesAsync_String_Encoding_CancellationToken_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string path = "foo"; + Encoding encoding = Encoding.UTF8; + CancellationToken cancellationToken = CancellationToken.None; + + await sut.File.ReadAllLinesAsync(path, encoding, cancellationToken); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.ReadAllLinesAsync), + path, encoding, cancellationToken); + } +#endif + + [SkippableFact] + public void ReadAllText_String_Encoding_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string path = "foo"; + Encoding encoding = Encoding.UTF8; + + sut.File.ReadAllText(path, encoding); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.ReadAllText), + path, encoding); + } + + [SkippableFact] + public void ReadAllText_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string path = "foo"; + + sut.File.ReadAllText(path); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.ReadAllText), + path); + } + +#if FEATURE_FILESYSTEM_ASYNC + [SkippableFact] + public async Task ReadAllTextAsync_String_CancellationToken_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string path = "foo"; + CancellationToken cancellationToken = CancellationToken.None; + + await sut.File.ReadAllTextAsync(path, cancellationToken); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.ReadAllTextAsync), + path, cancellationToken); + } + + [SkippableFact] + public async Task ReadAllTextAsync_String_Encoding_CancellationToken_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string path = "foo"; + Encoding encoding = Encoding.UTF8; + CancellationToken cancellationToken = CancellationToken.None; + + await sut.File.ReadAllTextAsync(path, encoding, cancellationToken); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.ReadAllTextAsync), + path, encoding, cancellationToken); + } +#endif + + [SkippableFact] + public void ReadLines_String_Encoding_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string path = "foo"; + Encoding encoding = Encoding.UTF8; + + sut.File.ReadLines(path, encoding); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.ReadLines), + path, encoding); + } + + [SkippableFact] + public void ReadLines_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string path = "foo"; + + sut.File.ReadLines(path); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.ReadLines), + path); + } + +#if FEATURE_FILESYSTEM_NET7 + [SkippableFact] + public void ReadLinesAsync_String_CancellationToken_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string path = "foo"; + CancellationToken cancellationToken = CancellationToken.None; + + sut.File.ReadLinesAsync(path, cancellationToken); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.ReadLinesAsync), + path, cancellationToken); + } + + [SkippableFact] + public void ReadLinesAsync_String_Encoding_CancellationToken_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string path = "foo"; + Encoding encoding = Encoding.UTF8; + CancellationToken cancellationToken = CancellationToken.None; + + sut.File.ReadLinesAsync(path, encoding, cancellationToken); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.ReadLinesAsync), + path, encoding, cancellationToken); + } +#endif + + [SkippableFact] + public void Replace_String_String_String_Bool_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo").WithFile("bar"); + string sourceFileName = "foo"; + string destinationFileName = "bar"; + string destinationBackupFileName = "xyz"; + bool ignoreMetadataErrors = true; + + sut.File.Replace(sourceFileName, destinationFileName, destinationBackupFileName, + ignoreMetadataErrors); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.Replace), + sourceFileName, destinationFileName, destinationBackupFileName, ignoreMetadataErrors); + } + + [SkippableFact] + public void Replace_String_String_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo").WithFile("bar"); + string sourceFileName = "foo"; + string destinationFileName = "bar"; + string destinationBackupFileName = "xyz"; + + sut.File.Replace(sourceFileName, destinationFileName, destinationBackupFileName); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.Replace), + sourceFileName, destinationFileName, destinationBackupFileName); + } + +#if FEATURE_FILESYSTEM_LINK + [SkippableFact] + public void ResolveLinkTarget_String_Bool_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string linkPath = "foo"; + bool returnFinalTarget = true; + + sut.File.ResolveLinkTarget(linkPath, returnFinalTarget); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.ResolveLinkTarget), + linkPath, returnFinalTarget); + } +#endif + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + [SkippableFact] + public void SetAttributes_SafeFileHandle_FileAttributes_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.WithSafeFileHandleStrategy( + new DefaultSafeFileHandleStrategy(_ => new SafeFileHandleMock("foo"))) + .Initialize().WithFile("foo"); + SafeFileHandle fileHandle = new(); + FileAttributes fileAttributes = new(); + + sut.File.SetAttributes(fileHandle, fileAttributes); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.SetAttributes), + fileHandle, fileAttributes); + } +#endif + + [SkippableFact] + public void SetAttributes_String_FileAttributes_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string path = "foo"; + FileAttributes fileAttributes = new(); + + sut.File.SetAttributes(path, fileAttributes); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.SetAttributes), + path, fileAttributes); + } + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + [SkippableFact] + public void SetCreationTime_SafeFileHandle_DateTime_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.WithSafeFileHandleStrategy( + new DefaultSafeFileHandleStrategy(_ => new SafeFileHandleMock("foo"))) + .Initialize().WithFile("foo"); + SafeFileHandle fileHandle = new(); + DateTime creationTime = new(); + + sut.File.SetCreationTime(fileHandle, creationTime); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.SetCreationTime), + fileHandle, creationTime); + } +#endif + + [SkippableFact] + public void SetCreationTime_String_DateTime_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string path = "foo"; + DateTime creationTime = new(); + + sut.File.SetCreationTime(path, creationTime); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.SetCreationTime), + path, creationTime); + } + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + [SkippableFact] + public void SetCreationTimeUtc_SafeFileHandle_DateTime_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.WithSafeFileHandleStrategy( + new DefaultSafeFileHandleStrategy(_ => new SafeFileHandleMock("foo"))) + .Initialize().WithFile("foo"); + SafeFileHandle fileHandle = new(); + DateTime creationTimeUtc = new(); + + sut.File.SetCreationTimeUtc(fileHandle, creationTimeUtc); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.SetCreationTimeUtc), + fileHandle, creationTimeUtc); + } +#endif + + [SkippableFact] + public void SetCreationTimeUtc_String_DateTime_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string path = "foo"; + DateTime creationTimeUtc = new(); + + sut.File.SetCreationTimeUtc(path, creationTimeUtc); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.SetCreationTimeUtc), + path, creationTimeUtc); + } + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + [SkippableFact] + public void SetLastAccessTime_SafeFileHandle_DateTime_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.WithSafeFileHandleStrategy( + new DefaultSafeFileHandleStrategy(_ => new SafeFileHandleMock("foo"))) + .Initialize().WithFile("foo"); + SafeFileHandle fileHandle = new(); + DateTime lastAccessTime = new(); + + sut.File.SetLastAccessTime(fileHandle, lastAccessTime); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.SetLastAccessTime), + fileHandle, lastAccessTime); + } +#endif + + [SkippableFact] + public void SetLastAccessTime_String_DateTime_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string path = "foo"; + DateTime lastAccessTime = new(); + + sut.File.SetLastAccessTime(path, lastAccessTime); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.SetLastAccessTime), + path, lastAccessTime); + } + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + [SkippableFact] + public void SetLastAccessTimeUtc_SafeFileHandle_DateTime_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.WithSafeFileHandleStrategy( + new DefaultSafeFileHandleStrategy(_ => new SafeFileHandleMock("foo"))) + .Initialize().WithFile("foo"); + SafeFileHandle fileHandle = new(); + DateTime lastAccessTimeUtc = new(); + + sut.File.SetLastAccessTimeUtc(fileHandle, lastAccessTimeUtc); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.SetLastAccessTimeUtc), + fileHandle, lastAccessTimeUtc); + } +#endif + + [SkippableFact] + public void SetLastAccessTimeUtc_String_DateTime_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string path = "foo"; + DateTime lastAccessTimeUtc = new(); + + sut.File.SetLastAccessTimeUtc(path, lastAccessTimeUtc); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.SetLastAccessTimeUtc), + path, lastAccessTimeUtc); + } + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + [SkippableFact] + public void SetLastWriteTime_SafeFileHandle_DateTime_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.WithSafeFileHandleStrategy( + new DefaultSafeFileHandleStrategy(_ => new SafeFileHandleMock("foo"))) + .Initialize().WithFile("foo"); + SafeFileHandle fileHandle = new(); + DateTime lastWriteTime = new(); + + sut.File.SetLastWriteTime(fileHandle, lastWriteTime); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.SetLastWriteTime), + fileHandle, lastWriteTime); + } +#endif + + [SkippableFact] + public void SetLastWriteTime_String_DateTime_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string path = "foo"; + DateTime lastWriteTime = new(); + + sut.File.SetLastWriteTime(path, lastWriteTime); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.SetLastWriteTime), + path, lastWriteTime); + } + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + [SkippableFact] + public void SetLastWriteTimeUtc_SafeFileHandle_DateTime_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.WithSafeFileHandleStrategy( + new DefaultSafeFileHandleStrategy(_ => new SafeFileHandleMock("foo"))) + .Initialize().WithFile("foo"); + SafeFileHandle fileHandle = new(); + DateTime lastWriteTimeUtc = new(); + + sut.File.SetLastWriteTimeUtc(fileHandle, lastWriteTimeUtc); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.SetLastWriteTimeUtc), + fileHandle, lastWriteTimeUtc); + } +#endif + + [SkippableFact] + public void SetLastWriteTimeUtc_String_DateTime_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string path = "foo"; + DateTime lastWriteTimeUtc = new(); + + sut.File.SetLastWriteTimeUtc(path, lastWriteTimeUtc); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.SetLastWriteTimeUtc), + path, lastWriteTimeUtc); + } + +#if FEATURE_FILESYSTEM_SAFEFILEHANDLE + [SkippableFact] + public void SetUnixFileMode_SafeFileHandle_UnixFileMode_ShouldRegisterCall() + { + Skip.If(!Test.RunsOnLinux); + + MockFileSystem sut = new(); + sut.WithSafeFileHandleStrategy( + new DefaultSafeFileHandleStrategy(_ => new SafeFileHandleMock("foo"))) + .Initialize().WithFile("foo"); + SafeFileHandle fileHandle = new(); + UnixFileMode mode = new(); + + #pragma warning disable CA1416 + sut.File.SetUnixFileMode(fileHandle, mode); + #pragma warning restore CA1416 + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.SetUnixFileMode), + fileHandle, mode); + } +#endif + +#if FEATURE_FILESYSTEM_UNIXFILEMODE + [SkippableFact] + public void SetUnixFileMode_String_UnixFileMode_ShouldRegisterCall() + { + Skip.If(!Test.RunsOnLinux); + + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string path = "foo"; + UnixFileMode mode = new(); + + #pragma warning disable CA1416 + sut.File.SetUnixFileMode(path, mode); + #pragma warning restore CA1416 + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.SetUnixFileMode), + path, mode); + } +#endif + + [SkippableFact] + public void WriteAllBytes_String_ByteArray_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + byte[] bytes = Encoding.UTF8.GetBytes("foo"); + + sut.File.WriteAllBytes(path, bytes); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.WriteAllBytes), + path, bytes); + } + +#if FEATURE_FILESYSTEM_ASYNC + [SkippableFact] + public async Task WriteAllBytesAsync_String_ByteArray_CancellationToken_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + byte[] bytes = "foo"u8.ToArray(); + CancellationToken cancellationToken = CancellationToken.None; + + await sut.File.WriteAllBytesAsync(path, bytes, cancellationToken); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.WriteAllBytesAsync), + path, bytes, cancellationToken); + } +#endif + + [SkippableFact] + public void WriteAllLines_String_IEnumerableString_Encoding_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + IEnumerable contents = ["foo", "bar"]; + Encoding encoding = Encoding.UTF8; + + sut.File.WriteAllLines(path, contents, encoding); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.WriteAllLines), + path, contents, encoding); + } + + [SkippableFact] + public void WriteAllLines_String_IEnumerableString_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + IEnumerable contents = ["foo", "bar"]; + + sut.File.WriteAllLines(path, contents); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.WriteAllLines), + path, contents); + } + + [SkippableFact] + public void WriteAllLines_String_StringArray_Encoding_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + string[] contents = ["foo", "bar"]; + Encoding encoding = Encoding.UTF8; + + sut.File.WriteAllLines(path, contents, encoding); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.WriteAllLines), + path, contents, encoding); + } + + [SkippableFact] + public void WriteAllLines_String_StringArray_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + string[] contents = ["foo", "bar"]; + + sut.File.WriteAllLines(path, contents); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.WriteAllLines), + path, contents); + } + +#if FEATURE_FILESYSTEM_ASYNC + [SkippableFact] + public async Task + WriteAllLinesAsync_String_IEnumerableString_CancellationToken_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + IEnumerable contents = ["foo", "bar"]; + CancellationToken cancellationToken = CancellationToken.None; + + await sut.File.WriteAllLinesAsync(path, contents, cancellationToken); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.WriteAllLinesAsync), + path, contents, cancellationToken); + } + + [SkippableFact] + public async Task + WriteAllLinesAsync_String_IEnumerableString_Encoding_CancellationToken_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + IEnumerable contents = ["foo", "bar"]; + Encoding encoding = Encoding.UTF8; + CancellationToken cancellationToken = CancellationToken.None; + + await sut.File.WriteAllLinesAsync(path, contents, encoding, cancellationToken); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.WriteAllLinesAsync), + path, contents, encoding, cancellationToken); + } +#endif + + [SkippableFact] + public void WriteAllText_String_String_Encoding_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + string contents = "foo"; + Encoding encoding = Encoding.UTF8; + + sut.File.WriteAllText(path, contents, encoding); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.WriteAllText), + path, contents, encoding); + } + + [SkippableFact] + public void WriteAllText_String_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + string contents = "foo"; + + sut.File.WriteAllText(path, contents); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.WriteAllText), + path, contents); + } + +#if FEATURE_FILESYSTEM_ASYNC + [SkippableFact] + public async Task WriteAllTextAsync_String_String_CancellationToken_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + string contents = "foo"; + CancellationToken cancellationToken = CancellationToken.None; + + await sut.File.WriteAllTextAsync(path, contents, cancellationToken); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.WriteAllTextAsync), + path, contents, cancellationToken); + } + + [SkippableFact] + public async Task + WriteAllTextAsync_String_String_Encoding_CancellationToken_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + string contents = "foo"; + Encoding encoding = Encoding.UTF8; + CancellationToken cancellationToken = CancellationToken.None; + + await sut.File.WriteAllTextAsync(path, contents, encoding, cancellationToken); + + sut.Statistics.File.ShouldOnlyContain(nameof(IFile.WriteAllTextAsync), + path, contents, encoding, cancellationToken); + } +#endif +} diff --git a/Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/FileStreamFactoryStatisticsTests.cs b/Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/FileStreamFactoryStatisticsTests.cs new file mode 100644 index 000000000..360c7d944 --- /dev/null +++ b/Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/FileStreamFactoryStatisticsTests.cs @@ -0,0 +1,195 @@ +using System.IO; +using Testably.Abstractions.Testing.FileSystem; +using Testably.Abstractions.Testing.Tests.TestHelpers; +#if NET6_0_OR_GREATER +using Microsoft.Win32.SafeHandles; +#endif + +namespace Testably.Abstractions.Testing.Tests.Statistics.FileSystem; + +public class FileStreamFactoryStatisticsTests +{ +#if NET6_0_OR_GREATER + [SkippableFact] + public void New_SafeFileHandle_FileAccess_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.WithSafeFileHandleStrategy( + new DefaultSafeFileHandleStrategy(_ => new SafeFileHandleMock("foo"))) + .Initialize().WithFile("foo"); + SafeFileHandle handle = new(); + FileAccess access = FileAccess.ReadWrite; + + sut.FileStream.New(handle, access); + + sut.Statistics.FileStream.ShouldOnlyContain(nameof(IFileStreamFactory.New), + handle, access); + } +#endif + + [SkippableFact] + public void New_String_FileMode_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + FileMode mode = FileMode.OpenOrCreate; + + sut.FileStream.New(path, mode); + + sut.Statistics.FileStream.ShouldOnlyContain(nameof(IFileStreamFactory.New), + path, mode); + } + +#if FEATURE_FILESYSTEM_STREAM_OPTIONS + [SkippableFact] + public void New_String_FileStreamOptions_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + string path = "foo"; + FileStreamOptions options = new(); + + sut.FileStream.New(path, options); + + sut.Statistics.FileStream.ShouldOnlyContain(nameof(IFileStreamFactory.New), + path, options); + } +#endif + +#if NET6_0_OR_GREATER + [SkippableFact] + public void New_SafeFileHandle_FileAccess_Int_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.WithSafeFileHandleStrategy( + new DefaultSafeFileHandleStrategy(_ => new SafeFileHandleMock("foo"))) + .Initialize().WithFile("foo"); + SafeFileHandle handle = new(); + FileAccess access = FileAccess.ReadWrite; + int bufferSize = 42; + + sut.FileStream.New(handle, access, bufferSize); + + sut.Statistics.FileStream.ShouldOnlyContain(nameof(IFileStreamFactory.New), + handle, access, bufferSize); + } +#endif + + [SkippableFact] + public void New_String_FileMode_FileAccess_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + FileMode mode = FileMode.OpenOrCreate; + FileAccess access = FileAccess.ReadWrite; + + sut.FileStream.New(path, mode, access); + + sut.Statistics.FileStream.ShouldOnlyContain(nameof(IFileStreamFactory.New), + path, mode, access); + } + +#if NET6_0_OR_GREATER + [SkippableFact] + public void New_SafeFileHandle_FileAccess_Int_Bool_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.WithSafeFileHandleStrategy( + new DefaultSafeFileHandleStrategy(_ => new SafeFileHandleMock("foo"))) + .Initialize().WithFile("foo"); + SafeFileHandle handle = new(); + FileAccess access = FileAccess.ReadWrite; + int bufferSize = 42; + bool isAsync = true; + + sut.FileStream.New(handle, access, bufferSize, isAsync); + + sut.Statistics.FileStream.ShouldOnlyContain(nameof(IFileStreamFactory.New), + handle, access, bufferSize, isAsync); + } +#endif + + [SkippableFact] + public void New_String_FileMode_FileAccess_FileShare_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + FileMode mode = FileMode.OpenOrCreate; + FileAccess access = FileAccess.ReadWrite; + FileShare share = FileShare.ReadWrite; + + sut.FileStream.New(path, mode, access, share); + + sut.Statistics.FileStream.ShouldOnlyContain(nameof(IFileStreamFactory.New), + path, mode, access, share); + } + + [SkippableFact] + public void New_String_FileMode_FileAccess_FileShare_Int_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + FileMode mode = FileMode.OpenOrCreate; + FileAccess access = FileAccess.ReadWrite; + FileShare share = FileShare.ReadWrite; + int bufferSize = 42; + + sut.FileStream.New(path, mode, access, share, bufferSize); + + sut.Statistics.FileStream.ShouldOnlyContain(nameof(IFileStreamFactory.New), + path, mode, access, share, bufferSize); + } + + [SkippableFact] + public void New_String_FileMode_FileAccess_FileShare_Int_Bool_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + FileMode mode = FileMode.OpenOrCreate; + FileAccess access = FileAccess.ReadWrite; + FileShare share = FileShare.ReadWrite; + int bufferSize = 42; + bool useAsync = true; + + sut.FileStream.New(path, mode, access, share, bufferSize, useAsync); + + sut.Statistics.FileStream.ShouldOnlyContain(nameof(IFileStreamFactory.New), + path, mode, access, share, bufferSize, useAsync); + } + + [SkippableFact] + public void New_String_FileMode_FileAccess_FileShare_Int_FileOptions_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + FileMode mode = FileMode.OpenOrCreate; + FileAccess access = FileAccess.ReadWrite; + FileShare share = FileShare.ReadWrite; + int bufferSize = 42; + FileOptions options = new(); + + sut.FileStream.New(path, mode, access, share, bufferSize, options); + + sut.Statistics.FileStream.ShouldOnlyContain(nameof(IFileStreamFactory.New), + path, mode, access, share, bufferSize, options); + } + + [SkippableFact] + public void Wrap_FileStream_ShouldRegisterCall() + { + MockFileSystem sut = new(); + FileStream fileStream = new("foo", FileMode.OpenOrCreate); + + try + { + sut.FileStream.Wrap(fileStream); + } + catch (NotSupportedException) + { + // Wrap is not possible on the MockFileSystem, but should still be registered! + } + + sut.Statistics.FileStream.ShouldOnlyContain(nameof(IFileStreamFactory.Wrap), + fileStream); + } +} diff --git a/Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/FileStreamStatisticsTests.cs b/Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/FileStreamStatisticsTests.cs new file mode 100644 index 000000000..23f64a08a --- /dev/null +++ b/Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/FileStreamStatisticsTests.cs @@ -0,0 +1,315 @@ +using System.IO; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Testably.Abstractions.Testing.Tests.TestHelpers; + +namespace Testably.Abstractions.Testing.Tests.Statistics.FileSystem; + +public class FileStreamStatisticsTests +{ + [SkippableFact] + public void BeginRead_ByteArray_Int_Int_AsyncCallback_Object_ShouldRegisterCall() + { + MockFileSystem sut = new(); + byte[] buffer = Encoding.UTF8.GetBytes("foo"); + int offset = 0; + int count = 2; + AsyncCallback? callback = null; + object? state = new(); + + sut.FileStream.New("foo", FileMode.OpenOrCreate) + .BeginRead(buffer, offset, count, callback, state); + + sut.Statistics.FileStream["foo"].ShouldOnlyContain(nameof(FileSystemStream.BeginRead), + buffer, offset, count, callback, state); + } + + [SkippableFact] + public void BeginWrite_ByteArray_Int_Int_AsyncCallback_Object_ShouldRegisterCall() + { + MockFileSystem sut = new(); + byte[] buffer = Encoding.UTF8.GetBytes("foo"); + int offset = 0; + int count = 2; + AsyncCallback? callback = null; + object? state = null; + + sut.FileStream.New("foo", FileMode.OpenOrCreate) + .BeginWrite(buffer, offset, count, callback, state); + + sut.Statistics.FileStream["foo"].ShouldOnlyContain(nameof(FileSystemStream.BeginWrite), + buffer, offset, count, callback, state); + } + + [SkippableFact] + public void CopyTo_Stream_Int_ShouldRegisterCall() + { + MockFileSystem sut = new(); + Stream destination = new MemoryStream(); + int bufferSize = 42; + + sut.FileStream.New("foo", FileMode.OpenOrCreate).CopyTo(destination, bufferSize); + + sut.Statistics.FileStream["foo"].ShouldOnlyContain(nameof(FileSystemStream.CopyTo), + destination, bufferSize); + } + + [SkippableFact] + public async Task CopyToAsync_Stream_Int_CancellationToken_ShouldRegisterCall() + { + MockFileSystem sut = new(); + Stream destination = new MemoryStream(); + int bufferSize = 42; + CancellationToken cancellationToken = CancellationToken.None; + + await sut.FileStream.New("foo", FileMode.OpenOrCreate) + .CopyToAsync(destination, bufferSize, cancellationToken); + + sut.Statistics.FileStream["foo"].ShouldOnlyContain(nameof(FileSystemStream.CopyToAsync), + destination, bufferSize, cancellationToken); + } + + [SkippableFact] + public void EndRead_IAsyncResult_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + FileSystemStream stream = sut.FileStream.New("foo", FileMode.OpenOrCreate); + IAsyncResult asyncResult = stream.BeginRead(new byte[10], 0, 10, null, null); + + stream.EndRead(asyncResult); + + sut.Statistics.FileStream["foo"].Methods.Count.Should().Be(2); + sut.Statistics.FileStream["foo"].Methods.Values.Should() + .ContainSingle(c => c.Name == nameof(FileSystemStream.EndRead) && + c.Parameters.Length == 1 && + c.Parameters[0].Is(asyncResult)); + } + + [SkippableFact] + public void EndWrite_IAsyncResult_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithFile("foo"); + FileSystemStream stream = sut.FileStream.New("foo", FileMode.OpenOrCreate); + IAsyncResult asyncResult = stream.BeginWrite(new byte[10], 0, 10, null, null); + + stream.EndWrite(asyncResult); + + sut.Statistics.FileStream["foo"].Methods.Count.Should().Be(2); + sut.Statistics.FileStream["foo"].Methods.Values.Should() + .ContainSingle(c => c.Name == nameof(FileSystemStream.EndWrite) && + c.Parameters.Length == 1 && + c.Parameters[0].Is(asyncResult)); + } + + [SkippableFact] + public void Flush_ShouldRegisterCall() + { + MockFileSystem sut = new(); + + sut.FileStream.New("foo", FileMode.OpenOrCreate).Flush(); + + sut.Statistics.FileStream["foo"].ShouldOnlyContain(nameof(FileSystemStream.Flush)); + } + + [SkippableFact] + public void Flush_Bool_ShouldRegisterCall() + { + MockFileSystem sut = new(); + bool flushToDisk = true; + + sut.FileStream.New("foo", FileMode.OpenOrCreate).Flush(flushToDisk); + + sut.Statistics.FileStream["foo"].ShouldOnlyContain(nameof(FileSystemStream.Flush), + flushToDisk); + } + + [SkippableFact] + public async Task FlushAsync_CancellationToken_ShouldRegisterCall() + { + MockFileSystem sut = new(); + CancellationToken cancellationToken = CancellationToken.None; + + await sut.FileStream.New("foo", FileMode.OpenOrCreate).FlushAsync(cancellationToken); + + sut.Statistics.FileStream["foo"].ShouldOnlyContain(nameof(FileSystemStream.FlushAsync), + cancellationToken); + } + +#if FEATURE_SPAN + [SkippableFact] + public void Read_SpanByte_ShouldRegisterCall() + { + MockFileSystem sut = new(); + Span buffer = new(); + + _ = sut.FileStream.New("foo", FileMode.OpenOrCreate).Read(buffer); + + sut.Statistics.FileStream["foo"].ShouldOnlyContain(nameof(FileSystemStream.Read), + buffer); + } +#endif + + [SkippableFact] + public void Read_ByteArray_Int_Int_ShouldRegisterCall() + { + MockFileSystem sut = new(); + byte[] buffer = Encoding.UTF8.GetBytes("foo"); + int offset = 0; + int count = 2; + + sut.FileStream.New("foo", FileMode.OpenOrCreate).Read(buffer, offset, count); + + sut.Statistics.FileStream["foo"].ShouldOnlyContain(nameof(FileSystemStream.Read), + buffer, offset, count); + } + +#if FEATURE_SPAN + [SkippableFact] + public void ReadAsync_MemoryByte_CancellationToken_ShouldRegisterCall() + { + MockFileSystem sut = new(); + Memory buffer = new(); + CancellationToken cancellationToken = CancellationToken.None; + + sut.FileStream.New("foo", FileMode.OpenOrCreate).ReadAsync(buffer, cancellationToken); + + sut.Statistics.FileStream["foo"].ShouldOnlyContain(nameof(FileSystemStream.ReadAsync), + buffer, cancellationToken); + } +#endif + + [SkippableFact] + public async Task ReadAsync_ByteArray_Int_Int_CancellationToken_ShouldRegisterCall() + { + MockFileSystem sut = new(); + byte[] buffer = Encoding.UTF8.GetBytes("foo"); + int offset = 0; + int count = 2; + CancellationToken cancellationToken = CancellationToken.None; + + await sut.FileStream.New("foo", FileMode.OpenOrCreate) + .ReadAsync(buffer, offset, count, cancellationToken); + + sut.Statistics.FileStream["foo"].ShouldOnlyContain(nameof(FileSystemStream.ReadAsync), + buffer, offset, count, cancellationToken); + } + + [SkippableFact] + public void ReadByte_ShouldRegisterCall() + { + MockFileSystem sut = new(); + + sut.FileStream.New("foo", FileMode.OpenOrCreate).ReadByte(); + + sut.Statistics.FileStream["foo"].ShouldOnlyContain(nameof(FileSystemStream.ReadByte)); + } + + [SkippableFact] + public void Seek_Int64_SeekOrigin_ShouldRegisterCall() + { + MockFileSystem sut = new(); + long offset = new(); + SeekOrigin origin = new(); + + sut.FileStream.New("foo", FileMode.OpenOrCreate).Seek(offset, origin); + + sut.Statistics.FileStream["foo"].ShouldOnlyContain(nameof(FileSystemStream.Seek), + offset, origin); + } + + [SkippableFact] + public void SetLength_Int64_ShouldRegisterCall() + { + MockFileSystem sut = new(); + long value = new(); + + sut.FileStream.New("foo", FileMode.OpenOrCreate).SetLength(value); + + sut.Statistics.FileStream["foo"].ShouldOnlyContain(nameof(FileSystemStream.SetLength), + value); + } + + [SkippableFact] + public void ToString_ShouldRegisterCall() + { + MockFileSystem sut = new(); + + sut.FileStream.New("foo", FileMode.OpenOrCreate).ToString(); + + sut.Statistics.FileStream["foo"].ShouldOnlyContain(nameof(FileSystemStream.ToString)); + } + +#if FEATURE_SPAN + [SkippableFact] + public void Write_ReadOnlySpanByte_ShouldRegisterCall() + { + MockFileSystem sut = new(); + ReadOnlySpan buffer = new(); + + sut.FileStream.New("foo", FileMode.OpenOrCreate).Write(buffer); + + sut.Statistics.FileStream["foo"].ShouldOnlyContain(nameof(FileSystemStream.Write), + buffer); + } +#endif + + [SkippableFact] + public void Write_ByteArray_Int_Int_ShouldRegisterCall() + { + MockFileSystem sut = new(); + byte[] buffer = Encoding.UTF8.GetBytes("foo"); + int offset = 0; + int count = 2; + + sut.FileStream.New("foo", FileMode.OpenOrCreate).Write(buffer, offset, count); + + sut.Statistics.FileStream["foo"].ShouldOnlyContain(nameof(FileSystemStream.Write), + buffer, offset, count); + } + +#if FEATURE_SPAN + [SkippableFact] + public void WriteAsync_ReadOnlyMemoryByte_CancellationToken_ShouldRegisterCall() + { + MockFileSystem sut = new(); + ReadOnlyMemory buffer = new(); + CancellationToken cancellationToken = CancellationToken.None; + + sut.FileStream.New("foo", FileMode.OpenOrCreate).WriteAsync(buffer, cancellationToken); + + sut.Statistics.FileStream["foo"].ShouldOnlyContain(nameof(FileSystemStream.WriteAsync), + buffer, cancellationToken); + } +#endif + + [SkippableFact] + public async Task WriteAsync_ByteArray_Int_Int_CancellationToken_ShouldRegisterCall() + { + MockFileSystem sut = new(); + byte[] buffer = Encoding.UTF8.GetBytes("foo"); + int offset = 0; + int count = 2; + CancellationToken cancellationToken = CancellationToken.None; + + await sut.FileStream.New("foo", FileMode.OpenOrCreate) + .WriteAsync(buffer, offset, count, cancellationToken); + + sut.Statistics.FileStream["foo"].ShouldOnlyContain(nameof(FileSystemStream.WriteAsync), + buffer, offset, count, cancellationToken); + } + + [SkippableFact] + public void WriteByte_Byte_ShouldRegisterCall() + { + MockFileSystem sut = new(); + byte value = new(); + + sut.FileStream.New("foo", FileMode.OpenOrCreate).WriteByte(value); + + sut.Statistics.FileStream["foo"].ShouldOnlyContain(nameof(FileSystemStream.WriteByte), + value); + } +} diff --git a/Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/FileSystemWatcherFactoryStatisticsTests.cs b/Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/FileSystemWatcherFactoryStatisticsTests.cs new file mode 100644 index 000000000..73cd43229 --- /dev/null +++ b/Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/FileSystemWatcherFactoryStatisticsTests.cs @@ -0,0 +1,57 @@ +using System.IO; +using Testably.Abstractions.Testing.Tests.TestHelpers; + +namespace Testably.Abstractions.Testing.Tests.Statistics.FileSystem; + +public class FileSystemWatcherFactoryStatisticsTests +{ + [SkippableFact] + public void New_ShouldRegisterCall() + { + MockFileSystem sut = new(); + + sut.FileSystemWatcher.New(); + + sut.Statistics.FileSystemWatcher.ShouldOnlyContain(nameof(IFileSystemWatcherFactory.New)); + } + + [SkippableFact] + public void New_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + string path = "foo"; + + sut.FileSystemWatcher.New(path); + + sut.Statistics.FileSystemWatcher.ShouldOnlyContain(nameof(IFileSystemWatcherFactory.New), + path); + } + + [SkippableFact] + public void New_String_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + string path = "foo"; + string filter = "bar"; + + sut.FileSystemWatcher.New(path, filter); + + sut.Statistics.FileSystemWatcher.ShouldOnlyContain(nameof(IFileSystemWatcherFactory.New), + path, filter); + } + + [SkippableFact] + public void Wrap_FileSystemWatcher_ShouldRegisterCall() + { + MockFileSystem sut = new(); + FileSystemWatcher fileSystemWatcher = new(); + + sut.FileSystemWatcher.Wrap(fileSystemWatcher); + + sut.Statistics.FileSystemWatcher.ShouldOnlyContain(nameof(IFileSystemWatcherFactory.Wrap), + fileSystemWatcher); + } + +} diff --git a/Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/FileSystemWatcherStatisticsTests.cs b/Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/FileSystemWatcherStatisticsTests.cs new file mode 100644 index 000000000..3410c90e8 --- /dev/null +++ b/Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/FileSystemWatcherStatisticsTests.cs @@ -0,0 +1,107 @@ +using System.IO; +using System.Threading.Tasks; +using System.Threading; +using Testably.Abstractions.Testing.Tests.TestHelpers; + +namespace Testably.Abstractions.Testing.Tests.Statistics.FileSystem; + +public class FileSystemWatcherStatisticsTests +{ + [SkippableFact] + public void BeginInit_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + + sut.FileSystemWatcher.New("foo").BeginInit(); + + sut.Statistics.FileSystemWatcher["foo"].ShouldOnlyContain(nameof(IFileSystemWatcher.BeginInit)); + } + + [SkippableFact] + public void EndInit_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + + sut.FileSystemWatcher.New("foo").EndInit(); + + sut.Statistics.FileSystemWatcher["foo"].ShouldOnlyContain(nameof(IFileSystemWatcher.EndInit)); + } + + [SkippableFact] + public void WaitForChanged_WatcherChangeTypes_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + // Changes in the background are necessary, so that FileSystemWatcher.WaitForChanged returns. + CancellationTokenSource cts = new(TimeSpan.FromSeconds(1)); + _ = Task.Run(async () => + { + while (!cts.Token.IsCancellationRequested) + { + await Task.Delay(10, cts.Token); + sut.Directory.CreateDirectory(sut.Path.Combine("foo", "some-directory")); + sut.Directory.Delete(sut.Path.Combine("foo", "some-directory")); + } + }, cts.Token); + WatcherChangeTypes changeType = WatcherChangeTypes.Created; + + sut.FileSystemWatcher.New("foo").WaitForChanged(changeType); + + sut.Statistics.FileSystemWatcher["foo"].ShouldOnlyContain(nameof(IFileSystemWatcher.WaitForChanged), + changeType); + } + + [SkippableFact] + public void WaitForChanged_WatcherChangeTypes_Int_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + // Changes in the background are necessary, so that FileSystemWatcher.WaitForChanged returns. + CancellationTokenSource cts = new(TimeSpan.FromSeconds(1)); + _ = Task.Run(async () => + { + while (!cts.Token.IsCancellationRequested) + { + await Task.Delay(10, cts.Token); + sut.Directory.CreateDirectory(sut.Path.Combine("foo", "some-directory")); + sut.Directory.Delete(sut.Path.Combine("foo", "some-directory")); + } + }, cts.Token); + WatcherChangeTypes changeType = WatcherChangeTypes.Created; + int timeout = 42; + + sut.FileSystemWatcher.New("foo").WaitForChanged(changeType, timeout); + + sut.Statistics.FileSystemWatcher["foo"].ShouldOnlyContain(nameof(IFileSystemWatcher.WaitForChanged), + changeType, timeout); + } + +#if FEATURE_FILESYSTEM_NET7 + [SkippableFact] + public void WaitForChanged_WatcherChangeTypes_TimeSpan_ShouldRegisterCall() + { + MockFileSystem sut = new(); + sut.Initialize().WithSubdirectory("foo"); + // Changes in the background are necessary, so that FileSystemWatcher.WaitForChanged returns. + CancellationTokenSource cts = new(TimeSpan.FromSeconds(1)); + _ = Task.Run(async () => + { + while (!cts.Token.IsCancellationRequested) + { + await Task.Delay(10, cts.Token); + sut.Directory.CreateDirectory(sut.Path.Combine("foo", "some-directory")); + sut.Directory.Delete(sut.Path.Combine("foo", "some-directory")); + } + }, cts.Token); + WatcherChangeTypes changeType = WatcherChangeTypes.Created; + TimeSpan timeout = TimeSpan.FromSeconds(2); + + sut.FileSystemWatcher.New("foo").WaitForChanged(changeType, timeout); + + sut.Statistics.FileSystemWatcher["foo"].ShouldOnlyContain(nameof(IFileSystemWatcher.WaitForChanged), + changeType, timeout); + } +#endif +} diff --git a/Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/PathStatisticsTests.cs b/Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/PathStatisticsTests.cs new file mode 100644 index 000000000..33a3831ac --- /dev/null +++ b/Tests/Testably.Abstractions.Testing.Tests/Statistics/FileSystem/PathStatisticsTests.cs @@ -0,0 +1,571 @@ +using Testably.Abstractions.Testing.Tests.TestHelpers; + +namespace Testably.Abstractions.Testing.Tests.Statistics.FileSystem; + +public class PathStatisticsTests +{ + [SkippableFact] + public void ChangeExtension_String_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + string extension = "foo"; + + sut.Path.ChangeExtension(path, extension); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.ChangeExtension), + path, extension); + } + + [SkippableFact] + public void Combine_StringArray_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string[] paths = ["foo", "bar"]; + + sut.Path.Combine(paths); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.Combine), + paths); + } + + [SkippableFact] + public void Combine_String_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path1 = "foo"; + string path2 = "foo"; + + sut.Path.Combine(path1, path2); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.Combine), + path1, path2); + } + + [SkippableFact] + public void Combine_String_String_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path1 = "foo"; + string path2 = "foo"; + string path3 = "foo"; + + sut.Path.Combine(path1, path2, path3); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.Combine), + path1, path2, path3); + } + + [SkippableFact] + public void Combine_String_String_String_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path1 = "foo"; + string path2 = "foo"; + string path3 = "foo"; + string path4 = "foo"; + + sut.Path.Combine(path1, path2, path3, path4); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.Combine), + path1, path2, path3, path4); + } + +#if FEATURE_PATH_ADVANCED + [SkippableFact] + public void EndsInDirectorySeparator_ReadOnlySpanChar_ShouldRegisterCall() + { + MockFileSystem sut = new(); + ReadOnlySpan path = new(); + + sut.Path.EndsInDirectorySeparator(path); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.EndsInDirectorySeparator), + path); + } + + [SkippableFact] + public void EndsInDirectorySeparator_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + + sut.Path.EndsInDirectorySeparator(path); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.EndsInDirectorySeparator), + path); + } +#endif + +#if FEATURE_FILESYSTEM_NET7 + [SkippableFact] + public void Exists_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + + sut.Path.Exists(path); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.Exists), + path); + } +#endif + +#if FEATURE_SPAN + [SkippableFact] + public void GetDirectoryName_ReadOnlySpanChar_ShouldRegisterCall() + { + MockFileSystem sut = new(); + ReadOnlySpan path = new(); + + sut.Path.GetDirectoryName(path); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.GetDirectoryName), + path); + } +#endif + + [SkippableFact] + public void GetDirectoryName_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + + sut.Path.GetDirectoryName(path); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.GetDirectoryName), + path); + } + +#if FEATURE_SPAN + [SkippableFact] + public void GetExtension_ReadOnlySpanChar_ShouldRegisterCall() + { + MockFileSystem sut = new(); + ReadOnlySpan path = new(); + + sut.Path.GetExtension(path); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.GetExtension), + path); + } +#endif + + [SkippableFact] + public void GetExtension_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + + sut.Path.GetExtension(path); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.GetExtension), + path); + } + +#if FEATURE_SPAN + [SkippableFact] + public void GetFileName_ReadOnlySpanChar_ShouldRegisterCall() + { + MockFileSystem sut = new(); + ReadOnlySpan path = new(); + + sut.Path.GetFileName(path); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.GetFileName), + path); + } +#endif + + [SkippableFact] + public void GetFileName_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + + sut.Path.GetFileName(path); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.GetFileName), + path); + } + +#if FEATURE_SPAN + [SkippableFact] + public void GetFileNameWithoutExtension_ReadOnlySpanChar_ShouldRegisterCall() + { + MockFileSystem sut = new(); + ReadOnlySpan path = new(); + + sut.Path.GetFileNameWithoutExtension(path); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.GetFileNameWithoutExtension), + path); + } +#endif + + [SkippableFact] + public void GetFileNameWithoutExtension_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + + sut.Path.GetFileNameWithoutExtension(path); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.GetFileNameWithoutExtension), + path); + } + + [SkippableFact] + public void GetFullPath_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + + sut.Path.GetFullPath(path); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.GetFullPath), + path); + } + +#if FEATURE_PATH_RELATIVE + [SkippableFact] + public void GetFullPath_String_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + string basePath = System.IO.Path.GetFullPath("bar"); + + sut.Path.GetFullPath(path, basePath); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.GetFullPath), + path, basePath); + } +#endif + + [SkippableFact] + public void GetInvalidFileNameChars_ShouldRegisterCall() + { + MockFileSystem sut = new(); + + sut.Path.GetInvalidFileNameChars(); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.GetInvalidFileNameChars)); + } + + [SkippableFact] + public void GetInvalidPathChars_ShouldRegisterCall() + { + MockFileSystem sut = new(); + + sut.Path.GetInvalidPathChars(); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.GetInvalidPathChars)); + } + +#if FEATURE_SPAN + [SkippableFact] + public void GetPathRoot_ReadOnlySpanChar_ShouldRegisterCall() + { + MockFileSystem sut = new(); + ReadOnlySpan path = new(); + + sut.Path.GetPathRoot(path); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.GetPathRoot), + path); + } +#endif + + [SkippableFact] + public void GetPathRoot_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + + sut.Path.GetPathRoot(path); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.GetPathRoot), + path); + } + + [SkippableFact] + public void GetRandomFileName_ShouldRegisterCall() + { + MockFileSystem sut = new(); + + sut.Path.GetRandomFileName(); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.GetRandomFileName)); + } + +#if FEATURE_PATH_RELATIVE + [SkippableFact] + public void GetRelativePath_String_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string relativeTo = "foo"; + string path = "foo"; + + sut.Path.GetRelativePath(relativeTo, path); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.GetRelativePath), + relativeTo, path); + } +#endif + + [SkippableFact] + public void GetTempFileName_ShouldRegisterCall() + { + MockFileSystem sut = new(); + + sut.Path.GetTempFileName(); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.GetTempFileName)); + } + + [SkippableFact] + public void GetTempPath_ShouldRegisterCall() + { + MockFileSystem sut = new(); + + sut.Path.GetTempPath(); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.GetTempPath)); + } + +#if FEATURE_SPAN + [SkippableFact] + public void HasExtension_ReadOnlySpanChar_ShouldRegisterCall() + { + MockFileSystem sut = new(); + ReadOnlySpan path = new(); + + sut.Path.HasExtension(path); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.HasExtension), + path); + } +#endif + + [SkippableFact] + public void HasExtension_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + + sut.Path.HasExtension(path); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.HasExtension), + path); + } + +#if FEATURE_SPAN + [SkippableFact] + public void IsPathFullyQualified_ReadOnlySpanChar_ShouldRegisterCall() + { + MockFileSystem sut = new(); + ReadOnlySpan path = new(); + + sut.Path.IsPathFullyQualified(path); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.IsPathFullyQualified), + path); + } +#endif + +#if FEATURE_PATH_RELATIVE + [SkippableFact] + public void IsPathFullyQualified_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + + sut.Path.IsPathFullyQualified(path); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.IsPathFullyQualified), + path); + } +#endif + +#if FEATURE_SPAN + [SkippableFact] + public void IsPathRooted_ReadOnlySpanChar_ShouldRegisterCall() + { + MockFileSystem sut = new(); + ReadOnlySpan path = new(); + + sut.Path.IsPathRooted(path); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.IsPathRooted), + path); + } +#endif + + [SkippableFact] + public void IsPathRooted_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + + sut.Path.IsPathRooted(path); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.IsPathRooted), + path); + } + +#if FEATURE_PATH_JOIN + [SkippableFact] + public void Join_StringArray_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string[] paths = ["foo", "bar"]; + + sut.Path.Join(paths); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.Join), + paths); + } + + [SkippableFact] + public void Join_ReadOnlySpanChar_ReadOnlySpanChar_ShouldRegisterCall() + { + MockFileSystem sut = new(); + ReadOnlySpan path1 = new(); + ReadOnlySpan path2 = new(); + + sut.Path.Join(path1, path2); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.Join), + path1, path2); + } + + [SkippableFact] + public void Join_String_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path1 = "foo"; + string path2 = "foo"; + + sut.Path.Join(path1, path2); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.Join), + path1, path2); + } + + [SkippableFact] + public void Join_ReadOnlySpanChar_ReadOnlySpanChar_ReadOnlySpanChar_ShouldRegisterCall() + { + MockFileSystem sut = new(); + ReadOnlySpan path1 = new(); + ReadOnlySpan path2 = new(); + ReadOnlySpan path3 = new(); + + sut.Path.Join(path1, path2, path3); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.Join), + path1, path2, path3); + } + + [SkippableFact] + public void Join_String_String_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path1 = "foo"; + string path2 = "foo"; + string path3 = "foo"; + + sut.Path.Join(path1, path2, path3); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.Join), + path1, path2, path3); + } + + [SkippableFact] + public void + Join_ReadOnlySpanChar_ReadOnlySpanChar_ReadOnlySpanChar_ReadOnlySpanChar_ShouldRegisterCall() + { + MockFileSystem sut = new(); + ReadOnlySpan path1 = new(); + ReadOnlySpan path2 = new(); + ReadOnlySpan path3 = new(); + ReadOnlySpan path4 = new(); + + sut.Path.Join(path1, path2, path3, path4); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.Join), + path1, path2, path3, path4); + } + + [SkippableFact] + public void Join_String_String_String_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path1 = "foo"; + string path2 = "foo"; + string path3 = "foo"; + string path4 = "foo"; + + sut.Path.Join(path1, path2, path3, path4); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.Join), + path1, path2, path3, path4); + } +#endif + +#if FEATURE_PATH_ADVANCED + [SkippableFact] + public void TrimEndingDirectorySeparator_ReadOnlySpanChar_ShouldRegisterCall() + { + MockFileSystem sut = new(); + ReadOnlySpan path = new(); + + sut.Path.TrimEndingDirectorySeparator(path); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.TrimEndingDirectorySeparator), + path); + } + + [SkippableFact] + public void TrimEndingDirectorySeparator_String_ShouldRegisterCall() + { + MockFileSystem sut = new(); + string path = "foo"; + + sut.Path.TrimEndingDirectorySeparator(path); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.TrimEndingDirectorySeparator), + path); + } +#endif + +#if FEATURE_PATH_JOIN + [SkippableFact] + public void TryJoin_ReadOnlySpanChar_ReadOnlySpanChar_SpanChar_OutInt_ShouldRegisterCall() + { + MockFileSystem sut = new(); + ReadOnlySpan path1 = new(); + ReadOnlySpan path2 = new(); + Span destination = new(); + + sut.Path.TryJoin(path1, path2, destination, out int charsWritten); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.TryJoin), + path1, path2, destination, charsWritten); + } + + [SkippableFact] + public void TryJoin_ReadOnlySpanChar_ReadOnlySpanChar_ReadOnlySpanChar_SpanChar_OutInt_ShouldRegisterCall() + { + MockFileSystem sut = new(); + ReadOnlySpan path1 = new(); + ReadOnlySpan path2 = new(); + ReadOnlySpan path3 = new(); + Span destination = new(); + + sut.Path.TryJoin(path1, path2, path3, destination, out int charsWritten); + + sut.Statistics.Path.ShouldOnlyContain(nameof(IPath.TryJoin), + path1, path2, path3, destination, charsWritten); + } +#endif +} diff --git a/Tests/Testably.Abstractions.Testing.Tests/Statistics/StatisticsTests.cs b/Tests/Testably.Abstractions.Testing.Tests/Statistics/StatisticsTests.cs new file mode 100644 index 000000000..b9ea16076 --- /dev/null +++ b/Tests/Testably.Abstractions.Testing.Tests/Statistics/StatisticsTests.cs @@ -0,0 +1,377 @@ +using Microsoft.Win32.SafeHandles; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Testably.Abstractions.Testing.Tests.Statistics.FileSystem; +using Testably.Abstractions.Testing.Tests.TestHelpers; + +namespace Testably.Abstractions.Testing.Tests.Statistics; + +public sealed class StatisticsTests +{ + [Fact] + public async Task Statistics_ShouldSupportParallelCalls() + { + int parallelTasks = 100; + MockFileSystem sut = new(); + string[] directories = Enumerable.Range(1, parallelTasks) + .Select(i => $"foo-{i}") + .ToArray(); + Task[] tasks = new Task[parallelTasks]; + + for (int i = 0; i < directories.Length; i++) + { + int taskId = i; + tasks[taskId] = Task.Run(() => + { + sut.Directory.CreateDirectory(directories[taskId]); + }); + } + + await Task.WhenAll(tasks); + + foreach (string directory in directories) + { + sut.Statistics.Directory.Methods.Values + .Should().ContainSingle(x => + x.Name == nameof(Directory.CreateDirectory) && + x.Parameters.Length == 1 && + x.Parameters[0].Is(directory)); + } + + sut.Statistics.Directory.Methods.Keys.Should() + .BeEquivalentTo(Enumerable.Range(1, directories.Length)); + } + + [Fact] + public void Statistics_ShouldIncrementCallOrder() + { + MockFileSystem sut = new(); + string[] directories = Enumerable.Range(1, 20) + .Select(i => $"foo-{i}") + .ToArray(); + + foreach (string directory in directories) + { + sut.Directory.CreateDirectory(directory); + } + + for (int i = 0; i < directories.Length; i++) + { + sut.Statistics.Directory.Methods[i + 1] + .Parameters[0].Is(directories[i]).Should().BeTrue(); + } + } + + [Fact] + public void Statistics_ShouldKeepCallOrder() + { + MockFileSystem sut = new(); + string[] directories = Enumerable.Range(1, 20) + .Select(i => $"foo-{i}") + .ToArray(); + + foreach (string directory in directories) + { + sut.Directory.CreateDirectory(directory); + } + + for (int i = 0; i < directories.Length; i++) + { + sut.Statistics.Directory.Methods + .OrderBy(x => x.Key) + .Skip(i) + .First() + .Value.Parameters[0].Is(directories[i]).Should().BeTrue(); + } + } + + [Fact] + public void Statistics_ShouldUseGlobalIncrement() + { + MockFileSystem sut = new(); + sut.Directory.CreateDirectory("foo"); + sut.File.WriteAllText("bar.txt", null); + IFileInfo fileInfo = sut.FileInfo.New("bar.txt"); + using FileSystemStream stream = fileInfo.Open(FileMode.Open, FileAccess.Read); + _ = new StreamReader(stream).ReadToEnd(); + + sut.Statistics.Directory.Methods[1].Name.Should().Be(nameof(IDirectory.CreateDirectory)); + sut.Statistics.File.Methods[2].Name.Should().Be(nameof(IFile.WriteAllText)); + sut.Statistics.FileInfo.Methods[3].Name.Should().Be(nameof(IFileInfoFactory.New)); + // Note: Index 4 ist used internally for creating the full path of the file info. + sut.Statistics.FileInfo["bar.txt"].Methods[5].Name.Should().Be(nameof(IFileInfo.Open)); + } + + [Fact] + public void FileSystem_Initialize_ShouldNotRegisterStatistics() + { + MockFileSystem sut = new(); + sut.Initialize() + .WithSubdirectory("d0").Initialized(d => d.WithASubdirectory()) + .WithSubdirectories("d1", "d2") + .WithASubdirectory() + .WithFile("f0").Which(f => f.HasBytesContent(Encoding.UTF8.GetBytes("bar"))) + .WithAFile().Which(f => f.HasStringContent("foo")); + + sut.Statistics.Directory.Methods.Should().BeEmpty(); + sut.Statistics.File.Methods.Should().BeEmpty(); + } + + [Theory] + [InlineData(nameof(MockFileSystem.Directory), false, + typeof(IDirectory), typeof(DirectoryStatisticsTests))] + [InlineData(nameof(MockFileSystem.DirectoryInfo), false, + typeof(IDirectoryInfoFactory), typeof(DirectoryInfoFactoryStatisticsTests))] + [InlineData(nameof(MockFileSystem.DirectoryInfo), true, + typeof(IDirectoryInfo), typeof(DirectoryInfoStatisticsTests))] + [InlineData(nameof(MockFileSystem.DriveInfo), false, + typeof(IDriveInfoFactory), typeof(DriveInfoFactoryStatisticsTests))] + [InlineData(nameof(MockFileSystem.DriveInfo), true, + typeof(IDriveInfo), typeof(DriveInfoStatisticsTests))] + [InlineData(nameof(MockFileSystem.File), false, + typeof(IFile), typeof(FileStatisticsTests))] + [InlineData(nameof(MockFileSystem.FileInfo), false, + typeof(IFileInfoFactory), typeof(FileInfoFactoryStatisticsTests))] + [InlineData(nameof(MockFileSystem.FileInfo), true, + typeof(IFileInfo), typeof(FileInfoStatisticsTests))] + [InlineData(nameof(MockFileSystem.FileStream), false, + typeof(IFileStreamFactory), typeof(FileStreamFactoryStatisticsTests))] + [InlineData(nameof(MockFileSystem.FileStream), true, + typeof(FileSystemStream), typeof(FileStreamStatisticsTests))] + [InlineData(nameof(MockFileSystem.FileSystemWatcher), false, + typeof(IFileSystemWatcherFactory), typeof(FileSystemWatcherFactoryStatisticsTests))] + [InlineData(nameof(MockFileSystem.FileSystemWatcher), true, + typeof(IFileSystemWatcher), typeof(FileSystemWatcherStatisticsTests))] + [InlineData(nameof(MockFileSystem.Path), false, + typeof(IPath), typeof(PathStatisticsTests))] + public void ShouldHaveTestedAllFileSystemMethods(string className, bool requireInstance, + Type mockType, Type testType) + { + string result = CheckMethods(className, requireInstance, mockType, testType); + + result.Should().BeEmpty(); + } + + private static string CheckMethods(string className, bool requireInstance, + Type mockType, Type testType) + { + StringBuilder builder = new(); + + string FirstCharToUpperAsSpan(string input) + { + if (string.IsNullOrEmpty(input)) + { + return string.Empty; + } + + return + $"{input[0].ToString().ToUpper(CultureInfo.InvariantCulture)}{input.Substring(1)}"; + } + + string GetName(Type type, bool firstCharUpperCase) + { + if (type.Name == "Int32&") + { + return "OutInt"; + } + + if (type.IsGenericType) + { + int idx = type.Name.IndexOf("`", StringComparison.Ordinal); + if (idx > 0) + { + return + $"{type.Name.Substring(0, idx)}<{string.Join(",", type.GenericTypeArguments.Select(x => GetName(x, firstCharUpperCase)))}>"; + } + + return type.ToString(); + } + + if (firstCharUpperCase) + { + if (type == typeof(int)) + { + return "Int"; + } + + if (type == typeof(bool)) + { + return "Bool"; + } + } + else + { + if (type == typeof(int)) + { + return "int"; + } + + if (type == typeof(bool)) + { + return "bool"; + } + + if (type == typeof(byte)) + { + return "byte"; + } + + if (type == typeof(string)) + { + return "string"; + } + + if (type == typeof(byte[])) + { + return "byte[]"; + } + + if (type == typeof(string[])) + { + return "string[]"; + } + } + + return type.Name; + } + + string GetDefaultValue(Type type) + { + if (type == typeof(string)) + { + return "\"foo\""; + } + + if (type == typeof(int)) + { + return "42"; + } + + if (type == typeof(bool)) + { + return "true"; + } + + if (type == typeof(SearchOption)) + { + return "SearchOption.AllDirectories"; + } + + if (type == typeof(FileMode)) + { + return "FileMode.OpenOrCreate"; + } + + if (type == typeof(FileAccess)) + { + return "FileAccess.ReadWrite"; + } + + if (type == typeof(FileShare)) + { + return "FileShare.ReadWrite"; + } + + if (type == typeof(SearchOption)) + { + return "SearchOption.AllDirectories"; + } + + if (type == typeof(IEnumerable) || + type == typeof(string[])) + { + return "[\"foo\", \"bar\"]"; + } + + if (type == typeof(byte[])) + { + return "\"foo\"u8.ToArray()"; + } + + if (type == typeof(Encoding)) + { + return "Encoding.UTF8"; + } + + if (type == typeof(Stream)) + { + return "new MemoryStream()"; + } + + if (type == typeof(CancellationToken)) + { + return "CancellationToken.None"; + } + + return "new()"; + } + + foreach (MethodInfo methodInfo in + mockType.GetInterfaces() + .Where(i => i != typeof(IDisposable) && i != typeof(IAsyncDisposable)) + .SelectMany(i => i.GetMethods()) + .Concat(mockType.GetMethods(BindingFlags.DeclaredOnly | + BindingFlags.Public | + BindingFlags.Instance)) + .Where(m => m is { IsPublic: true, IsSpecialName: false }) + .OrderBy(m => m.Name) + .ThenBy(m => m.GetParameters().Length)) + { + if (methodInfo.GetCustomAttribute() != null) + { + continue; + } + + ParameterInfo[] parameters = methodInfo.GetParameters(); + + if (Test.IsNetFramework && + parameters.Any(p => p.ParameterType == typeof(SafeFileHandle))) + { + // SafeFileHandle cannot be instantiated on .NET Framework + continue; + } + + string expectedName = $"{methodInfo.Name}_{string.Join("_", methodInfo + .GetParameters() + .Select(x => FirstCharToUpperAsSpan(GetName(x.ParameterType, true) + .Replace("<", "") + .Replace(">", "") + .Replace("IEnumerablestring", "IEnumerableString") + .Replace("[]", "Array"))))}{(parameters.Length > 0 ? "_" : "")}ShouldRegisterCall"; + if (testType.GetMethod(expectedName) != null) + { + continue; + } + + bool isAsync = typeof(Task).IsAssignableFrom(methodInfo.ReturnType); + builder.AppendLine("\t[SkippableFact]"); + builder.Append(isAsync ? "\tpublic async Task " : "\tpublic void "); + builder.Append(expectedName); + builder.AppendLine("()"); + builder.AppendLine("\t{"); + builder.AppendLine("\t\tMockFileSystem sut = new();"); + foreach (ParameterInfo parameterInfo in methodInfo.GetParameters()) + { + builder.AppendLine( + $"\t\t{GetName(parameterInfo.ParameterType, false)} {parameterInfo.Name} = {GetDefaultValue(parameterInfo.ParameterType)};"); + } + + builder.AppendLine(); + builder.AppendLine( + $"\t\t{(isAsync ? "await " : "")}sut.{className}{(requireInstance ? ".New(\"foo\")" : "")}.{methodInfo.Name}({string.Join(", ", methodInfo.GetParameters().Select(p => p.Name))});"); + builder.AppendLine(); + builder.AppendLine( + $"\t\tsut.Statistics.{className}{(requireInstance ? "[\"foo\"]" : "")}.ShouldOnlyContain(nameof({mockType.Name}.{methodInfo.Name}){(parameters.Length > 0 ? ",\n\t\t" : "")}{string.Join(", ", methodInfo.GetParameters().Select(p => p.Name))});"); + builder.AppendLine("\t}"); + builder.AppendLine(); + } + + return builder.ToString(); + } +} diff --git a/Tests/Testably.Abstractions.Testing.Tests/TestHelpers/StatisticsTestHelpers.cs b/Tests/Testably.Abstractions.Testing.Tests/TestHelpers/StatisticsTestHelpers.cs new file mode 100644 index 000000000..1e50837f3 --- /dev/null +++ b/Tests/Testably.Abstractions.Testing.Tests/TestHelpers/StatisticsTestHelpers.cs @@ -0,0 +1,209 @@ +using System.Linq; +using Testably.Abstractions.Testing.Statistics; + +namespace Testably.Abstractions.Testing.Tests.TestHelpers; + +public static class StatisticsTestHelpers +{ + public static void ShouldOnlyContain(this IStatistics statistics, string name, + object?[] parameters, string because = "") + { + statistics.Methods.Count.Should().Be(1, because); + statistics.Methods.Values.Should() + .ContainSingle(c => c.Name == name && + c.Parameters.SequenceEqual(parameters), + because); + } + + public static void ShouldOnlyContain(this IStatistics statistics, string name) + { + statistics.Methods.Count.Should().Be(1); + statistics.Methods.Values.Should() + .ContainSingle(c => c.Name == name && + c.Parameters.Length == 0); + } + + public static void ShouldOnlyContain(this IStatistics statistics, string name, + T1 parameter1) + { + statistics.Methods.Count.Should().Be(1); + statistics.Methods.Values.Should() + .ContainSingle(c => c.Name == name && + c.Parameters.Length == 1 && + c.Parameters[0].Is(parameter1)); + } + + public static void ShouldOnlyContain(this IStatistics statistics, string name, + T1[] parameter1) + { + statistics.Methods.Count.Should().Be(1); + statistics.Methods.Values.Should() + .ContainSingle(c => c.Name == name && + c.Parameters.Length == 1 && + c.Parameters[0].Is(parameter1)); + } + +#if FEATURE_SPAN + public static void ShouldOnlyContain(this IStatistics statistics, string name, + ReadOnlySpan parameter1) + { + statistics.Methods.Count.Should().Be(1); + ParameterDescription parameter = statistics.Methods.Values.Should() + .ContainSingle(c => c.Name == name && + c.Parameters.Length == 1).Which.Parameters[0]; + parameter.Is(parameter1).Should().BeTrue(); + } + + public static void ShouldOnlyContain(this IStatistics statistics, string name, + ReadOnlySpan parameter1, ReadOnlySpan parameter2) + { + statistics.Methods.Count.Should().Be(1); + MethodStatistic? statistic = statistics.Methods.Values.Should() + .ContainSingle(c => c.Name == name && + c.Parameters.Length == 2).Which; + statistic.Parameters[0].Is(parameter1).Should().BeTrue(); + statistic.Parameters[1].Is(parameter2).Should().BeTrue(); + } + + public static void ShouldOnlyContain(this IStatistics statistics, string name, + ReadOnlySpan parameter1, ReadOnlySpan parameter2, ReadOnlySpan parameter3) + { + statistics.Methods.Count.Should().Be(1); + MethodStatistic? statistic = statistics.Methods.Values.Should() + .ContainSingle(c => c.Name == name && + c.Parameters.Length == 3).Which; + statistic.Parameters[0].Is(parameter1).Should().BeTrue(); + statistic.Parameters[1].Is(parameter2).Should().BeTrue(); + statistic.Parameters[2].Is(parameter3).Should().BeTrue(); + } + + public static void ShouldOnlyContain(this IStatistics statistics, string name, + ReadOnlySpan parameter1, ReadOnlySpan parameter2, ReadOnlySpan parameter3, ReadOnlySpan parameter4) + { + statistics.Methods.Count.Should().Be(1); + MethodStatistic? statistic = statistics.Methods.Values.Should() + .ContainSingle(c => c.Name == name && + c.Parameters.Length == 4).Which; + statistic.Parameters[0].Is(parameter1).Should().BeTrue(); + statistic.Parameters[1].Is(parameter2).Should().BeTrue(); + statistic.Parameters[2].Is(parameter3).Should().BeTrue(); + statistic.Parameters[3].Is(parameter4).Should().BeTrue(); + } + + public static void ShouldOnlyContain(this IStatistics statistics, string name, + ReadOnlySpan parameter1, ReadOnlySpan parameter2, T3 parameter3, T4 parameter4) + { + statistics.Methods.Count.Should().Be(1); + MethodStatistic? statistic = statistics.Methods.Values.Should() + .ContainSingle(c => c.Name == name && + c.Parameters.Length == 4).Which; + statistic.Parameters[0].Is(parameter1).Should().BeTrue(); + statistic.Parameters[1].Is(parameter2).Should().BeTrue(); + statistic.Parameters[2].Is(parameter3).Should().BeTrue(); + statistic.Parameters[3].Is(parameter4).Should().BeTrue(); + } + + public static void ShouldOnlyContain(this IStatistics statistics, string name, + ReadOnlySpan parameter1, ReadOnlySpan parameter2, Span parameter3, T4 parameter4) + { + statistics.Methods.Count.Should().Be(1); + MethodStatistic? statistic = statistics.Methods.Values.Should() + .ContainSingle(c => c.Name == name && + c.Parameters.Length == 4).Which; + statistic.Parameters[0].Is(parameter1).Should().BeTrue(); + statistic.Parameters[1].Is(parameter2).Should().BeTrue(); + statistic.Parameters[2].Is(parameter3).Should().BeTrue(); + statistic.Parameters[3].Is(parameter4).Should().BeTrue(); + } + + public static void ShouldOnlyContain(this IStatistics statistics, string name, + ReadOnlySpan parameter1, ReadOnlySpan parameter2, ReadOnlySpan parameter3, Span parameter4, T5 parameter5) + { + statistics.Methods.Count.Should().Be(1); + MethodStatistic? statistic = statistics.Methods.Values.Should() + .ContainSingle(c => c.Name == name && + c.Parameters.Length == 5).Which; + statistic.Parameters[0].Is(parameter1).Should().BeTrue(); + statistic.Parameters[1].Is(parameter2).Should().BeTrue(); + statistic.Parameters[2].Is(parameter3).Should().BeTrue(); + statistic.Parameters[3].Is(parameter4).Should().BeTrue(); + statistic.Parameters[4].Is(parameter5).Should().BeTrue(); + } + + public static void ShouldOnlyContain(this IStatistics statistics, string name, + Span parameter1) + { + statistics.Methods.Count.Should().Be(1); + ParameterDescription parameter = statistics.Methods.Values.Should() + .ContainSingle(c => c.Name == name && + c.Parameters.Length == 1).Which.Parameters[0]; + parameter.Is(parameter1).Should().BeTrue(); + } +#endif + + public static void ShouldOnlyContain(this IStatistics statistics, string name, + T1 parameter1, T2 parameter2) + { + statistics.Methods.Count.Should().Be(1); + statistics.Methods.Values.Should() + .ContainSingle(c => c.Name == name && + c.Parameters.Length == 2 && + c.Parameters[0].Is(parameter1) && + c.Parameters[1].Is(parameter2)); + } + + public static void ShouldOnlyContain(this IStatistics statistics, string name, + T1 parameter1, T2 parameter2, T3 parameter3) + { + statistics.Methods.Count.Should().Be(1); + statistics.Methods.Values.Should() + .ContainSingle(c => c.Name == name && + c.Parameters.Length == 3 && + c.Parameters[0].Is(parameter1) && + c.Parameters[1].Is(parameter2) && + c.Parameters[2].Is(parameter3)); + } + + public static void ShouldOnlyContain(this IStatistics statistics, string name, + T1 parameter1, T2 parameter2, T3 parameter3, T4 parameter4) + { + statistics.Methods.Count.Should().Be(1); + statistics.Methods.Values.Should() + .ContainSingle(c => c.Name == name && + c.Parameters.Length == 4 && + c.Parameters[0].Is(parameter1) && + c.Parameters[1].Is(parameter2) && + c.Parameters[2].Is(parameter3) && + c.Parameters[3].Is(parameter4)); + } + + public static void ShouldOnlyContain(this IStatistics statistics, + string name, T1 parameter1, T2 parameter2, T3 parameter3, T4 parameter4, T5 parameter5) + { + statistics.Methods.Count.Should().Be(1); + statistics.Methods.Values.Should() + .ContainSingle(c => c.Name == name && + c.Parameters.Length == 5 && + c.Parameters[0].Is(parameter1) && + c.Parameters[1].Is(parameter2) && + c.Parameters[2].Is(parameter3) && + c.Parameters[3].Is(parameter4) && + c.Parameters[4].Is(parameter5)); + } + + public static void ShouldOnlyContain(this IStatistics statistics, + string name, T1 parameter1, T2 parameter2, T3 parameter3, T4 parameter4, T5 parameter5, + T6 parameter6) + { + statistics.Methods.Count.Should().Be(1); + statistics.Methods.Values.Should() + .ContainSingle(c => c.Name == name && + c.Parameters.Length == 6 && + c.Parameters[0].Is(parameter1) && + c.Parameters[1].Is(parameter2) && + c.Parameters[2].Is(parameter3) && + c.Parameters[3].Is(parameter4) && + c.Parameters[4].Is(parameter5) && + c.Parameters[5].Is(parameter6)); + } +} diff --git a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/Tests.cs b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/Tests.cs index efa9461f2..43fbd9288 100644 --- a/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/Tests.cs +++ b/Tests/Testably.Abstractions.Tests/FileSystem/FileSystemWatcher/Tests.cs @@ -194,31 +194,31 @@ public object GetService(Type serviceType) private sealed class ContainerMock : IContainer { - /// + /// public void Dispose() { // Ignore any call in tests } - /// + /// public void Add(IComponent? component) { // Ignore any call in tests } - /// + /// public void Add(IComponent? component, string? name) { // Ignore any call in tests } - /// + /// public void Remove(IComponent? component) { // Ignore any call in tests } - /// + /// public ComponentCollection Components => throw new NotSupportedException(); }