Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow simulating other operating systems #576

Merged
merged 5 commits into from
Apr 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Feature.Flags.props
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<IS_NET8_OR_HIGHER Condition="'$(TargetFramework)' == 'net8.0'">1</IS_NET8_OR_HIGHER>

<DefineConstants Condition="'$(TargetFramework)' == 'net48' OR '$(TargetFramework)' == 'netstandard2.0'">$(DefineConstants);NETFRAMEWORK</DefineConstants>
<DefineConstants Condition="'$(IS_NET21_OR_HIGHER)' == '1'">$(DefineConstants);CAN_SIMULATE_OTHER_OS</DefineConstants>
<DefineConstants Condition="'$(IS_NET21_OR_HIGHER)' == '1'">$(DefineConstants);FEATURE_FILESYSTEM_ASYNC</DefineConstants>
<DefineConstants Condition="'$(IS_NET21_OR_HIGHER)' == '1'">$(DefineConstants);FEATURE_FILESYSTEM_ENUMERATION_OPTIONS</DefineConstants>
<DefineConstants Condition="'$(IS_NET21_OR_HIGHER)' == '1'">$(DefineConstants);FEATURE_PATH_JOIN</DefineConstants>
Expand Down
21 changes: 20 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ The testing helper also supports advanced scenarios like

