Skip to content
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
26 changes: 18 additions & 8 deletions Docs/Nuget/AccessControl.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,24 @@
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=Testably_Testably.Abstractions&branch=main&metric=alert_status)](https://sonarcloud.io/summary/overall?id=Testably_Testably.Abstractions&branch=main)
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=Testably_Testably.Abstractions&branch=main&metric=coverage)](https://sonarcloud.io/summary/overall?id=Testably_Testably.Abstractions&branch=main)

ACL (access control list) extension methods for [Testably.Abstractions](https://github.com/Testably/Testably.Abstractions) using [System.IO.FileSystem.AccessControl](https://www.nuget.org/packages/System.IO.FileSystem.AccessControl/).
Implements the methods from [FileSystemAclExtensions](https://learn.microsoft.com/en-us/dotnet/api/system.io.filesystemaclextensions) on the `IFileSystem` interface.
ACL extensions for [`Testably.Abstractions`](https://www.nuget.org/packages/Testably.Abstractions) - adds the methods from [`System.IO.FileSystemAclExtensions`](https://learn.microsoft.com/en-us/dotnet/api/system.io.filesystemaclextensions) to `IFileSystem`, so production code that reads or writes ACLs works against both the real and the mocked file system.

```ps
dotnet add package Testably.Abstractions.AccessControl
```

**Full documentation: [docs.testably.org/Abstractions/companion-libraries/access-control](https://docs.testably.org/Abstractions/companion-libraries/access-control)**
Comment thread
vbreuss marked this conversation as resolved.

## Example
With this library loaded you can copy ACL settings from one directory to another.
```csharp
IFileSystem _fileSystem; // Set using DI

DirectorySecurity accessControl = _fileSystem.Directory.GetAccessControl("your-directory");
_fileSystem.Directory.SetAccessControl("another-directory", accessControl);
IFileSystem fileSystem; // injected

DirectorySecurity acl = fileSystem.Directory.GetAccessControl("data");
fileSystem.Directory.SetAccessControl("backup", acl);

fileSystem.File.WriteAllText("secret.txt", "x");
FileSecurity fileAcl = fileSystem.File.GetAccessControl("secret.txt");
```

The package adds `GetAccessControl` / `SetAccessControl` (plus the `DirectorySecurity` overloads of `CreateDirectory` and `DirectoryInfo.Create`) to `IDirectory`, `IDirectoryInfo`, `IFile`, `IFileInfo` and `FileSystemStream`. On `MockFileSystem` the security object is stored with the entry and returned unchanged on read.

> ⚠️ The ACL APIs are marked `[SupportedOSPlatform("windows")]`. Calling them on Linux or macOS throws `PlatformNotSupportedException`.
28 changes: 18 additions & 10 deletions Docs/Nuget/Compression.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,25 @@
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=Testably_Testably.Abstractions&branch=main&metric=alert_status)](https://sonarcloud.io/summary/overall?id=Testably_Testably.Abstractions&branch=main)
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=Testably_Testably.Abstractions&branch=main&metric=coverage)](https://sonarcloud.io/summary/overall?id=Testably_Testably.Abstractions&branch=main)

Compression extensions for [Testably.Abstractions](https://github.com/Testably/Testably.Abstractions) using [System.IO.Compression](https://www.nuget.org/packages/System.IO.Compression/).
Wraps the static methods from [ZipFile](https://learn.microsoft.com/en-us/dotnet/api/system.io.compression.zipfile) in an extension on `IFileSystem`.
Zip extensions for [`Testably.Abstractions`](https://www.nuget.org/packages/Testably.Abstractions) - adds the static methods from `System.IO.Compression.ZipFile` and `ZipArchive` to `IFileSystem`, so compression code can be tested against the in-memory `MockFileSystem`.

```ps
dotnet add package Testably.Abstractions.Compression
```

**Full documentation: [docs.testably.org/Abstractions/companion-libraries/compression](https://docs.testably.org/Abstractions/companion-libraries/compression)**
Comment thread
vbreuss marked this conversation as resolved.

## Example
Use the `ZipFile()` extension method to get access to `CreateFromDirectory`, `ExtractToDirectory` and `Open` methods:
```csharp
IFileSystem _fileSystem; // Set using DI

_fileSystem.ZipFile()
.CreateFromDirectory("your-directory", "your-file.zip");
IFileSystem fileSystem; // injected

_fileSystem.ZipFile()
.ExtractToDirectory("your-file.zip", "your-destination");
fileSystem.ZipFile()
.CreateFromDirectory("source", "out.zip");

fileSystem.ZipFile()
.ExtractToDirectory("out.zip", "destination");

using IZipArchive archive = fileSystem.ZipFile()
.Open("out.zip", ZipArchiveMode.Update);
```

All overloads from the .NET base class library (BCL) are present, including the async variants on .NET 10+. `fileSystem.ZipArchive().New(stream, mode)` returns an `IZipArchive` that wraps `ZipArchive`, with `IZipArchiveEntry` mirroring its BCL counterpart. On `RealFileSystem` every call forwards to the underlying BCL implementation; only `MockFileSystem` routes through the in-memory zip implementation.
18 changes: 18 additions & 0 deletions Docs/Nuget/FileSystemInterface.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Testably.Abstractions.FileSystem.Interface

[![Nuget](https://img.shields.io/nuget/v/Testably.Abstractions.FileSystem.Interface)](https://www.nuget.org/packages/Testably.Abstractions.FileSystem.Interface)
[![Build](https://github.com/Testably/Testably.Abstractions/actions/workflows/build.yml/badge.svg)](https://github.com/Testably/Testably.Abstractions/actions/workflows/build.yml)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=Testably_Testably.Abstractions&branch=main&metric=alert_status)](https://sonarcloud.io/summary/overall?id=Testably_Testably.Abstractions&branch=main)
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=Testably_Testably.Abstractions&branch=main&metric=coverage)](https://sonarcloud.io/summary/overall?id=Testably_Testably.Abstractions&branch=main)

File-system interface for [`Testably.Abstractions`](https://www.nuget.org/packages/Testably.Abstractions) - defines `IFileSystem` (and its sub-types) in the `System.IO.Abstractions` namespace, making it source-compatible with [`TestableIO.System.IO.Abstractions`](https://github.com/TestableIO/System.IO.Abstractions).

```ps
dotnet add package Testably.Abstractions.FileSystem.Interface
```

**Full documentation: [docs.testably.org/Abstractions](https://docs.testably.org/Abstractions/)**
Comment thread
vbreuss marked this conversation as resolved.

> Most users install [`Testably.Abstractions`](https://www.nuget.org/packages/Testably.Abstractions) (production) and [`Testably.Abstractions.Testing`](https://www.nuget.org/packages/Testably.Abstractions.Testing) (tests) instead - both pull this package in transitively. Reach for this package directly only if you need just the file-system interface, without `ITimeSystem` / `IRandomSystem`.

`IFileSystem` mirrors `System.IO` (`File`, `Directory`, `FileInfo`, `DirectoryInfo`, `FileStream`, `Path`, `DriveInfo`, `FileSystemWatcher`, `FileVersionInfo`). Constructors are exposed as factory methods (e.g. `fileSystem.FileInfo.New(path)`).
20 changes: 12 additions & 8 deletions Docs/Nuget/Interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=Testably_Testably.Abstractions&branch=main&metric=alert_status)](https://sonarcloud.io/summary/overall?id=Testably_Testably.Abstractions&branch=main)
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=Testably_Testably.Abstractions&branch=main&metric=coverage)](https://sonarcloud.io/summary/overall?id=Testably_Testably.Abstractions&branch=main)

This library contains the abstraction interfaces for [Testably.Abstractions](https://github.com/Testably/Testably.Abstractions), which allow replacing system dependencies:
Interfaces for [`Testably.Abstractions`](https://www.nuget.org/packages/Testably.Abstractions) - `IFileSystem`, `ITimeSystem` and `IRandomSystem` for abstracting the static parts of the .NET base class library (BCL).

- The `IFileSystem` interface abstracts away all I/O-related functionality from the `System.IO` namespace:
Static methods are directly implemented on the `IFileSystem` interface.
Constructors are implemented as factory methods, e.g. `IFileSystem.FileInfo.New(string)` instead of `new FileInfo(string)`.
- The `ITimeSystem` interface abstracts away time-related functionality:
`DateTime` methods give access to the current time, `Thread` allows replacing `Thread.Sleep` and `Task` allows replacing `Task.Delay`.
- The `IRandomSystem` interface abstracts away functionality related to randomness:
`Random` methods implement a thread-safe Shared instance also under .NET Framework and `Guid` methods allow creating new GUIDs.
```ps
dotnet add package Testably.Abstractions.Interface
```

**Full documentation: [docs.testably.org/Abstractions](https://docs.testably.org/Abstractions/)**
Comment thread
vbreuss marked this conversation as resolved.

> Most users install [`Testably.Abstractions`](https://www.nuget.org/packages/Testably.Abstractions) (production) and [`Testably.Abstractions.Testing`](https://www.nuget.org/packages/Testably.Abstractions.Testing) (tests) instead - both pull these interfaces in transitively.

- `IFileSystem` mirrors `System.IO` (`File`, `Directory`, `FileInfo`, `DirectoryInfo`, `FileStream`, `Path`, `DriveInfo`, `FileSystemWatcher`, `FileVersionInfo`). Constructors are exposed as factory methods (e.g. `fileSystem.FileInfo.New(path)`). Lives in the `System.IO.Abstractions` namespace, so it is source-compatible with [`TestableIO.System.IO.Abstractions`](https://github.com/TestableIO/System.IO.Abstractions).
- `ITimeSystem` covers `DateTime`, `Stopwatch`, `Task.Delay`, `Thread.Sleep`, `Timer` and (on supported targets) `PeriodicTimer`.
- `IRandomSystem` covers `Random` (with a thread-safe `Shared` instance on all targets) and `Guid`.
58 changes: 12 additions & 46 deletions Docs/Nuget/Testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,53 +5,19 @@
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=Testably_Testably.Abstractions&branch=main&metric=alert_status)](https://sonarcloud.io/summary/overall?id=Testably_Testably.Abstractions&branch=main)
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=Testably_Testably.Abstractions&branch=main&metric=coverage)](https://sonarcloud.io/summary/overall?id=Testably_Testably.Abstractions&branch=main)

This library contains the testing helpers for [Testably.Abstractions](https://github.com/Testably/Testably.Abstractions).
Testing helpers for [`Testably.Abstractions`](https://www.nuget.org/packages/Testably.Abstractions) - in-memory `MockFileSystem`, `MockTimeSystem` and `MockRandomSystem` that behave identically to the .NET base class library (BCL) but stay deterministic and never touch disk, the system clock or randomness.

## MockFileSystem

### Initialization
Shows how to initialize the file system:
```csharp
fileSystem.InitializeIn("current-directory")
.WithASubdirectory()
.WithSubdirectory("foo").Initialized(s => s
.WithAFile())
.WithFile("bar.txt");
```ps
dotnet add package Testably.Abstractions.Testing
```
Initialize the file system in "current-directory" with
- a randomly named directory
- a directory named "foo" which contains a randomly named file
- a file named "bar.txt"

In order to use multiple drives on Windows (or network shares) you have to first register them:
```csharp
fileSystem.WithDrive(@"D:", drive => drive.SetTotalSize(1024));
```
The optional configuration allows limiting the maximum available space on the drive.
**Full documentation: [docs.testably.org/Abstractions](https://docs.testably.org/Abstractions/)**
Comment thread
vbreuss marked this conversation as resolved.

The test suite runs every assertion against both the real and the mocked file system, so the mock behaves identically to the BCL. Highlights:

### Events
All changes in the file system trigger certain events. All events can be
- _intercepted_, before they occur (and e.g. an exception thrown to prevent the event from completing) on the `Intercept` property:
```csharp
MockFileSystem fileSystem = new();
fileSystem.Intercept.Creating(FileSystemTypes.File,
_ => throw new Exception("my custom exception"));
```
- _notified_, after they occured to allow a test to react to changes on the `MockFileSystem.Notify` property:
These methods return an awaitable object that
- Removes the notification on dispose
- Provides a blocking mechanism until the notification happens
```csharp
MockFileSystem fileSystem = new();
fileSystem.Notify
.OnCreated(FileSystemTypes.File, _ =>
{
// Do something
})
.ExecuteWhileWaiting(() =>
{
// This will trigger the callback
fileSystem.File.Create("some-file.txt");
})
.Wait();
```
- **MockFileSystem** - fluent `Initialize()` API, multiple drives with size limits, `FileSystemWatcher`, `SafeFileHandle`, file-version metadata and unix file modes.
- **Cross-platform simulation** - run a Linux, macOS or Windows file system regardless of the host via `new MockFileSystem(o => o.SimulatingOperatingSystem(SimulationMode.Linux))`.
- **Intercept and Notify** - inject exceptions before a file-system or time-system operation completes, or react to them after the fact (including replay of events that fired before the subscription).
Comment thread
vbreuss marked this conversation as resolved.
- **MockTimeSystem** - control `DateTime.Now`, advance time manually or via auto-advance, mock `Timer`/`PeriodicTimer` with persistent execution counters.
- **MockRandomSystem** - seed `Random` and `Guid.NewGuid()` for reproducible tests.
- **Statistics** - inspect how the system-under-test used each abstraction (`fileSystem.Statistics`).
2 changes: 1 addition & 1 deletion Docs/pages/docs/index.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Testably.Abstractions
slug: /abstractions/
slug: /Abstractions/
sidebar_position: -1
---

Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=Testably_Testably.Abstractions&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=Testably_Testably.Abstractions)
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=Testably_Testably.Abstractions&metric=coverage)](https://sonarcloud.io/summary/new_code?id=Testably_Testably.Abstractions)

Injectable abstractions for the static parts of the .NET BCL - file system, time and randomness - with feature-complete in-memory mocks for tests.
Injectable abstractions for the static parts of the .NET base class library (BCL) - file system, time and randomness - with feature-complete in-memory mocks for tests.

**📖 Full documentation: [docs.testably.org](https://docs.testably.org)**
**📖 Full documentation: [docs.testably.org/Abstractions](https://docs.testably.org/Abstractions/)**
Comment thread
vbreuss marked this conversation as resolved.

## Quick example

Expand Down Expand Up @@ -44,7 +44,7 @@ dotnet add package Testably.Abstractions
dotnet add package Testably.Abstractions.Testing
```

Then register the implementations in your DI container - see [Getting Started](https://docs.testably.org/docs/getting-started).
Then register the implementations in your DI container - see [Getting Started](https://docs.testably.org/Abstractions/getting-started).

## Packages

Expand All @@ -57,7 +57,7 @@ Then register the implementations in your DI container - see [Getting Started](h

## Already on TestableIO?

`Testably.Abstractions` shares the `IFileSystem` interface with [TestableIO.System.IO.Abstractions](https://github.com/TestableIO/System.IO.Abstractions), so production code stays untouched. See the [migration guide](https://docs.testably.org/docs/migration-from-testableio).
`Testably.Abstractions` shares the `IFileSystem` interface with [TestableIO.System.IO.Abstractions](https://github.com/TestableIO/System.IO.Abstractions), so production code stays untouched. See the [migration guide](https://docs.testably.org/Abstractions/migration-from-testableio).

## Contributing

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
<PropertyGroup>
<RootNamespace>System.IO.Abstractions</RootNamespace>
<Description>A set of abstractions to help make file system interactions testable.</Description>
<PackageReadmeFile>Docs/Interface.md</PackageReadmeFile>
<PackageReadmeFile>Docs/FileSystemInterface.md</PackageReadmeFile>
</PropertyGroup>

<ItemGroup>
<None Include="$(SolutionDir)Docs/Nuget/Interface.md" Pack="true" PackagePath="/Docs/" Link="Docs\Interface.md" />
<None Include="$(SolutionDir)Docs/Nuget/FileSystemInterface.md" Pack="true" PackagePath="/Docs/" Link="Docs\FileSystemInterface.md" />
</ItemGroup>

<ItemGroup>
Expand Down
Loading