The companion projects [Testably.Abstractions.Compression](https://www.nuget.org/packages/Testably.Abstractions.Compression) and [Testably.Abstractions.AccessControl](https://www.nuget.org/packages/Testably.Abstractions.AccessControl) allow working with [Zip-Files](Examples/ZipFile/README.md) and [Access Control Lists](Examples/AccessControlLists/README.md) respectively.

As the test suite runs both against the mocked and the real file system, the behaviour between the two is identical.
As the test suite runs both against the mocked and the real file system, the behaviour between the two is identical and it also allows [simulating the file system on other operating systems](#simulating-other-operating-systems) (Linux, MacOS and Windows).

In addition, the following interfaces are defined:
- The `ITimeSystem` interface abstracts away time-related functionality:
Expand Down Expand Up @@ -111,6 +111,25 @@ fileSystem.Initialize()
.WithFile("foo.txt").Which(f => f.HasStringContent("some file content"));
```

### Simulating other operating systems

The `MockFileSystem` can also simulate other operating systems than the one it is currently running on. This can be achieved, by providing the corresponding `SimulationMode` in the constructor:

```csharp
var linuxFileSystem = new MockFileSystem(o => o.SimulatingOperatingSystem(SimulationMode.Linux));
// The `linuxFileSystem` now behaves like a Linux file system even under Windows:
// - case-sensitive
// - slash as directory separator

var windowsFileSystem = new MockFileSystem(o => o.SimulatingOperatingSystem(SimulationMode.Windows));
// The `windowsFileSystem` now behaves like a Windows file system even under Linux or MacOS:
// - multiple drives
// - case-insensitive
// - backslash as directory separator
```

By running all tests against the real file system and the simulated under Linux, MacOS and Windows, the behaviour is consistent between the native and simulated mock file systems.

### Drive management
```csharp
var fileSystem = new MockFileSystem();
Expand Down
3 changes: 3 additions & 0 deletions Source/Testably.Abstractions.Testing/Helpers/Execute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ internal partial class Execute
/// </summary>
public StringComparison StringComparisonMode { get; }

#if !CAN_SIMULATE_OTHER_OS
[Obsolete("Simulating other operating systems is not supported on .NET Framework")]
#endif
internal Execute(MockFileSystem fileSystem, SimulationMode simulationMode)
{
IsLinux = simulationMode == SimulationMode.Linux;
Expand Down
33 changes: 26 additions & 7 deletions Source/Testably.Abstractions.Testing/MockFileSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,18 +97,29 @@ public MockFileSystem() : this(_ => { }) { }
/// <summary>
/// Initializes the <see cref="MockFileSystem" /> with the <paramref name="initializationCallback" />.
/// </summary>
internal MockFileSystem(Action<Initialization> initializationCallback)
public MockFileSystem(Action<Initialization> initializationCallback)
{
Initialization initialization = new();
initializationCallback(initialization);

SimulationMode = initialization.SimulationMode;
#if CAN_SIMULATE_OTHER_OS
Execute = SimulationMode == SimulationMode.Native
? new Execute(this)
: new Execute(this, SimulationMode);
#else
if (SimulationMode != SimulationMode.Native)
{
throw new NotSupportedException(
"Simulating other operating systems is not supported on .NET Framework");
}

Execute = new Execute(this);
#endif
StatisticsRegistration = new FileSystemStatistics(this);
using IDisposable release = StatisticsRegistration.Ignore();
RandomSystem = new MockRandomSystem(initialization.RandomProvider ?? RandomProvider.Default());
RandomSystem =
new MockRandomSystem(initialization.RandomProvider ?? RandomProvider.Default());
TimeSystem = new MockTimeSystem(TimeProvider.Now());
_pathMock = new PathMock(this);
_storage = new InMemoryStorage(this);
Expand Down Expand Up @@ -225,7 +236,7 @@ private void InitializeFileSystem(Initialization initialization)
/// <summary>
/// The initialization options for the <see cref="MockFileSystem" />.
/// </summary>
internal class Initialization
public class Initialization
{
/// <summary>
/// The current directory.
Expand All @@ -242,10 +253,18 @@ internal class Initialization
/// </summary>
internal SimulationMode SimulationMode { get; private set; } = SimulationMode.Native;

internal Initialization()
{
// Avoid public constructor
}

/// <summary>
/// Specify the operating system that should be simulated.
/// </summary>
internal Initialization SimulatingOperatingSystem(SimulationMode simulationMode)
#if !CAN_SIMULATE_OTHER_OS
[Obsolete("Simulating other operating systems is not supported on .NET Framework")]
#endif
public Initialization SimulatingOperatingSystem(SimulationMode simulationMode)
{
SimulationMode = simulationMode;
return this;
Expand All @@ -254,7 +273,7 @@ internal Initialization SimulatingOperatingSystem(SimulationMode simulationMode)
/// <summary>
/// Use the provided <paramref name="path" /> as current directory.
/// </summary>
internal Initialization UseCurrentDirectory(string path)
public Initialization UseCurrentDirectory(string path)
{
CurrentDirectory = path;
return this;
Expand All @@ -263,7 +282,7 @@ internal Initialization UseCurrentDirectory(string path)
/// <summary>
/// Use <see cref="Directory.GetCurrentDirectory()" /> as current directory.
/// </summary>
internal Initialization UseCurrentDirectory()
public Initialization UseCurrentDirectory()
{
CurrentDirectory = System.IO.Directory.GetCurrentDirectory();
return this;
Expand All @@ -272,7 +291,7 @@ internal Initialization UseCurrentDirectory()
/// <summary>
/// Use the given <paramref name="randomProvider" /> for the <see cref="RandomSystem" />.
/// </summary>
internal Initialization UseRandomProvider(IRandomProvider randomProvider)
public Initialization UseRandomProvider(IRandomProvider randomProvider)
{
RandomProvider = randomProvider;
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
<_Parameter1>Testably.Abstractions.Testing.Tests</_Parameter1>
</AssemblyAttribute>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
<_Parameter1>Testably.Abstractions.Tests</_Parameter1>
</AssemblyAttribute>
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
[assembly: System.CLSCompliant(true)]
[assembly: System.Reflection.AssemblyMetadata("RepositoryUrl", "https://github.com/Testably/Testably.Abstractions.git")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Testably.Abstractions.Testing.Tests")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Testably.Abstractions.Tests")]
[assembly: System.Runtime.Versioning.TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName=".NET 6.0")]
namespace Testably.Abstractions.Testing.FileSystem
{
Expand Down Expand Up @@ -94,6 +93,7 @@ namespace Testably.Abstractions.Testing
public sealed class MockFileSystem : System.IO.Abstractions.IFileSystem
{
public MockFileSystem() { }
public MockFileSystem(System.Action<Testably.Abstractions.Testing.MockFileSystem.Initialization> initializationCallback) { }
public System.IO.Abstractions.IDirectory Directory { get; }
public System.IO.Abstractions.IDirectoryInfoFactory DirectoryInfo { get; }
public System.IO.Abstractions.IDriveInfoFactory DriveInfo { get; }
Expand All @@ -112,6 +112,13 @@ namespace Testably.Abstractions.Testing
public Testably.Abstractions.Testing.MockFileSystem WithAccessControlStrategy(Testably.Abstractions.Testing.FileSystem.IAccessControlStrategy accessControlStrategy) { }
public Testably.Abstractions.Testing.MockFileSystem WithDrive(string? drive, System.Action<Testably.Abstractions.Testing.Storage.IStorageDrive>? driveCallback = null) { }
public Testably.Abstractions.Testing.MockFileSystem WithSafeFileHandleStrategy(Testably.Abstractions.Testing.FileSystem.ISafeFileHandleStrategy safeFileHandleStrategy) { }
public class Initialization
{
public Testably.Abstractions.Testing.MockFileSystem.Initialization SimulatingOperatingSystem(Testably.Abstractions.Testing.SimulationMode simulationMode) { }
public Testably.Abstractions.Testing.MockFileSystem.Initialization UseCurrentDirectory() { }
public Testably.Abstractions.Testing.MockFileSystem.Initialization UseCurrentDirectory(string path) { }
public Testably.Abstractions.Testing.MockFileSystem.Initialization UseRandomProvider(Testably.Abstractions.Testing.RandomSystem.IRandomProvider randomProvider) { }
}
}
public static class MockFileSystemExtensions
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
[assembly: System.CLSCompliant(true)]
[assembly: System.Reflection.AssemblyMetadata("RepositoryUrl", "https://github.com/Testably/Testably.Abstractions.git")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Testably.Abstractions.Testing.Tests")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Testably.Abstractions.Tests")]
[assembly: System.Runtime.Versioning.TargetFramework(".NETCoreApp,Version=v7.0", FrameworkDisplayName=".NET 7.0")]
namespace Testably.Abstractions.Testing.FileSystem
{
Expand Down Expand Up @@ -94,6 +93,7 @@ namespace Testably.Abstractions.Testing
public sealed class MockFileSystem : System.IO.Abstractions.IFileSystem
{
public MockFileSystem() { }
public MockFileSystem(System.Action<Testably.Abstractions.Testing.MockFileSystem.Initialization> initializationCallback) { }
public System.IO.Abstractions.IDirectory Directory { get; }
public System.IO.Abstractions.IDirectoryInfoFactory DirectoryInfo { get; }
public System.IO.Abstractions.IDriveInfoFactory DriveInfo { get; }
Expand All @@ -112,6 +112,13 @@ namespace Testably.Abstractions.Testing
public Testably.Abstractions.Testing.MockFileSystem WithAccessControlStrategy(Testably.Abstractions.Testing.FileSystem.IAccessControlStrategy accessControlStrategy) { }
public Testably.Abstractions.Testing.MockFileSystem WithDrive(string? drive, System.Action<Testably.Abstractions.Testing.Storage.IStorageDrive>? driveCallback = null) { }
public Testably.Abstractions.Testing.MockFileSystem WithSafeFileHandleStrategy(Testably.Abstractions.Testing.FileSystem.ISafeFileHandleStrategy safeFileHandleStrategy) { }
public class Initialization
{
public Testably.Abstractions.Testing.MockFileSystem.Initialization SimulatingOperatingSystem(Testably.Abstractions.Testing.SimulationMode simulationMode) { }
public Testably.Abstractions.Testing.MockFileSystem.Initialization UseCurrentDirectory() { }
public Testably.Abstractions.Testing.MockFileSystem.Initialization UseCurrentDirectory(string path) { }
public Testably.Abstractions.Testing.MockFileSystem.Initialization UseRandomProvider(Testably.Abstractions.Testing.RandomSystem.IRandomProvider randomProvider) { }
}
}
public static class MockFileSystemExtensions
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
[assembly: System.CLSCompliant(true)]
[assembly: System.Reflection.AssemblyMetadata("RepositoryUrl", "https://github.com/Testably/Testably.Abstractions.git")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Testably.Abstractions.Testing.Tests")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Testably.Abstractions.Tests")]
[assembly: System.Runtime.Versioning.TargetFramework(".NETCoreApp,Version=v8.0", FrameworkDisplayName=".NET 8.0")]
namespace Testably.Abstractions.Testing.FileSystem
{
Expand Down Expand Up @@ -94,6 +93,7 @@ namespace Testably.Abstractions.Testing
public sealed class MockFileSystem : System.IO.Abstractions.IFileSystem
{
public MockFileSystem() { }
public MockFileSystem(System.Action<Testably.Abstractions.Testing.MockFileSystem.Initialization> initializationCallback) { }
public System.IO.Abstractions.IDirectory Directory { get; }
public System.IO.Abstractions.IDirectoryInfoFactory DirectoryInfo { get; }
public System.IO.Abstractions.IDriveInfoFactory DriveInfo { get; }
Expand All @@ -112,6 +112,13 @@ namespace Testably.Abstractions.Testing
public Testably.Abstractions.Testing.MockFileSystem WithAccessControlStrategy(Testably.Abstractions.Testing.FileSystem.IAccessControlStrategy accessControlStrategy) { }
public Testably.Abstractions.Testing.MockFileSystem WithDrive(string? drive, System.Action<Testably.Abstractions.Testing.Storage.IStorageDrive>? driveCallback = null) { }
public Testably.Abstractions.Testing.MockFileSystem WithSafeFileHandleStrategy(Testably.Abstractions.Testing.FileSystem.ISafeFileHandleStrategy safeFileHandleStrategy) { }
public class Initialization
{
public Testably.Abstractions.Testing.MockFileSystem.Initialization SimulatingOperatingSystem(Testably.Abstractions.Testing.SimulationMode simulationMode) { }
public Testably.Abstractions.Testing.MockFileSystem.Initialization UseCurrentDirectory() { }
public Testably.Abstractions.Testing.MockFileSystem.Initialization UseCurrentDirectory(string path) { }
public Testably.Abstractions.Testing.MockFileSystem.Initialization UseRandomProvider(Testably.Abstractions.Testing.RandomSystem.IRandomProvider randomProvider) { }
}
}
public static class MockFileSystemExtensions
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
[assembly: System.CLSCompliant(true)]
[assembly: System.Reflection.AssemblyMetadata("RepositoryUrl", "https://github.com/Testably/Testably.Abstractions.git")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Testably.Abstractions.Testing.Tests")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Testably.Abstractions.Tests")]
[assembly: System.Runtime.Versioning.TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName=".NET Standard 2.0")]
namespace Testably.Abstractions.Testing.FileSystem
{
Expand Down Expand Up @@ -92,6 +91,7 @@ namespace Testably.Abstractions.Testing
public sealed class MockFileSystem : System.IO.Abstractions.IFileSystem
{
public MockFileSystem() { }
public MockFileSystem(System.Action<Testably.Abstractions.Testing.MockFileSystem.Initialization> initializationCallback) { }
public System.IO.Abstractions.IDirectory Directory { get; }
public System.IO.Abstractions.IDirectoryInfoFactory DirectoryInfo { get; }
public System.IO.Abstractions.IDriveInfoFactory DriveInfo { get; }
Expand All @@ -110,6 +110,14 @@ namespace Testably.Abstractions.Testing
public Testably.Abstractions.Testing.MockFileSystem WithAccessControlStrategy(Testably.Abstractions.Testing.FileSystem.IAccessControlStrategy accessControlStrategy) { }
public Testably.Abstractions.Testing.MockFileSystem WithDrive(string? drive, System.Action<Testably.Abstractions.Testing.Storage.IStorageDrive>? driveCallback = null) { }
public Testably.Abstractions.Testing.MockFileSystem WithSafeFileHandleStrategy(Testably.Abstractions.Testing.FileSystem.ISafeFileHandleStrategy safeFileHandleStrategy) { }
public class Initialization
{
[System.Obsolete("Simulating other operating systems is not supported on .NET Framework")]
public Testably.Abstractions.Testing.MockFileSystem.Initialization SimulatingOperatingSystem(Testably.Abstractions.Testing.SimulationMode simulationMode) { }
public Testably.Abstractions.Testing.MockFileSystem.Initialization UseCurrentDirectory() { }
public Testably.Abstractions.Testing.MockFileSystem.Initialization UseCurrentDirectory(string path) { }
public Testably.Abstractions.Testing.MockFileSystem.Initialization UseRandomProvider(Testably.Abstractions.Testing.RandomSystem.IRandomProvider randomProvider) { }
}
}
public static class MockFileSystemExtensions
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
[assembly: System.CLSCompliant(true)]
[assembly: System.Reflection.AssemblyMetadata("RepositoryUrl", "https://github.com/Testably/Testably.Abstractions.git")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Testably.Abstractions.Testing.Tests")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Testably.Abstractions.Tests")]
[assembly: System.Runtime.Versioning.TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName=".NET Standard 2.1")]
namespace Testably.Abstractions.Testing.FileSystem
{
Expand Down Expand Up @@ -92,6 +91,7 @@ namespace Testably.Abstractions.Testing
public sealed class MockFileSystem : System.IO.Abstractions.IFileSystem
{
public MockFileSystem() { }
public MockFileSystem(System.Action<Testably.Abstractions.Testing.MockFileSystem.Initialization> initializationCallback) { }
public System.IO.Abstractions.IDirectory Directory { get; }
public System.IO.Abstractions.IDirectoryInfoFactory DirectoryInfo { get; }
public System.IO.Abstractions.IDriveInfoFactory DriveInfo { get; }
Expand All @@ -110,6 +110,13 @@ namespace Testably.Abstractions.Testing
public Testably.Abstractions.Testing.MockFileSystem WithAccessControlStrategy(Testably.Abstractions.Testing.FileSystem.IAccessControlStrategy accessControlStrategy) { }
public Testably.Abstractions.Testing.MockFileSystem WithDrive(string? drive, System.Action<Testably.Abstractions.Testing.Storage.IStorageDrive>? driveCallback = null) { }
public Testably.Abstractions.Testing.MockFileSystem WithSafeFileHandleStrategy(Testably.Abstractions.Testing.FileSystem.ISafeFileHandleStrategy safeFileHandleStrategy) { }
public class Initialization
{
public Testably.Abstractions.Testing.MockFileSystem.Initialization SimulatingOperatingSystem(Testably.Abstractions.Testing.SimulationMode simulationMode) { }
public Testably.Abstractions.Testing.MockFileSystem.Initialization UseCurrentDirectory() { }
public Testably.Abstractions.Testing.MockFileSystem.Initialization UseCurrentDirectory(string path) { }
public Testably.Abstractions.Testing.MockFileSystem.Initialization UseRandomProvider(Testably.Abstractions.Testing.RandomSystem.IRandomProvider randomProvider) { }
}
}
public static class MockFileSystemExtensions
{
Expand Down
Loading
Loading