Repository: System-IO-Abstractions/System.IO.Abstractions
Branch: main
Commit: a091ac7f9655
Files: 268
Total size: 1.7 MB
Directory structure:
gitextract_mxqetcdw/
├── .devcontainer/
│ └── devcontainer.json
├── .editorconfig
├── .gitattributes
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ └── feature_request.md
│ ├── copilot-instructions.md
│ ├── mergify.yml
│ └── workflows/
│ ├── build.yml
│ └── ci.yml
├── .gitignore
├── .idea/
│ └── .idea.System.IO.Abstractions/
│ └── .idea/
│ ├── .gitignore
│ ├── encodings.xml
│ ├── indexLayout.xml
│ └── vcs.xml
├── .nuke/
│ ├── build.schema.json
│ └── parameters.json
├── .remarkrc.yaml
├── .vscode/
│ └── extensions.json
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Directory.Build.props
├── Directory.Packages.props
├── LICENSE
├── Pipeline/
│ ├── .editorconfig
│ ├── Build.ApiChecks.cs
│ ├── Build.CodeAnalysis.cs
│ ├── Build.CodeCoverage.cs
│ ├── Build.Compile.cs
│ ├── Build.Pack.cs
│ ├── Build.UnitTest.cs
│ ├── Build.cs
│ ├── Build.csproj
│ ├── Build.csproj.DotSettings
│ ├── BuildExtensions.cs
│ ├── Configuration.cs
│ ├── Directory.Build.props
│ └── Directory.Build.targets
├── README.md
├── StrongName.snk
├── System.IO.Abstractions.slnx
├── benchmarks/
│ └── TestableIO.System.IO.Abstractions.Benchmarks/
│ ├── FileSystemAbstractionBenchmarks.cs
│ ├── MockFileSystemBenchmarks.cs
│ ├── Program.cs
│ ├── Properties/
│ │ └── launchSettings.json
│ ├── Support/
│ │ ├── DirectorySupport.cs
│ │ ├── DirectorySupportStatic.cs
│ │ ├── FileSupport.cs
│ │ └── FileSupportStatic.cs
│ └── TestableIO.System.IO.Abstractions.Benchmarks.csproj
├── build.cmd
├── build.ps1
├── build.sh
├── global.json
├── nuget.config
├── renovate.json5
├── src/
│ ├── Directory.Build.props
│ ├── System.IO.Abstractions/
│ │ └── System.IO.Abstractions.csproj
│ ├── System.IO.Abstractions.TestingHelpers/
│ │ └── System.IO.Abstractions.TestingHelpers.csproj
│ ├── TestableIO.System.IO.Abstractions/
│ │ ├── AssemblyRedirects.cs
│ │ └── TestableIO.System.IO.Abstractions.csproj
│ ├── TestableIO.System.IO.Abstractions.TestingHelpers/
│ │ ├── CommonExceptions.cs
│ │ ├── FileHandles.cs
│ │ ├── IMockFileDataAccessor.cs
│ │ ├── MockDirectory.cs
│ │ ├── MockDirectoryData.cs
│ │ ├── MockDirectoryInfo.cs
│ │ ├── MockDirectoryInfoFactory.cs
│ │ ├── MockDriveData.cs
│ │ ├── MockDriveInfo.cs
│ │ ├── MockDriveInfoFactory.cs
│ │ ├── MockFile.Async.cs
│ │ ├── MockFile.cs
│ │ ├── MockFileData.cs
│ │ ├── MockFileInfo.cs
│ │ ├── MockFileInfoFactory.cs
│ │ ├── MockFileStream.cs
│ │ ├── MockFileStreamFactory.cs
│ │ ├── MockFileSystem.cs
│ │ ├── MockFileSystemOptions.cs
│ │ ├── MockFileSystemWatcherFactory.cs
│ │ ├── MockFileVersionInfo.cs
│ │ ├── MockFileVersionInfoFactory.cs
│ │ ├── MockPath.cs
│ │ ├── MockUnixSupport.cs
│ │ ├── PathVerifier.cs
│ │ ├── Polyfills/
│ │ │ └── SupportedOSPlatformAttribute.cs
│ │ ├── ProductVersionParser.cs
│ │ ├── Properties/
│ │ │ └── Resources.resx
│ │ ├── StringExtensions.cs
│ │ ├── StringOperations.cs
│ │ ├── StringResources.cs
│ │ ├── TestableIO.System.IO.Abstractions.TestingHelpers.csproj
│ │ └── TimeAdjustments.cs
│ └── TestableIO.System.IO.Abstractions.Wrappers/
│ ├── Converters.cs
│ ├── DirectoryAclExtensions.cs
│ ├── DirectoryBase.cs
│ ├── DirectoryInfoAclExtensions.cs
│ ├── DirectoryInfoBase.cs
│ ├── DirectoryInfoFactory.cs
│ ├── DirectoryInfoWrapper.cs
│ ├── DirectoryWrapper.cs
│ ├── DriveInfoBase.cs
│ ├── DriveInfoFactory.cs
│ ├── DriveInfoWrapper.cs
│ ├── FileAclExtensions.cs
│ ├── FileBase.Async.cs
│ ├── FileBase.cs
│ ├── FileInfoAclExtensions.cs
│ ├── FileInfoBase.cs
│ ├── FileInfoFactory.cs
│ ├── FileInfoWrapper.cs
│ ├── FileStreamAclExtensions.cs
│ ├── FileStreamFactory.cs
│ ├── FileStreamWrapper.cs
│ ├── FileSystem.cs
│ ├── FileSystemBase.cs
│ ├── FileSystemInfoBase.cs
│ ├── FileSystemWatcherBase.cs
│ ├── FileSystemWatcherFactory.cs
│ ├── FileSystemWatcherWrapper.cs
│ ├── FileVersionInfoBase.cs
│ ├── FileVersionInfoFactory.cs
│ ├── FileVersionInfoWrapper.cs
│ ├── FileWrapper.Async.cs
│ ├── FileWrapper.cs
│ ├── PathBase.cs
│ ├── PathWrapper.cs
│ ├── Polyfills/
│ │ └── SupportedOSPlatformAttribute.cs
│ └── TestableIO.System.IO.Abstractions.Wrappers.csproj
└── tests/
├── Directory.Build.props
├── TestableIO.System.IO.Abstractions.Api.Tests/
│ ├── ApiAcceptance.cs
│ ├── ApiApprovalTests.cs
│ ├── Expected/
│ │ ├── TestableIO.System.IO.Abstractions.TestingHelpers_net10.0.txt
│ │ ├── TestableIO.System.IO.Abstractions.TestingHelpers_net472.txt
│ │ ├── TestableIO.System.IO.Abstractions.TestingHelpers_net6.0.txt
│ │ ├── TestableIO.System.IO.Abstractions.TestingHelpers_net8.0.txt
│ │ ├── TestableIO.System.IO.Abstractions.TestingHelpers_net9.0.txt
│ │ ├── TestableIO.System.IO.Abstractions.TestingHelpers_netstandard2.0.txt
│ │ ├── TestableIO.System.IO.Abstractions.TestingHelpers_netstandard2.1.txt
│ │ ├── TestableIO.System.IO.Abstractions.Wrappers_net10.0.txt
│ │ ├── TestableIO.System.IO.Abstractions.Wrappers_net472.txt
│ │ ├── TestableIO.System.IO.Abstractions.Wrappers_net6.0.txt
│ │ ├── TestableIO.System.IO.Abstractions.Wrappers_net8.0.txt
│ │ ├── TestableIO.System.IO.Abstractions.Wrappers_net9.0.txt
│ │ ├── TestableIO.System.IO.Abstractions.Wrappers_netstandard2.0.txt
│ │ └── TestableIO.System.IO.Abstractions.Wrappers_netstandard2.1.txt
│ ├── Helper.cs
│ ├── TestableIO.System.IO.Abstractions.Api.Tests.csproj
│ └── Usings.cs
├── TestableIO.System.IO.Abstractions.Parity.Tests/
│ ├── ApiParityTests.cs
│ ├── TestableIO.System.IO.Abstractions.Parity.Tests.csproj
│ └── __snapshots__/
│ ├── ApiParityTests.DirectoryInfo_.NET 10.0.snap
│ ├── ApiParityTests.DirectoryInfo_.NET 6.0.snap
│ ├── ApiParityTests.DirectoryInfo_.NET 8.0.snap
│ ├── ApiParityTests.DirectoryInfo_.NET 9.0.snap
│ ├── ApiParityTests.DirectoryInfo_.NET Framework 4.7.2.snap
│ ├── ApiParityTests.Directory_.NET 10.0.snap
│ ├── ApiParityTests.Directory_.NET 6.0.snap
│ ├── ApiParityTests.Directory_.NET 8.0.snap
│ ├── ApiParityTests.Directory_.NET 9.0.snap
│ ├── ApiParityTests.Directory_.NET Framework 4.7.2.snap
│ ├── ApiParityTests.DriveInfo_.NET 10.0.snap
│ ├── ApiParityTests.DriveInfo_.NET 6.0.snap
│ ├── ApiParityTests.DriveInfo_.NET 8.0.snap
│ ├── ApiParityTests.DriveInfo_.NET 9.0.snap
│ ├── ApiParityTests.DriveInfo_.NET Framework 4.7.2.snap
│ ├── ApiParityTests.FileInfo_.NET 10.0.snap
│ ├── ApiParityTests.FileInfo_.NET 6.0.snap
│ ├── ApiParityTests.FileInfo_.NET 8.0.snap
│ ├── ApiParityTests.FileInfo_.NET 9.0.snap
│ ├── ApiParityTests.FileInfo_.NET Framework 4.7.2.snap
│ ├── ApiParityTests.FileSystemWatcher_.NET 10.0.snap
│ ├── ApiParityTests.FileSystemWatcher_.NET 6.0.snap
│ ├── ApiParityTests.FileSystemWatcher_.NET 8.0.snap
│ ├── ApiParityTests.FileSystemWatcher_.NET 9.0.snap
│ ├── ApiParityTests.FileSystemWatcher_.NET Framework 4.7.2.snap
│ ├── ApiParityTests.FileVersionInfo_.NET 10.0.snap
│ ├── ApiParityTests.FileVersionInfo_.NET 6.0.snap
│ ├── ApiParityTests.FileVersionInfo_.NET 8.0.snap
│ ├── ApiParityTests.FileVersionInfo_.NET 9.0.snap
│ ├── ApiParityTests.FileVersionInfo_.NET Framework 4.7.2.snap
│ ├── ApiParityTests.File_.NET 10.0.snap
│ ├── ApiParityTests.File_.NET 6.0.snap
│ ├── ApiParityTests.File_.NET 8.0.snap
│ ├── ApiParityTests.File_.NET 9.0.snap
│ ├── ApiParityTests.File_.NET Framework 4.7.2.snap
│ ├── ApiParityTests.Path_.NET 10.0.snap
│ ├── ApiParityTests.Path_.NET 6.0.snap
│ ├── ApiParityTests.Path_.NET 8.0.snap
│ ├── ApiParityTests.Path_.NET 9.0.snap
│ └── ApiParityTests.Path_.NET Framework 4.7.2.snap
├── TestableIO.System.IO.Abstractions.TestingHelpers.Tests/
│ ├── MockDirectoryArgumentPathTests.cs
│ ├── MockDirectoryGetAccessControlTests.cs
│ ├── MockDirectoryInfoAccessControlTests.cs
│ ├── MockDirectoryInfoFactoryTests.cs
│ ├── MockDirectoryInfoSymlinkTests.cs
│ ├── MockDirectoryInfoTests.cs
│ ├── MockDirectorySetAccessControlTests.cs
│ ├── MockDirectorySymlinkTests.cs
│ ├── MockDirectoryTests.cs
│ ├── MockDriveInfoFactoryTests.cs
│ ├── MockDriveInfoTests.cs
│ ├── MockFileAdjustTimesTest.cs
│ ├── MockFileAppendAllLinesTests.cs
│ ├── MockFileAppendAllTextTests.cs
│ ├── MockFileArgumentPathTests.cs
│ ├── MockFileCopyTests.cs
│ ├── MockFileCreateTests.cs
│ ├── MockFileDeleteTests.cs
│ ├── MockFileExistsTests.cs
│ ├── MockFileGetAccessControlTests.cs
│ ├── MockFileGetCreationTimeTests.cs
│ ├── MockFileGetCreationTimeUtcTests.cs
│ ├── MockFileGetLastAccessTimeTests.cs
│ ├── MockFileGetLastAccessTimeUtcTests.cs
│ ├── MockFileGetLastWriteTimeTests.cs
│ ├── MockFileGetLastWriteTimeUtcTests.cs
│ ├── MockFileGetUnixFileModeTests.cs
│ ├── MockFileInfoAccessControlTests.cs
│ ├── MockFileInfoFactoryTests.cs
│ ├── MockFileInfoSymlinkTests.cs
│ ├── MockFileInfoTests.cs
│ ├── MockFileLockTests.cs
│ ├── MockFileMoveTests.cs
│ ├── MockFileOpenTests.cs
│ ├── MockFileReadAllBytesTests.cs
│ ├── MockFileReadAllLinesTests.cs
│ ├── MockFileReadLinesTests.cs
│ ├── MockFileSetAccessControlTests.cs
│ ├── MockFileSetAttributesTests.cs
│ ├── MockFileSetUnixFileModeTests.cs
│ ├── MockFileStreamFactoryTests.cs
│ ├── MockFileStreamTests.cs
│ ├── MockFileSymlinkTests.cs
│ ├── MockFileSystemOptionTests.cs
│ ├── MockFileSystemSerializationTests.cs
│ ├── MockFileSystemTests.cs
│ ├── MockFileSystemWatcherFactoryTests.cs
│ ├── MockFileTests.cs
│ ├── MockFileVersionInfoFactoryTests.cs
│ ├── MockFileVersionInfoTests.cs
│ ├── MockFileWriteAllBytesTests.cs
│ ├── MockFileWriteAllLinesTests.cs
│ ├── MockFileWriteAllTextTests.cs
│ ├── MockPathTests.cs
│ ├── MockUnixSupportTests.cs
│ ├── Polyfills/
│ │ └── SupportedOSPlatformAttribute.cs
│ ├── ProductVersionParserTests.cs
│ ├── Shared.cs
│ ├── TestFiles/
│ │ ├── SecondTestFile.txt
│ │ └── TestFile.txt
│ ├── TestableIO.System.IO.Abstractions.TestingHelpers.Tests.csproj
│ ├── UnixOnlyAttribute.cs
│ ├── UnixSpecifics.cs
│ ├── Usings.cs
│ ├── WindowsOnlyAttribute.cs
│ └── WindowsSpecifics.cs
└── TestableIO.System.IO.Abstractions.Wrappers.Tests/
├── DirectoryInfoFactoryTests.cs
├── DirectoryInfoTests.cs
├── DirectoryWrapperTests.cs
├── DriveInfoFactoryTests.cs
├── FileInfoBaseConversionTests.cs
├── FileInfoFactoryTests.cs
├── FileSystemTests.cs
├── FileSystemWatcherFactoryTests.cs
├── FileVersionInfoBaseConversionTests.cs
├── TestableIO.System.IO.Abstractions.Wrappers.Tests.csproj
└── Usings.cs
================================================
FILE CONTENTS
================================================
================================================
FILE: .devcontainer/devcontainer.json
================================================
{
"name": "C# (.NET Core)",
"image": "mcr.microsoft.com/vscode/devcontainers/dotnet:10.0",
"settings": {
"terminal.integrated.shell.linux": "/bin/bash"
},
"postCreateCommand": "dotnet restore"
}
================================================
FILE: .editorconfig
================================================
; This file is for unifying the coding style for different editors and IDEs.
; More information at http://EditorConfig.org
root = true
[*]
end_of_line = CRLF
[*.cs]
indent_style = space
indent_size = 4
================================================
FILE: .gitattributes
================================================
# Auto detect text files and perform LF normalization
* text=auto
# Custom for Visual Studio
*.cs diff=csharp
*.sln merge=union
*.csproj merge=union
*.vbproj merge=union
*.fsproj merge=union
*.dbproj merge=union
# Standard to msysgit
*.doc diff=astextplain
*.DOC diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot diff=astextplain
*.DOT diff=astextplain
*.pdf diff=astextplain
*.PDF diff=astextplain
*.rtf diff=astextplain
*.RTF diff=astextplain
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: 'state: needs discussion, type: bug'
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Additional context**
Add any other context about the problem here.
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: 'state: needs discussion, type: enhancement'
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
================================================
FILE: .github/copilot-instructions.md
================================================
# System.IO.Abstractions Development Guide
System.IO.Abstractions is a .NET library that provides testable abstractions for System.IO operations, enabling developers to write unit tests that don't rely on the actual file system.
**ALWAYS reference these instructions first and fallback to search or bash commands only when you encounter unexpected information that does not match the info here.**
## Working Effectively
### Bootstrap and Build Process
- **CRITICAL**: Install .NET SDK 9.0.304 (required version specified in global.json):
```bash
curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --version 9.0.304
export PATH="$HOME/.dotnet:$PATH"
```
- Verify installation: `dotnet --version` should return `9.0.304`
- **Build the solution**: `dotnet build` -- takes ~70 seconds. NEVER CANCEL. Set timeout to 120+ minutes.
- **Run all tests**: `dotnet test --configuration Release` -- takes ~30 seconds. NEVER CANCEL. Set timeout to 60+ minutes.
### Code Quality and Formatting
- **ALWAYS run code formatting before committing**: `dotnet format`
- **Verify formatting compliance**: `dotnet format --verify-no-changes` -- takes ~40 seconds. NEVER CANCEL.
- The codebase uses EditorConfig with CRLF line endings and 4-space indentation for C# files
- **CRITICAL**: All formatting issues must be resolved before CI will pass
### Build System Details
- **Primary build method**: `dotnet build` and `dotnet test` commands work reliably
- **NUKE build script**: `./build.sh` available but may have GitVersion issues with shallow clones
- **Available NUKE targets**: UnitTests, ApiChecks, CodeCoverage, CodeAnalysis, Pack
- **Build artifacts**: Generated in `Artifacts/` and `TestResults/` directories
## Project Structure
### Key Projects
- **System.IO.Abstractions**: Core abstractions and interfaces
- **TestableIO.System.IO.Abstractions**: Main implementation
- **TestableIO.System.IO.Abstractions.Wrappers**: Wrapper implementations
- **TestableIO.System.IO.Abstractions.TestingHelpers**: Mock implementations for testing
- **Multiple test projects**: Comprehensive test coverage across different scenarios
### Target Frameworks
- .NET Framework 4.7.2 (net472)
- .NET Standard 2.0, 2.1 (netstandard2.0, netstandard2.1)
- .NET 6.0, 8.0, 9.0 (net6.0, net8.0, net9.0)
### Important Directories
- `src/`: All source code projects
- `tests/`: All test projects including unit tests, API tests, and parity tests
- `benchmarks/`: Performance benchmarking projects
- `Pipeline/`: NUKE build system configuration
- `.github/workflows/`: CI/CD pipeline definitions
## Validation
### Manual Validation Steps
After making changes, ALWAYS validate functionality by:
1. **Build verification**: `dotnet build` must succeed
2. **Test execution**: `dotnet test --configuration Release` must pass
3. **Code formatting**: `dotnet format --verify-no-changes` must pass
4. **Functional validation**: Create a test scenario to verify your changes work:
```csharp
using System.IO.Abstractions;
using System.IO.Abstractions.TestingHelpers;
// Test MockFileSystem functionality
var mockFileSystem = new MockFileSystem();
mockFileSystem.File.WriteAllText(@"C:\test.txt", "Test content");
var content = mockFileSystem.File.ReadAllText(@"C:\test.txt");
// Test real FileSystem functionality
var realFileSystem = new FileSystem();
var tempFile = "/tmp/test.txt";
realFileSystem.File.WriteAllText(tempFile, "Real test");
var realContent = realFileSystem.File.ReadAllText(tempFile);
realFileSystem.File.Delete(tempFile);
```
### Test Suite Information
- **Total tests**: ~2,234 tests across all projects
- **Expected passing**: ~1,993 tests (some platform-specific tests are skipped on Linux)
- **Test categories**: Unit tests, API compatibility tests, parity tests
- **Platform considerations**: Some Windows-specific functionality is skipped on Linux/macOS
### Continuous Integration Requirements
The CI pipeline (.github/workflows/ci.yml) requires:
- All unit tests to pass on Ubuntu, Windows, and macOS
- API compatibility checks to succeed
- Code formatting compliance
- Static code analysis (SonarCloud) to pass
## Common Development Tasks
### Adding New Functionality
- Implement abstractions in the main System.IO.Abstractions project
- Add wrapper implementations in TestableIO.System.IO.Abstractions.Wrappers
- Create mock implementations in TestableIO.System.IO.Abstractions.TestingHelpers
- Add comprehensive tests covering all target frameworks
- Update XML documentation for all public APIs
### Debugging Build Issues
- Check .NET SDK version compatibility first
- Verify all package references are properly restored
- For NUKE build issues, use direct `dotnet` commands instead
- Review build logs in Artifacts/ directory for detailed error information
### Package Management
- Uses Central Package Management (Directory.Packages.props)
- NuGet source: nuget.org only (configured in nuget.config)
- Package versioning uses Nerdbank.GitVersioning
## Pull Request Guidelines
### Pull Request Title
To communicate intent to the consumers of your library, the title of the pull requests is prefixed with one of the following elements:
- `fix:`: patches a bug
- `feat:`: introduces a new feature
- `refactor:`: improves internal structure without changing the observable behavior
- `docs:`: updates documentation or XML comments
- `chore:`: updates to dependencies, build pipelines, ...
## Performance Expectations
### Command Timing (with appropriate timeouts)
- **dotnet build**: ~70 seconds (set timeout: 120+ minutes)
- **dotnet test**: ~30 seconds (set timeout: 60+ minutes)
- **dotnet format**: ~40 seconds (set timeout: 10+ minutes)
- **NUKE build restore**: ~30 seconds (set timeout: 10+ minutes)
### **NEVER CANCEL** long-running operations
Builds and tests may occasionally take longer than expected. Always wait for completion rather than canceling operations.
## Troubleshooting
### Common Issues
- **GitVersion errors**: Use direct `dotnet` commands instead of NUKE build script
- **Shallow clone issues**: Expected in GitHub Actions environment, doesn't affect functionality
- **Platform-specific test failures**: Normal for Windows-specific functionality on Linux/macOS
- **Code formatting failures**: Run `dotnet format` to fix automatically
### SDK Installation Issues
If .NET SDK 9.0.304 is not available:
- Check global.json for exact version requirement
- Use dotnet-install script with specific version
- Ensure PATH includes the installed SDK location
**Remember**: This is a mature, well-tested library with extensive CI/CD. Focus on maintaining compatibility across all target frameworks and ensuring comprehensive test coverage for any changes.
================================================
FILE: .github/mergify.yml
================================================
pull_request_rules:
- name: Automatic merge on approval
conditions:
- "#approved-reviews-by>=1"
actions:
merge:
method: squash
commit_message_template: |-
{{ title }} (#{{ number }})
{{ body }}
- name: Automatic merge for Renovate pull requests
conditions:
- author=renovate[bot]
actions:
merge:
method: squash
================================================
FILE: .github/workflows/build.yml
================================================
name: Build
on:
push:
branches: [ main ]
tags: [ 'v[0-9]+.[0-9]+.[0-9]+*' ]
jobs:
unit-tests:
name: "Unit tests"
strategy:
matrix:
os: [ ubuntu-latest, windows-latest, macos-latest ]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setup .NET SDKs
uses: actions/setup-dotnet@v5
with:
dotnet-version: |
6.0.x
7.0.x
8.0.x
9.0.x
10.0.x
- name: Run unit tests (windows)
if: matrix.os == 'windows-latest'
run: ./build.ps1 CodeCoverage
- name: Run unit tests (ubuntu|macos)
if: matrix.os != 'windows-latest'
run: ./build.sh CodeCoverage
- name: Upload artifacts
if: always()
uses: actions/upload-artifact@v7
with:
name: ${{ runner.os }}-artifacts
path: |
./Artifacts/*
./TestResults/*.trx
api-tests:
name: "API tests"
runs-on: ubuntu-latest
env:
DOTNET_NOLOGO: true
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setup .NET SDKs
uses: actions/setup-dotnet@v5
with:
dotnet-version: |
6.0.x
7.0.x
8.0.x
9.0.x
10.0.x
- name: API checks
run: ./build.sh ApiChecks
- name: Upload artifacts
if: always()
uses: actions/upload-artifact@v7
with:
name: API-tests
path: |
./Artifacts/*
./TestResults/*.trx
static-code-analysis:
name: "Static code analysis"
runs-on: ubuntu-latest
env:
REPORTGENERATOR_LICENSE: ${{ secrets.REPORTGENERATOR_LICENSE }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
DOTNET_NOLOGO: true
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setup .NET SDKs
uses: actions/setup-dotnet@v5
with:
dotnet-version: |
6.0.x
7.0.x
8.0.x
9.0.x
10.0.x
- name: Run sonarcloud analysis
run: ./build.sh CodeAnalysis
publish-test-results:
name: "Publish Tests Results"
needs: [ api-tests, unit-tests ]
runs-on: ubuntu-latest
permissions:
checks: write
pull-requests: write
if: always()
steps:
- name: Download Artifacts
uses: actions/download-artifact@v8
with:
path: artifacts
- name: Publish Test Results
uses: EnricoMi/publish-unit-test-result-action@v2
with:
comment_mode: always
files: "artifacts/**/**/*.trx"
pack:
name: "Pack"
runs-on: ubuntu-latest
needs: [ publish-test-results, static-code-analysis ]
env:
DOTNET_NOLOGO: true
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setup .NET SDKs
uses: actions/setup-dotnet@v5
with:
dotnet-version: |
6.0.x
7.0.x
8.0.x
9.0.x
10.0.x
- name: Pack nuget packages
run: ./build.sh Pack
- name: Upload packages
if: always()
uses: actions/upload-artifact@v7
with:
path: Artifacts/Packages
name: Packages
push:
name: "Push"
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
runs-on: macos-latest
environment: production
needs: [ pack ]
permissions:
contents: write
steps:
- name: Download packages
uses: actions/download-artifact@v8
with:
name: Packages
path: Artifacts/Packages
- name: Setup .NET SDKs
uses: actions/setup-dotnet@v5
- name: Publish
run: |
echo "Found the following packages to push:"
search_dir=Artifacts/Packages
for entry in Artifacts/Packages/*.nupkg
do
echo "- $entry"
done
for entry in Artifacts/Packages/*.nupkg
do
dotnet nuget push $entry --source https://api.nuget.org/v3/index.json --api-key "${{secrets.NUGET_API_KEY}}" --skip-duplicate
done
- name: Check pre-release
id: check-pre-release
run: |
if [[ ${{ github.event.ref }} =~ ^refs/tags/v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "release=true" >> $GITHUB_OUTPUT
fi
- name: Create GitHub release
if: steps.check-pre-release.outputs.release == 'true'
continue-on-error: true
uses: softprops/action-gh-release@v2
with:
name: ${{ steps.tag.outputs.release_version }}
tag_name: ${{ steps.tag.outputs.release_version }}
token: ${{ secrets.GITHUB_TOKEN }}
generate_release_notes: true
finalize-release:
name: "Finalize release"
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
needs: [ push ]
permissions:
contents: read
issues: write
pull-requests: write
steps:
- name: Check pre-release
id: check-pre-release
run: |
if [[ ${{ github.event.ref }} =~ ^refs/tags/v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "release=true" >> $GITHUB_OUTPUT
fi
- name: Comment relevant issues and pull requests
if: steps.check-pre-release.outputs.release == 'true'
continue-on-error: true
uses: apexskier/github-release-commenter@v1.4.1
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
comment-template: |
This is addressed in release {release_link}.
label-template: |
state: released
skip-label: |
state: released
================================================
FILE: .github/workflows/ci.yml
================================================
name: CI
on:
workflow_dispatch:
pull_request:
branches: [ main ]
jobs:
unit-tests:
name: "Unit tests"
strategy:
matrix:
os: [ ubuntu-latest, windows-latest, macos-latest ]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setup .NET SDKs
uses: actions/setup-dotnet@v5
with:
dotnet-version: |
6.0.x
7.0.x
8.0.x
9.0.x
10.0.x
- name: Run unit tests (windows)
if: matrix.os == 'windows-latest'
run: ./build.ps1 CodeCoverage
- name: Run unit tests (ubuntu|macos)
if: matrix.os != 'windows-latest'
run: ./build.sh CodeCoverage
- name: Upload artifacts
if: always()
uses: actions/upload-artifact@v7
with:
name: ${{ runner.os }}-artifacts
path: |
./Artifacts/*
./TestResults/*.trx
api-tests:
name: "API tests"
runs-on: ubuntu-latest
env:
DOTNET_NOLOGO: true
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setup .NET SDKs
uses: actions/setup-dotnet@v5
with:
dotnet-version: |
6.0.x
7.0.x
8.0.x
9.0.x
10.0.x
- name: API checks
run: ./build.sh ApiChecks
- name: Upload artifacts
if: always()
uses: actions/upload-artifact@v7
with:
name: API-tests
path: |
./Artifacts/*
./TestResults/*.trx
static-code-analysis:
name: "Static code analysis"
if: ${{ github.actor != 'dependabot[bot]' && github.event.pull_request.head.repo.full_name == github.event.pull_request.base.repo.full_name }}
runs-on: ubuntu-latest
env:
REPORTGENERATOR_LICENSE: ${{ secrets.REPORTGENERATOR_LICENSE }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
DOTNET_NOLOGO: true
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setup .NET SDKs
uses: actions/setup-dotnet@v5
with:
dotnet-version: |
6.0.x
7.0.x
8.0.x
9.0.x
10.0.x
- name: Run sonarcloud analysis
run: ./build.sh CodeAnalysis
publish-test-results:
name: "Publish Tests Results"
needs: [ api-tests, unit-tests ]
runs-on: ubuntu-latest
permissions:
checks: write
pull-requests: write
if: always()
steps:
- name: Download Artifacts
uses: actions/download-artifact@v8
with:
path: artifacts
- name: Publish Test Results
uses: EnricoMi/publish-unit-test-result-action@v2
with:
comment_mode: always
files: "artifacts/**/**/*.trx"
================================================
FILE: .gitignore
================================================
# Artifact and test results directories
/Artifacts
/TestResults
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.sln.docstates
*.ide/
.vs/
# Build results
[Dd]ebug/
[Rr]elease/
x64/
build/
[Bb]in/
[Oo]bj/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
*_i.c
*_p.c
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.log
*.scc
# Benchmarks results
BenchmarkDotNet.Artifacts
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
*.ncrunch*
.*crunch*.local.xml
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.Publish.xml
*.pubxml
# NuGet Packages Directory
## TODO: If you have NuGet Package Restore enabled, uncomment the next line
packages/
# Windows Azure Build Output
csx
*.build.csdef
# Windows Store app package directory
AppPackages/
# Others
sql/
*.Cache
ClientBin/
[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
*.[Pp]ublish.xml
*.pfx
*.publishsettings
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file to a newer
# Visual Studio version. Backup files are not needed, because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
App_Data/*.mdf
App_Data/*.ldf
# =========================
# Windows detritus
# =========================
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Mac crap
.DS_Store
#System.IO.Abstractions specific ignores
Releases
System.IO.Abstractions.*.nupkg
# Common IntelliJ Platform excludes
# https://github.com/JetBrains/resharper-rider-samples/blob/master/.gitignore
# User specific
**/.idea/**/workspace.xml
**/.idea/**/tasks.xml
**/.idea/shelf/*
**/.idea/dictionaries
**/.idea/httpRequests/
# Sensitive or high-churn files
**/.idea/**/dataSources/
**/.idea/**/dataSources.ids
**/.idea/**/dataSources.xml
**/.idea/**/dataSources.local.xml
**/.idea/**/sqlDataSources.xml
**/.idea/**/dynamic.xml
# Rider
# Rider auto-generates .iml files, and contentModel.xml
**/.idea/**/*.iml
**/.idea/**/contentModel.xml
**/.idea/**/modules.xml
================================================
FILE: .idea/.idea.System.IO.Abstractions/.idea/.gitignore
================================================
# Default ignored files
/shelf/
/workspace.xml
# Rider ignored files
/contentModel.xml
/projectSettingsUpdater.xml
/.idea.System.IO.Abstractions.iml
/modules.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
================================================
FILE: .idea/.idea.System.IO.Abstractions/.idea/encodings.xml
================================================
================================================
FILE: .idea/.idea.System.IO.Abstractions/.idea/indexLayout.xml
================================================
================================================
FILE: .idea/.idea.System.IO.Abstractions/.idea/vcs.xml
================================================
================================================
FILE: .nuke/build.schema.json
================================================
{
"$schema": "http://json-schema.org/draft-04/schema#",
"definitions": {
"Host": {
"type": "string",
"enum": [
"AppVeyor",
"AzurePipelines",
"Bamboo",
"Bitbucket",
"Bitrise",
"GitHubActions",
"GitLab",
"Jenkins",
"Rider",
"SpaceAutomation",
"TeamCity",
"Terminal",
"TravisCI",
"VisualStudio",
"VSCode"
]
},
"ExecutableTarget": {
"type": "string",
"enum": [
"ApiChecks",
"CalculateNugetVersion",
"Clean",
"CodeAnalysis",
"CodeAnalysisBegin",
"CodeAnalysisEnd",
"CodeCoverage",
"Compile",
"DotNetFrameworkUnitTests",
"DotNetUnitTests",
"Pack",
"Restore",
"UnitTests",
"UpdateReadme"
]
},
"Verbosity": {
"type": "string",
"description": "",
"enum": [
"Verbose",
"Normal",
"Minimal",
"Quiet"
]
},
"NukeBuild": {
"properties": {
"Continue": {
"type": "boolean",
"description": "Indicates to continue a previously failed build attempt"
},
"Help": {
"type": "boolean",
"description": "Shows the help text for this build assembly"
},
"Host": {
"description": "Host for execution. Default is 'automatic'",
"$ref": "#/definitions/Host"
},
"NoLogo": {
"type": "boolean",
"description": "Disables displaying the NUKE logo"
},
"Partition": {
"type": "string",
"description": "Partition to use on CI"
},
"Plan": {
"type": "boolean",
"description": "Shows the execution plan (HTML)"
},
"Profile": {
"type": "array",
"description": "Defines the profiles to load",
"items": {
"type": "string"
}
},
"Root": {
"type": "string",
"description": "Root directory during build execution"
},
"Skip": {
"type": "array",
"description": "List of targets to be skipped. Empty list skips all dependencies",
"items": {
"$ref": "#/definitions/ExecutableTarget"
}
},
"Target": {
"type": "array",
"description": "List of targets to be invoked. Default is '{default_target}'",
"items": {
"$ref": "#/definitions/ExecutableTarget"
}
},
"Verbosity": {
"description": "Logging verbosity during build execution. Default is 'Normal'",
"$ref": "#/definitions/Verbosity"
}
}
}
},
"allOf": [
{
"properties": {
"Configuration": {
"type": "string",
"description": "Configuration to build - Default is 'Debug' (local) or 'Release' (server)",
"enum": [
"Debug",
"Release"
]
},
"GithubToken": {
"type": "string",
"description": "Github Token"
},
"Solution": {
"type": "string",
"description": "Path to a solution file that is automatically loaded"
},
"SonarToken": {
"type": "string",
"description": "The key to push to sonarcloud",
"default": "Secrets must be entered via 'nuke :secrets [profile]'"
}
}
},
{
"$ref": "#/definitions/NukeBuild"
}
]
}
================================================
FILE: .nuke/parameters.json
================================================
{
"$schema": "build.schema.json",
"Solution": "System.IO.Abstractions.slnx"
}
================================================
FILE: .remarkrc.yaml
================================================
plugins:
- remark-preset-lint-consistent
- remark-preset-lint-markdown-style-guide
================================================
FILE: .vscode/extensions.json
================================================
{
"recommendations": [
"ms-vscode-remote.remote-containers",
"ms-dotnettools.csharp"
]
}
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at code-of-conduct@testably.org. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq
================================================
FILE: CONTRIBUTING.md
================================================
# Contributor guide
## Versioning
This library uses [Nerdbank.GitVersioning](https://github.com/AArnott/Nerdbank.GitVersioning) for generating stable and reproducible version numbers.
The base version is manually maintained in [the version config](version.json). Every build calculates its final version number based on the base version and the number of changes that occured since the last change to the version config.
The base version represents the MAJOR and MINOR parts of [SemVer](https://semver.org). If a PR contains breaking changes or new features the base version has to be changed accordingly. If a PR solely contains minor changes (bug fixes, code improvements) nothing needs to be done as the PATCH number will automatically increment with each commit.
## Branches / tags
- `main` contains the latest sources. Each merge there triggers a deploy to `nuget.org`.
- All versions on `nuget.org` have a matching GitHub release/tag.
## Commits and PR title
- Please use [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) to name your commits and PR title.
We use [action-semantic-pull-request](https://github.com/amannn/action-semantic-pull-request?tab=readme-ov-file#configuration) to enforce this policy, feel free to have a closer look.
- Available prefixes:
- `feat:` A new feature
- `fix:` A bug fix
- `docs:` Documentation only changes
- `style:` Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
- `refactor:` A code change that neither fixes a bug nor adds a feature
- `perf:` A code change that improves performance
- `test:` Adding missing tests or correcting existing tests
- `build:` Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
- `ci:` Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
- `chore:` Other changes that don't modify src or test files
- `revert:` Reverts a previous commit
================================================
FILE: Directory.Build.props
================================================
System.IO.Abstractions
Copyright © Tatham Oddie & friends 2010-$([System.DateTime]::Now.ToString('yyyy'))
Tatham Oddie & friends
True
00240000048000009400000006020000002400005253413100040000010001001160c7a0f907c400c5392975b66d2f3752fb82625d5674d386b83896d4d4ae8d0ef8319ef391fbb3466de0058ad2f361b8f5cb8a32ecb4e908bece5c519387552cedd2ca0250e36b59c6d6dc3dc260ca73a7e27c3add4ae22d5abaa562225d7ba34d427e8f3f52928a46a674deb0208eca7d379aa22712355b91a55a5ce521d2
$(MSBuildThisFileDirectory)StrongName.snk
latest
testing
CS1591
https://github.com/TestableIO/System.IO.Abstractions
MIT
README.md
$(DefineConstants);FEATURE_FILE_SYSTEM_ACL_EXTENSIONS
$(DefineConstants);FEATURE_ASYNC_FILE;FEATURE_ENUMERATION_OPTIONS;FEATURE_ADVANCED_PATH_OPERATIONS;FEATURE_PATH_JOIN_WITH_SPAN;FEATURE_SPAN
$(DefineConstants);FEATURE_FILE_MOVE_WITH_OVERWRITE;FEATURE_SUPPORTED_OS_ATTRIBUTE;FEATURE_FILE_SYSTEM_WATCHER_FILTERS;FEATURE_ENDS_IN_DIRECTORY_SEPARATOR;FEATURE_PATH_JOIN_WITH_PARAMS;FEATURE_PATH_JOIN_WITH_FOUR_PATHS;FEATURE_FILE_SYSTEM_INFO_LINK_TARGET;FEATURE_CREATE_SYMBOLIC_LINK;FEATURE_FILESTREAM_OPTIONS
$(DefineConstants);FEATURE_PATH_EXISTS;FEATURE_FILE_SYSTEM_WATCHER_WAIT_WITH_TIMESPAN;FEATURE_FILE_ATTRIBUTES_VIA_HANDLE;FEATURE_CREATE_TEMP_SUBDIRECTORY;FEATURE_READ_LINES_ASYNC;FEATURE_UNIX_FILE_MODE
$(DefineConstants);FEATURE_PATH_SPAN;FEATURE_FILE_SPAN
$(DefineConstants);FEATURE_SERIALIZABLE
true
true
true
snupkg
false
================================================
FILE: Directory.Packages.props
================================================
true
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) Tatham Oddie and Contributors
All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: Pipeline/.editorconfig
================================================
[*.cs]
dotnet_style_qualification_for_field = false:warning
dotnet_style_qualification_for_property = false:warning
dotnet_style_qualification_for_method = false:warning
dotnet_style_qualification_for_event = false:warning
dotnet_style_require_accessibility_modifiers = never:warning
csharp_style_expression_bodied_methods = true:silent
csharp_style_expression_bodied_properties = true:warning
csharp_style_expression_bodied_indexers = true:warning
csharp_style_expression_bodied_accessors = true:warning
================================================
FILE: Pipeline/Build.ApiChecks.cs
================================================
using Nuke.Common;
using Nuke.Common.ProjectModel;
using Nuke.Common.Tooling;
using Nuke.Common.Tools.DotNet;
using static Nuke.Common.Tools.DotNet.DotNetTasks;
// ReSharper disable AllUnderscoreLocalParameterName
namespace Build;
partial class Build
{
Target ApiChecks => _ => _
.DependsOn(Compile)
.Executes(() =>
{
Project project = Solution.Tests.TestableIO_System_IO_Abstractions_Api_Tests;
DotNetTest(s => s
.SetConfiguration(Configuration == Configuration.Debug ? "Debug" : "Release")
.SetProcessEnvironmentVariable("DOTNET_CLI_UI_LANGUAGE", "en-US")
.EnableNoBuild()
.SetResultsDirectory(TestResultsDirectory)
.CombineWith(cc => cc
.SetProjectFile(project)
.AddLoggers($"trx;LogFileName={project.Name}.trx")), completeOnFailure: true);
});
}
================================================
FILE: Pipeline/Build.CodeAnalysis.cs
================================================
using Nuke.Common;
using Nuke.Common.Tools.SonarScanner;
// ReSharper disable AllUnderscoreLocalParameterName
namespace Build;
partial class Build
{
[Parameter("The key to push to sonarcloud")] [Secret] readonly string SonarToken;
Target CodeAnalysisBegin => _ => _
.Unlisted()
.Before(Compile)
.Before(CodeCoverage)
.Executes(() =>
{
SonarScannerTasks.SonarScannerBegin(s => s
.SetOrganization("testableio")
.SetProjectKey("TestableIO_System.IO.Abstractions")
.AddVSTestReports(TestResultsDirectory / "*.trx")
.AddOpenCoverPaths(TestResultsDirectory / "reports" / "OpenCover.xml")
.SetPullRequestOrBranchName(GitHubActions, GitVersion)
.SetVersion(GitVersion.SemVer)
.SetToken(SonarToken));
});
Target CodeAnalysisEnd => _ => _
.Unlisted()
.DependsOn(Compile)
.DependsOn(CodeCoverage)
.OnlyWhenDynamic(() => IsServerBuild)
.Executes(() =>
{
SonarScannerTasks.SonarScannerEnd(s => s
.SetToken(SonarToken));
});
Target CodeAnalysis => _ => _
.DependsOn(CodeAnalysisBegin)
.DependsOn(CodeAnalysisEnd);
}
================================================
FILE: Pipeline/Build.CodeCoverage.cs
================================================
using Nuke.Common;
using Nuke.Common.Tooling;
using Nuke.Common.Tools.ReportGenerator;
using static Nuke.Common.Tools.ReportGenerator.ReportGeneratorTasks;
// ReSharper disable AllUnderscoreLocalParameterName
namespace Build;
partial class Build
{
Target CodeCoverage => _ => _
.DependsOn(UnitTests)
.Executes(() =>
{
ReportGenerator(s => s
.SetProcessToolPath(NuGetToolPathResolver.GetPackageExecutable("ReportGenerator", "ReportGenerator.dll",
framework: "net8.0"))
.SetTargetDirectory(TestResultsDirectory / "reports")
.AddReports(TestResultsDirectory / "**/coverage.cobertura.xml")
.AddReportTypes(ReportTypes.OpenCover)
.AddFileFilters("-*.g.cs")
.SetAssemblyFilters("+TestableIO*", "+System.IO.Abstractions*"));
});
}
================================================
FILE: Pipeline/Build.Compile.cs
================================================
using System;
using System.Linq;
using Nuke.Common;
using Nuke.Common.IO;
using Nuke.Common.Tools.DotNet;
using Nuke.Common.Tools.GitVersion;
using Nuke.Common.Utilities;
using Nuke.Common.Utilities.Collections;
using Serilog;
using static Nuke.Common.Tools.DotNet.DotNetTasks;
// ReSharper disable AllUnderscoreLocalParameterName
namespace Build;
partial class Build
{
string BranchName;
string SemVer;
AssemblyVersion MainVersion;
Target CalculateNugetVersion => _ => _
.Unlisted()
.Executes(() =>
{
string preRelease = "-CI";
if (GitHubActions == null)
{
preRelease = "-DEV";
}
else if (GitHubActions.Ref.StartsWith("refs/tags/", StringComparison.OrdinalIgnoreCase))
{
int preReleaseIndex = GitHubActions.Ref.IndexOf('-');
preRelease = preReleaseIndex > 0 ? GitHubActions.Ref[preReleaseIndex..] : "";
}
SemVer = GitVersion.SemVer;
BranchName = GitVersion.BranchName;
MainVersion = AssemblyVersion.FromGitVersion(GitVersion, preRelease);
if (GitHubActions?.IsPullRequest == true)
{
string buildNumber = GitHubActions.RunNumber.ToString();
Console.WriteLine(
$"Branch spec is a pull request. Adding build number {buildNumber}");
SemVer = string.Join('.', GitVersion.SemVer.Split('.').Take(3).Union([buildNumber,]));
}
Console.WriteLine($"SemVer = {SemVer}");
});
Target Clean => _ => _
.Unlisted()
.Before(Restore)
.Executes(() =>
{
ArtifactsDirectory.CreateOrCleanDirectory();
Log.Information("Cleaned {path}...", ArtifactsDirectory);
TestResultsDirectory.CreateOrCleanDirectory();
Log.Information("Cleaned {path}...", TestResultsDirectory);
});
Target Restore => _ => _
.Unlisted()
.DependsOn(Clean)
.Executes(() =>
{
DotNetRestore(s => s
.SetProjectFile(Solution)
.EnableNoCache()
.SetConfigFile(RootDirectory / "nuget.config"));
});
Target Compile => _ => _
.DependsOn(Restore)
.DependsOn(CalculateNugetVersion)
.Executes(() =>
{
string preRelease = "-CI";
if (GitHubActions == null)
{
preRelease = "-DEV";
}
else if (GitHubActions.Ref.StartsWith("refs/tags/", StringComparison.OrdinalIgnoreCase))
{
int preReleaseIndex = GitHubActions.Ref.IndexOf('-');
preRelease = preReleaseIndex > 0 ? GitHubActions.Ref[preReleaseIndex..] : "";
}
ReportSummary(s => s
.WhenNotNull(SemVer, (summary, semVer) => summary
.AddPair("Version", MainVersion.FileVersion + MainVersion.PreRelease)));
DotNetBuild(s => s
.SetProjectFile(Solution)
.SetConfiguration(Configuration)
.EnableNoLogo()
.EnableNoRestore()
.SetVersion(MainVersion.FileVersion + MainVersion.PreRelease)
.SetAssemblyVersion(MainVersion.FileVersion)
.SetFileVersion(MainVersion.FileVersion)
.SetInformationalVersion(MainVersion.InformationalVersion));
});
public record AssemblyVersion(string FileVersion, string InformationalVersion, string PreRelease)
{
public static AssemblyVersion FromGitVersion(GitVersion gitVersion, string preRelease)
{
if (gitVersion is null)
{
return null;
}
return new AssemblyVersion(gitVersion.AssemblySemVer, gitVersion.InformationalVersion, preRelease);
}
}
}
================================================
FILE: Pipeline/Build.Pack.cs
================================================
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Nuke.Common;
using Nuke.Common.IO;
using Nuke.Common.ProjectModel;
using Nuke.Common.Utilities;
using Nuke.Common.Utilities.Collections;
using static Serilog.Log;
// ReSharper disable AllUnderscoreLocalParameterName
namespace Build;
partial class Build
{
Target UpdateReadme => _ => _
.DependsOn(Clean)
.Before(Compile)
.Executes(() =>
{
string version = string.Join('.', GitVersion.SemVer.Split('.').Take(3));
if (version.IndexOf('-') != -1)
{
version = version.Substring(0, version.IndexOf('-'));
}
StringBuilder sb = new();
string[] lines = File.ReadAllLines(Solution.Directory / "README.md");
sb.AppendLine(lines.First());
sb.AppendLine(
$"[](https://github.com/TestableIO/System.IO.Abstractions/releases/tag/v{version})");
foreach (string line in lines.Skip(1))
{
if (line.StartsWith("[ ||
line.StartsWith("[)
{
continue;
}
if (line.StartsWith("[)
{
sb.AppendLine(line
.Replace(")", $"&branch=release/v{version})"));
continue;
}
if (line.StartsWith("[)
{
sb.AppendLine(line
.Replace("%2Fmain)", $"%2Frelease%2Fv{version})")
.Replace("/main)", $"/release/v{version})"));
continue;
}
sb.AppendLine(line);
}
File.WriteAllText(ArtifactsDirectory / "README.md", sb.ToString());
});
Target Pack => _ => _
.DependsOn(UpdateReadme)
.DependsOn(Compile)
.Executes(() =>
{
AbsolutePath packagesDirectory = ArtifactsDirectory / "Packages";
packagesDirectory.CreateOrCleanDirectory();
List packages = new();
foreach (Project project in new[]
{
Solution.TestableIO_System_IO_Abstractions_Wrappers,
Solution.TestableIO_System_IO_Abstractions_TestingHelpers,
Solution.Meta.TestableIO_System_IO_Abstractions,
Solution.Meta.System_IO_Abstractions,
Solution.Meta.System_IO_Abstractions_TestingHelpers,
})
{
foreach (string package in
Directory.EnumerateFiles(project.Directory / "bin", "*.nupkg", SearchOption.AllDirectories))
{
File.Move(package, packagesDirectory / Path.GetFileName(package));
Debug("Found nuget package: {PackagePath}", package);
packages.Add(Path.GetFileName(package));
}
foreach (string symbolPackage in
Directory.EnumerateFiles(project.Directory / "bin", "*.snupkg", SearchOption.AllDirectories))
{
File.Move(symbolPackage, packagesDirectory / Path.GetFileName(symbolPackage));
Debug("Found symbol package: {PackagePath}", symbolPackage);
}
}
ReportSummary(s => s
.AddPair("Packages", string.Join(", ", packages)));
});
}
================================================
FILE: Pipeline/Build.UnitTest.cs
================================================
using System.IO;
using System.Linq;
using Nuke.Common;
using Nuke.Common.IO;
using Nuke.Common.ProjectModel;
using Nuke.Common.Tooling;
using Nuke.Common.Tools.DotNet;
using Nuke.Common.Tools.NUnit;
using static Nuke.Common.Tools.DotNet.DotNetTasks;
// ReSharper disable AllUnderscoreLocalParameterName
namespace Build;
partial class Build
{
Target UnitTests => _ => _
.DependsOn(DotNetFrameworkUnitTests)
.DependsOn(DotNetUnitTests);
Project[] UnitTestProjects =>
[
Solution.Tests.TestableIO_System_IO_Abstractions_Wrappers_Tests,
Solution.Tests.TestableIO_System_IO_Abstractions_TestingHelpers_Tests,
Solution.Tests.TestableIO_System_IO_Abstractions_Parity_Tests,
];
Target DotNetFrameworkUnitTests => _ => _
.Unlisted()
.DependsOn(Compile)
.OnlyWhenDynamic(() => EnvironmentInfo.IsWin)
.Executes(() =>
{
var testAssemblies = UnitTestProjects
.SelectMany(project =>
project.Directory.GlobFiles(
$"bin/{(Configuration == Configuration.Debug ? "Debug" : "Release")}/net472/*.Tests.dll"))
.Select(p => p.ToString())
.ToArray();
Assert.NotEmpty(testAssemblies.ToList());
foreach (var testAssembly in testAssemblies)
{
var currentDirectory = Path.GetDirectoryName(testAssembly);
NUnitTasks.NUnit3(s => s
.SetInputFiles(testAssembly)
.SetProcessWorkingDirectory(currentDirectory)
);
}
});
Target DotNetUnitTests => _ => _
.Unlisted()
.DependsOn(Compile)
.Executes(() =>
{
string[] excludedFrameworks = ["net48",];
DotNetTest(s => s
.SetConfiguration(Configuration)
.SetProcessEnvironmentVariable("DOTNET_CLI_UI_LANGUAGE", "en-US")
.EnableNoBuild()
.SetDataCollector("XPlat Code Coverage")
.SetResultsDirectory(TestResultsDirectory)
.CombineWith(
UnitTestProjects,
(settings, project) => settings
.SetProjectFile(project)
.CombineWith(
project.GetTargetFrameworks()?.Except(excludedFrameworks),
(frameworkSettings, framework) => frameworkSettings
.SetFramework(framework)
.AddLoggers($"trx;LogFileName={project.Name}_{framework}.trx")
)
), completeOnFailure: true
);
});
}
================================================
FILE: Pipeline/Build.cs
================================================
using Nuke.Common;
using Nuke.Common.CI.GitHubActions;
using Nuke.Common.IO;
using Nuke.Common.ProjectModel;
using Nuke.Common.Tools.GitVersion;
namespace Build;
[GitHubActions(
"Build",
GitHubActionsImage.UbuntuLatest,
AutoGenerate = false,
ImportSecrets = [nameof(GithubToken),]
)]
partial class Build : NukeBuild
{
[Parameter("Configuration to build - Default is 'Debug' (local) or 'Release' (server)")]
readonly Configuration Configuration = IsLocalBuild ? Configuration.Debug : Configuration.Release;
[Parameter("Github Token")] readonly string GithubToken;
[Required] [GitVersion(Framework = "net8.0", NoCache = true, NoFetch = true)] readonly GitVersion GitVersion;
[Solution(GenerateProjects = true)] readonly Solution Solution;
AbsolutePath ArtifactsDirectory => RootDirectory / "Artifacts";
AbsolutePath TestResultsDirectory => RootDirectory / "TestResults";
GitHubActions GitHubActions => GitHubActions.Instance;
public static int Main() => Execute(x => x.UnitTests, x => x.Pack);
}
================================================
FILE: Pipeline/Build.csproj
================================================
Exe
net10.0
CS0649;CS0169;CA1050;CA1822;CA2211;IDE1006
..
..
1
false
================================================
FILE: Pipeline/Build.csproj.DotSettings
================================================
DO_NOT_SHOW
DO_NOT_SHOW
DO_NOT_SHOW
DO_NOT_SHOW
DO_NOT_SHOW
Implicit
Implicit
ExpressionBody
0
NEXT_LINE
True
False
120
IF_OWNER_IS_SINGLE_LINE
WRAP_IF_LONG
False
<Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
<Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
<Policy><Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Instance fields (private)"><ElementKinds><Kind Name="FIELD" /><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="AaBb" /></Policy>
<Policy><Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static fields (private)"><ElementKinds><Kind Name="FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" WarnAboutPrefixesAndSuffixes="False" Prefix="" Suffix="" Style="AaBb" /></Policy>
True
True
True
True
True
True
True
True
True
True
================================================
FILE: Pipeline/BuildExtensions.cs
================================================
using System;
using Nuke.Common.CI.GitHubActions;
using Nuke.Common.Tools.GitVersion;
using Nuke.Common.Tools.SonarScanner;
using Serilog;
namespace Build;
public static class BuildExtensions
{
public static SonarScannerBeginSettings SetPullRequestOrBranchName(
this SonarScannerBeginSettings settings,
GitHubActions gitHubActions,
GitVersion gitVersion)
{
if (gitHubActions?.IsPullRequest == true)
{
Log.Information("Use pull request analysis");
return settings
.SetPullRequestKey(gitHubActions.PullRequestNumber.ToString())
.SetPullRequestBranch(gitHubActions.Ref)
.SetPullRequestBase(gitHubActions.BaseRef);
}
if (gitHubActions?.Ref.StartsWith("refs/tags/", StringComparison.OrdinalIgnoreCase) == true)
{
string version = gitHubActions.Ref.Substring("refs/tags/".Length);
string branchName = "release/" + version;
Log.Information("Use release branch analysis for '{BranchName}'", branchName);
return settings.SetBranchName(branchName);
}
Log.Information("Use branch analysis for '{BranchName}'", gitVersion.BranchName);
return settings.SetBranchName(gitVersion.BranchName);
}
}
================================================
FILE: Pipeline/Configuration.cs
================================================
using System;
using System.ComponentModel;
using System.Linq;
using Nuke.Common.Tooling;
[TypeConverter(typeof(TypeConverter))]
public class Configuration : Enumeration
{
public static Configuration Debug = new Configuration { Value = nameof(Debug) };
public static Configuration Release = new Configuration { Value = nameof(Release) };
public static implicit operator string(Configuration configuration)
{
return configuration.Value;
}
}
================================================
FILE: Pipeline/Directory.Build.props
================================================
================================================
FILE: Pipeline/Directory.Build.targets
================================================
================================================
FILE: README.md
================================================

[](https://www.nuget.org/packages/TestableIO.System.IO.Abstractions)
[](https://github.com/TestableIO/System.IO.Abstractions/actions/workflows/build.yml)
[](https://sonarcloud.io/summary/new_code?id=TestableIO_System.IO.Abstractions)
[](https://sonarcloud.io/summary/new_code?id=TestableIO_System.IO.Abstractions)
[](https://renovatebot.com/)
[](https://app.fossa.com/projects/git%2Bgithub.com%2FTestableIO%2FSystem.IO.Abstractions?ref=badge_shield)
At the core of the library is `IFileSystem` and `FileSystem`. Instead of calling methods like `File.ReadAllText` directly, use `IFileSystem.File.ReadAllText`. We have exactly the same API, except that ours is injectable and testable.
## Usage
```shell
dotnet add package TestableIO.System.IO.Abstractions.Wrappers
```
*Note: This NuGet package is also published as `System.IO.Abstractions` but we suggest to use the prefix to make clear that this is not an official .NET package.*
```csharp
public class MyComponent
{
readonly IFileSystem fileSystem;
// Create MyComponent with the given fileSystem implementation
public MyComponent(IFileSystem fileSystem)
{
this.fileSystem = fileSystem;
}
/// Create MyComponent
public MyComponent() : this(
fileSystem: new FileSystem() //use default implementation which calls System.IO
)
{
}
public void Validate()
{
foreach (var textFile in fileSystem.Directory.GetFiles(@"c:\", "*.txt", SearchOption.TopDirectoryOnly))
{
var text = fileSystem.File.ReadAllText(textFile);
if (text != "Testing is awesome.")
throw new NotSupportedException("We can't go on together. It's not me, it's you.");
}
}
}
```
### Test helpers
The library also ships with a series of test helpers to save you from having to mock out every call, for basic scenarios. They are not a complete copy of a real-life file system, but they'll get you most of the way there.
```shell
dotnet add package TestableIO.System.IO.Abstractions.TestingHelpers
```
*Note: This NuGet package is also published as `System.IO.Abstractions.TestingHelpers` but we suggest to use the prefix to make clear that this is not an official .NET package.*
```csharp
[Test]
public void MyComponent_Validate_ShouldThrowNotSupportedExceptionIfTestingIsNotAwesome()
{
// Arrange
var fileSystem = new MockFileSystem(new Dictionary
{
{ @"c:\myfile.txt", new MockFileData("Testing is meh.") },
{ @"c:\demo\jQuery.js", new MockFileData("some js") },
{ @"c:\demo\image.gif", new MockFileData(new byte[] { 0x12, 0x34, 0x56, 0xd2 }) }
});
var component = new MyComponent(fileSystem);
try
{
// Act
component.Validate();
}
catch (NotSupportedException ex)
{
// Assert
Assert.That(ex.Message, Is.EqualTo("We can't go on together. It's not me, it's you."));
return;
}
Assert.Fail("The expected exception was not thrown.");
}
```
We even support casting from the .NET Framework's untestable types to our testable wrappers:
```csharp
FileInfo SomeApiMethodThatReturnsFileInfo()
{
return new FileInfo("a");
}
void MyFancyMethod()
{
var testableFileInfo = (FileInfoBase)SomeApiMethodThatReturnsFileInfo();
...
}
```
### Mock support
Since version 4.0 the top-level APIs expose interfaces instead of abstract base classes (these still exist, though), allowing you to completely mock the file system. Here's a small example, using [Mockolate](https://github.com/aweXpect/Mockolate):
```csharp
[Test]
public void Test1()
{
var watcher = Mock.Create();
var file = Mock.Create();
file.SetupMock.Method.Exists(It.IsAny()).Returns(true);
file.SetupMock.Method.ReadAllText(It.IsAny()).Throws();
var unitUnderTest = new SomeClassUsingFileSystemWatcher(watcher, file);
Assert.Throws(() => {
watcher.RaiseOnMock.Created(null, new System.IO.FileSystemEventArgs(System.IO.WatcherChangeTypes.Created, @"C:\Some\Directory", "Some.File"));
});
file.VerifyMock.Invoked.Exists(It.IsAny()).Once();
Assert.That(unitUnderTest.FileWasCreated, Is.True);
}
public class SomeClassUsingFileSystemWatcher
{
private readonly IFileSystemWatcher _watcher;
private readonly IFile _file;
public bool FileWasCreated { get; private set; }
public SomeClassUsingFileSystemWatcher(IFileSystemWatcher watcher, IFile file)
{
this._file = file;
this._watcher = watcher;
this._watcher.Created += Watcher_Created;
}
private void Watcher_Created(object sender, System.IO.FileSystemEventArgs e)
{
FileWasCreated = true;
if(_file.Exists(e.FullPath))
{
var text = _file.ReadAllText(e.FullPath);
}
}
}
```
## Relationship with Testably.Abstractions
[`Testably.Abstractions`](https://github.com/Testably/Testably.Abstractions) is a complementary project that uses the same interfaces as TestableIO. This means **no changes to your production code are necessary** when switching between the testing libraries.
Both projects share the same maintainer, but active development and new features are primarily focused on the Testably.Abstractions project. TestableIO.System.IO.Abstractions continues to be maintained for stability and compatibility, but significant new functionality is unlikely to be added.
### When to use Testably.Abstractions vs TestableIO
- **Use TestableIO.System.IO.Abstractions** if you need:
- Basic file system mocking capabilities
- Direct manipulation of stored file entities (MockFileData, MockDirectoryData)
- Established codebase with existing TestableIO integration
- **Use Testably.Abstractions** if you need:
- Advanced testing scenarios (FileSystemWatcher, SafeFileHandles, multiple drives)
- Additional abstractions (ITimeSystem, IRandomSystem)
- Cross-platform file system simulation (Linux, MacOS, Windows)
- More extensive and consistent behavior validation
- Active development and new features
### Migrating from TestableIO
Switching from TestableIO to Testably only requires changes in your test projects:
1. Replace the NuGet package reference in your test projects:
```xml
```
2. Update your test code to use the new `MockFileSystem`:
```csharp
// Before (TestableIO)
var fileSystem = new MockFileSystem();
fileSystem.AddDirectory("some-directory");
fileSystem.AddFile("some-file.txt", new MockFileData("content"));
// After (Testably)
var fileSystem = new MockFileSystem();
fileSystem.Directory.CreateDirectory("some-directory");
fileSystem.File.WriteAllText("some-file.txt", "content");
// or using fluent initialization:
fileSystem.Initialize()
.WithSubdirectory("some-directory")
.WithFile("some-file.txt").Which(f => f
.HasStringContent("content"));
```
Your production code using `IFileSystem` remains unchanged.
## Other related projects
- [`System.IO.Abstractions.Extensions`](https://github.com/TestableIO/System.IO.Abstractions.Extensions)
provides convenience functionality on top of the core abstractions.
- [`System.IO.Abstractions.Analyzers`](https://github.com/TestableIO/System.IO.Abstractions.Analyzers)
provides Roslyn analyzers to help use abstractions over static methods.
================================================
FILE: System.IO.Abstractions.slnx
================================================
================================================
FILE: benchmarks/TestableIO.System.IO.Abstractions.Benchmarks/FileSystemAbstractionBenchmarks.cs
================================================
using System.IO.Abstractions.Benchmarks.Support;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Order;
namespace System.IO.Abstractions.Benchmarks;
//[SimpleJob(launchCount: 3, warmupCount: 10, targetCount: 30)]
[RPlotExporter]
[MemoryDiagnoser]
[Orderer(summaryOrderPolicy: SummaryOrderPolicy.FastestToSlowest)]
[RankColumn]
public class FileSystemAbstractionBenchmarks
{
///
/// FileSupport type to avoid counting object initialisation on the benchmark
///
private readonly FileSupport _fileSupport;
private readonly DirectorySupport _directorySupport;
private readonly string _path = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
public FileSystemAbstractionBenchmarks()
{
// Initialize file support
_fileSupport = new FileSupport();
_directorySupport = new DirectorySupport();
}
#region File IsFile
[Benchmark]
public void FileExists_DotNet() => FileSupportStatic.IsFile(_path);
[Benchmark]
public void FileExists_Abstraction() => _fileSupport.IsFile(_path);
#endregion
#region Directory Exists
[Benchmark]
public void DirectoryExists_DotNet() => DirectorySupportStatic.Exists(_path);
[Benchmark]
public void DirectoryExists_Abstraction() => _directorySupport.Exists(_path);
#endregion
}
================================================
FILE: benchmarks/TestableIO.System.IO.Abstractions.Benchmarks/MockFileSystemBenchmarks.cs
================================================
using BenchmarkDotNet.Attributes;
using System.Collections.Generic;
using System.IO.Abstractions.TestingHelpers;
using System.Linq;
using XFS = System.IO.Abstractions.TestingHelpers.MockUnixSupport;
namespace System.IO.Abstractions.Benchmarks;
[RPlotExporter]
[MemoryDiagnoser]
public class MockFileSystemBenchmarks
{
private readonly Dictionary testData = CreateTestData();
private static Dictionary CreateTestData()
{
var filesCount = 100000;
var maxDirectoryDepth = 8;
return Enumerable.Range(0, filesCount).ToDictionary(
i => XFS.Path(@$"C:\{string.Join(@"\", Enumerable.Range(0, i % maxDirectoryDepth + 1).Select(i => i.ToString()))}\{i}.bin"),
i => new MockFileData(i.ToString()));
}
[Benchmark]
public MockFileSystem MockFileSystem_Constructor() => new(testData);
}
================================================
FILE: benchmarks/TestableIO.System.IO.Abstractions.Benchmarks/Program.cs
================================================
namespace System.IO.Abstractions.Benchmarks;
using BenchmarkDotNet.Running;
static class Program
{
public static void Main(string[] args)
{
BenchmarkRunner.Run(typeof(Program).Assembly);
}
}
================================================
FILE: benchmarks/TestableIO.System.IO.Abstractions.Benchmarks/Properties/launchSettings.json
================================================
{
"profiles": {
"yapm": {
"commandName": "Project",
"commandLineArgs": "apply"
}
}
}
================================================
FILE: benchmarks/TestableIO.System.IO.Abstractions.Benchmarks/Support/DirectorySupport.cs
================================================
namespace System.IO.Abstractions.Benchmarks.Support;
public class DirectorySupport
{
private readonly IFileSystem _fileSystem;
public DirectorySupport(IFileSystem fileSystem)
{
_fileSystem = fileSystem;
}
public DirectorySupport() : this(new FileSystem())
{
// Default implementation for FileSystem
}
public bool IsDirectory(string path)
{
return _fileSystem.Directory.Exists(path);
}
private static string GetRandomTempDirectory()
{
return Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
}
public string CreateRandomDirectory()
{
var randomPath = GetRandomTempDirectory();
_fileSystem.Directory.CreateDirectory(randomPath);
return randomPath;
}
private void DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs = true, bool overwrite = true)
{
// Get the subdirectories for the specified directory.
var dir = _fileSystem.DirectoryInfo.New(sourceDirName);
if (!dir.Exists)
{
throw new DirectoryNotFoundException(
"Source directory does not exist or could not be found: "
+ sourceDirName);
}
var dirs = dir.GetDirectories();
// If the destination directory doesn't exist, create it.
if (!_fileSystem.Directory.Exists(destDirName))
{
_fileSystem.Directory.CreateDirectory(destDirName);
}
// Get the files in the directory and copy them to the new location.
var files = dir.GetFiles();
foreach (var file in files)
{
string temppath = Path.Combine(destDirName, file.Name);
file.CopyTo(temppath, overwrite);
}
// If copying subdirectories, copy them and their contents to new location.
if (copySubDirs)
{
foreach (var subdir in dirs)
{
string temppath = Path.Combine(destDirName, subdir.Name);
DirectoryCopy(subdir.FullName, temppath, copySubDirs);
}
}
}
public void CreateIfNotExists(string directory)
{
if (!_fileSystem.Directory.Exists(directory))
{
_fileSystem.Directory.CreateDirectory(directory);
}
}
public bool Exists(string directory)
{
return _fileSystem.Directory.Exists(directory);
}
}
================================================
FILE: benchmarks/TestableIO.System.IO.Abstractions.Benchmarks/Support/DirectorySupportStatic.cs
================================================
namespace System.IO.Abstractions.Benchmarks.Support;
public static class DirectorySupportStatic
{
public static bool IsDirectory(string path)
{
return Directory.Exists(path);
}
private static string GetRandomTempDirectory()
{
return Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
}
public static string CreateDirectory()
{
var randomPath = GetRandomTempDirectory();
Directory.CreateDirectory(randomPath);
return randomPath;
}
private static void DirectoryCopy(string sourceDirName, string destDirName, bool copySubDirs = true, bool overwrite = true)
{
// Get the subdirectories for the specified directory.
DirectoryInfo dir = new DirectoryInfo(sourceDirName);
if (!dir.Exists)
{
throw new DirectoryNotFoundException(
"Source directory does not exist or could not be found: "
+ sourceDirName);
}
DirectoryInfo[] dirs = dir.GetDirectories();
// If the destination directory doesn't exist, create it.
if (!Directory.Exists(destDirName))
{
Directory.CreateDirectory(destDirName);
}
// Get the files in the directory and copy them to the new location.
FileInfo[] files = dir.GetFiles();
foreach (FileInfo file in files)
{
string temppath = Path.Combine(destDirName, file.Name);
file.CopyTo(temppath, overwrite);
}
// If copying subdirectories, copy them and their contents to new location.
if (copySubDirs)
{
foreach (DirectoryInfo subdir in dirs)
{
string temppath = Path.Combine(destDirName, subdir.Name);
DirectoryCopy(subdir.FullName, temppath, copySubDirs);
}
}
}
public static void CreateIfNotExists(string directory)
{
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
}
public static bool Exists(string directory) => Directory.Exists(directory);
}
================================================
FILE: benchmarks/TestableIO.System.IO.Abstractions.Benchmarks/Support/FileSupport.cs
================================================
namespace System.IO.Abstractions.Benchmarks.Support;
public class FileSupport
{
private readonly IFileSystem _fileSystem;
public FileSupport(IFileSystem fileSystem)
{
_fileSystem = fileSystem;
}
public FileSupport() : this(new FileSystem())
{
// Default implementation for FileSystem
}
private static string GetRandomTempFile()
{
return Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
}
public bool IsFile(string path)
{
return _fileSystem.File.Exists(path);
}
///
/// Checks and deletes given file if it does exists.
///
/// Path of the file
public void DeleteIfExists(string filePath)
{
if (_fileSystem.File.Exists(filePath))
{
_fileSystem.File.Delete(filePath);
}
}
}
================================================
FILE: benchmarks/TestableIO.System.IO.Abstractions.Benchmarks/Support/FileSupportStatic.cs
================================================
namespace System.IO.Abstractions.Benchmarks.Support;
public static class FileSupportStatic
{
public static string GetRandomTempFile()
{
return Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
}
public static bool IsFile(string path)
{
return File.Exists(path);
}
///
/// Checks and deletes given file if it does exists.
///
/// Path of the file
public static void DeleteIfExists(string filePath)
{
if (File.Exists(filePath))
{
File.Delete(filePath);
}
}
}
================================================
FILE: benchmarks/TestableIO.System.IO.Abstractions.Benchmarks/TestableIO.System.IO.Abstractions.Benchmarks.csproj
================================================
TestableIO.System.IO.Abstractions.Benchmarks
TestableIO.System.IO.Abstractions.Benchmarks
Bencharmks comparisons.
net9.0
https://github.com/TestableIO/System.IO.Abstractions
MIT
testing
false
false
Exe
Exe
================================================
FILE: build.cmd
================================================
:; set -eo pipefail
:; SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd)
:; ${SCRIPT_DIR}/build.sh "$@"
:; exit $?
@ECHO OFF
powershell -ExecutionPolicy ByPass -NoProfile -File "%~dp0build.ps1" %*
================================================
FILE: build.ps1
================================================
[CmdletBinding()]
Param(
[Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)]
[string[]]$BuildArguments
)
Write-Output "PowerShell $($PSVersionTable.PSEdition) version $($PSVersionTable.PSVersion)"
Set-StrictMode -Version 2.0; $ErrorActionPreference = "Stop"; $ConfirmPreference = "None"; trap { Write-Error $_ -ErrorAction Continue; exit 1 }
$PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
###########################################################################
# CONFIGURATION
###########################################################################
$BuildProjectFile = "$PSScriptRoot\Pipeline\Build.csproj"
$TempDirectory = "$PSScriptRoot\\.nuke\temp"
$DotNetGlobalFile = "$PSScriptRoot\\global.json"
$DotNetInstallUrl = "https://dot.net/v1/dotnet-install.ps1"
$DotNetChannel = "STS"
$env:DOTNET_CLI_TELEMETRY_OPTOUT = 1
$env:DOTNET_NOLOGO = 1
###########################################################################
# EXECUTION
###########################################################################
function ExecSafe([scriptblock] $cmd) {
& $cmd
if ($LASTEXITCODE) { exit $LASTEXITCODE }
}
# If dotnet CLI is installed globally and it matches requested version, use for execution
if ($null -ne (Get-Command "dotnet" -ErrorAction SilentlyContinue) -and `
$(dotnet --version) -and $LASTEXITCODE -eq 0) {
$env:DOTNET_EXE = (Get-Command "dotnet").Path
}
else {
# Download install script
$DotNetInstallFile = "$TempDirectory\dotnet-install.ps1"
New-Item -ItemType Directory -Path $TempDirectory -Force | Out-Null
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
(New-Object System.Net.WebClient).DownloadFile($DotNetInstallUrl, $DotNetInstallFile)
# If global.json exists, load expected version
if (Test-Path $DotNetGlobalFile) {
$DotNetGlobal = $(Get-Content $DotNetGlobalFile | Out-String | ConvertFrom-Json)
if ($DotNetGlobal.PSObject.Properties["sdk"] -and $DotNetGlobal.sdk.PSObject.Properties["version"]) {
$DotNetVersion = $DotNetGlobal.sdk.version
}
}
# Install by channel or version
$DotNetDirectory = "$TempDirectory\dotnet-win"
if (!(Test-Path variable:DotNetVersion)) {
ExecSafe { & powershell $DotNetInstallFile -InstallDir $DotNetDirectory -Channel $DotNetChannel -NoPath }
} else {
ExecSafe { & powershell $DotNetInstallFile -InstallDir $DotNetDirectory -Version $DotNetVersion -NoPath }
}
$env:DOTNET_EXE = "$DotNetDirectory\dotnet.exe"
$env:PATH = "$DotNetDirectory;$env:PATH"
}
Write-Output "Microsoft (R) .NET SDK version $(& $env:DOTNET_EXE --version)"
if (Test-Path env:NUKE_ENTERPRISE_TOKEN) {
& $env:DOTNET_EXE nuget remove source "nuke-enterprise" > $null
& $env:DOTNET_EXE nuget add source "https://f.feedz.io/nuke/enterprise/nuget" --name "nuke-enterprise" --username "PAT" --password $env:NUKE_ENTERPRISE_TOKEN > $null
}
ExecSafe { & $env:DOTNET_EXE build $BuildProjectFile /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet }
ExecSafe { & $env:DOTNET_EXE run --project $BuildProjectFile --no-build -- $BuildArguments }
================================================
FILE: build.sh
================================================
#!/usr/bin/env bash
bash --version 2>&1 | head -n 1
set -eo pipefail
SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd)
###########################################################################
# CONFIGURATION
###########################################################################
BUILD_PROJECT_FILE="$SCRIPT_DIR/Pipeline/Build.csproj"
TEMP_DIRECTORY="$SCRIPT_DIR//.nuke/temp"
DOTNET_GLOBAL_FILE="$SCRIPT_DIR//global.json"
DOTNET_INSTALL_URL="https://dot.net/v1/dotnet-install.sh"
DOTNET_CHANNEL="STS"
export DOTNET_CLI_TELEMETRY_OPTOUT=1
export DOTNET_NOLOGO=1
###########################################################################
# EXECUTION
###########################################################################
function FirstJsonValue {
perl -nle 'print $1 if m{"'"$1"'": "([^"]+)",?}' <<< "${@:2}"
}
# If dotnet CLI is installed globally and it matches requested version, use for execution
if [ -x "$(command -v dotnet)" ] && dotnet --version &>/dev/null; then
export DOTNET_EXE="$(command -v dotnet)"
else
# Download install script
DOTNET_INSTALL_FILE="$TEMP_DIRECTORY/dotnet-install.sh"
mkdir -p "$TEMP_DIRECTORY"
curl -Lsfo "$DOTNET_INSTALL_FILE" "$DOTNET_INSTALL_URL"
chmod +x "$DOTNET_INSTALL_FILE"
# If global.json exists, load expected version
if [[ -f "$DOTNET_GLOBAL_FILE" ]]; then
DOTNET_VERSION=$(FirstJsonValue "version" "$(cat "$DOTNET_GLOBAL_FILE")")
if [[ "$DOTNET_VERSION" == "" ]]; then
unset DOTNET_VERSION
fi
fi
# Install by channel or version
DOTNET_DIRECTORY="$TEMP_DIRECTORY/dotnet-unix"
if [[ -z ${DOTNET_VERSION+x} ]]; then
"$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --channel "$DOTNET_CHANNEL" --no-path
else
"$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --version "$DOTNET_VERSION" --no-path
fi
export DOTNET_EXE="$DOTNET_DIRECTORY/dotnet"
export PATH="$DOTNET_DIRECTORY:$PATH"
fi
echo "Microsoft (R) .NET SDK version $("$DOTNET_EXE" --version)"
if [[ ! -z ${NUKE_ENTERPRISE_TOKEN+x} && "$NUKE_ENTERPRISE_TOKEN" != "" ]]; then
"$DOTNET_EXE" nuget remove source "nuke-enterprise" &>/dev/null || true
"$DOTNET_EXE" nuget add source "https://f.feedz.io/nuke/enterprise/nuget" --name "nuke-enterprise" --username "PAT" --password "$NUKE_ENTERPRISE_TOKEN" --store-password-in-clear-text &>/dev/null || true
fi
"$DOTNET_EXE" build "$BUILD_PROJECT_FILE" /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet
"$DOTNET_EXE" run --project "$BUILD_PROJECT_FILE" --no-build -- "$@"
================================================
FILE: global.json
================================================
{
"sdk": {
"version": "10.0.201",
"rollForward": "latestMinor"
}
}
================================================
FILE: nuget.config
================================================
================================================
FILE: renovate.json5
================================================
{
extends: ["config:base"],
ignorePaths: [
// Clear the default list to enable Renovate for all files
],
}
================================================
FILE: src/Directory.Build.props
================================================
net472;netstandard2.0;netstandard2.1;net6.0;net8.0;net9.0;net10.0
https://github.com/TestableIO/System.IO.Abstractions.git
true
snupkg
true
true
icon_256x256.png
all
runtime; build; native; contentfiles; analyzers; buildtransitive
================================================
FILE: src/System.IO.Abstractions/System.IO.Abstractions.csproj
================================================
System.IO.Abstractions
System.IO.Abstractions
A set of abstractions to help make file system interactions testable.
================================================
FILE: src/System.IO.Abstractions.TestingHelpers/System.IO.Abstractions.TestingHelpers.csproj
================================================
System.IO.Abstractions.TestingHelpers
System.IO.Abstractions.TestingHelpers
A set of pre-built mocks to help when testing file system interactions.
================================================
FILE: src/TestableIO.System.IO.Abstractions/AssemblyRedirects.cs
================================================
using System.IO.Abstractions;
using System.Runtime.CompilerServices;
[assembly: TypeForwardedTo(typeof(FileSystemStream))]
[assembly: TypeForwardedTo(typeof(IDirectory))]
[assembly: TypeForwardedTo(typeof(IDirectoryInfo))]
[assembly: TypeForwardedTo(typeof(IDirectoryInfoFactory))]
[assembly: TypeForwardedTo(typeof(IDriveInfo))]
[assembly: TypeForwardedTo(typeof(IDriveInfoFactory))]
[assembly: TypeForwardedTo(typeof(IFile))]
[assembly: TypeForwardedTo(typeof(IFileInfo))]
[assembly: TypeForwardedTo(typeof(IFileInfoFactory))]
[assembly: TypeForwardedTo(typeof(IFileStreamFactory))]
[assembly: TypeForwardedTo(typeof(IFileSystem))]
[assembly: TypeForwardedTo(typeof(IFileSystemAclSupport))]
[assembly: TypeForwardedTo(typeof(IFileSystemEntity))]
[assembly: TypeForwardedTo(typeof(IFileSystemInfo))]
[assembly: TypeForwardedTo(typeof(IFileSystemWatcher))]
[assembly: TypeForwardedTo(typeof(IFileSystemWatcherFactory))]
[assembly: TypeForwardedTo(typeof(IFileVersionInfo))]
[assembly: TypeForwardedTo(typeof(IFileVersionInfoFactory))]
[assembly: TypeForwardedTo(typeof(IPath))]
[assembly: TypeForwardedTo(typeof(IWaitForChangedResult))]
================================================
FILE: src/TestableIO.System.IO.Abstractions/TestableIO.System.IO.Abstractions.csproj
================================================
TestableIO.System.IO.Abstractions
System.IO.Abstractions
A set of abstractions to help make file system interactions testable.
enable
preview
all
runtime; build; native; contentfiles; analyzers; buildtransitive
================================================
FILE: src/TestableIO.System.IO.Abstractions.TestingHelpers/CommonExceptions.cs
================================================
using System.Globalization;
using System.Runtime.InteropServices;
namespace System.IO.Abstractions.TestingHelpers;
internal static class CommonExceptions
{
private const int _fileLockHResult = unchecked((int)0x80070020);
public static FileNotFoundException FileNotFound(string path) =>
new FileNotFoundException(
string.Format(
CultureInfo.InvariantCulture,
StringResources.Manager.GetString("COULD_NOT_FIND_FILE_EXCEPTION"),
path
),
path
);
public static DirectoryNotFoundException CouldNotFindPartOfPath(string path) =>
new DirectoryNotFoundException(
string.Format(
CultureInfo.InvariantCulture,
StringResources.Manager.GetString("COULD_NOT_FIND_PART_OF_PATH_EXCEPTION"),
path
)
);
public static UnauthorizedAccessException AccessDenied(string path) =>
new UnauthorizedAccessException(
string.Format(
CultureInfo.InvariantCulture,
StringResources.Manager.GetString("ACCESS_TO_THE_PATH_IS_DENIED"),
path
)
);
public static NotSupportedException InvalidUseOfVolumeSeparator() =>
new NotSupportedException(StringResources.Manager.GetString("THE_PATH_IS_NOT_OF_A_LEGAL_FORM"));
public static ArgumentException PathIsNotOfALegalForm(string paramName) =>
new ArgumentException(
StringResources.Manager.GetString("THE_PATH_IS_NOT_OF_A_LEGAL_FORM"),
paramName
);
public static ArgumentNullException FilenameCannotBeNull(string paramName) =>
new ArgumentNullException(
paramName,
StringResources.Manager.GetString("FILENAME_CANNOT_BE_NULL")
);
public static ArgumentException IllegalCharactersInPath(string paramName = null) =>
paramName != null
? new ArgumentException(StringResources.Manager.GetString("ILLEGAL_CHARACTERS_IN_PATH_EXCEPTION"), paramName)
: new ArgumentException(StringResources.Manager.GetString("ILLEGAL_CHARACTERS_IN_PATH_EXCEPTION"));
public static ArgumentException InvalidUncPath(string paramName) =>
RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
? new ArgumentException(@"The UNC path should be of the form \\server\share.", paramName)
: new ArgumentException(@"The UNC path should be of the form //server/share.", paramName);
public static IOException ProcessCannotAccessFileInUse(string paramName = null) =>
paramName != null
? new IOException(string.Format(StringResources.Manager.GetString("PROCESS_CANNOT_ACCESS_FILE_IN_USE_WITH_FILENAME"), paramName), _fileLockHResult)
: new IOException(StringResources.Manager.GetString("PROCESS_CANNOT_ACCESS_FILE_IN_USE"), _fileLockHResult);
public static IOException FileAlreadyExists(string paramName) =>
new IOException(string.Format(StringResources.Manager.GetString("FILE_ALREADY_EXISTS"), paramName));
public static ArgumentException InvalidAccessCombination(FileMode mode, FileAccess access)
=> new ArgumentException(string.Format(StringResources.Manager.GetString("INVALID_ACCESS_COMBINATION"), mode, access), nameof(access));
public static ArgumentException AppendAccessOnlyInWriteOnlyMode()
=> new ArgumentException(string.Format(StringResources.Manager.GetString("APPEND_ACCESS_ONLY_IN_WRITE_ONLY_MODE")), "access");
public static NotImplementedException NotImplemented() =>
new NotImplementedException(StringResources.Manager.GetString("NOT_IMPLEMENTED_EXCEPTION"));
public static IOException CannotCreateBecauseSameNameAlreadyExists(string path) =>
new IOException(
string.Format(
CultureInfo.InvariantCulture,
StringResources.Manager.GetString("CANNOT_CREATE_BECAUSE_SAME_NAME_ALREADY_EXISTS"),
path
)
);
public static IOException NameCannotBeResolvedByTheSystem(string path) =>
new IOException(
string.Format(
CultureInfo.InvariantCulture,
StringResources.Manager.GetString("NAME_CANNOT_BE_RESOLVED_BY_THE_SYSTEM"),
path
)
);
public static DirectoryNotFoundException PathDoesNotExistOrCouldNotBeFound(string path) =>
new DirectoryNotFoundException(
string.Format(
CultureInfo.InvariantCulture,
StringResources.Manager.GetString("PATH_DOES_NOT_EXIST_OR_COULD_NOT_BE_FOUND"),
path
)
);
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.TestingHelpers/FileHandles.cs
================================================
using System;
using System.Collections.Concurrent;
using System.IO;
using System.IO.Abstractions.TestingHelpers;
public class FileHandles
{
private readonly ConcurrentDictionary> handles = new();
public void AddHandle(string path, Guid guid, FileAccess access, FileShare share)
{
var pathHandles = handles.GetOrAdd(
path,
_ => new ConcurrentDictionary());
var requiredShare = AccessToShare(access);
foreach (var (existingAccess, existingShare) in pathHandles.Values)
{
var existingRequiredShare = AccessToShare(existingAccess);
var existingBlocksNew = (existingShare & requiredShare) != requiredShare;
var newBlocksExisting = (share & existingRequiredShare) != existingRequiredShare;
if (existingBlocksNew || newBlocksExisting)
{
throw CommonExceptions.ProcessCannotAccessFileInUse(path);
}
}
pathHandles[guid] = (access, share);
}
public void RemoveHandle(string path, Guid guid)
{
if (handles.TryGetValue(path, out var pathHandles))
{
pathHandles.TryRemove(guid, out _);
if (pathHandles.IsEmpty)
{
handles.TryRemove(path, out _);
}
}
}
private static FileShare AccessToShare(FileAccess access)
{
var share = FileShare.None;
if (access.HasFlag(FileAccess.Read))
{
share |= FileShare.Read;
}
if (access.HasFlag(FileAccess.Write))
{
share |= FileShare.Write;
}
return share;
}
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.TestingHelpers/IMockFileDataAccessor.cs
================================================
using System.Collections.Generic;
using System.Reflection;
namespace System.IO.Abstractions.TestingHelpers;
///
/// Provides access to the file system storage.
///
public interface IMockFileDataAccessor : IFileSystem
{
///
/// Adjust the times of the .
///
/// The for which the times should be adjusted.
/// The adjustments to make on the .
/// The adjusted file.
MockFileData AdjustTimes(MockFileData fileData, TimeAdjustments timeAdjustments);
///
/// Gets a file.
///
/// The path of the file to get.
/// The file. if the file does not exist.
MockFileData GetFile(string path);
///
/// Gets a drive.
///
/// The name of the drive to get.
/// The drive. if the drive does not exist.
MockDriveData GetDrive(string name);
///
/// Adds the file.
///
/// The path of the file to add.
/// The file data to add.
/// Flag indicating if the access conditions should be verified.
void AddFile(string path, MockFileData mockFile, bool verifyAccess = true);
///
///
void AddDirectory(string path);
///
///
void AddDrive(string name, MockDriveData mockDrive);
///
///
void AddFileFromEmbeddedResource(string path, Assembly resourceAssembly, string embeddedResourcePath);
///
///
void AddFilesFromEmbeddedNamespace(string path, Assembly resourceAssembly, string embeddedResourcePath);
///
///
void MoveDirectory(string sourcePath, string destPath);
///
/// Removes the file.
///
/// The file to remove.
/// Flag indicating if the access conditions should be verified.
///
/// The file must not exist.
///
void RemoveFile(string path, bool verifyAccess = true);
///
/// Determines whether the file exists.
///
/// The file to check.
/// if the file exists; otherwise, .
bool FileExists(string path);
///
/// Gets all unique paths of all files and directories.
///
IEnumerable AllPaths { get; }
///
/// Gets the paths of all files.
///
IEnumerable AllFiles { get; }
///
/// Gets the paths of all directories.
///
IEnumerable AllDirectories { get; }
///
/// Gets the names of all drives.
///
IEnumerable AllDrives { get; }
///
/// Gets a helper for string operations.
///
StringOperations StringOperations { get; }
///
/// Gets a helper for verifying file system paths.
///
PathVerifier PathVerifier { get; }
///
/// Gets a reference to the underlying file system.
///
IFileSystem FileSystem { get; }
///
/// Gets a reference to the open file handles.
///
FileHandles FileHandles { get; }
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.TestingHelpers/MockDirectory.cs
================================================
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
namespace System.IO.Abstractions.TestingHelpers;
using XFS = MockUnixSupport;
///
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public class MockDirectory : DirectoryBase
{
private readonly IMockFileDataAccessor mockFileDataAccessor;
private string currentDirectory;
///
public MockDirectory(IMockFileDataAccessor mockFileDataAccessor, FileBase fileBase, string currentDirectory) :
this(mockFileDataAccessor, currentDirectory)
{
}
///
public MockDirectory(IMockFileDataAccessor mockFileDataAccessor, string currentDirectory) : base(
mockFileDataAccessor?.FileSystem)
{
this.currentDirectory = currentDirectory;
this.mockFileDataAccessor =
mockFileDataAccessor ?? throw new ArgumentNullException(nameof(mockFileDataAccessor));
}
///
public override IDirectoryInfo CreateDirectory(string path)
{
return CreateDirectoryInternal(path);
}
#if FEATURE_UNIX_FILE_MODE
///
public override IDirectoryInfo CreateDirectory(string path, UnixFileMode unixCreateMode)
{
throw CommonExceptions.NotImplemented();
}
#endif
private IDirectoryInfo CreateDirectoryInternal(string path)
{
if (path == null)
{
throw new ArgumentNullException(nameof(path));
}
if (path.Length == 0)
{
throw new ArgumentException(
StringResources.Manager.GetString("PATH_CANNOT_BE_THE_EMPTY_STRING_OR_ALL_WHITESPACE"), "path");
}
if (mockFileDataAccessor.PathVerifier.HasIllegalCharacters(path, true))
{
throw CommonExceptions.IllegalCharactersInPath(nameof(path));
}
path = mockFileDataAccessor.Path.GetFullPath(path).TrimSlashes();
if (XFS.IsWindowsPlatform())
{
path = path.TrimEnd(' ');
}
var existingFile = mockFileDataAccessor.GetFile(path);
if (existingFile == null)
{
mockFileDataAccessor.AddDirectory(path);
}
else if (!existingFile.IsDirectory)
{
throw CommonExceptions.FileAlreadyExists("path");
}
var created = new MockDirectoryInfo(mockFileDataAccessor, path);
return created;
}
#if FEATURE_CREATE_SYMBOLIC_LINK
///
public override IFileSystemInfo CreateSymbolicLink(string path, string pathToTarget)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, nameof(path));
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(pathToTarget, nameof(pathToTarget));
if (Exists(path))
{
throw CommonExceptions.FileAlreadyExists(nameof(path));
}
mockFileDataAccessor.AddDirectory(path);
mockFileDataAccessor.GetFile(path).LinkTarget = pathToTarget;
var directoryInfo = new MockDirectoryInfo(mockFileDataAccessor, path);
directoryInfo.Attributes |= FileAttributes.ReparsePoint;
return directoryInfo;
}
#endif
#if FEATURE_CREATE_TEMP_SUBDIRECTORY
///
public override IDirectoryInfo CreateTempSubdirectory(string prefix = null)
{
prefix ??= "";
string potentialTempDirectory;
// Perform directory name generation in a loop, just in case the randomly generated name already exists.
do
{
var randomDir = $"{prefix}{FileSystem.Path.GetRandomFileName()}";
potentialTempDirectory = Path.Combine(FileSystem.Path.GetTempPath(), randomDir);
} while (Exists(potentialTempDirectory));
return CreateDirectoryInternal(potentialTempDirectory);
}
#endif
///
public override void Delete(string path)
{
Delete(path, false);
}
///
public override void Delete(string path, bool recursive)
{
path = mockFileDataAccessor.Path.GetFullPath(path).TrimSlashes();
var stringOps = mockFileDataAccessor.StringOperations;
var pathWithDirectorySeparatorChar = $"{path}{Path.DirectorySeparatorChar}";
var affectedPaths = mockFileDataAccessor
.AllPaths
.Where(p => stringOps.Equals(p, path) || stringOps.StartsWith(p, pathWithDirectorySeparatorChar))
.ToList();
if (!affectedPaths.Any())
{
throw CommonExceptions.PathDoesNotExistOrCouldNotBeFound(path);
}
if (!recursive && affectedPaths.Count > 1)
{
throw new IOException("The directory specified by " + path +
" is read-only, or recursive is false and " + path +
" is not an empty directory.");
}
bool isFile = !mockFileDataAccessor.GetFile(path).IsDirectory;
if (isFile)
{
throw new IOException("The directory name is invalid.");
}
foreach (var affectedPath in affectedPaths)
{
mockFileDataAccessor.RemoveFile(affectedPath);
}
}
///
public override bool Exists(string path)
{
if (path == "/")
{
return true;
}
try
{
path = path.TrimSlashes();
path = mockFileDataAccessor.Path.GetFullPath(path);
return mockFileDataAccessor.GetFile(path)?.IsDirectory ?? false;
}
catch (Exception)
{
return false;
}
}
///
public override DateTime GetCreationTime(string path)
{
return mockFileDataAccessor.File.GetCreationTime(path);
}
///
public override DateTime GetCreationTimeUtc(string path)
{
return mockFileDataAccessor.File.GetCreationTimeUtc(path);
}
///
public override string GetCurrentDirectory()
{
return currentDirectory;
}
///
public override string[] GetDirectories(string path)
{
return GetDirectories(path, "*");
}
///
public override string[] GetDirectories(string path, string searchPattern)
{
return GetDirectories(path, searchPattern, SearchOption.TopDirectoryOnly);
}
///
public override string[] GetDirectories(string path, string searchPattern, SearchOption searchOption)
{
return EnumerateDirectories(path, searchPattern, searchOption).ToArray();
}
#if FEATURE_ENUMERATION_OPTIONS
///
public override string[] GetDirectories(string path, string searchPattern,
EnumerationOptions enumerationOptions)
{
return GetDirectories(path, "*", EnumerationOptionsToSearchOption(enumerationOptions));
}
#endif
///
public override string GetDirectoryRoot(string path)
{
return Path.GetPathRoot(path);
}
///
public override string[] GetFiles(string path)
{
// Same as what the real framework does
return GetFiles(path, "*");
}
///
public override string[] GetFiles(string path, string searchPattern)
{
// Same as what the real framework does
return GetFiles(path, searchPattern, SearchOption.TopDirectoryOnly);
}
///
public override string[] GetFiles(string path, string searchPattern, SearchOption searchOption)
{
return GetFilesInternal(mockFileDataAccessor.AllFiles, path, searchPattern, searchOption);
}
#if FEATURE_ENUMERATION_OPTIONS
///
public override string[] GetFiles(string path, string searchPattern, EnumerationOptions enumerationOptions)
{
return GetFiles(path, searchPattern, EnumerationOptionsToSearchOption(enumerationOptions));
}
#endif
private string[] GetFilesInternal(
IEnumerable files,
string path,
string searchPattern,
SearchOption searchOption)
{
if (path == null)
{
throw new ArgumentNullException(nameof(path));
}
if (path.Any(c => Path.GetInvalidPathChars().Contains(c)))
{
throw new ArgumentException("Invalid character(s) in path", nameof(path));
}
CheckSearchPattern(searchPattern);
if (searchPattern.Equals(string.Empty, StringComparison.OrdinalIgnoreCase))
{
searchPattern = "*";
}
path = path.TrimSlashes();
path = path.NormalizeSlashes();
path = mockFileDataAccessor.Path.GetFullPath(path);
if (!Exists(path))
{
throw CommonExceptions.CouldNotFindPartOfPath(path);
}
if (!path.EndsWith(Path.DirectorySeparatorChar.ToString()))
{
path += Path.DirectorySeparatorChar;
}
var isUnix = XFS.IsUnixPlatform();
var allDirectoriesPattern = isUnix
? @"([^<>:""/|?*]*/)*"
: @"([^<>:""/\\|?*]*\\)*";
var searchEndInStarDot = searchPattern.EndsWith(@"*.");
string fileNamePattern;
string pathPatternNoExtension = string.Empty;
string pathPatternEndsInDot = string.Empty;
string pathPatternSpecial = null;
if (searchPattern == "*")
{
fileNamePattern = isUnix ? @"[^/]*?/?" : @"[^\\]*?\\?";
}
else
{
fileNamePattern = Regex.Escape(searchPattern)
.Replace(@"\*", isUnix ? @"[^<>:""/|?*]*?" : @"[^<>:""/\\|?*]*?")
.Replace(@"\?", isUnix ? @"[^<>:""/|?*]?" : @"[^<>:""/\\|?*]?");
var extension = Path.GetExtension(searchPattern);
bool hasExtensionLengthOfThree = extension != null && extension.Length == 4 &&
!extension.Contains("*") && !extension.Contains("?");
if (hasExtensionLengthOfThree)
{
var fileNamePatternSpecial =
string.Format(CultureInfo.InvariantCulture, "{0}[^.]", fileNamePattern);
pathPatternSpecial = string.Format(
CultureInfo.InvariantCulture,
isUnix ? @"(?i:^{0}{1}{2}(?:/?)$)" : @"(?i:^{0}{1}{2}(?:\\?)$)",
Regex.Escape(path),
searchOption == SearchOption.AllDirectories ? allDirectoriesPattern : string.Empty,
fileNamePatternSpecial);
}
}
var pathPattern = string.Format(
CultureInfo.InvariantCulture,
isUnix ? @"(?i:^{0}{1}{2}(?:/?)$)" : @"(?i:^{0}{1}{2}(?:\\?)$)",
Regex.Escape(path),
searchOption == SearchOption.AllDirectories ? allDirectoriesPattern : string.Empty,
fileNamePattern);
if (searchEndInStarDot)
{
pathPatternNoExtension = ReplaceLastOccurrence(pathPattern, @"]*?\.", @"\.]*?[.]*");
pathPatternEndsInDot = ReplaceLastOccurrence(pathPattern, @"]*?\.", @"]*?[.]{1,}");
}
return files.Where(p =>
!searchEndInStarDot
? (Regex.IsMatch(p, pathPattern) ||
(pathPatternSpecial != null && Regex.IsMatch(p, pathPatternSpecial)))
: (Regex.IsMatch(p, pathPatternNoExtension) || Regex.IsMatch(p, pathPatternEndsInDot))
)
.ToArray();
}
///
public override string[] GetFileSystemEntries(string path)
{
return GetFileSystemEntries(path, "*");
}
///
public override string[] GetFileSystemEntries(string path, string searchPattern)
{
var dirs = GetDirectories(path, searchPattern);
var files = GetFiles(path, searchPattern);
return dirs.Union(files).ToArray();
}
///
public override string[] GetFileSystemEntries(string path, string searchPattern, SearchOption searchOption)
{
var dirs = GetDirectories(path, searchPattern, searchOption);
var files = GetFiles(path, searchPattern, searchOption);
return dirs.Union(files).ToArray();
}
#if FEATURE_ENUMERATION_OPTIONS
///
public override string[] GetFileSystemEntries(string path, string searchPattern,
EnumerationOptions enumerationOptions)
{
return GetFileSystemEntries(path, "*", EnumerationOptionsToSearchOption(enumerationOptions));
}
#endif
///
public override DateTime GetLastAccessTime(string path)
{
return mockFileDataAccessor.File.GetLastAccessTime(path);
}
///
public override DateTime GetLastAccessTimeUtc(string path)
{
return mockFileDataAccessor.File.GetLastAccessTimeUtc(path);
}
///
public override DateTime GetLastWriteTime(string path)
{
return mockFileDataAccessor.File.GetLastWriteTime(path);
}
///
public override DateTime GetLastWriteTimeUtc(string path)
{
return mockFileDataAccessor.File.GetLastWriteTimeUtc(path);
}
///
public override string[] GetLogicalDrives()
{
return mockFileDataAccessor
.AllDirectories
.Select(d => new MockDirectoryInfo(mockFileDataAccessor, d).Root.FullName)
.Select(r => mockFileDataAccessor.StringOperations.ToUpper(r))
.Distinct()
.ToArray();
}
///
public override IDirectoryInfo GetParent(string path)
{
if (path == null)
{
throw new ArgumentNullException(nameof(path));
}
if (path.Length == 0)
{
throw new ArgumentException(
StringResources.Manager.GetString("PATH_CANNOT_BE_THE_EMPTY_STRING_OR_ALL_WHITESPACE"), "path");
}
if (mockFileDataAccessor.PathVerifier.HasIllegalCharacters(path, false))
{
throw new ArgumentException("Path contains invalid path characters.", "path");
}
var absolutePath = mockFileDataAccessor.Path.GetFullPath(path);
var sepAsString = mockFileDataAccessor.Path.DirectorySeparatorChar.ToString();
var lastIndex = 0;
if (absolutePath != sepAsString)
{
var startIndex = mockFileDataAccessor.StringOperations.EndsWith(absolutePath, sepAsString)
? absolutePath.Length - 1
: absolutePath.Length;
lastIndex = absolutePath.LastIndexOf(mockFileDataAccessor.Path.DirectorySeparatorChar, startIndex - 1);
if (lastIndex < 0)
{
return null;
}
}
var parentPath = absolutePath.Substring(0, lastIndex);
if (string.IsNullOrEmpty(parentPath))
{
// On the Unix platform, the parent of a path consisting of a slash followed by
// non-slashes is the root, '/'.
if (XFS.IsUnixPlatform())
{
absolutePath = absolutePath.TrimSlashes();
if (absolutePath.Length > 1 &&
absolutePath.LastIndexOf(mockFileDataAccessor.Path.DirectorySeparatorChar) == 0)
{
return new MockDirectoryInfo(mockFileDataAccessor,
mockFileDataAccessor.Path.DirectorySeparatorChar.ToString());
}
}
return null;
}
return new MockDirectoryInfo(mockFileDataAccessor, parentPath);
}
///
public override void Move(string sourceDirName, string destDirName)
{
var fullSourcePath = mockFileDataAccessor.Path.GetFullPath(sourceDirName).TrimSlashes();
var fullDestPath = mockFileDataAccessor.Path.GetFullPath(destDirName).TrimSlashes();
if (string.Equals(fullSourcePath, fullDestPath, StringComparison.Ordinal))
{
throw new IOException("Source and destination path must be different.");
}
//if we're moving a file, not a directory, call the appropriate file moving function.
var fileData = mockFileDataAccessor.GetFile(fullSourcePath);
if (fileData?.Attributes.HasFlag(FileAttributes.Directory) == false)
{
mockFileDataAccessor.File.Move(fullSourcePath, fullDestPath);
return;
}
var sourceRoot = mockFileDataAccessor.Path.GetPathRoot(fullSourcePath);
var destinationRoot = mockFileDataAccessor.Path.GetPathRoot(fullDestPath);
if (!mockFileDataAccessor.StringOperations.Equals(sourceRoot, destinationRoot))
{
throw new IOException(
"Source and destination path must have identical roots. Move will not work across volumes.");
}
if (!mockFileDataAccessor.Directory.Exists(fullSourcePath))
{
throw CommonExceptions.CouldNotFindPartOfPath(sourceDirName);
}
if (!mockFileDataAccessor.Directory.GetParent(fullDestPath).Exists)
{
throw CommonExceptions.CouldNotFindPartOfPath(destDirName);
}
if (mockFileDataAccessor.Directory.Exists(fullDestPath) || mockFileDataAccessor.File.Exists(fullDestPath))
{
// In Windows, file/dir names are case sensetive, C:\\temp\\src and C:\\temp\\SRC and treated different
if (XFS.IsUnixPlatform() ||
!string.Equals(fullSourcePath, fullDestPath, StringComparison.OrdinalIgnoreCase))
{
throw CommonExceptions.CannotCreateBecauseSameNameAlreadyExists(fullDestPath);
}
}
mockFileDataAccessor.MoveDirectory(fullSourcePath, fullDestPath);
}
#if FEATURE_CREATE_SYMBOLIC_LINK
///
public override IFileSystemInfo ResolveLinkTarget(string linkPath, bool returnFinalTarget)
{
var initialContainer = mockFileDataAccessor.GetFile(linkPath);
if (initialContainer.LinkTarget != null)
{
var nextLocation = initialContainer.LinkTarget;
var nextContainer = mockFileDataAccessor.GetFile(nextLocation);
if (returnFinalTarget)
{
// The maximum number of symbolic links that are followed:
// https://learn.microsoft.com/en-us/dotnet/api/system.io.directory.resolvelinktarget?view=net-6.0#remarks
int maxResolveLinks = XFS.IsWindowsPlatform() ? 63 : 40;
for (int i = 1; i < maxResolveLinks; i++)
{
if (nextContainer.LinkTarget == null)
{
break;
}
nextLocation = nextContainer.LinkTarget;
nextContainer = mockFileDataAccessor.GetFile(nextLocation);
}
if (nextContainer.LinkTarget != null)
{
throw CommonExceptions.NameCannotBeResolvedByTheSystem(linkPath);
}
}
if (nextContainer.IsDirectory)
{
return new MockDirectoryInfo(mockFileDataAccessor, nextLocation);
}
else
{
return new MockFileInfo(mockFileDataAccessor, nextLocation);
}
}
throw CommonExceptions.NameCannotBeResolvedByTheSystem(linkPath);
}
#endif
///
public override void SetCreationTime(string path, DateTime creationTime)
{
mockFileDataAccessor.File.SetCreationTime(path, creationTime);
}
///
public override void SetCreationTimeUtc(string path, DateTime creationTimeUtc)
{
mockFileDataAccessor.File.SetCreationTimeUtc(path, creationTimeUtc);
}
///
public override void SetCurrentDirectory(string path)
{
currentDirectory = mockFileDataAccessor.Path.GetFullPath(path);
}
///
public override void SetLastAccessTime(string path, DateTime lastAccessTime)
{
mockFileDataAccessor.File.SetLastAccessTime(path, lastAccessTime);
}
///
public override void SetLastAccessTimeUtc(string path, DateTime lastAccessTimeUtc)
{
mockFileDataAccessor.File.SetLastAccessTimeUtc(path, lastAccessTimeUtc);
}
///
public override void SetLastWriteTime(string path, DateTime lastWriteTime)
{
mockFileDataAccessor.File.SetLastWriteTime(path, lastWriteTime);
}
///
public override void SetLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc)
{
mockFileDataAccessor.File.SetLastWriteTimeUtc(path, lastWriteTimeUtc);
}
///
public override IEnumerable EnumerateDirectories(string path)
{
return EnumerateDirectories(path, "*");
}
///
public override IEnumerable EnumerateDirectories(string path, string searchPattern)
{
return EnumerateDirectories(path, searchPattern, SearchOption.TopDirectoryOnly);
}
///
public override IEnumerable EnumerateDirectories(string path, string searchPattern, SearchOption searchOption)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
var originalPath = path;
path = path.TrimSlashes();
path = mockFileDataAccessor.Path.GetFullPath(path);
return GetFilesInternal(mockFileDataAccessor.AllDirectories, path, searchPattern, searchOption)
.Where(p => !mockFileDataAccessor.StringOperations.Equals(p, path))
.Select(p => FixPrefix(p, originalPath));
}
private string FixPrefix(string path, string originalPath)
{
var normalizedOriginalPath = mockFileDataAccessor.Path.GetFullPath(originalPath);
var pathWithoutOriginalPath = path.Substring(normalizedOriginalPath.Length)
.TrimStart(mockFileDataAccessor.Path.DirectorySeparatorChar);
return mockFileDataAccessor.Path.Combine(originalPath, pathWithoutOriginalPath);
}
#if FEATURE_ENUMERATION_OPTIONS
///
public override IEnumerable EnumerateDirectories(string path, string searchPattern, EnumerationOptions enumerationOptions)
{
var searchOption = enumerationOptions.RecurseSubdirectories ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
return EnumerateDirectories(path, searchPattern, searchOption);
}
#endif
///
public override IEnumerable EnumerateFiles(string path)
{
return GetFiles(path);
}
///
public override IEnumerable EnumerateFiles(string path, string searchPattern)
{
return GetFiles(path, searchPattern);
}
///
public override IEnumerable EnumerateFiles(string path, string searchPattern, SearchOption searchOption)
{
return GetFiles(path, searchPattern, searchOption);
}
#if FEATURE_ENUMERATION_OPTIONS
///
public override IEnumerable EnumerateFiles(string path, string searchPattern, EnumerationOptions enumerationOptions)
{
var searchOption = enumerationOptions.RecurseSubdirectories ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
return GetFiles(path, searchPattern, searchOption);
}
#endif
///
public override IEnumerable EnumerateFileSystemEntries(string path)
{
return GetFileSystemEntries(path);
}
///
public override IEnumerable EnumerateFileSystemEntries(string path, string searchPattern)
{
return GetFileSystemEntries(path, searchPattern);
}
///
public override IEnumerable EnumerateFileSystemEntries(string path, string searchPattern, SearchOption searchOption)
{
return GetFileSystemEntries(path, searchPattern, searchOption);
}
#if FEATURE_ENUMERATION_OPTIONS
///
public override IEnumerable EnumerateFileSystemEntries(string path, string searchPattern, EnumerationOptions enumerationOptions)
{
var searchOption = enumerationOptions.RecurseSubdirectories ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
var fileSystemEntries = new List(GetFiles(path, searchPattern, searchOption));
fileSystemEntries.AddRange(GetDirectories(path, searchPattern, searchOption));
return fileSystemEntries;
}
#endif
private string EnsureAbsolutePath(string path)
{
return Path.IsPathRooted(path)
? path
: Path.Combine(GetCurrentDirectory(), path);
}
private void CheckSearchPattern(string searchPattern)
{
if (searchPattern == null)
{
throw new ArgumentNullException(nameof(searchPattern));
}
const string TWO_DOTS = "..";
Func createException = () => new ArgumentException(@"Search pattern cannot contain "".."" to move up directories and can be contained only internally in file/directory names, as in ""a..b"".", searchPattern);
if (mockFileDataAccessor.StringOperations.EndsWith(searchPattern, TWO_DOTS))
{
throw createException();
}
var position = mockFileDataAccessor.StringOperations.IndexOf(searchPattern, TWO_DOTS);
if (position >= 0)
{
var characterAfterTwoDots = searchPattern[position + 2];
if (characterAfterTwoDots == Path.DirectorySeparatorChar || characterAfterTwoDots == Path.AltDirectorySeparatorChar)
{
throw createException();
}
}
var invalidPathChars = Path.GetInvalidPathChars();
if (searchPattern.IndexOfAny(invalidPathChars) > -1)
{
throw CommonExceptions.IllegalCharactersInPath(nameof(searchPattern));
}
}
private string ReplaceLastOccurrence(string source, string find, string replace)
{
if (source == null)
{
return source;
}
var place = source.LastIndexOf(find);
if (place == -1)
{
return source;
}
var result = source.Remove(place, find.Length).Insert(place, replace);
return result;
}
#if FEATURE_ENUMERATION_OPTIONS
private SearchOption EnumerationOptionsToSearchOption(EnumerationOptions enumerationOptions)
{
static Exception CreateExceptionForUnsupportedProperty(string propertyName)
{
return new NotSupportedException(
$"Changing EnumerationOptions.{propertyName} is not yet implemented for the mock file system."
);
}
if (enumerationOptions.AttributesToSkip != (FileAttributes.System | FileAttributes.Hidden))
{
throw CreateExceptionForUnsupportedProperty("AttributesToSkip");
}
if (!enumerationOptions.IgnoreInaccessible)
{
throw CreateExceptionForUnsupportedProperty("IgnoreInaccessible");
}
if (enumerationOptions.MatchCasing != MatchCasing.PlatformDefault)
{
throw CreateExceptionForUnsupportedProperty("MatchCasing");
}
if (enumerationOptions.MatchType != MatchType.Simple)
{
throw CreateExceptionForUnsupportedProperty("MatchType");
}
if (enumerationOptions.ReturnSpecialDirectories)
{
throw CreateExceptionForUnsupportedProperty("ReturnSpecialDirectories");
}
return enumerationOptions.RecurseSubdirectories
? SearchOption.AllDirectories
: SearchOption.TopDirectoryOnly;
}
#endif
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.TestingHelpers/MockDirectoryData.cs
================================================
using System.Runtime.Versioning;
using System.Security.AccessControl;
namespace System.IO.Abstractions.TestingHelpers;
///
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public class MockDirectoryData : MockFileData
{
#if FEATURE_SERIALIZABLE
[NonSerialized]
#endif
private DirectorySecurity accessControl;
///
public MockDirectoryData() : base(string.Empty)
{
Attributes = FileAttributes.Directory;
}
///
[SupportedOSPlatform("windows")]
public new DirectorySecurity AccessControl
{
get
{
// DirectorySecurity's constructor will throw PlatformNotSupportedException on non-Windows platform, so we initialize it in lazy way.
// This let's us use this class as long as we don't use AccessControl property.
return accessControl ?? (accessControl = new DirectorySecurity());
}
set { accessControl = value; }
}
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.TestingHelpers/MockDirectoryInfo.cs
================================================
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Versioning;
using System.Security.AccessControl;
namespace System.IO.Abstractions.TestingHelpers;
using XFS = MockUnixSupport;
///
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public class MockDirectoryInfo : DirectoryInfoBase, IFileSystemAclSupport
{
private readonly IMockFileDataAccessor mockFileDataAccessor;
private string directoryPath;
private string originalPath;
private MockFileData cachedMockFileData;
private bool refreshOnNextRead;
///
/// Initializes a new instance of the class.
///
/// The mock file data accessor.
/// The directory path.
/// Thrown if or is .
public MockDirectoryInfo(IMockFileDataAccessor mockFileDataAccessor, string directoryPath) : base(mockFileDataAccessor?.FileSystem)
{
this.mockFileDataAccessor = mockFileDataAccessor ?? throw new ArgumentNullException(nameof(mockFileDataAccessor));
if (directoryPath == null)
{
throw new ArgumentNullException("path", StringResources.Manager.GetString("VALUE_CANNOT_BE_NULL"));
}
if (directoryPath.Trim() == string.Empty)
{
throw CommonExceptions.PathIsNotOfALegalForm("path");
}
SetDirectoryPath(directoryPath);
Refresh();
}
#if FEATURE_CREATE_SYMBOLIC_LINK
///
public override void CreateAsSymbolicLink(string pathToTarget)
{
FileSystem.Directory.CreateSymbolicLink(FullName, pathToTarget);
}
#endif
///
public override void Delete()
{
mockFileDataAccessor.Directory.Delete(directoryPath);
refreshOnNextRead = true;
}
///
public override void Refresh()
{
var mockFileData = mockFileDataAccessor.GetFile(directoryPath) ?? MockFileData.NullObject;
cachedMockFileData = mockFileData.Clone();
}
#if FEATURE_CREATE_SYMBOLIC_LINK
///
public override IFileSystemInfo ResolveLinkTarget(bool returnFinalTarget)
{
return FileSystem.Directory.ResolveLinkTarget(FullName, returnFinalTarget);
}
#endif
///
public override FileAttributes Attributes
{
get { return GetMockFileDataForRead().Attributes; }
set { GetMockFileDataForWrite().Attributes = value | FileAttributes.Directory; }
}
///
public override DateTime CreationTime
{
get { return GetMockFileDataForRead().CreationTime.LocalDateTime; }
set { GetMockFileDataForWrite().CreationTime = value; }
}
///
public override DateTime CreationTimeUtc
{
get { return GetMockFileDataForRead().CreationTime.UtcDateTime; }
set { GetMockFileDataForWrite().CreationTime = value; }
}
///
public override bool Exists
{
get {
var mockFileData = GetMockFileDataForRead();
return (int)mockFileData.Attributes != -1 && mockFileData.IsDirectory;
}
}
///
public override string Extension
{
get
{
// System.IO.Path.GetExtension does only string manipulation,
// so it's safe to delegate.
return Path.GetExtension(directoryPath);
}
}
///
public override string FullName
{
get
{
var root = mockFileDataAccessor.Path.GetPathRoot(directoryPath);
if (mockFileDataAccessor.StringOperations.Equals(directoryPath, root))
{
// drives have the trailing slash
return directoryPath;
}
// directories do not have a trailing slash
return directoryPath.TrimEnd('\\').TrimEnd('/');
}
}
///
public override DateTime LastAccessTime
{
get { return GetMockFileDataForRead().LastAccessTime.LocalDateTime; }
set { GetMockFileDataForWrite().LastAccessTime = value; }
}
///
public override DateTime LastAccessTimeUtc
{
get { return GetMockFileDataForRead().LastAccessTime.UtcDateTime; }
set { GetMockFileDataForWrite().LastAccessTime = value; }
}
///
public override DateTime LastWriteTime
{
get { return GetMockFileDataForRead().LastWriteTime.LocalDateTime; }
set { GetMockFileDataForWrite().LastWriteTime = value; }
}
///
public override DateTime LastWriteTimeUtc
{
get { return GetMockFileDataForRead().LastWriteTime.UtcDateTime; }
set { GetMockFileDataForWrite().LastWriteTime = value; }
}
#if FEATURE_FILE_SYSTEM_INFO_LINK_TARGET
///
public override string LinkTarget
{
get { return GetMockFileDataForRead().LinkTarget; }
}
#endif
///
public override string Name
{
get
{
var mockPath = new MockPath(mockFileDataAccessor);
return string.Equals(mockPath.GetPathRoot(directoryPath), directoryPath) ? directoryPath : mockPath.GetFileName(directoryPath.TrimEnd(mockFileDataAccessor.Path.DirectorySeparatorChar));
}
}
///
public override void Create()
{
mockFileDataAccessor.Directory.CreateDirectory(FullName);
refreshOnNextRead = true;
}
///
public override IDirectoryInfo CreateSubdirectory(string path)
{
return mockFileDataAccessor.Directory.CreateDirectory(Path.Combine(FullName, path));
}
///
public override void Delete(bool recursive)
{
mockFileDataAccessor.Directory.Delete(directoryPath, recursive);
refreshOnNextRead = true;
}
///
public override IEnumerable EnumerateDirectories()
{
return GetDirectories();
}
///
public override IEnumerable EnumerateDirectories(string searchPattern)
{
return GetDirectories(searchPattern);
}
///
public override IEnumerable EnumerateDirectories(string searchPattern, SearchOption searchOption)
{
return GetDirectories(searchPattern, searchOption);
}
#if FEATURE_ENUMERATION_OPTIONS
///
public override IEnumerable EnumerateDirectories(string searchPattern, EnumerationOptions enumerationOptions)
{
return GetDirectories(searchPattern, enumerationOptions);
}
#endif
///
public override IEnumerable EnumerateFiles()
{
return GetFiles();
}
///
public override IEnumerable EnumerateFiles(string searchPattern)
{
return GetFiles(searchPattern);
}
///
public override IEnumerable EnumerateFiles(string searchPattern, SearchOption searchOption)
{
return GetFiles(searchPattern, searchOption);
}
#if FEATURE_ENUMERATION_OPTIONS
///
public override IEnumerable EnumerateFiles(string searchPattern, EnumerationOptions enumerationOptions)
{
return GetFiles(searchPattern, enumerationOptions);
}
#endif
///
public override IEnumerable EnumerateFileSystemInfos()
{
return GetFileSystemInfos();
}
///
public override IEnumerable EnumerateFileSystemInfos(string searchPattern)
{
return GetFileSystemInfos(searchPattern);
}
///
public override IEnumerable EnumerateFileSystemInfos(string searchPattern, SearchOption searchOption)
{
return GetFileSystemInfos(searchPattern, searchOption);
}
#if FEATURE_ENUMERATION_OPTIONS
///
public override IEnumerable EnumerateFileSystemInfos(string searchPattern, EnumerationOptions enumerationOptions)
{
return GetFileSystemInfos(searchPattern, enumerationOptions);
}
#endif
///
public override IDirectoryInfo[] GetDirectories()
{
return ConvertStringsToDirectories(mockFileDataAccessor.Directory.GetDirectories(directoryPath));
}
///
public override IDirectoryInfo[] GetDirectories(string searchPattern)
{
return ConvertStringsToDirectories(mockFileDataAccessor.Directory.GetDirectories(directoryPath, searchPattern));
}
///
public override IDirectoryInfo[] GetDirectories(string searchPattern, SearchOption searchOption)
{
return ConvertStringsToDirectories(mockFileDataAccessor.Directory.GetDirectories(directoryPath, searchPattern, searchOption));
}
#if FEATURE_ENUMERATION_OPTIONS
///
public override IDirectoryInfo[] GetDirectories(string searchPattern, EnumerationOptions enumerationOptions)
{
return ConvertStringsToDirectories(mockFileDataAccessor.Directory.GetDirectories(directoryPath, searchPattern, enumerationOptions));
}
#endif
private DirectoryInfoBase[] ConvertStringsToDirectories(IEnumerable paths)
{
return paths
.Select(path => new MockDirectoryInfo(mockFileDataAccessor, path))
.Cast()
.ToArray();
}
///
public override IFileInfo[] GetFiles()
{
return ConvertStringsToFiles(mockFileDataAccessor.Directory.GetFiles(FullName));
}
///
public override IFileInfo[] GetFiles(string searchPattern)
{
return ConvertStringsToFiles(mockFileDataAccessor.Directory.GetFiles(FullName, searchPattern));
}
///
public override IFileInfo[] GetFiles(string searchPattern, SearchOption searchOption)
{
return ConvertStringsToFiles(mockFileDataAccessor.Directory.GetFiles(FullName, searchPattern, searchOption));
}
#if FEATURE_ENUMERATION_OPTIONS
///
public override IFileInfo[] GetFiles(string searchPattern, EnumerationOptions enumerationOptions)
{
return ConvertStringsToFiles(mockFileDataAccessor.Directory.GetFiles(FullName, searchPattern, enumerationOptions));
}
#endif
IFileInfo[] ConvertStringsToFiles(IEnumerable paths)
{
return paths
.Select(mockFileDataAccessor.FileInfo.New)
.ToArray();
}
///
public override IFileSystemInfo[] GetFileSystemInfos()
{
return GetFileSystemInfos("*");
}
///
public override IFileSystemInfo[] GetFileSystemInfos(string searchPattern)
{
return GetFileSystemInfos(searchPattern, SearchOption.TopDirectoryOnly);
}
///
public override IFileSystemInfo[] GetFileSystemInfos(string searchPattern, SearchOption searchOption)
{
return GetDirectories(searchPattern, searchOption).OfType().Concat(GetFiles(searchPattern, searchOption)).ToArray();
}
#if FEATURE_ENUMERATION_OPTIONS
///
public override IFileSystemInfo[] GetFileSystemInfos(string searchPattern, EnumerationOptions enumerationOptions)
{
return GetDirectories(searchPattern, enumerationOptions).OfType().Concat(GetFiles(searchPattern, enumerationOptions)).ToArray();
}
#endif
///
public override void MoveTo(string destDirName)
{
mockFileDataAccessor.Directory.Move(directoryPath, destDirName);
SetDirectoryPath(destDirName);
}
///
public override IDirectoryInfo Parent
{
get
{
return mockFileDataAccessor.Directory.GetParent(directoryPath);
}
}
///
public override IDirectoryInfo Root
{
get
{
return new MockDirectoryInfo(mockFileDataAccessor, mockFileDataAccessor.Directory.GetDirectoryRoot(FullName));
}
}
private MockFileData GetMockFileDataForRead()
{
if (refreshOnNextRead)
{
Refresh();
refreshOnNextRead = false;
}
return cachedMockFileData;
}
private MockFileData GetMockFileDataForWrite()
{
refreshOnNextRead = true;
return mockFileDataAccessor.GetFile(directoryPath)
?? throw CommonExceptions.CouldNotFindPartOfPath(directoryPath);
}
///
public override string ToString()
{
return originalPath;
}
///
[SupportedOSPlatform("windows")]
public object GetAccessControl()
{
return GetMockDirectoryData().AccessControl;
}
///
[SupportedOSPlatform("windows")]
public object GetAccessControl(IFileSystemAclSupport.AccessControlSections includeSections)
{
return GetMockDirectoryData().AccessControl;
}
///
[SupportedOSPlatform("windows")]
public void SetAccessControl(object value)
{
GetMockDirectoryData().AccessControl = value as DirectorySecurity;
}
private void SetDirectoryPath(string path)
{
originalPath = path;
path = mockFileDataAccessor.Path.GetFullPath(path);
path = path.TrimSlashes();
if (XFS.IsWindowsPlatform())
{
path = path.TrimEnd(' ');
}
this.directoryPath = path;
}
private MockDirectoryData GetMockDirectoryData()
{
return mockFileDataAccessor.GetFile(directoryPath) as MockDirectoryData
?? throw CommonExceptions.CouldNotFindPartOfPath(directoryPath);
}
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.TestingHelpers/MockDirectoryInfoFactory.cs
================================================
namespace System.IO.Abstractions.TestingHelpers;
///
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public class MockDirectoryInfoFactory : IDirectoryInfoFactory
{
readonly IMockFileDataAccessor mockFileSystem;
///
public MockDirectoryInfoFactory(IMockFileDataAccessor mockFileSystem)
{
this.mockFileSystem = mockFileSystem;
}
///
public IFileSystem FileSystem
=> mockFileSystem;
///
public IDirectoryInfo New(string path)
{
return new MockDirectoryInfo(mockFileSystem, path);
}
///
public IDirectoryInfo Wrap(DirectoryInfo directoryInfo)
{
if (directoryInfo == null)
{
return null;
}
return new MockDirectoryInfo(mockFileSystem, directoryInfo.FullName);
}
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.TestingHelpers/MockDriveData.cs
================================================
namespace System.IO.Abstractions.TestingHelpers;
///
/// The class represents the associated data of a drive.
///
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public class MockDriveData
{
///
/// Initializes a new instance of the class.
///
public MockDriveData()
{
IsReady = true;
}
///
/// Initializes a new instance of the class by copying the given .
///
/// The template instance.
/// Thrown if is .
public MockDriveData(MockDriveData template)
{
if (template == null)
{
throw new ArgumentNullException(nameof(template));
}
AvailableFreeSpace = template.AvailableFreeSpace;
DriveFormat = template.DriveFormat;
DriveType = template.DriveType;
IsReady = template.IsReady;
TotalFreeSpace = template.TotalFreeSpace;
TotalSize = template.TotalSize;
VolumeLabel = template.VolumeLabel;
}
///
/// Gets or sets the amount of available free space of the , in bytes.
///
public long AvailableFreeSpace { get; set; }
///
/// Gets or sets the name of the file system of the , such as NTFS or FAT32.
///
public string DriveFormat { get; set; }
///
/// Gets or sets the drive type of the , such as CD-ROM, removable, network, or fixed.
///
public DriveType DriveType { get; set; }
///
/// Gets or sets the value that indicates whether the is ready.
///
public bool IsReady { get; set; }
///
/// Gets or sets the total amount of free space available on the , in bytes.
///
public long TotalFreeSpace { get; set; }
///
/// Gets or sets the total size of storage space on the , in bytes.
///
public long TotalSize { get; set; }
///
/// Gets or sets the volume label of the .
///
public string VolumeLabel { get; set; }
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.TestingHelpers/MockDriveInfo.cs
================================================
namespace System.IO.Abstractions.TestingHelpers;
///
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public class MockDriveInfo : DriveInfoBase
{
private readonly IMockFileDataAccessor mockFileDataAccessor;
private readonly string name;
///
public MockDriveInfo(IMockFileDataAccessor mockFileDataAccessor, string name) : base(mockFileDataAccessor?.FileSystem)
{
this.mockFileDataAccessor = mockFileDataAccessor ?? throw new ArgumentNullException(nameof(mockFileDataAccessor));
this.name = mockFileDataAccessor.PathVerifier.NormalizeDriveName(name);
}
///
public override long AvailableFreeSpace
{
get
{
var mockDriveData = GetMockDriveData();
return mockDriveData.AvailableFreeSpace;
}
}
///
public override string DriveFormat
{
get
{
var mockDriveData = GetMockDriveData();
return mockDriveData.DriveFormat;
}
}
///
public override DriveType DriveType
{
get
{
var mockDriveData = GetMockDriveData();
return mockDriveData.DriveType;
}
}
///
public override bool IsReady
{
get
{
var mockDriveData = GetMockDriveData();
return mockDriveData.IsReady;
}
}
///
public override string Name
{
get { return name; }
}
///
public override IDirectoryInfo RootDirectory
{
get
{
return mockFileDataAccessor.DirectoryInfo.New(Name);
}
}
///
public override long TotalFreeSpace
{
get
{
var mockDriveData = GetMockDriveData();
return mockDriveData.TotalFreeSpace;
}
}
///
public override long TotalSize
{
get
{
var mockDriveData = GetMockDriveData();
return mockDriveData.TotalSize;
}
}
///
public override string VolumeLabel
{
get
{
var mockDriveData = GetMockDriveData();
return mockDriveData.VolumeLabel;
}
set
{
var mockDriveData = GetMockDriveData();
mockDriveData.VolumeLabel = value;
}
}
///
public override string ToString()
{
return Name;
}
private MockDriveData GetMockDriveData()
{
return mockFileDataAccessor.GetDrive(name)
?? throw CommonExceptions.FileNotFound(name);
}
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.TestingHelpers/MockDriveInfoFactory.cs
================================================
using System.Collections.Generic;
namespace System.IO.Abstractions.TestingHelpers;
///
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public class MockDriveInfoFactory : IDriveInfoFactory
{
private readonly IMockFileDataAccessor mockFileSystem;
///
public MockDriveInfoFactory(IMockFileDataAccessor mockFileSystem)
{
this.mockFileSystem = mockFileSystem ?? throw new ArgumentNullException(nameof(mockFileSystem));
}
///
public IFileSystem FileSystem
=> mockFileSystem;
///
public IDriveInfo[] GetDrives()
{
var result = new List();
foreach (string driveLetter in mockFileSystem.AllDrives)
{
try
{
var mockDriveInfo = new MockDriveInfo(mockFileSystem, driveLetter);
result.Add(mockDriveInfo);
}
catch (ArgumentException)
{
// invalid drives should be ignored
}
}
return result.ToArray();
}
///
public IDriveInfo New(string driveName)
{
var drive = mockFileSystem.Path.GetPathRoot(driveName);
return new MockDriveInfo(mockFileSystem, drive);
}
///
public IDriveInfo Wrap(DriveInfo driveInfo)
{
if (driveInfo == null)
{
return null;
}
return New(driveInfo.Name);
}
private string NormalizeDriveName(string driveName)
{
if (driveName.Length == 3 && mockFileSystem.StringOperations.EndsWith(driveName, @":\"))
{
return mockFileSystem.StringOperations.ToUpper(driveName[0]) + @":\";
}
if (mockFileSystem.StringOperations.StartsWith(driveName, @"\\"))
{
return null;
}
return driveName;
}
private class DriveEqualityComparer : IEqualityComparer
{
private readonly IMockFileDataAccessor mockFileSystem;
public DriveEqualityComparer(IMockFileDataAccessor mockFileSystem)
{
this.mockFileSystem = mockFileSystem ?? throw new ArgumentNullException(nameof(mockFileSystem));
}
public bool Equals(string x, string y)
{
return ReferenceEquals(x, y) ||
(HasDrivePrefix(x) && HasDrivePrefix(y) && mockFileSystem.StringOperations.Equals(x[0], y[0]));
}
private static bool HasDrivePrefix(string x)
{
return x != null && x.Length >= 2 && x[1] == ':';
}
public int GetHashCode(string obj)
{
return mockFileSystem.StringOperations.ToUpper(obj).GetHashCode();
}
}
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFile.Async.cs
================================================
#if FEATURE_ASYNC_FILE
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace System.IO.Abstractions.TestingHelpers
{
partial class MockFile
{
#if FEATURE_FILE_SPAN
///
public override Task AppendAllBytesAsync(string path, byte[] bytes, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
AppendAllBytes(path, bytes);
return Task.CompletedTask;
}
///
public override Task AppendAllBytesAsync(string path, ReadOnlyMemory bytes, CancellationToken cancellationToken = default)
{
return AppendAllBytesAsync(path, bytes.ToArray(), cancellationToken);
}
#endif
///
public override Task AppendAllLinesAsync(string path, IEnumerable contents, CancellationToken cancellationToken = default) =>
AppendAllLinesAsync(path, contents, MockFileData.DefaultEncoding, cancellationToken);
///
public override Task AppendAllLinesAsync(string path, IEnumerable contents, Encoding encoding, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
AppendAllLines(path, contents, encoding);
return Task.CompletedTask;
}
///
public override Task AppendAllTextAsync(string path, string contents, CancellationToken cancellationToken = default) =>
AppendAllTextAsync(path, contents, MockFileData.DefaultEncoding, cancellationToken);
///
public override Task AppendAllTextAsync(string path, string contents, Encoding encoding, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
AppendAllText(path, contents, encoding);
return Task.CompletedTask;
}
#if FEATURE_FILE_SPAN
///
public override Task AppendAllTextAsync(string path, ReadOnlyMemory contents, CancellationToken cancellationToken = default)
{
return AppendAllTextAsync(path, contents.ToString(), cancellationToken);
}
///
public override Task AppendAllTextAsync(string path, ReadOnlyMemory contents, Encoding encoding,
CancellationToken cancellationToken = default)
{
return AppendAllTextAsync(path, contents.ToString(), encoding, cancellationToken);
}
#endif
///
public override Task ReadAllBytesAsync(string path, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
return Task.FromResult(ReadAllBytes(path));
}
///
public override Task ReadAllLinesAsync(string path, CancellationToken cancellationToken = default) =>
ReadAllLinesAsync(path, MockFileData.DefaultEncoding, cancellationToken);
///
public override Task ReadAllLinesAsync(string path, Encoding encoding, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
return Task.FromResult(ReadAllLines(path, encoding));
}
///
public override Task ReadAllTextAsync(string path, CancellationToken cancellationToken = default) =>
ReadAllTextAsync(path, MockFileData.DefaultEncoding, cancellationToken);
///
public override Task ReadAllTextAsync(string path, Encoding encoding, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
return Task.FromResult(ReadAllText(path, encoding));
}
#if FEATURE_READ_LINES_ASYNC
///
public override IAsyncEnumerable ReadLinesAsync(string path, CancellationToken cancellationToken = default) =>
ReadLinesAsync(path, MockFileData.DefaultEncoding, cancellationToken);
///
public override async IAsyncEnumerable ReadLinesAsync(string path, Encoding encoding,
[EnumeratorCancellation] CancellationToken cancellationToken = default)
{
var lines = await ReadAllLinesAsync(path, encoding, cancellationToken);
foreach (var line in lines)
yield return line;
}
#endif
///
public override Task WriteAllBytesAsync(string path, byte[] bytes, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
WriteAllBytes(path, bytes);
return Task.CompletedTask;
}
#if FEATURE_FILE_SPAN
///
public override Task WriteAllBytesAsync(string path, ReadOnlyMemory bytes, CancellationToken cancellationToken = default)
{
return WriteAllBytesAsync(path, bytes.ToArray(), cancellationToken);
}
#endif
///
public override Task WriteAllLinesAsync(string path, IEnumerable contents, CancellationToken cancellationToken = default) =>
WriteAllLinesAsync(path, contents, MockFileData.DefaultEncoding, cancellationToken);
///
public override Task WriteAllLinesAsync(string path, IEnumerable contents, Encoding encoding, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
WriteAllLines(path, contents, encoding);
return Task.CompletedTask;
}
///
public override Task WriteAllTextAsync(string path, string contents, CancellationToken cancellationToken = default) =>
WriteAllTextAsync(path, contents, MockFileData.DefaultEncoding, cancellationToken);
///
public override Task WriteAllTextAsync(string path, string contents, Encoding encoding, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
WriteAllText(path, contents, encoding);
return Task.CompletedTask;
}
#if FEATURE_FILE_SPAN
///
public override Task WriteAllTextAsync(string path, ReadOnlyMemory contents, CancellationToken cancellationToken = default)
{
return WriteAllTextAsync(path, contents.ToString(), cancellationToken);
}
///
public override Task WriteAllTextAsync(string path, ReadOnlyMemory contents, Encoding encoding,
CancellationToken cancellationToken = default)
{
return WriteAllTextAsync(path, contents.ToString(), encoding, cancellationToken);
}
#endif
}
}
#endif
================================================
FILE: src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFile.cs
================================================
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using Microsoft.Win32.SafeHandles;
namespace System.IO.Abstractions.TestingHelpers;
using XFS = MockUnixSupport;
///
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public partial class MockFile : FileBase
{
private readonly IMockFileDataAccessor mockFileDataAccessor;
///
public MockFile(IMockFileDataAccessor mockFileDataAccessor) : base(mockFileDataAccessor?.FileSystem)
{
this.mockFileDataAccessor = mockFileDataAccessor ?? throw new ArgumentNullException(nameof(mockFileDataAccessor));
}
#if FEATURE_FILE_SPAN
///
public override void AppendAllBytes(string path, byte[] bytes)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
if (!mockFileDataAccessor.FileExists(path))
{
VerifyDirectoryExists(path);
mockFileDataAccessor.AddFile(path, mockFileDataAccessor.AdjustTimes(new MockFileData(bytes), TimeAdjustments.All));
}
else
{
var file = mockFileDataAccessor.GetFile(path);
file.CheckFileAccess(path, FileAccess.Write);
mockFileDataAccessor.AdjustTimes(file, TimeAdjustments.LastAccessTime | TimeAdjustments.LastWriteTime);
file.Contents = file.Contents.Concat(bytes).ToArray();
}
}
///
public override void AppendAllBytes(string path, ReadOnlySpan bytes)
{
AppendAllBytes(path, bytes.ToArray());
}
#endif
///
public override void AppendAllLines(string path, IEnumerable contents)
{
AppendAllLines(path, contents, MockFileData.DefaultEncoding);
}
///
public override void AppendAllLines(string path, IEnumerable contents, Encoding encoding)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
VerifyValueIsNotNull(contents, nameof(contents));
VerifyValueIsNotNull(encoding, nameof(encoding));
var concatContents = contents.Aggregate("", (a, b) => a + b + Environment.NewLine);
AppendAllText(path, concatContents, encoding);
}
///
public override void AppendAllText(string path, string contents)
{
AppendAllText(path, contents, MockFileData.DefaultEncoding);
}
///
public override void AppendAllText(string path, string contents, Encoding encoding)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
if (encoding == null)
{
throw new ArgumentNullException(nameof(encoding));
}
if (!mockFileDataAccessor.FileExists(path))
{
VerifyDirectoryExists(path);
mockFileDataAccessor.AddFile(path, mockFileDataAccessor.AdjustTimes(new MockFileData(contents, encoding), TimeAdjustments.All));
}
else
{
var file = mockFileDataAccessor.GetFile(path);
file.CheckFileAccess(path, FileAccess.Write);
mockFileDataAccessor.AdjustTimes(file, TimeAdjustments.LastAccessTime | TimeAdjustments.LastWriteTime);
var bytesToAppend = encoding.GetBytes(contents);
file.Contents = file.Contents.Concat(bytesToAppend).ToArray();
}
}
#if FEATURE_FILE_SPAN
///
public override void AppendAllText(string path, ReadOnlySpan contents)
{
AppendAllText(path, contents.ToString());
}
///
public override void AppendAllText(string path, ReadOnlySpan contents, Encoding encoding)
{
AppendAllText(path, contents.ToString(), encoding);
}
#endif
///
public override StreamWriter AppendText(string path)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
if (mockFileDataAccessor.FileExists(path))
{
StreamWriter sw = new StreamWriter(OpenWrite(path));
sw.BaseStream.Seek(0, SeekOrigin.End); //push the stream pointer at the end for append.
return sw;
}
return new StreamWriter(Create(path));
}
///
public override void Copy(string sourceFileName, string destFileName)
{
Copy(sourceFileName, destFileName, false);
}
///
public override void Copy(string sourceFileName, string destFileName, bool overwrite)
{
if (sourceFileName == null)
{
throw CommonExceptions.FilenameCannotBeNull(nameof(sourceFileName));
}
if (destFileName == null)
{
throw CommonExceptions.FilenameCannotBeNull(nameof(destFileName));
}
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(sourceFileName, nameof(sourceFileName));
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(destFileName, nameof(destFileName));
if (!Exists(sourceFileName))
{
throw CommonExceptions.FileNotFound(sourceFileName);
}
VerifyDirectoryExists(destFileName);
var fileExists = mockFileDataAccessor.FileExists(destFileName);
if (fileExists)
{
if (!overwrite)
{
throw CommonExceptions.FileAlreadyExists(destFileName);
}
if (string.Equals(sourceFileName, destFileName, StringComparison.OrdinalIgnoreCase) && XFS.IsWindowsPlatform())
{
throw CommonExceptions.ProcessCannotAccessFileInUse(destFileName);
}
mockFileDataAccessor.RemoveFile(destFileName);
}
var sourceFileData = mockFileDataAccessor.GetFile(sourceFileName);
sourceFileData.CheckFileAccess(sourceFileName, FileAccess.Read);
var destFileData = new MockFileData(sourceFileData);
mockFileDataAccessor.AdjustTimes(destFileData, TimeAdjustments.CreationTime | TimeAdjustments.LastAccessTime);
mockFileDataAccessor.AddFile(destFileName, destFileData);
}
///
public override FileSystemStream Create(string path) =>
Create(path, 4096);
///
public override FileSystemStream Create(string path, int bufferSize) =>
Create(path, bufferSize, FileOptions.None);
///
public override FileSystemStream Create(string path, int bufferSize, FileOptions options) =>
CreateInternal(path, FileAccess.ReadWrite, options);
private FileSystemStream CreateInternal(string path, FileAccess access, FileOptions options)
{
if (path == null)
{
throw new ArgumentNullException(nameof(path), "Path cannot be null.");
}
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, nameof(path));
VerifyDirectoryExists(path);
var mockFileData = new MockFileData(new byte[0]);
mockFileDataAccessor.AdjustTimes(mockFileData, TimeAdjustments.All);
mockFileDataAccessor.AddFile(path, mockFileData);
return OpenInternal(path, FileMode.Open, access, options);
}
#if FEATURE_CREATE_SYMBOLIC_LINK
///
public override IFileSystemInfo CreateSymbolicLink(string path, string pathToTarget)
{
if (path == null)
{
throw CommonExceptions.FilenameCannotBeNull(nameof(path));
}
if (pathToTarget == null)
{
throw CommonExceptions.FilenameCannotBeNull(nameof(pathToTarget));
}
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, nameof(path));
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(pathToTarget, nameof(pathToTarget));
if (Exists(path))
{
throw CommonExceptions.FileAlreadyExists(nameof(path));
}
VerifyDirectoryExists(path);
var fileExists = mockFileDataAccessor.FileExists(pathToTarget);
if (!fileExists)
{
throw CommonExceptions.FileNotFound(pathToTarget);
}
var sourceFileData = mockFileDataAccessor.GetFile(pathToTarget);
sourceFileData.CheckFileAccess(pathToTarget, FileAccess.Read);
var destFileData = new MockFileData(new byte[0]);
mockFileDataAccessor.AdjustTimes(destFileData, TimeAdjustments.CreationTime | TimeAdjustments.LastAccessTime);
destFileData.LinkTarget = pathToTarget;
mockFileDataAccessor.AddFile(path, destFileData);
var mockFileInfo = new MockFileInfo(mockFileDataAccessor, path);
mockFileInfo.Attributes |= FileAttributes.ReparsePoint;
return mockFileInfo;
}
#endif
///
public override StreamWriter CreateText(string path)
{
return new StreamWriter(Create(path));
}
///
public override void Decrypt(string path)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
new MockFileInfo(mockFileDataAccessor, path).Decrypt();
}
///
public override void Delete(string path)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
// We mimic exact behavior of the standard File.Delete() method
// which throws exception only if the folder does not exist,
// but silently returns if deleting a non-existing file in an existing folder.
VerifyDirectoryExists(path);
var file = mockFileDataAccessor.GetFile(path);
if (file != null && !file.AllowedFileShare.HasFlag(FileShare.Delete))
{
throw CommonExceptions.ProcessCannotAccessFileInUse(path);
}
if (file != null && file.IsDirectory)
{
throw new UnauthorizedAccessException($"Access to the path '{path}' is denied.");
}
mockFileDataAccessor.RemoveFile(path);
}
///
public override void Encrypt(string path)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
new MockFileInfo(mockFileDataAccessor, path).Encrypt();
}
///
public override bool Exists(string path)
{
if (path == null)
{
return false;
}
if (path.Trim() == string.Empty)
{
return false;
}
//Not handling exceptions here so that mock behaviour is as similar as possible to System.IO.File.Exists (See #810)
try
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, nameof(path));
var file = mockFileDataAccessor.GetFile(path);
return file != null && !file.IsDirectory;
}
catch (ArgumentException) { }
catch (NotSupportedException) { }
catch (IOException) { }
catch (UnauthorizedAccessException) { }
return false;
}
///
/// Gets the of the file on the path.
///
/// The path to the file.
/// The of the file on the path.
/// is empty, contains only white spaces, or contains invalid characters.
/// The specified path, file name, or both exceed the system-defined maximum length. For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters.
/// is in an invalid format.
/// represents a file and is invalid, such as being on an unmapped drive, or the file cannot be found.
/// represents a directory and is invalid, such as being on an unmapped drive, or the directory cannot be found.
/// This file is being used by another process.
/// The caller does not have the required permission.
public override FileAttributes GetAttributes(string path)
{
if (path != null && path.Length == 0)
{
throw CommonExceptions.PathIsNotOfALegalForm(nameof(path));
}
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
var possibleFileData = mockFileDataAccessor.GetFile(path);
FileAttributes result;
if (possibleFileData != null)
{
result = possibleFileData.Attributes;
}
else
{
var directoryInfo = mockFileDataAccessor.DirectoryInfo.New(path);
if (directoryInfo.Exists)
{
result = directoryInfo.Attributes;
}
else
{
VerifyDirectoryExists(path);
throw CommonExceptions.FileNotFound(path);
}
}
return result;
}
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public override FileAttributes GetAttributes(SafeFileHandle fileHandle)
{
throw CommonExceptions.NotImplemented();
}
#endif
///
public override DateTime GetCreationTime(string path)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
return GetTimeFromFile(path, data => data.CreationTime.LocalDateTime, () => MockFileData.DefaultDateTimeOffset.LocalDateTime);
}
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public override DateTime GetCreationTime(SafeFileHandle fileHandle)
{
throw CommonExceptions.NotImplemented();
}
#endif
///
public override DateTime GetCreationTimeUtc(string path)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
return GetTimeFromFile(path, data => data.CreationTime.UtcDateTime, () => MockFileData.DefaultDateTimeOffset.UtcDateTime);
}
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public override DateTime GetCreationTimeUtc(SafeFileHandle fileHandle)
{
throw CommonExceptions.NotImplemented();
}
#endif
///
public override DateTime GetLastAccessTime(string path)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
return GetTimeFromFile(path, data => data.LastAccessTime.LocalDateTime, () => MockFileData.DefaultDateTimeOffset.LocalDateTime);
}
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public override DateTime GetLastAccessTime(SafeFileHandle fileHandle)
{
throw CommonExceptions.NotImplemented();
}
#endif
///
public override DateTime GetLastAccessTimeUtc(string path)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
return GetTimeFromFile(path, data => data.LastAccessTime.UtcDateTime, () => MockFileData.DefaultDateTimeOffset.UtcDateTime);
}
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public override DateTime GetLastAccessTimeUtc(SafeFileHandle fileHandle)
{
throw CommonExceptions.NotImplemented();
}
#endif
///
public override DateTime GetLastWriteTime(string path)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
return GetTimeFromFile(path, data => data.LastWriteTime.LocalDateTime, () => MockFileData.DefaultDateTimeOffset.LocalDateTime);
}
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public override DateTime GetLastWriteTime(SafeFileHandle fileHandle)
{
throw CommonExceptions.NotImplemented();
}
#endif
///
public override DateTime GetLastWriteTimeUtc(string path)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
return GetTimeFromFile(path, data => data.LastWriteTime.UtcDateTime, () => MockFileData.DefaultDateTimeOffset.UtcDateTime);
}
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public override DateTime GetLastWriteTimeUtc(SafeFileHandle fileHandle)
{
throw CommonExceptions.NotImplemented();
}
#endif
#if FEATURE_UNIX_FILE_MODE
///
public override UnixFileMode GetUnixFileMode(string path)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
if (!mockFileDataAccessor.FileExists(path))
{
throw CommonExceptions.FileNotFound(path);
}
var mockFileData = mockFileDataAccessor.GetFile(path);
return mockFileData.UnixMode;
}
#endif
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public override UnixFileMode GetUnixFileMode(SafeFileHandle fileHandle)
{
throw CommonExceptions.NotImplemented();
}
#endif
private DateTime GetTimeFromFile(string path, Func existingFileFunction, Func nonExistingFileFunction)
{
DateTime result;
MockFileData file = mockFileDataAccessor.GetFile(path);
if (file != null)
{
result = existingFileFunction(file);
}
else
{
result = nonExistingFileFunction();
}
return result;
}
///
public override void Move(string sourceFileName, string destFileName)
{
if (sourceFileName == null)
{
throw CommonExceptions.FilenameCannotBeNull(nameof(sourceFileName));
}
if (destFileName == null)
{
throw CommonExceptions.FilenameCannotBeNull(nameof(destFileName));
}
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(sourceFileName, nameof(sourceFileName));
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(destFileName, nameof(destFileName));
var sourceFile = mockFileDataAccessor.GetFile(sourceFileName);
if (sourceFile == null)
{
throw CommonExceptions.FileNotFound(sourceFileName);
}
if (!sourceFile.AllowedFileShare.HasFlag(FileShare.Delete))
{
throw CommonExceptions.ProcessCannotAccessFileInUse();
}
VerifyDirectoryExists(destFileName);
if (mockFileDataAccessor.GetFile(destFileName) != null)
{
if (mockFileDataAccessor.StringOperations.Equals(destFileName, sourceFileName))
{
if (XFS.IsWindowsPlatform())
{
mockFileDataAccessor.RemoveFile(sourceFileName);
mockFileDataAccessor.AddFile(destFileName, mockFileDataAccessor.AdjustTimes(new MockFileData(sourceFile), TimeAdjustments.LastAccessTime), false);
}
return;
}
else
{
throw new IOException("A file can not be created if it already exists.");
}
}
mockFileDataAccessor.RemoveFile(sourceFileName, false);
mockFileDataAccessor.AddFile(destFileName, mockFileDataAccessor.AdjustTimes(new MockFileData(sourceFile), TimeAdjustments.LastAccessTime), false);
}
#if FEATURE_FILE_MOVE_WITH_OVERWRITE
///
public override void Move(string sourceFileName, string destFileName, bool overwrite)
{
if (sourceFileName == null)
{
throw CommonExceptions.FilenameCannotBeNull(nameof(sourceFileName));
}
if (destFileName == null)
{
throw CommonExceptions.FilenameCannotBeNull(nameof(destFileName));
}
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(sourceFileName, nameof(sourceFileName));
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(destFileName, nameof(destFileName));
if (mockFileDataAccessor.GetFile(destFileName) != null)
{
if (destFileName.Equals(sourceFileName))
{
return;
}
else if (!overwrite)
{
throw new IOException("A file can not be created if it already exists.");
}
}
var sourceFile = mockFileDataAccessor.GetFile(sourceFileName);
if (sourceFile == null)
{
throw CommonExceptions.FileNotFound(sourceFileName);
}
if (!sourceFile.AllowedFileShare.HasFlag(FileShare.Delete))
{
throw CommonExceptions.ProcessCannotAccessFileInUse();
}
VerifyDirectoryExists(destFileName);
mockFileDataAccessor.RemoveFile(sourceFileName);
mockFileDataAccessor.AddFile(destFileName, mockFileDataAccessor.AdjustTimes(new MockFileData(sourceFile), TimeAdjustments.LastAccessTime));
}
#endif
///
public override FileSystemStream Open(string path, FileMode mode)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
return Open(path, mode, mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite, FileShare.None);
}
///
public override FileSystemStream Open(string path, FileMode mode, FileAccess access)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
return Open(path, mode, access, FileShare.None);
}
///
public override FileSystemStream Open(string path, FileMode mode, FileAccess access, FileShare share) =>
OpenInternal(path, mode, access, FileOptions.None);
#if FEATURE_FILESTREAM_OPTIONS
///
public override FileSystemStream Open(string path, FileStreamOptions options)
{
return OpenInternal(path, options.Mode, options.Access, options.Options);
}
#endif
private FileSystemStream OpenInternal(
string path,
FileMode mode,
FileAccess access,
FileOptions options)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
bool exists = mockFileDataAccessor.FileExists(path);
if (mode == FileMode.CreateNew && exists)
{
throw CommonExceptions.FileAlreadyExists(path);
}
if ((mode == FileMode.Open || mode == FileMode.Truncate) && !exists)
{
throw CommonExceptions.FileNotFound(path);
}
if (!exists || mode == FileMode.CreateNew)
{
return CreateInternal(path, access, options);
}
if (mode == FileMode.Create || mode == FileMode.Truncate)
{
Delete(path);
return CreateInternal(path, access, options);
}
var mockFileData = mockFileDataAccessor.GetFile(path);
mockFileData.CheckFileAccess(path, access);
var timeAdjustments = TimeAdjustments.LastAccessTime;
if (access.HasFlag(FileAccess.Write))
{
timeAdjustments |= TimeAdjustments.LastWriteTime;
}
mockFileDataAccessor.AdjustTimes(mockFileData, timeAdjustments);
return new MockFileStream(mockFileDataAccessor, path, mode, access, FileShare.Read, options);
}
///
public override FileSystemStream OpenRead(string path)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
return Open(path, FileMode.Open, FileAccess.Read, FileShare.Read);
}
///
public override StreamReader OpenText(string path)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
return new StreamReader(OpenRead(path));
}
///
public override FileSystemStream OpenWrite(string path) => OpenWriteInternal(path, FileOptions.None);
private FileSystemStream OpenWriteInternal(string path, FileOptions options)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
return OpenInternal(path, FileMode.OpenOrCreate, FileAccess.Write, options);
}
///
public override byte[] ReadAllBytes(string path)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
if (!mockFileDataAccessor.FileExists(path))
{
throw CommonExceptions.FileNotFound(path);
}
mockFileDataAccessor.GetFile(path).CheckFileAccess(path, FileAccess.Read);
var fileData = mockFileDataAccessor.GetFile(path);
mockFileDataAccessor.AdjustTimes(fileData, TimeAdjustments.LastAccessTime);
return fileData.Contents.ToArray();
}
///
public override string[] ReadAllLines(string path)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
if (!mockFileDataAccessor.FileExists(path))
{
throw CommonExceptions.FileNotFound(path);
}
var fileData = mockFileDataAccessor.GetFile(path);
fileData.CheckFileAccess(path, FileAccess.Read);
mockFileDataAccessor.AdjustTimes(fileData, TimeAdjustments.LastAccessTime);
return fileData
.TextContents
.SplitLines();
}
///
public override string[] ReadAllLines(string path, Encoding encoding)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
if (encoding == null)
{
throw new ArgumentNullException(nameof(encoding));
}
if (!mockFileDataAccessor.FileExists(path))
{
throw CommonExceptions.FileNotFound(path);
}
var fileData = mockFileDataAccessor.GetFile(path);
fileData.CheckFileAccess(path, FileAccess.Read);
mockFileDataAccessor.AdjustTimes(fileData, TimeAdjustments.LastAccessTime);
using (var ms = new MemoryStream(fileData.Contents))
using (var sr = new StreamReader(ms, encoding))
{
return sr.ReadToEnd().SplitLines();
}
}
///
public override string ReadAllText(string path)
{
return ReadAllText(path, MockFileData.DefaultEncoding);
}
///
public override string ReadAllText(string path, Encoding encoding)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
if (!mockFileDataAccessor.FileExists(path))
{
throw CommonExceptions.FileNotFound(path);
}
if (encoding == null)
{
throw new ArgumentNullException(nameof(encoding));
}
return ReadAllTextInternal(path, encoding);
}
///
public override IEnumerable ReadLines(string path)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
return ReadAllLines(path);
}
///
public override IEnumerable ReadLines(string path, Encoding encoding)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
VerifyValueIsNotNull(encoding, "encoding");
return ReadAllLines(path, encoding);
}
///
public override void Replace(string sourceFileName, string destinationFileName, string destinationBackupFileName)
{
Replace(sourceFileName, destinationFileName, destinationBackupFileName, false);
}
///
public override void Replace(string sourceFileName, string destinationFileName, string destinationBackupFileName, bool ignoreMetadataErrors)
{
if (sourceFileName == null)
{
throw new ArgumentNullException(nameof(sourceFileName));
}
if (destinationFileName == null)
{
throw new ArgumentNullException(nameof(destinationFileName));
}
if (!mockFileDataAccessor.FileExists(sourceFileName))
{
throw CommonExceptions.FileNotFound(sourceFileName);
}
if (!mockFileDataAccessor.FileExists(destinationFileName))
{
throw CommonExceptions.FileNotFound(destinationFileName);
}
if (mockFileDataAccessor.StringOperations.Equals(sourceFileName, destinationFileName) && XFS.IsWindowsPlatform())
{
throw CommonExceptions.ProcessCannotAccessFileInUse();
}
if (destinationBackupFileName != null)
{
Copy(destinationFileName, destinationBackupFileName, overwrite: true);
}
Delete(destinationFileName);
Move(sourceFileName, destinationFileName);
}
#if FEATURE_CREATE_SYMBOLIC_LINK
///
public override IFileSystemInfo ResolveLinkTarget(string linkPath, bool returnFinalTarget)
{
var initialContainer = mockFileDataAccessor.GetFile(linkPath);
if (initialContainer.LinkTarget != null)
{
var nextLocation = initialContainer.LinkTarget;
var nextContainer = mockFileDataAccessor.GetFile(nextLocation);
if (returnFinalTarget)
{
// The maximum number of symbolic links that are followed:
// https://learn.microsoft.com/en-us/dotnet/api/system.io.directory.resolvelinktarget?view=net-6.0#remarks
int maxResolveLinks = XFS.IsWindowsPlatform() ? 63 : 40;
for (int i = 1; i < maxResolveLinks; i++)
{
if (nextContainer.LinkTarget == null)
{
break;
}
nextLocation = nextContainer.LinkTarget;
nextContainer = mockFileDataAccessor.GetFile(nextLocation);
}
if (nextContainer.LinkTarget != null)
{
throw CommonExceptions.NameCannotBeResolvedByTheSystem(linkPath);
}
}
if (nextContainer.IsDirectory)
{
return new MockDirectoryInfo(mockFileDataAccessor, nextLocation);
}
else
{
return new MockFileInfo(mockFileDataAccessor, nextLocation);
}
}
throw CommonExceptions.NameCannotBeResolvedByTheSystem(linkPath);
}
#endif
///
public override void SetAttributes(string path, FileAttributes fileAttributes)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
var possibleFileData = mockFileDataAccessor.GetFile(path);
if (possibleFileData == null)
{
var directoryInfo = mockFileDataAccessor.DirectoryInfo.New(path);
if (directoryInfo.Exists)
{
directoryInfo.Attributes = fileAttributes;
}
else
{
throw CommonExceptions.FileNotFound(path);
}
}
else
{
mockFileDataAccessor.AdjustTimes(possibleFileData, TimeAdjustments.LastAccessTime);
possibleFileData.Attributes = fileAttributes;
}
}
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public override void SetAttributes(SafeFileHandle fileHandle, FileAttributes fileAttributes)
{
throw CommonExceptions.NotImplemented();
}
#endif
///
public override void SetCreationTime(string path, DateTime creationTime)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
mockFileDataAccessor.GetFile(path).CreationTime = new DateTimeOffset(creationTime);
}
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public override void SetCreationTime(SafeFileHandle fileHandle, DateTime creationTime)
{
throw CommonExceptions.NotImplemented();
}
#endif
///
public override void SetCreationTimeUtc(string path, DateTime creationTimeUtc)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
mockFileDataAccessor.GetFile(path).CreationTime = new DateTimeOffset(creationTimeUtc, TimeSpan.Zero);
}
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public override void SetCreationTimeUtc(SafeFileHandle fileHandle, DateTime creationTimeUtc)
{
throw CommonExceptions.NotImplemented();
}
#endif
///
public override void SetLastAccessTime(string path, DateTime lastAccessTime)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
mockFileDataAccessor.GetFile(path).LastAccessTime = new DateTimeOffset(lastAccessTime);
}
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public override void SetLastAccessTime(SafeFileHandle fileHandle, DateTime lastAccessTime)
{
throw CommonExceptions.NotImplemented();
}
#endif
///
public override void SetLastAccessTimeUtc(string path, DateTime lastAccessTimeUtc)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
mockFileDataAccessor.GetFile(path).LastAccessTime = new DateTimeOffset(lastAccessTimeUtc, TimeSpan.Zero);
}
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public override void SetLastAccessTimeUtc(SafeFileHandle fileHandle, DateTime lastAccessTimeUtc)
{
throw CommonExceptions.NotImplemented();
}
#endif
///
public override void SetLastWriteTime(string path, DateTime lastWriteTime)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
mockFileDataAccessor.GetFile(path).LastWriteTime = new DateTimeOffset(lastWriteTime);
}
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public override void SetLastWriteTime(SafeFileHandle fileHandle, DateTime lastWriteTime)
{
throw CommonExceptions.NotImplemented();
}
#endif
///
public override void SetLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
mockFileDataAccessor.GetFile(path).LastWriteTime = new DateTimeOffset(lastWriteTimeUtc, TimeSpan.Zero);
}
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public override void SetLastWriteTimeUtc(SafeFileHandle fileHandle, DateTime lastWriteTimeUtc)
{
throw CommonExceptions.NotImplemented();
}
#endif
#if FEATURE_UNIX_FILE_MODE
///
public override void SetUnixFileMode(string path, UnixFileMode mode)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
if (!mockFileDataAccessor.FileExists(path))
{
throw CommonExceptions.FileNotFound(path);
}
var mockFileData = mockFileDataAccessor.GetFile(path);
mockFileData.UnixMode = mode;
}
#endif
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public override void SetUnixFileMode(SafeFileHandle fileHandle, UnixFileMode mode)
{
throw CommonExceptions.NotImplemented();
}
#endif
///
/// Creates a new file, writes the specified byte array to the file, and then closes the file.
/// If the target file already exists, it is overwritten.
///
/// The file to write to.
/// The bytes to write to the file.
/// is a zero-length string, contains only white space, or contains one or more invalid characters as defined by .
/// is or contents is empty.
///
/// The specified path, file name, or both exceed the system-defined maximum length.
/// For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters.
///
/// The specified path is invalid (for example, it is on an unmapped drive).
/// An I/O error occurred while opening the file.
///
/// path specified a file that is read-only.
/// -or-
/// This operation is not supported on the current platform.
/// -or-
/// path specified a directory.
/// -or-
/// The caller does not have the required permission.
///
/// The file specified in was not found.
/// is in an invalid format.
/// The caller does not have the required permission.
///
/// Given a byte array and a file path, this method opens the specified file, writes the contents of the byte array to the file, and then closes the file.
///
public override void WriteAllBytes(string path, byte[] bytes)
{
VerifyValueIsNotNull(bytes, "bytes");
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
VerifyDirectoryExists(path);
mockFileDataAccessor.AddFile(path, mockFileDataAccessor.AdjustTimes(new MockFileData(bytes.ToArray()), TimeAdjustments.All));
}
#if FEATURE_FILE_SPAN
///
public override void WriteAllBytes(string path, ReadOnlySpan bytes)
{
WriteAllBytes(path, bytes.ToArray());
}
#endif
///
/// Creates a new file, writes a collection of strings to the file, and then closes the file.
///
/// The file to write to.
/// The lines to write to the file.
/// is a zero-length string, contains only white space, or contains one or more invalid characters as defined by .
/// Either or is .
/// The specified path is invalid (for example, it is on an unmapped drive).
/// The file specified in was not found.
/// An I/O error occurred while opening the file.
///
/// The specified path, file name, or both exceed the system-defined maximum length.
/// For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters.
///
/// is in an invalid format.
/// The caller does not have the required permission.
///
/// specified a file that is read-only.
/// -or-
/// This operation is not supported on the current platform.
/// -or-
/// specified a directory.
/// -or-
/// The caller does not have the required permission.
///
///
///
/// If the target file already exists, it is overwritten.
///
///
/// You can use this method to create the contents for a collection class that takes an in its constructor, such as a , , or a class.
///
///
public override void WriteAllLines(string path, IEnumerable contents)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
VerifyValueIsNotNull(contents, "contents");
WriteAllLines(path, contents, MockFileData.DefaultEncoding);
}
///
/// Creates a new file by using the specified encoding, writes a collection of strings to the file, and then closes the file.
///
/// The file to write to.
/// The lines to write to the file.
/// The character encoding to use.
/// is a zero-length string, contains only white space, or contains one or more invalid characters as defined by .
/// Either , , or is .
/// The specified path is invalid (for example, it is on an unmapped drive).
/// The file specified in was not found.
/// An I/O error occurred while opening the file.
///
/// The specified path, file name, or both exceed the system-defined maximum length.
/// For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters.
///
/// is in an invalid format.
/// The caller does not have the required permission.
///
/// specified a file that is read-only.
/// -or-
/// This operation is not supported on the current platform.
/// -or-
/// specified a directory.
/// -or-
/// The caller does not have the required permission.
///
///
///
/// If the target file already exists, it is overwritten.
///
///
/// You can use this method to create a file that contains the following:
///
/// -
/// The results of a LINQ to Objects query on the lines of a file, as obtained by using the ReadLines method.
///
/// -
/// The contents of a collection that implements an of strings.
///
///
///
///
public override void WriteAllLines(string path, IEnumerable contents, Encoding encoding)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
VerifyValueIsNotNull(contents, "contents");
VerifyValueIsNotNull(encoding, "encoding");
var sb = new StringBuilder();
foreach (var line in contents)
{
sb.AppendLine(line);
}
WriteAllText(path, sb.ToString(), encoding);
}
///
/// Creates a new file, writes the specified string array to the file by using the specified encoding, and then closes the file.
///
/// The file to write to.
/// The string array to write to the file.
/// is a zero-length string, contains only white space, or contains one or more invalid characters as defined by .
/// Either or is .
///
/// The specified path, file name, or both exceed the system-defined maximum length.
/// For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters.
///
/// The specified path is invalid (for example, it is on an unmapped drive).
/// An I/O error occurred while opening the file.
///
/// specified a file that is read-only.
/// -or-
/// This operation is not supported on the current platform.
/// -or-
/// specified a directory.
/// -or-
/// The caller does not have the required permission.
///
/// The file specified in was not found.
/// is in an invalid format.
/// The caller does not have the required permission.
///
///
/// If the target file already exists, it is overwritten.
///
///
/// The default behavior of the WriteAllLines method is to write out data using UTF-8 encoding without a byte order mark (BOM). If it is necessary to include a UTF-8 identifier, such as a byte order mark, at the beginning of a file, use the method overload with encoding.
///
///
/// Given a string array and a file path, this method opens the specified file, writes the string array to the file using the specified encoding,
/// and then closes the file.
///
///
public override void WriteAllLines(string path, string[] contents)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
VerifyValueIsNotNull(contents, "contents");
WriteAllLines(path, contents, MockFileData.DefaultEncoding);
}
///
/// Creates a new file, writes the specified string array to the file by using the specified encoding, and then closes the file.
///
/// The file to write to.
/// The string array to write to the file.
/// An object that represents the character encoding applied to the string array.
/// is a zero-length string, contains only white space, or contains one or more invalid characters as defined by .
/// Either or is .
///
/// The specified path, file name, or both exceed the system-defined maximum length.
/// For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters.
///
/// The specified path is invalid (for example, it is on an unmapped drive).
/// An I/O error occurred while opening the file.
///
/// specified a file that is read-only.
/// -or-
/// This operation is not supported on the current platform.
/// -or-
/// specified a directory.
/// -or-
/// The caller does not have the required permission.
///
/// The file specified in was not found.
/// is in an invalid format.
/// The caller does not have the required permission.
///
///
/// If the target file already exists, it is overwritten.
///
///
/// Given a string array and a file path, this method opens the specified file, writes the string array to the file using the specified encoding,
/// and then closes the file.
///
///
public override void WriteAllLines(string path, string[] contents, Encoding encoding)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
VerifyValueIsNotNull(contents, "contents");
VerifyValueIsNotNull(encoding, "encoding");
WriteAllLines(path, new List(contents), encoding);
}
///
/// Creates a new file, writes the specified string to the file using the specified encoding, and then closes the file. If the target file already exists, it is overwritten.
///
/// The file to write to.
/// The string to write to the file.
/// is a zero-length string, contains only white space, or contains one or more invalid characters as defined by .
/// is or contents is empty.
///
/// The specified path, file name, or both exceed the system-defined maximum length.
/// For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters.
///
/// The specified path is invalid (for example, it is on an unmapped drive).
/// An I/O error occurred while opening the file.
///
/// path specified a file that is read-only.
/// -or-
/// This operation is not supported on the current platform.
/// -or-
/// path specified a directory.
/// -or-
/// The caller does not have the required permission.
///
/// The file specified in was not found.
/// is in an invalid format.
/// The caller does not have the required permission.
///
/// This method uses UTF-8 encoding without a Byte-Order Mark (BOM), so using the method will return an empty byte array.
/// If it is necessary to include a UTF-8 identifier, such as a byte order mark, at the beginning of a file, use the method overload with encoding.
///
/// Given a string and a file path, this method opens the specified file, writes the string to the file, and then closes the file.
///
///
public override void WriteAllText(string path, string contents)
{
WriteAllText(path, contents, MockFileData.DefaultEncoding);
}
///
/// Creates a new file, writes the specified string to the file using the specified encoding, and then closes the file. If the target file already exists, it is overwritten.
///
/// The file to write to.
/// The string to write to the file.
/// The encoding to apply to the string.
/// is a zero-length string, contains only white space, or contains one or more invalid characters as defined by .
/// is or contents is empty.
///
/// The specified path, file name, or both exceed the system-defined maximum length.
/// For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters.
///
/// The specified path is invalid (for example, it is on an unmapped drive).
/// An I/O error occurred while opening the file.
///
/// path specified a file that is read-only.
/// -or-
/// This operation is not supported on the current platform.
/// -or-
/// path specified a directory.
/// -or-
/// The caller does not have the required permission.
///
/// The file specified in was not found.
/// is in an invalid format.
/// The caller does not have the required permission.
///
/// Given a string and a file path, this method opens the specified file, writes the string to the file using the specified encoding, and then closes the file.
/// The file handle is guaranteed to be closed by this method, even if exceptions are raised.
///
public override void WriteAllText(string path, string contents, Encoding encoding)
{
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
VerifyValueIsNotNull(path, "path");
if (mockFileDataAccessor.Directory.Exists(path))
{
throw CommonExceptions.AccessDenied(path);
}
VerifyDirectoryExists(path);
MockFileData data = contents == null ? new MockFileData(new byte[0]) : new MockFileData(contents, encoding);
mockFileDataAccessor.AddFile(path, mockFileDataAccessor.AdjustTimes(data, TimeAdjustments.All));
}
#if FEATURE_FILE_SPAN
///
public override void WriteAllText(string path, ReadOnlySpan contents)
{
WriteAllText(path, contents.ToString());
}
///
public override void WriteAllText(string path, ReadOnlySpan contents, Encoding encoding)
{
WriteAllText(path, contents.ToString(), encoding);
}
#endif
internal static string ReadAllBytes(byte[] contents, Encoding encoding)
{
using (var ms = new MemoryStream(contents))
using (var sr = new StreamReader(ms, encoding))
{
return sr.ReadToEnd();
}
}
private string ReadAllTextInternal(string path, Encoding encoding)
{
var mockFileData = mockFileDataAccessor.GetFile(path);
mockFileData.CheckFileAccess(path, FileAccess.Read);
mockFileDataAccessor.AdjustTimes(mockFileData, TimeAdjustments.LastAccessTime);
return ReadAllBytes(mockFileData.Contents, encoding);
}
private void VerifyValueIsNotNull(object value, string parameterName)
{
if (value == null)
{
throw new ArgumentNullException(parameterName, StringResources.Manager.GetString("VALUE_CANNOT_BE_NULL"));
}
}
private void VerifyDirectoryExists(string path)
{
var pathOps = mockFileDataAccessor.Path;
var dir = pathOps.GetDirectoryName(pathOps.GetFullPath(path));
if (!mockFileDataAccessor.Directory.Exists(dir))
{
throw CommonExceptions.CouldNotFindPartOfPath(path);
}
}
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFileData.cs
================================================
using System.Linq;
using System.Runtime.Versioning;
using System.Security.AccessControl;
using System.Text;
namespace System.IO.Abstractions.TestingHelpers;
///
/// The class represents the associated data of a file.
///
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public class MockFileData
{
///
/// The default encoding.
///
public static readonly Encoding DefaultEncoding = new UTF8Encoding(false, true);
///
/// The null object. It represents the data of a non-existing file or directory.
///
internal static readonly MockFileData NullObject = new MockFileData(string.Empty)
{
LastWriteTime = new DateTime(1601, 01, 01, 00, 00, 00, DateTimeKind.Utc),
LastAccessTime = new DateTime(1601, 01, 01, 00, 00, 00, DateTimeKind.Utc),
CreationTime = new DateTime(1601, 01, 01, 00, 00, 00, DateTimeKind.Utc),
Attributes = (FileAttributes)(-1),
};
///
/// Gets the default date time offset.
/// E.g. for not existing files.
///
public static readonly DateTimeOffset DefaultDateTimeOffset = new DateTime(1601, 01, 01, 00, 00, 00, DateTimeKind.Utc);
///
/// The access control of the .
///
#if FEATURE_SERIALIZABLE
[NonSerialized]
#endif
private FileSecurity accessControl;
///
/// Gets a value indicating whether the is a directory or not.
///
public bool IsDirectory { get { return Attributes.HasFlag(FileAttributes.Directory); } }
///
/// Initializes a new instance of the class with an empty content.
///
private MockFileData()
{
var now = DateTime.UtcNow;
LastWriteTime = now;
LastAccessTime = now;
CreationTime = now;
}
///
/// Initializes a new instance of the class with the content of using the encoding of .
///
/// The textual content encoded into bytes with .
public MockFileData(string textContents)
: this(DefaultEncoding.GetBytes(textContents))
{ }
///
/// Initializes a new instance of the class with the content of using the encoding of .
///
/// The textual content.
/// The specific encoding used the encode the text.
/// The constructor respect the BOM of .
public MockFileData(string textContents, Encoding encoding)
: this()
{
Contents = encoding.GetPreamble().Concat(encoding.GetBytes(textContents)).ToArray();
}
///
/// Initializes a new instance of the class with the content of .
///
/// The actual content.
/// Thrown if is .
public MockFileData(byte[] contents)
: this()
{
Contents = contents ?? throw new ArgumentNullException(nameof(contents));
}
///
/// Initializes a new instance of the class by copying the given .
///
/// The template instance.
/// Thrown if is .
public MockFileData(MockFileData template)
{
if (template == null)
{
throw new ArgumentNullException(nameof(template));
}
accessControl = template.accessControl;
Attributes = template.Attributes;
Contents = template.Contents.ToArray();
CreationTime = template.CreationTime;
FileVersionInfo = template.FileVersionInfo;
LastAccessTime = template.LastAccessTime;
LastWriteTime = template.LastWriteTime;
#if FEATURE_FILE_SYSTEM_INFO_LINK_TARGET
LinkTarget = template.LinkTarget;
#endif
}
///
/// Gets or sets the byte contents of the .
///
public byte[] Contents { get; set; }
///
/// Gets or sets the file version info of the
///
public IFileVersionInfo FileVersionInfo { get; set; }
///
/// Gets or sets the string contents of the .
///
///
/// The setter uses the using this can scramble the actual contents.
///
public string TextContents
{
get { return MockFile.ReadAllBytes(Contents, DefaultEncoding); }
set { Contents = DefaultEncoding.GetBytes(value); }
}
///
/// Gets or sets the date and time the was created.
///
public DateTimeOffset CreationTime
{
get { return creationTime; }
set { creationTime = value.ToUniversalTime(); }
}
private DateTimeOffset creationTime;
///
/// Gets or sets the date and time of the was last accessed to.
///
public DateTimeOffset LastAccessTime
{
get { return lastAccessTime; }
set { lastAccessTime = value.ToUniversalTime(); }
}
private DateTimeOffset lastAccessTime;
///
/// Gets or sets the date and time of the was last written to.
///
public DateTimeOffset LastWriteTime
{
get { return lastWriteTime; }
set { lastWriteTime = value.ToUniversalTime(); }
}
private DateTimeOffset lastWriteTime;
#if FEATURE_FILE_SYSTEM_INFO_LINK_TARGET
///
/// Gets or sets the link target of the .
///
public string LinkTarget { get; set; }
#endif
///
/// Casts a string into .
///
/// The path of the to be created.
public static implicit operator MockFileData(string s)
{
return new MockFileData(s);
}
///
/// Gets or sets the specified of the .
///
public FileAttributes Attributes { get; set; } = FileAttributes.Normal;
///
/// Gets or sets of the .
///
[SupportedOSPlatform("windows")]
public FileSecurity AccessControl
{
get
{
// FileSecurity's constructor will throw PlatformNotSupportedException on non-Windows platform, so we initialize it in lazy way.
// This let's us use this class as long as we don't use AccessControl property.
return accessControl ?? (accessControl = new FileSecurity());
}
set { accessControl = value; }
}
///
/// Gets or sets the File sharing mode for this file, this allows you to lock a file for reading or writing.
///
public FileShare AllowedFileShare { get; set; } = FileShare.ReadWrite | FileShare.Delete;
#if FEATURE_UNIX_FILE_MODE
///
/// Gets or sets the Unix file mode (permissions) for this file.
/// This allows you to configure the read, write and execute access for user, group and other.
///
public UnixFileMode UnixMode { get; set; } = UnixFileMode.UserRead | UnixFileMode.GroupRead |
UnixFileMode.OtherRead | UnixFileMode.UserWrite;
#endif
///
/// Checks whether the file is accessible for this type of FileAccess.
/// MockFileData can be configured to have FileShare.None, which indicates it is locked by a 'different process'.
///
/// If the file is 'locked by a different process', an IOException will be thrown.
/// If the file is read-only and is accessed for writing, an UnauthorizedAccessException will be thrown.
///
/// The path is used in the exception message to match the message in real life situations
/// The access type to check
internal void CheckFileAccess(string path, FileAccess access)
{
if (!AllowedFileShare.HasFlag((FileShare)access))
{
throw CommonExceptions.ProcessCannotAccessFileInUse(path);
}
if (Attributes.HasFlag(FileAttributes.ReadOnly) && access.HasFlag(FileAccess.Write))
{
throw CommonExceptions.AccessDenied(path);
}
}
internal virtual MockFileData Clone()
{
return new MockFileData(this);
}
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFileInfo.cs
================================================
using System.Runtime.Versioning;
using System.Security.AccessControl;
namespace System.IO.Abstractions.TestingHelpers;
///
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public class MockFileInfo : FileInfoBase, IFileSystemAclSupport
{
private readonly IMockFileDataAccessor mockFileSystem;
private string path;
private readonly string originalPath;
private MockFileData cachedMockFileData;
private MockFile mockFile;
private bool refreshOnNextRead;
///
public MockFileInfo(IMockFileDataAccessor mockFileSystem, string path) : base(mockFileSystem?.FileSystem)
{
this.mockFileSystem = mockFileSystem ?? throw new ArgumentNullException(nameof(mockFileSystem));
mockFileSystem.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
this.originalPath = path;
this.path = mockFileSystem.Path.GetFullPath(path);
this.mockFile = new MockFile(mockFileSystem);
Refresh();
}
#if FEATURE_CREATE_SYMBOLIC_LINK
///
public override void CreateAsSymbolicLink(string pathToTarget)
{
FileSystem.File.CreateSymbolicLink(FullName, pathToTarget);
}
#endif
///
public override void Delete()
{
refreshOnNextRead = true;
mockFile.Delete(path);
}
///
public override void Refresh()
{
var mockFileData = mockFileSystem.GetFile(path)?.Clone();
cachedMockFileData = mockFileData ?? MockFileData.NullObject.Clone();
}
#if FEATURE_CREATE_SYMBOLIC_LINK
///
public override IFileSystemInfo ResolveLinkTarget(bool returnFinalTarget)
{
return FileSystem.File.ResolveLinkTarget(FullName, returnFinalTarget);
}
#endif
///
public override FileAttributes Attributes
{
get
{
var mockFileData = GetMockFileDataForRead();
return mockFileData.Attributes;
}
set
{
var mockFileData = GetMockFileDataForWrite();
mockFileData.Attributes = value & ~FileAttributes.Directory;
}
}
///
public override DateTime CreationTime
{
get
{
var mockFileData = GetMockFileDataForRead();
return mockFileData.CreationTime.LocalDateTime;
}
set
{
var mockFileData = GetMockFileDataForWrite();
mockFileData.CreationTime = AdjustUnspecifiedKind(value, DateTimeKind.Local);
}
}
///
public override DateTime CreationTimeUtc
{
get
{
var mockFileData = GetMockFileDataForRead();
return mockFileData.CreationTime.UtcDateTime;
}
set
{
var mockFileData = GetMockFileDataForWrite();
mockFileData.CreationTime = AdjustUnspecifiedKind(value, DateTimeKind.Utc);
}
}
///
public override bool Exists
{
get
{
var mockFileData = GetMockFileDataForRead();
return (int)mockFileData.Attributes != -1 && !mockFileData.IsDirectory;
}
}
///
public override string Extension
{
get
{
// System.IO.Path.GetExtension does only string manipulation,
// so it's safe to delegate.
return Path.GetExtension(path);
}
}
///
public override string FullName
{
get { return path; }
}
///
public override DateTime LastAccessTime
{
get
{
var mockFileData = GetMockFileDataForRead();
return mockFileData.LastAccessTime.LocalDateTime;
}
set
{
var mockFileData = GetMockFileDataForWrite();
mockFileData.LastAccessTime = AdjustUnspecifiedKind(value, DateTimeKind.Local);
}
}
///
public override DateTime LastAccessTimeUtc
{
get
{
var mockFileData = GetMockFileDataForRead();
return mockFileData.LastAccessTime.UtcDateTime;
}
set
{
var mockFileData = GetMockFileDataForWrite();
mockFileData.LastAccessTime = AdjustUnspecifiedKind(value, DateTimeKind.Utc);
}
}
///
public override DateTime LastWriteTime
{
get
{
var mockFileData = GetMockFileDataForRead();
return mockFileData.LastWriteTime.LocalDateTime;
}
set
{
var mockFileData = GetMockFileDataForWrite();
mockFileData.LastWriteTime = AdjustUnspecifiedKind(value, DateTimeKind.Local);
}
}
///
public override DateTime LastWriteTimeUtc
{
get
{
var mockFileData = GetMockFileDataForRead();
return mockFileData.LastWriteTime.UtcDateTime;
}
set
{
var mockFileData = GetMockFileDataForWrite();
mockFileData.LastWriteTime = AdjustUnspecifiedKind(value, DateTimeKind.Utc);
}
}
#if FEATURE_FILE_SYSTEM_INFO_LINK_TARGET
///
public override string LinkTarget
{
get
{
var mockFileData = GetMockFileDataForRead();
return mockFileData.LinkTarget;
}
}
#endif
///
public override string Name
{
get { return new MockPath(mockFileSystem).GetFileName(path); }
}
///
public override StreamWriter AppendText()
{
return new StreamWriter(new MockFileStream(mockFileSystem, FullName, FileMode.Append, FileAccess.Write));
}
///
public override IFileInfo CopyTo(string destFileName)
{
return CopyTo(destFileName, false);
}
///
public override IFileInfo CopyTo(string destFileName, bool overwrite)
{
if (destFileName == FullName)
{
return this;
}
mockFile.Copy(FullName, destFileName, overwrite);
return mockFileSystem.FileInfo.New(destFileName);
}
///
public override FileSystemStream Create()
{
var result = mockFile.Create(FullName);
refreshOnNextRead = true;
return result;
}
///
public override StreamWriter CreateText()
{
var result = mockFile.CreateText(FullName);
refreshOnNextRead = true;
return result;
}
///
public override void Decrypt()
{
var mockFileData = GetMockFileDataForWrite();
mockFileData.Attributes &= ~FileAttributes.Encrypted;
}
///
public override void Encrypt()
{
var mockFileData = GetMockFileDataForWrite();
mockFileData.Attributes |= FileAttributes.Encrypted;
}
///
public override void MoveTo(string destFileName)
{
mockFile.Move(path, destFileName);
path = mockFileSystem.Path.GetFullPath(destFileName);
}
#if FEATURE_FILE_MOVE_WITH_OVERWRITE
///
public override void MoveTo(string destFileName, bool overwrite)
{
mockFile.Move(path, destFileName, overwrite);
path = mockFileSystem.Path.GetFullPath(destFileName);
}
#endif
///
public override FileSystemStream Open(FileMode mode)
{
return mockFile.Open(FullName, mode);
}
///
public override FileSystemStream Open(FileMode mode, FileAccess access)
{
return mockFile.Open(FullName, mode, access);
}
///
public override FileSystemStream Open(FileMode mode, FileAccess access, FileShare share)
{
return mockFile.Open(FullName, mode, access, share);
}
#if FEATURE_FILESTREAM_OPTIONS
///
public override FileSystemStream Open(FileStreamOptions options)
{
return mockFile.Open(FullName, options.Mode, options.Access, options.Share);
}
#endif
///
public override FileSystemStream OpenRead() => mockFile.OpenRead(path);
///
public override StreamReader OpenText() => mockFile.OpenText(path);
///
public override FileSystemStream OpenWrite() => mockFile.OpenWrite(path);
///
public override IFileInfo Replace(string destinationFileName, string destinationBackupFileName)
{
return Replace(destinationFileName, destinationBackupFileName, false);
}
///
public override IFileInfo Replace(string destinationFileName, string destinationBackupFileName, bool ignoreMetadataErrors)
{
mockFile.Replace(path, destinationFileName, destinationBackupFileName, ignoreMetadataErrors);
return mockFileSystem.FileInfo.New(destinationFileName);
}
///
public override IDirectoryInfo Directory
{
get
{
return mockFileSystem.DirectoryInfo.New(DirectoryName);
}
}
///
public override string DirectoryName
{
get
{
// System.IO.Path.GetDirectoryName does only string manipulation,
// so it's safe to delegate.
return Path.GetDirectoryName(path);
}
}
///
public override bool IsReadOnly
{
get
{
var mockFileData = GetMockFileDataForRead();
return (mockFileData.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly;
}
set
{
var mockFileData = GetMockFileDataForWrite();
if (value)
{
mockFileData.Attributes |= FileAttributes.ReadOnly;
}
else
{
mockFileData.Attributes &= ~FileAttributes.ReadOnly;
}
}
}
///
public override long Length
{
get
{
var mockFileData = GetMockFileDataForRead();
if (mockFileData == null || mockFileData.IsDirectory)
{
throw CommonExceptions.FileNotFound(path);
}
return mockFileData.Contents.Length;
}
}
///
public override string ToString()
{
return originalPath;
}
///
[SupportedOSPlatform("windows")]
public object GetAccessControl()
{
return GetMockFileData().AccessControl;
}
///
[SupportedOSPlatform("windows")]
public object GetAccessControl(IFileSystemAclSupport.AccessControlSections includeSections)
{
return GetMockFileData().AccessControl;
}
///
[SupportedOSPlatform("windows")]
public void SetAccessControl(object value)
{
GetMockFileData().AccessControl = value as FileSecurity;
}
private MockFileData GetMockFileData()
{
return mockFileSystem.GetFile(path)
?? throw CommonExceptions.FileNotFound(path);
}
private static DateTime AdjustUnspecifiedKind(DateTime time, DateTimeKind fallbackKind)
{
if (time.Kind == DateTimeKind.Unspecified)
{
return DateTime.SpecifyKind(time, fallbackKind);
}
return time;
}
private MockFileData GetMockFileDataForRead()
{
if (refreshOnNextRead)
{
Refresh();
refreshOnNextRead = false;
}
return cachedMockFileData;
}
private MockFileData GetMockFileDataForWrite()
{
refreshOnNextRead = true;
return mockFileSystem.GetFile(path)
?? throw CommonExceptions.FileNotFound(path);
}
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFileInfoFactory.cs
================================================
namespace System.IO.Abstractions.TestingHelpers;
///
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public class MockFileInfoFactory : IFileInfoFactory
{
private readonly IMockFileDataAccessor mockFileSystem;
///
public MockFileInfoFactory(IMockFileDataAccessor mockFileSystem)
{
this.mockFileSystem = mockFileSystem ?? throw new ArgumentNullException(nameof(mockFileSystem));
}
///
public IFileSystem FileSystem
=> mockFileSystem;
///
public IFileInfo New(string fileName)
{
return new MockFileInfo(mockFileSystem, fileName);
}
///
public IFileInfo Wrap(FileInfo fileInfo)
{
if (fileInfo == null)
{
return null;
}
return new MockFileInfo(mockFileSystem, fileInfo.FullName);
}
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFileStream.cs
================================================
using System.Threading.Tasks;
using System.Threading;
using System.Runtime.Versioning;
using System.Security.AccessControl;
namespace System.IO.Abstractions.TestingHelpers;
///
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public class MockFileStream : FileSystemStream, IFileSystemAclSupport
{
///
/// Wrapper around a with no backing store, which
/// is used as a replacement for a . As such
/// it implements the same properties and methods as a .
///
public new static FileSystemStream Null { get; } = new NullFileSystemStream();
private class NullFileSystemStream : FileSystemStream
{
///
/// Initializes a new instance of .
///
public NullFileSystemStream() : base(Null, ".", true)
{
}
}
private readonly IMockFileDataAccessor mockFileDataAccessor;
private readonly string path;
private readonly Guid guid = Guid.NewGuid();
private readonly FileAccess access = FileAccess.ReadWrite;
private readonly FileShare share = FileShare.Read;
private readonly FileOptions options;
private readonly MockFileData fileData;
private bool disposed;
///
public MockFileStream(
IMockFileDataAccessor mockFileDataAccessor,
string path,
FileMode mode,
FileAccess access = FileAccess.ReadWrite,
FileShare share = FileShare.Read,
FileOptions options = FileOptions.None)
: base(new MemoryStream(),
path == null ? null : Path.GetFullPath(path),
(options & FileOptions.Asynchronous) != 0)
{
ThrowIfInvalidModeAccess(mode, access);
this.mockFileDataAccessor = mockFileDataAccessor ?? throw new ArgumentNullException(nameof(mockFileDataAccessor));
path = mockFileDataAccessor.PathVerifier.FixPath(path);
this.path = path;
this.options = options;
if (mockFileDataAccessor.FileExists(path))
{
if (mode.Equals(FileMode.CreateNew))
{
throw CommonExceptions.FileAlreadyExists(path);
}
fileData = mockFileDataAccessor.GetFile(path);
fileData.CheckFileAccess(path, access);
var timeAdjustments = GetTimeAdjustmentsForFileStreamWhenFileExists(mode, access);
mockFileDataAccessor.AdjustTimes(fileData, timeAdjustments);
var existingContents = fileData.Contents;
var keepExistingContents =
existingContents?.Length > 0 &&
mode != FileMode.Truncate && mode != FileMode.Create;
if (keepExistingContents)
{
base.Write(existingContents, 0, existingContents.Length);
base.Seek(0, mode == FileMode.Append
? SeekOrigin.End
: SeekOrigin.Begin);
}
}
else
{
var directoryPath = mockFileDataAccessor.Path.GetDirectoryName(path);
if (!string.IsNullOrEmpty(directoryPath) && !mockFileDataAccessor.Directory.Exists(directoryPath))
{
throw CommonExceptions.CouldNotFindPartOfPath(path);
}
if (mode.Equals(FileMode.Open) || mode.Equals(FileMode.Truncate))
{
throw CommonExceptions.FileNotFound(path);
}
fileData = new MockFileData(new byte[] { });
mockFileDataAccessor.AdjustTimes(fileData,
TimeAdjustments.CreationTime | TimeAdjustments.LastAccessTime);
mockFileDataAccessor.AddFile(path, fileData);
}
mockFileDataAccessor.FileHandles.AddHandle(path, guid, access, share);
this.access = access;
this.share = share;
}
private static void ThrowIfInvalidModeAccess(FileMode mode, FileAccess access)
{
if (mode == FileMode.Append)
{
if (access == FileAccess.Read)
{
throw CommonExceptions.InvalidAccessCombination(mode, access);
}
if (access != FileAccess.Write)
{
throw CommonExceptions.AppendAccessOnlyInWriteOnlyMode();
}
}
if (!access.HasFlag(FileAccess.Write) &&
(mode == FileMode.Truncate || mode == FileMode.CreateNew ||
mode == FileMode.Create || mode == FileMode.Append))
{
throw CommonExceptions.InvalidAccessCombination(mode, access);
}
}
///
public override bool CanRead => access.HasFlag(FileAccess.Read);
///
public override bool CanWrite => access.HasFlag(FileAccess.Write);
///
public override int Read(byte[] buffer, int offset, int count)
{
mockFileDataAccessor.AdjustTimes(fileData,
TimeAdjustments.LastAccessTime);
return base.Read(buffer, offset, count);
}
///
protected override void Dispose(bool disposing)
{
if (disposed)
{
return;
}
mockFileDataAccessor.FileHandles.RemoveHandle(path, guid);
InternalFlush();
base.Dispose(disposing);
OnClose();
disposed = true;
}
///
public override void EndWrite(IAsyncResult asyncResult)
{
if (!CanWrite)
{
throw new NotSupportedException("Stream does not support writing.");
}
base.EndWrite(asyncResult);
}
///
public override void SetLength(long value)
{
if (!CanWrite)
{
throw new NotSupportedException("Stream does not support writing.");
}
base.SetLength(value);
}
///
public override void Write(byte[] buffer, int offset, int count)
{
if (!CanWrite)
{
throw new NotSupportedException("Stream does not support writing.");
}
mockFileDataAccessor.AdjustTimes(fileData,
TimeAdjustments.LastAccessTime | TimeAdjustments.LastWriteTime);
base.Write(buffer, offset, count);
}
#if FEATURE_SPAN
///
public override void Write(ReadOnlySpan buffer)
{
if (!CanWrite)
{
throw new NotSupportedException("Stream does not support writing.");
}
mockFileDataAccessor.AdjustTimes(fileData,
TimeAdjustments.LastAccessTime | TimeAdjustments.LastWriteTime);
base.Write(buffer);
}
#endif
///
public override Task WriteAsync(byte[] buffer, int offset, int count,
CancellationToken cancellationToken)
{
if (!CanWrite)
{
throw new NotSupportedException("Stream does not support writing.");
}
mockFileDataAccessor.AdjustTimes(fileData,
TimeAdjustments.LastAccessTime | TimeAdjustments.LastWriteTime);
return base.WriteAsync(buffer, offset, count, cancellationToken);
}
#if FEATURE_SPAN
///
public override ValueTask WriteAsync(ReadOnlyMemory buffer,
CancellationToken cancellationToken = new())
{
if (!CanWrite)
{
throw new NotSupportedException("Stream does not support writing.");
}
mockFileDataAccessor.AdjustTimes(fileData,
TimeAdjustments.LastAccessTime | TimeAdjustments.LastWriteTime);
return base.WriteAsync(buffer, cancellationToken);
}
#endif
///
public override void WriteByte(byte value)
{
if (!CanWrite)
{
throw new NotSupportedException("Stream does not support writing.");
}
mockFileDataAccessor.AdjustTimes(fileData,
TimeAdjustments.LastAccessTime | TimeAdjustments.LastWriteTime);
base.WriteByte(value);
}
///
public override void Flush()
{
InternalFlush();
}
///
public override void Flush(bool flushToDisk)
=> InternalFlush();
///
public override Task FlushAsync(CancellationToken cancellationToken)
{
InternalFlush();
return Task.CompletedTask;
}
///
[SupportedOSPlatform("windows")]
public object GetAccessControl()
{
return GetMockFileData().AccessControl;
}
///
[SupportedOSPlatform("windows")]
public object GetAccessControl(IFileSystemAclSupport.AccessControlSections includeSections)
{
return GetMockFileData().AccessControl;
}
///
[SupportedOSPlatform("windows")]
public void SetAccessControl(object value)
{
GetMockFileData().AccessControl = value as FileSecurity;
}
private MockFileData GetMockFileData()
{
return mockFileDataAccessor.GetFile(path)
?? throw CommonExceptions.FileNotFound(path);
}
private void InternalFlush()
{
if (mockFileDataAccessor.FileExists(path))
{
var mockFileData = mockFileDataAccessor.GetFile(path);
/* reset back to the beginning .. */
var position = Position;
Seek(0, SeekOrigin.Begin);
/* .. read everything out */
var data = new byte[Length];
_ = Read(data, 0, (int)Length);
/* restore to original position */
Seek(position, SeekOrigin.Begin);
/* .. put it in the mock system */
mockFileData.Contents = data;
}
}
private void OnClose()
{
if (options.HasFlag(FileOptions.DeleteOnClose) && mockFileDataAccessor.FileExists(path))
{
mockFileDataAccessor.RemoveFile(path);
}
if (options.HasFlag(FileOptions.Encrypted) && mockFileDataAccessor.FileExists(path))
{
#pragma warning disable CA1416 // Ignore SupportedOSPlatform for testing helper encryption
mockFileDataAccessor.FileInfo.New(path).Encrypt();
#pragma warning restore CA1416
}
}
private TimeAdjustments GetTimeAdjustmentsForFileStreamWhenFileExists(FileMode mode, FileAccess access)
{
switch (mode)
{
case FileMode.Append:
case FileMode.CreateNew:
if (access.HasFlag(FileAccess.Read))
{
return TimeAdjustments.LastAccessTime;
}
return TimeAdjustments.None;
case FileMode.Create:
case FileMode.Truncate:
if (access.HasFlag(FileAccess.Write))
{
return TimeAdjustments.LastAccessTime | TimeAdjustments.LastWriteTime;
}
return TimeAdjustments.LastAccessTime;
case FileMode.Open:
case FileMode.OpenOrCreate:
default:
return TimeAdjustments.None;
}
}
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFileStreamFactory.cs
================================================
using System.Security.AccessControl;
using Microsoft.Win32.SafeHandles;
namespace System.IO.Abstractions.TestingHelpers;
///
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public class MockFileStreamFactory : IFileStreamFactory
{
private readonly IMockFileDataAccessor mockFileSystem;
///
public MockFileStreamFactory(IMockFileDataAccessor mockFileSystem)
=> this.mockFileSystem = mockFileSystem ?? throw new ArgumentNullException(nameof(mockFileSystem));
///
public IFileSystem FileSystem
=> mockFileSystem;
///
public FileSystemStream New(SafeFileHandle handle, FileAccess access)
=> new MockFileStream(mockFileSystem, handle.ToString(), FileMode.Open, access: access);
///
public FileSystemStream New(SafeFileHandle handle, FileAccess access, int bufferSize)
=> new MockFileStream(mockFileSystem, handle.ToString(), FileMode.Open, access: access);
///
public FileSystemStream New(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync)
=> new MockFileStream(mockFileSystem, handle.ToString(), FileMode.Open, access: access);
///
public FileSystemStream New(string path, FileMode mode)
=> new MockFileStream(mockFileSystem, path, mode);
///
public FileSystemStream New(string path, FileMode mode, FileAccess access)
=> new MockFileStream(mockFileSystem, path, mode, access);
///
public FileSystemStream New(string path, FileMode mode, FileAccess access, FileShare share)
=> new MockFileStream(mockFileSystem, path, mode, access, share);
///
public FileSystemStream New(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize)
=> new MockFileStream(mockFileSystem, path, mode, access, share);
///
public FileSystemStream New(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool useAsync)
=> new MockFileStream(mockFileSystem, path, mode, access, share);
///
public FileSystemStream New(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize,
FileOptions options)
=> new MockFileStream(mockFileSystem, path, mode, access, share, options);
#if FEATURE_FILESTREAM_OPTIONS
///
public FileSystemStream New(string path, FileStreamOptions options)
=> new MockFileStream(mockFileSystem, path, options.Mode, options.Access, options.Share, options.Options);
#endif
///
public FileSystemStream Wrap(FileStream fileStream)
=> throw new NotSupportedException("You cannot wrap an existing FileStream in the MockFileSystem instance!");
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFileSystem.cs
================================================
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
namespace System.IO.Abstractions.TestingHelpers;
using XFS = MockUnixSupport;
///
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public class MockFileSystem : FileSystemBase, IMockFileDataAccessor
{
private const string DEFAULT_CURRENT_DIRECTORY = @"C:\";
private const string TEMP_DIRECTORY = @"C:\temp\";
private readonly IDictionary files;
private readonly IDictionary drives;
private readonly PathVerifier pathVerifier;
#if FEATURE_SERIALIZABLE
[NonSerialized]
#endif
private readonly FileHandles fileHandles = new();
#if FEATURE_SERIALIZABLE
[NonSerialized]
#endif
private Func dateTimeProvider = defaultDateTimeProvider;
private static Func defaultDateTimeProvider = () => DateTime.UtcNow;
///
public MockFileSystem() : this(null) { }
///
public MockFileSystem(IDictionary files, string currentDirectory = "")
: this(files, new MockFileSystemOptions
{
CurrentDirectory = currentDirectory,
CreateDefaultTempDir = true
}) { }
///
public MockFileSystem(MockFileSystemOptions options)
: this(null, options) { }
///
public MockFileSystem(IDictionary files, MockFileSystemOptions options)
{
options ??= new MockFileSystemOptions();
var currentDirectory = options.CurrentDirectory;
if (string.IsNullOrEmpty(currentDirectory))
{
currentDirectory = XFS.Path(DEFAULT_CURRENT_DIRECTORY);
}
else if (!System.IO.Path.IsPathRooted(currentDirectory))
{
throw new ArgumentException("Current directory needs to be rooted.", nameof(currentDirectory));
}
var defaultTempDirectory = XFS.Path(TEMP_DIRECTORY);
StringOperations = new StringOperations(XFS.IsUnixPlatform());
pathVerifier = new PathVerifier(this);
this.files = new Dictionary(StringOperations.Comparer);
drives = new Dictionary(StringOperations.Comparer);
Path = new MockPath(this, defaultTempDirectory);
File = new MockFile(this);
Directory = new MockDirectory(this, currentDirectory);
FileInfo = new MockFileInfoFactory(this);
FileVersionInfo = new MockFileVersionInfoFactory(this);
FileStream = new MockFileStreamFactory(this);
DirectoryInfo = new MockDirectoryInfoFactory(this);
DriveInfo = new MockDriveInfoFactory(this);
FileSystemWatcher = new MockFileSystemWatcherFactory(this);
if (files != null)
{
foreach (var entry in files)
{
AddFile(entry.Key, entry.Value);
}
}
if (!FileExists(currentDirectory))
{
AddDirectory(currentDirectory);
}
if (options.CreateDefaultTempDir && !FileExists(defaultTempDirectory))
{
AddDirectory(defaultTempDirectory);
}
}
///
public StringOperations StringOperations { get; }
///
public override IFile File { get; }
///
public override IDirectory Directory { get; }
///
public override IFileInfoFactory FileInfo { get; }
///
public override IFileVersionInfoFactory FileVersionInfo { get; }
///
public override IFileStreamFactory FileStream { get; }
///
public override IPath Path { get; }
///
public override IDirectoryInfoFactory DirectoryInfo { get; }
///
public override IDriveInfoFactory DriveInfo { get; }
///
public override IFileSystemWatcherFactory FileSystemWatcher { get; }
///
public IFileSystem FileSystem => this;
///
public PathVerifier PathVerifier => pathVerifier;
///
public FileHandles FileHandles => fileHandles;
///
/// Replaces the time provider with a mocked instance. This allows to influence the used time in tests.
///
/// If not set, the default implementation returns .
///
/// The function that returns the current .
///
public MockFileSystem MockTime(Func dateTimeProvider)
{
this.dateTimeProvider = dateTimeProvider ?? defaultDateTimeProvider;
return this;
}
//If C:\foo exists, ensures that trying to save a file to "C:\FOO\file.txt" instead saves it to "C:\foo\file.txt".
private string GetPathWithCorrectDirectoryCapitalization(string fullPath)
{
string[] splitPath = fullPath.Split(Path.DirectorySeparatorChar);
string leftHalf = fullPath;
string rightHalf = "";
for (int i = splitPath.Length - 1; i > 1; i--)
{
rightHalf = i == splitPath.Length - 1 ? splitPath[i] : splitPath[i] + Path.DirectorySeparatorChar + rightHalf;
int lastSeparator = leftHalf.LastIndexOf(Path.DirectorySeparatorChar);
leftHalf = lastSeparator > 0 ? leftHalf.Substring(0, lastSeparator) : leftHalf;
if (DirectoryExistsWithoutFixingPath(leftHalf))
{
string baseDirectory;
lock (files)
{
baseDirectory = files[leftHalf].Path;
}
return baseDirectory + Path.DirectorySeparatorChar + rightHalf;
}
}
return fullPath.TrimSlashes();
}
///
public MockFileData AdjustTimes(MockFileData fileData, TimeAdjustments timeAdjustments)
{
var now = dateTimeProvider();
if (timeAdjustments.HasFlag(TimeAdjustments.CreationTime))
{
fileData.CreationTime = now;
}
if (timeAdjustments.HasFlag(TimeAdjustments.LastAccessTime))
{
fileData.LastAccessTime = now;
}
if (timeAdjustments.HasFlag(TimeAdjustments.LastWriteTime))
{
fileData.LastWriteTime = now;
}
return fileData;
}
///
public MockFileData GetFile(string path)
{
path = pathVerifier.FixPath(path).TrimSlashes();
return GetFileWithoutFixingPath(path);
}
///
public MockDriveData GetDrive(string name)
{
name = PathVerifier.NormalizeDriveName(name);
lock (drives)
{
return drives.TryGetValue(name, out var result) ? result : null;
}
}
private void SetEntry(string path, MockFileData mockFile)
{
path = GetPathWithCorrectDirectoryCapitalization(
pathVerifier.FixPath(path)
).TrimSlashes();
lock (files)
{
files[path] = new FileSystemEntry { Path = path, Data = mockFile };
}
lock (drives)
{
if (PathVerifier.TryNormalizeDriveName(path, out string driveLetter))
{
if (!drives.ContainsKey(driveLetter))
{
drives[driveLetter] = new MockDriveData();
}
}
}
}
///
public void AddFile(string path, MockFileData mockFile, bool verifyAccess = true)
{
var fixedPath = GetPathWithCorrectDirectoryCapitalization(
pathVerifier.FixPath(path)
);
mockFile ??= new MockFileData(string.Empty);
var file = GetFile(fixedPath);
if (file != null)
{
var isReadOnly = (file.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly;
var isHidden = (file.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden;
if (verifyAccess && (isReadOnly || isHidden))
{
throw CommonExceptions.AccessDenied(path);
}
file.CheckFileAccess(fixedPath, FileAccess.Write);
mockFile.CreationTime = file.CreationTime;
}
var directoryPath = Path.GetDirectoryName(fixedPath);
if (directoryPath == null)
{
AddDrive(fixedPath, new MockDriveData());
}
else if (!DirectoryExistsWithoutFixingPath(directoryPath))
{
AddDirectory(directoryPath);
}
mockFile.FileVersionInfo ??= new MockFileVersionInfo(fixedPath);
SetEntry(fixedPath, mockFile);
}
///
/// Add a new file that is empty.
///
/// A string representing the path of the new file to add.
public void AddEmptyFile(string path)
{
AddFile(path, new MockFileData(""));
}
///
/// Add a new file that is empty.
///
/// An representing the path of the new file to add.
public void AddEmptyFile(IFileInfo path)
{
AddEmptyFile(path.FullName);
path.Refresh();
}
///
/// Add a new, empty directory.
///
/// An representing the path of the new directory to add.
public void AddDirectory(IDirectoryInfo path)
{
AddDirectory(path.FullName);
path.Refresh();
}
///
/// Add a new file with its contents set to a specified .
///
/// An representing the path of the new file to add.
/// The data to use for the contents of the new file.
/// Flag indicating if the access conditions should be verified.
public void AddFile(IFileInfo path, MockFileData data, bool verifyAccess = true)
{
AddFile(path.FullName, data, verifyAccess);
path.Refresh();
}
///
/// Gets a file.
///
/// The path of the file to get.
/// The file. if the file does not exist.
public MockFileData GetFile(IFileInfo path)
{
return GetFile(path.FullName);
}
///
public void AddDirectory(string path)
{
var fixedPath = GetPathWithCorrectDirectoryCapitalization(
pathVerifier.FixPath(path)
);
var separator = Path.DirectorySeparatorChar.ToString();
if (FileExists(fixedPath) && FileIsReadOnly(fixedPath))
{
throw CommonExceptions.AccessDenied(fixedPath);
}
var lastIndex = 0;
var isUnc =
StringOperations.StartsWith(fixedPath, @"\\") ||
StringOperations.StartsWith(fixedPath, @"//");
if (isUnc)
{
//First, confirm they aren't trying to create '\\server\'
lastIndex = StringOperations.IndexOf(fixedPath, separator, 2);
if (lastIndex < 0)
{
throw CommonExceptions.InvalidUncPath(nameof(path));
}
/*
* Although CreateDirectory(@"\\server\share\") is not going to work in real code, we allow it here for the purposes of setting up test doubles.
* See PR https://github.com/TestableIO/System.IO.Abstractions/pull/90 for conversation
*/
}
while ((lastIndex = StringOperations.IndexOf(fixedPath, separator, lastIndex + 1)) > -1)
{
var segment = fixedPath.Substring(0, lastIndex + 1);
if (!DirectoryExistsWithoutFixingPath(segment))
{
SetEntry(segment, new MockDirectoryData());
}
}
var s = StringOperations.EndsWith(fixedPath, separator) ? fixedPath : fixedPath + separator;
SetEntry(s, new MockDirectoryData());
}
///
public void AddFileFromEmbeddedResource(string path, Assembly resourceAssembly, string embeddedResourcePath)
{
using (var embeddedResourceStream = resourceAssembly.GetManifestResourceStream(embeddedResourcePath))
{
if (embeddedResourceStream == null)
{
throw new ArgumentException("Resource not found in assembly", nameof(embeddedResourcePath));
}
using (var streamReader = new BinaryReader(embeddedResourceStream))
{
var fileData = streamReader.ReadBytes((int)embeddedResourceStream.Length);
AddFile(path, new MockFileData(fileData));
}
}
}
///
public void AddFilesFromEmbeddedNamespace(string path, Assembly resourceAssembly, string embeddedResourcePath)
{
var matchingResources = resourceAssembly.GetManifestResourceNames().Where(f => f.StartsWith(embeddedResourcePath));
foreach (var resource in matchingResources)
{
using (var embeddedResourceStream = resourceAssembly.GetManifestResourceStream(resource))
using (var streamReader = new BinaryReader(embeddedResourceStream))
{
var fileName = resource.Substring(embeddedResourcePath.Length + 1);
var fileData = streamReader.ReadBytes((int)embeddedResourceStream.Length);
var filePath = Path.Combine(path, fileName);
AddFile(filePath, new MockFileData(fileData));
}
}
}
///
public void AddDrive(string name, MockDriveData mockDrive)
{
name = PathVerifier.NormalizeDriveName(name);
lock (drives)
{
drives[name] = mockDrive;
}
}
///
public void MoveDirectory(string sourcePath, string destPath)
{
sourcePath = pathVerifier.FixPath(sourcePath);
destPath = pathVerifier.FixPath(destPath);
var sourcePathSequence = sourcePath.Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries);
lock (files)
{
var affectedPaths = files.Keys
.Where(p => PathStartsWith(p, sourcePathSequence))
.ToList();
foreach (var path in affectedPaths)
{
var newPath = Path.Combine(destPath, path.Substring(sourcePath.Length).TrimStart(Path.DirectorySeparatorChar));
var entry = files[path];
entry.Path = newPath;
files[newPath] = entry;
files.Remove(path);
}
}
bool PathStartsWith(string path, string[] minMatch)
{
var pathSequence = path.Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries);
if (pathSequence.Length < minMatch.Length)
{
return false;
}
for (var i = 0; i < minMatch.Length; i++)
{
if (!StringOperations.Equals(minMatch[i], pathSequence[i]))
{
return false;
}
}
return true;
}
}
///
public void RemoveFile(string path, bool verifyAccess = true)
{
path = pathVerifier.FixPath(path);
lock (files)
{
if (FileExists(path) && verifyAccess && (FileIsReadOnly(path) || Directory.Exists(path) && AnyFileIsReadOnly(path)))
{
throw CommonExceptions.AccessDenied(path);
}
files.Remove(path);
}
}
///
public bool FileExists(string path)
{
if (string.IsNullOrEmpty(path))
{
return false;
}
path = pathVerifier.FixPath(path).TrimSlashes();
lock (files)
{
return files.ContainsKey(path);
}
}
///
public IEnumerable AllPaths
{
get
{
lock (files)
{
return files.Keys.ToArray();
}
}
}
///
public IEnumerable AllNodes
{
get
{
lock (files)
{
return AllPaths.Where(path => !IsStartOfAnotherPath(path)).ToArray();
}
}
}
///
public IEnumerable AllFiles
{
get
{
lock (files)
{
return files.Where(f => !f.Value.Data.IsDirectory).Select(f => f.Key).ToArray();
}
}
}
///
public IEnumerable AllDirectories
{
get
{
lock (files)
{
return files.Where(f => f.Value.Data.IsDirectory).Select(f => f.Key).ToArray();
}
}
}
///
public IEnumerable AllDrives
{
get
{
lock (drives)
{
return drives.Keys.ToArray();
}
}
}
[OnDeserializing]
private void OnDeserializing(StreamingContext c)
{
dateTimeProvider = defaultDateTimeProvider;
}
private bool AnyFileIsReadOnly(string path)
{
return Directory.GetFiles(path).Any(file => FileIsReadOnly(file));
}
private bool IsStartOfAnotherPath(string path)
{
return AllPaths.Any(otherPath => otherPath.StartsWith(path) && otherPath != path);
}
private MockFileData GetFileWithoutFixingPath(string path)
{
lock (files)
{
return files.TryGetValue(path, out var result) ? result.Data : null;
}
}
private bool DirectoryExistsWithoutFixingPath(string path)
{
lock (files)
{
return files.TryGetValue(path, out var result) && result.Data.IsDirectory;
}
}
private bool FileIsReadOnly(string path)
{
return (GetFile(path).Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly;
}
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
private class FileSystemEntry
{
public string Path { get; set; }
public MockFileData Data { get; set; }
}
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFileSystemOptions.cs
================================================
namespace System.IO.Abstractions.TestingHelpers;
///
/// Constructor options for
///
public class MockFileSystemOptions
{
///
/// The with which the is initialized.
///
public string CurrentDirectory { get; init; } = "";
///
/// Flag indicating, if a temporary directory should be created.
///
public bool CreateDefaultTempDir { get; init; } = true;
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFileSystemWatcherFactory.cs
================================================
namespace System.IO.Abstractions.TestingHelpers;
///
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public class MockFileSystemWatcherFactory : IFileSystemWatcherFactory
{
///
public MockFileSystemWatcherFactory(MockFileSystem mockFileSystem)
{
FileSystem = mockFileSystem;
}
///
public IFileSystem FileSystem { get; }
///
public IFileSystemWatcher New()
=> throw new NotImplementedException(StringResources.Manager.GetString("FILE_SYSTEM_WATCHER_NOT_IMPLEMENTED_EXCEPTION"));
///
public IFileSystemWatcher New(string path)
=> throw new NotImplementedException(StringResources.Manager.GetString("FILE_SYSTEM_WATCHER_NOT_IMPLEMENTED_EXCEPTION"));
///
public IFileSystemWatcher New(string path, string filter)
=> throw new NotImplementedException(StringResources.Manager.GetString("FILE_SYSTEM_WATCHER_NOT_IMPLEMENTED_EXCEPTION"));
///
public IFileSystemWatcher Wrap(FileSystemWatcher fileSystemWatcher)
{
if (fileSystemWatcher == null)
{
return null;
}
throw new NotImplementedException(StringResources.Manager.GetString("FILE_SYSTEM_WATCHER_NOT_IMPLEMENTED_EXCEPTION"));
}
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFileVersionInfo.cs
================================================
using System.Diagnostics;
using System.Text;
namespace System.IO.Abstractions.TestingHelpers;
///
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public class MockFileVersionInfo : FileVersionInfoBase
{
///
public MockFileVersionInfo(
string fileName,
string fileVersion = null,
string productVersion = null,
string fileDescription = null,
string productName = null,
string companyName = null,
string comments = null,
string internalName = null,
bool isDebug = false,
bool isPatched = false,
bool isPrivateBuild = false,
bool isPreRelease = false,
bool isSpecialBuild = false,
string language = null,
string legalCopyright = null,
string legalTrademarks = null,
string originalFilename = null,
string privateBuild = null,
string specialBuild = null)
{
FileName = fileName;
FileVersion = fileVersion;
ProductVersion = productVersion;
FileDescription = fileDescription;
ProductName = productName;
CompanyName = companyName;
Comments = comments;
InternalName = internalName;
IsDebug = isDebug;
IsPatched = isPatched;
IsPrivateBuild = isPrivateBuild;
IsPreRelease = isPreRelease;
IsSpecialBuild = isSpecialBuild;
Language = language;
LegalCopyright = legalCopyright;
LegalTrademarks = legalTrademarks;
OriginalFilename = originalFilename;
PrivateBuild = privateBuild;
SpecialBuild = specialBuild;
if (Version.TryParse(fileVersion, out Version version))
{
FileMajorPart = version.Major;
FileMinorPart = version.Minor;
FileBuildPart = version.Build;
FilePrivatePart = version.Revision;
}
var parsedProductVersion = ProductVersionParser.Parse(productVersion);
ProductMajorPart = parsedProductVersion.Major;
ProductMinorPart = parsedProductVersion.Minor;
ProductBuildPart = parsedProductVersion.Build;
ProductPrivatePart = parsedProductVersion.PrivatePart;
}
///
public override string FileName { get; }
///
public override string FileVersion { get; }
///
public override string ProductVersion { get; }
///
public override string FileDescription { get; }
///
public override string ProductName { get; }
///
public override string CompanyName { get; }
///
public override string Comments { get; }
///
public override string InternalName { get; }
///
public override bool IsDebug { get; }
///
public override bool IsPatched { get; }
///
public override bool IsPrivateBuild { get; }
///
public override bool IsPreRelease { get; }
///
public override bool IsSpecialBuild { get; }
///
public override string Language { get; }
///
public override string LegalCopyright { get; }
///
public override string LegalTrademarks { get; }
///
public override string OriginalFilename { get; }
///
public override string PrivateBuild { get; }
///
public override string SpecialBuild { get; }
///
public override int FileMajorPart { get; }
///
public override int FileMinorPart { get; }
///
public override int FileBuildPart { get; }
///
public override int FilePrivatePart { get; }
///
public override int ProductMajorPart { get; }
///
public override int ProductMinorPart { get; }
///
public override int ProductBuildPart { get; }
///
public override int ProductPrivatePart { get; }
///
public override string ToString()
{
// An initial capacity of 512 was chosen because it is large enough to cover
// the size of the static strings with enough capacity left over to cover
// average length property values.
var sb = new StringBuilder(512);
sb.Append("File: ").AppendLine(FileName);
sb.Append("InternalName: ").AppendLine(InternalName);
sb.Append("OriginalFilename: ").AppendLine(OriginalFilename);
sb.Append("FileVersion: ").AppendLine(FileVersion);
sb.Append("FileDescription: ").AppendLine(FileDescription);
sb.Append("Product: ").AppendLine(ProductName);
sb.Append("ProductVersion: ").AppendLine(ProductVersion);
sb.Append("Debug: ").AppendLine(IsDebug.ToString());
sb.Append("Patched: ").AppendLine(IsPatched.ToString());
sb.Append("PreRelease: ").AppendLine(IsPreRelease.ToString());
sb.Append("PrivateBuild: ").AppendLine(IsPrivateBuild.ToString());
sb.Append("SpecialBuild: ").AppendLine(IsSpecialBuild.ToString());
sb.Append("Language: ").AppendLine(Language);
return sb.ToString();
}
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.TestingHelpers/MockFileVersionInfoFactory.cs
================================================
namespace System.IO.Abstractions.TestingHelpers;
///
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public class MockFileVersionInfoFactory : IFileVersionInfoFactory
{
private readonly IMockFileDataAccessor mockFileSystem;
///
public MockFileVersionInfoFactory(IMockFileDataAccessor mockFileSystem)
{
this.mockFileSystem = mockFileSystem ?? throw new ArgumentNullException(nameof(mockFileSystem));
}
///
public IFileSystem FileSystem => mockFileSystem;
///
public IFileVersionInfo GetVersionInfo(string fileName)
{
MockFileData mockFileData = mockFileSystem.GetFile(fileName);
if (mockFileData != null)
{
return mockFileData.FileVersionInfo;
}
throw CommonExceptions.FileNotFound(fileName);
}
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.TestingHelpers/MockPath.cs
================================================
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
namespace System.IO.Abstractions.TestingHelpers;
///
/// PathWrapper calls direct to Path but all this does is string manipulation so we can inherit directly from PathWrapper as no IO is done
///
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public class MockPath : PathWrapper
{
private readonly IMockFileDataAccessor mockFileDataAccessor;
private readonly string defaultTempDirectory;
///
public MockPath(IMockFileDataAccessor mockFileDataAccessor) : this(mockFileDataAccessor, string.Empty) { }
///
public MockPath(IMockFileDataAccessor mockFileDataAccessor, string defaultTempDirectory) : base(mockFileDataAccessor?.FileSystem)
{
this.mockFileDataAccessor = mockFileDataAccessor ?? throw new ArgumentNullException(nameof(mockFileDataAccessor));
this.defaultTempDirectory = !string.IsNullOrEmpty(defaultTempDirectory) ? defaultTempDirectory : base.GetTempPath();
}
#if FEATURE_PATH_EXISTS
///
public override bool Exists(string path)
{
return mockFileDataAccessor.FileExists(path);
}
#endif
///
public override string GetFullPath(string path)
{
if (path == null)
{
throw new ArgumentNullException(nameof(path), StringResources.Manager.GetString("VALUE_CANNOT_BE_NULL"));
}
if (string.IsNullOrWhiteSpace(path))
{
throw CommonExceptions.PathIsNotOfALegalForm(nameof(path));
}
path = path.Replace(AltDirectorySeparatorChar, DirectorySeparatorChar);
bool isUnc =
mockFileDataAccessor.StringOperations.StartsWith(path, @"\\") ||
mockFileDataAccessor.StringOperations.StartsWith(path, @"//");
string root = GetPathRoot(path);
bool hasTrailingSlash = path.Length > 1 && path[path.Length - 1] == DirectorySeparatorChar;
string[] pathSegments;
if (root.Length == 0)
{
// relative path on the current drive or volume
path = mockFileDataAccessor.Directory.GetCurrentDirectory() + DirectorySeparatorChar + path;
pathSegments = GetSegments(path);
}
else if (isUnc)
{
// unc path
pathSegments = GetSegments(path);
if (pathSegments.Length < 2)
{
throw CommonExceptions.InvalidUncPath(nameof(path));
}
}
else if (mockFileDataAccessor.StringOperations.Equals(@"\", root) ||
mockFileDataAccessor.StringOperations.Equals(@"/", root))
{
// absolute path on the current drive or volume
pathSegments = GetSegments(GetPathRoot(mockFileDataAccessor.Directory.GetCurrentDirectory()), path);
}
else
{
pathSegments = GetSegments(path);
}
// unc paths need at least two segments, the others need one segment
var isUnixRooted = mockFileDataAccessor.StringOperations.StartsWith(
mockFileDataAccessor.Directory.GetCurrentDirectory(),
"/");
var minPathSegments = isUnc
? 2
: isUnixRooted ? 0 : 1;
var stack = new Stack();
foreach (var segment in pathSegments)
{
if (mockFileDataAccessor.StringOperations.Equals("..", segment))
{
// only pop, if afterwards are at least the minimal amount of path segments
if (stack.Count > minPathSegments)
{
stack.Pop();
}
}
else if (mockFileDataAccessor.StringOperations.Equals(".", segment))
{
// ignore .
}
else
{
stack.Push(segment);
}
}
var fullPath = string.Join(string.Format(CultureInfo.InvariantCulture, "{0}", DirectorySeparatorChar), stack.Reverse().ToArray());
if (hasTrailingSlash)
{
fullPath += DirectorySeparatorChar;
}
if (isUnixRooted && !isUnc)
{
fullPath = "/" + fullPath;
}
else if (isUnixRooted)
{
fullPath = @"//" + fullPath;
}
else if (isUnc)
{
fullPath = @"\\" + fullPath;
}
return fullPath;
}
private string[] GetSegments(params string[] paths)
{
return paths.SelectMany(path => path.Split(new[] { DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries)).ToArray();
}
///
public override string GetTempFileName()
{
string fileName = mockFileDataAccessor.Path.GetRandomFileName();
string tempDir = this.GetTempPath();
string fullPath = mockFileDataAccessor.Path.Combine(tempDir, fileName);
mockFileDataAccessor.AddFile(fullPath, new MockFileData(string.Empty));
return fullPath;
}
///
public override string GetTempPath() => defaultTempDirectory;
#if FEATURE_ADVANCED_PATH_OPERATIONS
///
public override string GetRelativePath(string relativeTo, string path)
{
if (relativeTo == null)
{
throw new ArgumentNullException(nameof(relativeTo), StringResources.Manager.GetString("VALUE_CANNOT_BE_NULL"));
}
if (string.IsNullOrWhiteSpace(relativeTo))
{
throw CommonExceptions.PathIsNotOfALegalForm(nameof(relativeTo));
}
if (path == null)
{
throw new ArgumentNullException(nameof(path), StringResources.Manager.GetString("VALUE_CANNOT_BE_NULL"));
}
if (path.Length == 0)
{
throw CommonExceptions.PathIsNotOfALegalForm(nameof(path));
}
relativeTo = GetFullPath(relativeTo);
path = GetFullPath(path);
return Path.GetRelativePath(relativeTo, path);
}
#endif
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.TestingHelpers/MockUnixSupport.cs
================================================
using System.Text.RegularExpressions;
namespace System.IO.Abstractions.TestingHelpers;
///
/// Provides helper methods for handling paths in a portable way.
///
public static class MockUnixSupport
{
private static readonly Regex pathTransform = new Regex(@"^[a-zA-Z]:(?.*)$");
///
/// Normalizes the given path so that it works on all platfoms.
///
public static string Path(string path) => path != null && IsUnixPlatform()
? pathTransform.Replace(path, "${path}").Replace(@"\", "/")
: path;
///
/// Determines whether the current runtime platform is Unix.
///
public static bool IsUnixPlatform() => IO.Path.DirectorySeparatorChar == '/';
///
/// Determines whether the current runtime platform is Windows.
///
public static bool IsWindowsPlatform() => IO.Path.DirectorySeparatorChar == '\\';
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.TestingHelpers/PathVerifier.cs
================================================
using System;
using System.Linq;
namespace System.IO.Abstractions.TestingHelpers;
using XFS = MockUnixSupport;
///
/// Provides helper methods for verifying paths.
///
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public class PathVerifier
{
private static readonly char[] AdditionalInvalidPathChars = { '*', '?' };
private readonly IMockFileDataAccessor _mockFileDataAccessor;
// Windows supports extended-length paths with a `\\?\` prefix, to work around low path length limits.
// Ref: https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=registry
private const string WINDOWS_EXTENDED_LENGTH_PATH_PREFIX = @"\\?\";
///
/// Creates a new verifier instance.
///
public PathVerifier(IMockFileDataAccessor mockFileDataAccessor)
{
_mockFileDataAccessor = mockFileDataAccessor ?? throw new ArgumentNullException(nameof(mockFileDataAccessor));
}
///
/// Determines whether the given path is legal.
///
public void IsLegalAbsoluteOrRelative(string path, string paramName)
{
if (path == null)
{
throw new ArgumentNullException(paramName, StringResources.Manager.GetString("VALUE_CANNOT_BE_NULL"));
}
if (path == string.Empty)
{
throw new ArgumentException("Empty file name is not legal.", paramName);
}
if (path.Trim() == string.Empty)
{
throw CommonExceptions.PathIsNotOfALegalForm(paramName);
}
if (XFS.IsWindowsPlatform() && !IsValidUseOfVolumeSeparatorChar(path))
{
throw CommonExceptions.InvalidUseOfVolumeSeparator();
}
if (ExtractFileName(path).IndexOfAny(_mockFileDataAccessor.Path.GetInvalidFileNameChars()) > -1)
{
throw CommonExceptions.IllegalCharactersInPath();
}
var filePath = ExtractFilePath(path);
if (HasIllegalCharacters(filePath, checkAdditional: false))
{
throw CommonExceptions.IllegalCharactersInPath();
}
}
private static bool IsValidUseOfVolumeSeparatorChar(string path)
{
if (XFS.IsWindowsPlatform() && path.StartsWith(WINDOWS_EXTENDED_LENGTH_PATH_PREFIX))
{
// Skip over the `\\?\` prefix if there is one.
path = path.Substring(WINDOWS_EXTENDED_LENGTH_PATH_PREFIX.Length);
}
var lastVolSepIndex = path.LastIndexOf(Path.VolumeSeparatorChar);
return lastVolSepIndex == -1 || lastVolSepIndex == 1 && char.IsLetter(path[0]);
}
private string ExtractFileName(string fullFileName)
{
return fullFileName.Split(
_mockFileDataAccessor.Path.DirectorySeparatorChar,
_mockFileDataAccessor.Path.AltDirectorySeparatorChar).Last();
}
private string ExtractFilePath(string fullFileName)
{
var extractFilePath = fullFileName.Split(
_mockFileDataAccessor.Path.DirectorySeparatorChar,
_mockFileDataAccessor.Path.AltDirectorySeparatorChar);
return string.Join(_mockFileDataAccessor.Path.DirectorySeparatorChar.ToString(), extractFilePath.Take(extractFilePath.Length - 1));
}
///
/// Determines whether the given path contains illegal characters.
///
public bool HasIllegalCharacters(string path, bool checkAdditional)
{
if (path == null)
{
throw new ArgumentNullException(nameof(path));
}
var invalidPathChars = _mockFileDataAccessor.Path.GetInvalidPathChars();
if (checkAdditional)
{
// AdditionalInvalidPathChars includes '?', but this character is allowed in extended-length
// windows path prefixes (`\\?\`). If we're dealing with such a path, check for invalid
// characters after the prefix.
if (XFS.IsWindowsPlatform() && path.StartsWith(WINDOWS_EXTENDED_LENGTH_PATH_PREFIX))
{
path = path.Substring(WINDOWS_EXTENDED_LENGTH_PATH_PREFIX.Length);
}
return path.IndexOfAny(invalidPathChars.Concat(AdditionalInvalidPathChars).ToArray()) >= 0;
}
return path.IndexOfAny(invalidPathChars) >= 0;
}
///
/// Throws an excpetion if the given path contains invalid characters.
///
public void CheckInvalidPathChars(string path, bool checkAdditional = false)
{
if (path == null)
{
throw new ArgumentNullException(nameof(path));
}
if (HasIllegalCharacters(path, checkAdditional))
{
throw CommonExceptions.IllegalCharactersInPath();
}
}
///
/// Determines the normalized drive name used for drive identification.
///
/// Thrown if the is not a valid drive name.
public string NormalizeDriveName(string name)
{
return TryNormalizeDriveName(name, out var result)
? result
: throw new ArgumentException(
@"Object must be a root directory (""C:\"") or a drive letter (""C"").");
}
///
/// Tries to determine the normalized drive name used for drive identification.
///
public bool TryNormalizeDriveName(string name, out string result)
{
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
const string DRIVE_SEPARATOR = @":\";
if (name.Length == 1
|| (name.Length == 2 && name[1] == ':')
|| (name.Length == 3 && _mockFileDataAccessor.StringOperations.EndsWith(name, DRIVE_SEPARATOR)))
{
name = name[0] + DRIVE_SEPARATOR;
}
else
{
CheckInvalidPathChars(name);
name = _mockFileDataAccessor.Path.GetPathRoot(name);
if (string.IsNullOrEmpty(name) || _mockFileDataAccessor.StringOperations.StartsWith(name, @"\\"))
{
result = null;
return false;
}
}
result = name;
return true;
}
///
/// Resolves and normalizes a path.
///
internal string FixPath(string path)
{
if (path == null)
{
throw new ArgumentNullException(nameof(path), StringResources.Manager.GetString("VALUE_CANNOT_BE_NULL"));
}
var pathSeparatorFixed = path.Replace(
_mockFileDataAccessor.Path.AltDirectorySeparatorChar,
_mockFileDataAccessor.Path.DirectorySeparatorChar
);
var fullPath = _mockFileDataAccessor.Path.GetFullPath(pathSeparatorFixed);
return fullPath;
}
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.TestingHelpers/Polyfills/SupportedOSPlatformAttribute.cs
================================================
#if !FEATURE_SUPPORTED_OS_ATTRIBUTE
namespace System.Runtime.Versioning
{
[AttributeUsage(AttributeTargets.All)]
internal class SupportedOSPlatformAttribute : Attribute
{
public SupportedOSPlatformAttribute(string _)
{
}
}
}
#endif
================================================
FILE: src/TestableIO.System.IO.Abstractions.TestingHelpers/ProductVersionParser.cs
================================================
using System.Reflection;
using System.Text.RegularExpressions;
namespace System.IO.Abstractions.TestingHelpers;
///
/// Provides functionality to parse a product version string into its major, minor, build, and private parts.
///
internal static class ProductVersionParser
{
///
/// Parses a product version string and extracts the numeric values for the major, minor, build, and private parts,
/// mimicking the behavior of the attribute.
///
/// The product version string to parse.
///
/// A object containing the parsed major, minor, build, and private parts.
/// If the input is invalid, returns a with all parts set to 0.
///
///
/// The method splits the input string into segments separated by dots ('.') and attempts to extract
/// the leading numeric value from each segment. A maximum of 4 segments are processed; if more than
/// 4 segments are present, all segments are ignored. Additionally, if a segment does not contain
/// a valid numeric part at its start or it contains more than just a number, the rest of the segments are ignored.
///
public static ProductVersion Parse(string productVersion)
{
if (string.IsNullOrWhiteSpace(productVersion))
{
return new();
}
var segments = productVersion.Split('.');
if (segments.Length > 4)
{
// if more than 4 segments are present, all segments are ignored
return new();
}
var regex = new Regex(@"^\d+");
int[] parts = new int[4];
for (int i = 0; i < segments.Length; i++)
{
var match = regex.Match(segments[i]);
if (match.Success && int.TryParse(match.Value, out int number))
{
parts[i] = number;
if (match.Value != segments[i])
{
// when a segment contains more than a number, the rest of the segments are ignored
break;
}
}
else
{
// when a segment is not valid, the rest of the segments are ignored
break;
}
}
return new()
{
Major = parts[0],
Minor = parts[1],
Build = parts[2],
PrivatePart = parts[3]
};
}
///
/// Represents a product version with numeric parts for major, minor, build, and private versions.
///
public class ProductVersion
{
///
/// Gets the major part of the version number
///
public int Major { get; init; }
///
/// Gets the minor part of the version number
///
public int Minor { get; init; }
///
/// Gets the build part of the version number
///
public int Build { get; init; }
///
/// Gets the private part of the version number
///
public int PrivatePart { get; init; }
}
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.TestingHelpers/Properties/Resources.resx
================================================
text/microsoft-resx
2.0
System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
Access to the path '{0}' is denied.
Could not find a part of the path '{0}'.
Illegal characters in path.
This test helper hasn't been implemented yet. They are implemented on an as-needed basis. As it seems like you need it, now would be a great time to send us a pull request over at https://github.com/TestableIO/System.IO.Abstractions. You know, because it's open source and all.
The path is not of a legal form.
Value cannot be null.
Path cannot be the empty string or all whitespace.
File name cannot be null.
Could not find file '{0}'.
MockFileSystem does not have a built-in FileSystemWatcher implementation. You must provide your own mock or implementation of IFileSystemWatcherFactory and assign it to MockFileSystem.FileSystemWatcher.
The process cannot access the file because it is being used by another process.
The process cannot access the file '{0}' because it is being used by another process.
The file '{0}' already exists.
Append access can be requested only in write-only mode.
Combining FileMode: {0} with FileAccess: {1} is invalid.
Cannot create '{0}' because a file or directory with the same name already exists.
The name of the file cannot be resolved by the system. : '{0}'
'{0}' does not exist or could not be found.
================================================
FILE: src/TestableIO.System.IO.Abstractions.TestingHelpers/StringExtensions.cs
================================================
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
namespace System.IO.Abstractions.TestingHelpers;
using XFS = MockUnixSupport;
internal static class StringExtensions
{
[Pure]
public static string[] SplitLines(this string input)
{
var list = new List();
using (var reader = new StringReader(input))
{
string str;
while ((str = reader.ReadLine()) != null)
{
list.Add(str);
}
}
return list.ToArray();
}
[Pure]
public static string Replace(this string source, string oldValue, string newValue, StringComparison comparisonType)
{
// from http://stackoverflow.com/a/22565605 with some adaptions
if (string.IsNullOrEmpty(oldValue))
{
throw new ArgumentNullException(nameof(oldValue));
}
if (source.Length == 0)
{
return source;
}
if (newValue == null)
{
newValue = string.Empty;
}
var result = new StringBuilder();
int startingPos = 0;
int nextMatch;
while ((nextMatch = source.IndexOf(oldValue, startingPos, comparisonType)) > -1)
{
result.Append(source, startingPos, nextMatch - startingPos);
result.Append(newValue);
startingPos = nextMatch + oldValue.Length;
}
result.Append(source, startingPos, source.Length - startingPos);
return result.ToString();
}
[Pure]
public static string TrimSlashes(this string path)
{
if (string.IsNullOrEmpty(path))
{
return path;
}
var trimmed = path.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
if (XFS.IsUnixPlatform()
&& (path[0] == Path.DirectorySeparatorChar || path[0] == Path.AltDirectorySeparatorChar)
&& trimmed == "")
{
return Path.DirectorySeparatorChar.ToString();
}
if (XFS.IsWindowsPlatform()
&& trimmed.Length == 2
&& char.IsLetter(trimmed[0])
&& trimmed[1] == ':')
{
return trimmed + Path.DirectorySeparatorChar;
}
return trimmed;
}
[Pure]
public static string NormalizeSlashes(this string path)
{
path = path.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
var sep = Path.DirectorySeparatorChar.ToString();
var doubleSep = sep + sep;
var prefixSeps = new string(path.TakeWhile(c => c == Path.DirectorySeparatorChar).ToArray());
path = path.Substring(prefixSeps.Length);
// UNC Paths start with double slash but no reason
// to have more than 2 slashes at the start of a path
if (XFS.IsWindowsPlatform() && prefixSeps.Length >= 2)
{
prefixSeps = prefixSeps.Substring(0, 2);
}
else if (prefixSeps.Length > 1)
{
prefixSeps = prefixSeps.Substring(0, 1);
}
while (true)
{
var newPath = path.Replace(doubleSep, sep);
if (path == newPath)
{
return prefixSeps + path;
}
path = newPath;
}
}
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.TestingHelpers/StringOperations.cs
================================================
namespace System.IO.Abstractions.TestingHelpers;
///
/// Provides operations against path strings dependeing on the case-senstivity of the runtime platform.
///
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public class StringOperations
{
private readonly bool caseSensitive;
private readonly StringComparison comparison;
///
/// Creates a new instance.
///
public StringOperations(bool caseSensitive)
{
this.caseSensitive = caseSensitive;
comparison = caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase;
}
///
/// Provides a string comparer.
///
public StringComparer Comparer => caseSensitive ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase;
///
/// Determines whether the given string starts with the given prefix.
///
public bool StartsWith(string s, string prefix) => s.StartsWith(prefix, comparison);
///
/// Determines whether the given string ends with the given suffix.
///
public bool EndsWith(string s, string suffix) => s.EndsWith(suffix, comparison);
///
/// Determines whether the given strings are equal.
///
public bool Equals(string x, string y) => string.Equals(x, y, comparison);
///
/// Determines whether the given characters are equal.
///
public bool Equals(char x, char y) => caseSensitive ? x == y : char.ToUpper(x) == char.ToUpper(y);
///
/// Determines the index of the given substring in the string.
///
public int IndexOf(string s, string substring) => s.IndexOf(substring, comparison);
///
/// Determines the index of the given substring in the string.
///
public int IndexOf(string s, string substring, int startIndex) => s.IndexOf(substring, startIndex, comparison);
///
/// Determines whether the given string contains the given substring.
///
public bool Contains(string s, string substring) => s.IndexOf(substring, comparison) >= 0;
///
/// Replaces a given value by a new value.
///
public string Replace(string s, string oldValue, string newValue) => s.Replace(oldValue, newValue, comparison);
///
/// Provides the lower-case representation of the given character.
///
public char ToLower(char c) => caseSensitive ? c : char.ToLower(c);
///
/// Provides the upper-case representation of the given character.
///
public char ToUpper(char c) => caseSensitive ? c : char.ToUpper(c);
///
/// Provides the lower-case representation of the given string.
///
public string ToLower(string s) => caseSensitive ? s : s.ToLower();
///
/// Provides the upper-case representation of the given string.
///
public string ToUpper(string s) => caseSensitive ? s : s.ToUpper();
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.TestingHelpers/StringResources.cs
================================================
using System.Reflection;
using System.Resources;
namespace System.IO.Abstractions.TestingHelpers;
internal static class StringResources
{
public static ResourceManager Manager { get; } = new ResourceManager(
$"{typeof(StringResources).Namespace}.Properties.Resources",
typeof(StringResources).GetTypeInfo().Assembly);
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.TestingHelpers/TestableIO.System.IO.Abstractions.TestingHelpers.csproj
================================================
TestableIO.System.IO.Abstractions.TestingHelpers
System.IO.Abstractions.TestingHelpers
A set of pre-built mocks to help when testing file system interactions.
================================================
FILE: src/TestableIO.System.IO.Abstractions.TestingHelpers/TimeAdjustments.cs
================================================
namespace System.IO.Abstractions.TestingHelpers;
///
/// Flags indicating which times to adjust for a .
///
[Flags]
public enum TimeAdjustments
{
///
/// Adjusts no times on the
///
None = 0,
///
/// Adjusts the
///
CreationTime = 1 << 0,
///
/// Adjusts the
///
LastAccessTime = 1 << 1,
///
/// Adjusts the
///
LastWriteTime = 1 << 2,
///
/// Adjusts all times on the
///
All = ~0
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.Wrappers/Converters.cs
================================================
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
namespace System.IO.Abstractions;
internal static class Converters
{
internal static IEnumerable WrapFileSystemInfos(this IEnumerable input, IFileSystem fileSystem)
=> input.Select(info => WrapFileSystemInfo(fileSystem, info));
internal static FileSystemInfoBase[] WrapFileSystemInfos(this FileSystemInfo[] input, IFileSystem fileSystem)
=> input.Select(info => WrapFileSystemInfo(fileSystem, info)).ToArray();
internal static FileSystemInfoBase WrapFileSystemInfo(this FileSystemInfo input, IFileSystem fileSystem)
=> WrapFileSystemInfo(fileSystem, input);
internal static IEnumerable WrapDirectories(this IEnumerable input, IFileSystem fileSystem)
=> input.Select(info => WrapDirectoryInfo(fileSystem, info));
internal static DirectoryInfoBase[] WrapDirectories(this DirectoryInfo[] input, IFileSystem fileSystem)
=> input.Select(info => WrapDirectoryInfo(fileSystem, info)).ToArray();
internal static IEnumerable WrapFiles(this IEnumerable input, IFileSystem fileSystem)
=> input.Select(info => WrapFileInfo(fileSystem, info));
internal static FileInfoBase[] WrapFiles(this FileInfo[] input, IFileSystem fileSystem)
=> input.Select(info => WrapFileInfo(fileSystem, info)).ToArray();
private static FileSystemInfoBase WrapFileSystemInfo(IFileSystem fileSystem, FileSystemInfo item)
{
if (item is null)
{
return null;
}
if (item is FileInfo fileInfo)
{
return WrapFileInfo(fileSystem, fileInfo);
}
else if (item is DirectoryInfo directoryInfo)
{
return WrapDirectoryInfo(fileSystem, directoryInfo);
}
else
{
throw new NotImplementedException(string.Format(
CultureInfo.InvariantCulture,
"The type {0} is not recognized by the System.IO.Abstractions library.",
item.GetType().AssemblyQualifiedName
));
}
}
private static FileInfoBase WrapFileInfo(IFileSystem fileSystem, FileInfo f)
=> f is null ? null : new FileInfoWrapper(fileSystem, f);
private static DirectoryInfoBase WrapDirectoryInfo(IFileSystem fileSystem, DirectoryInfo d)
=> d is null ? null : new DirectoryInfoWrapper(fileSystem, d);
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.Wrappers/DirectoryAclExtensions.cs
================================================
using System.Runtime.Versioning;
using System.Security.AccessControl;
namespace System.IO.Abstractions;
///
/// ACL (access control list) extension methods for .
///
public static class DirectoryAclExtensions
{
#if FEATURE_FILE_SYSTEM_ACL_EXTENSIONS
///
#else
///
#endif
[SupportedOSPlatform("windows")]
public static void CreateDirectory(this IDirectory directory,
string path,
DirectorySecurity directorySecurity)
{
IDirectoryInfo directoryInfo = directory.FileSystem.DirectoryInfo.New(path);
directoryInfo.Create(directorySecurity);
}
#if FEATURE_FILE_SYSTEM_ACL_EXTENSIONS
///
#else
///
#endif
[SupportedOSPlatform("windows")]
public static DirectorySecurity GetAccessControl(
this IDirectory directory, string path)
{
IDirectoryInfo directoryInfo = directory.FileSystem.DirectoryInfo.New(path);
return directoryInfo.GetAccessControl();
}
#if FEATURE_FILE_SYSTEM_ACL_EXTENSIONS
///
#else
///
#endif
[SupportedOSPlatform("windows")]
public static DirectorySecurity GetAccessControl(
this IDirectory directory,
string path,
AccessControlSections includeSections)
{
IDirectoryInfo directoryInfo = directory.FileSystem.DirectoryInfo.New(path);
return directoryInfo.GetAccessControl(includeSections);
}
#if FEATURE_FILE_SYSTEM_ACL_EXTENSIONS
///
#else
///
#endif
[SupportedOSPlatform("windows")]
public static void SetAccessControl(this IDirectory directory,
string path,
DirectorySecurity directorySecurity)
{
IDirectoryInfo directoryInfo = directory.FileSystem.DirectoryInfo.New(path);
directoryInfo.SetAccessControl(directorySecurity);
}
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.Wrappers/DirectoryBase.cs
================================================
using System.Collections.Generic;
namespace System.IO.Abstractions;
///
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public abstract class DirectoryBase : IDirectory
{
///
/// Base class for calling static methods of
///
protected DirectoryBase(IFileSystem fileSystem)
{
FileSystem = fileSystem;
}
[Obsolete("This constructor only exists to support mocking libraries.", error: true)]
internal DirectoryBase() { }
///
/// Exposes the underlying filesystem implementation. This is useful for implementing extension methods.
///
public IFileSystem FileSystem { get; }
///
public abstract IDirectoryInfo CreateDirectory(string path);
#if FEATURE_UNIX_FILE_MODE
///
public abstract IDirectoryInfo CreateDirectory(string path, UnixFileMode unixCreateMode);
#endif
#if FEATURE_CREATE_SYMBOLIC_LINK
///
public abstract IFileSystemInfo CreateSymbolicLink(string path, string pathToTarget);
#endif
#if FEATURE_CREATE_TEMP_SUBDIRECTORY
///
public abstract IDirectoryInfo CreateTempSubdirectory(string prefix = null);
#endif
///
public abstract void Delete(string path);
///
public abstract void Delete(string path, bool recursive);
///
public abstract bool Exists(string path);
///
public abstract DateTime GetCreationTime(string path);
///
public abstract DateTime GetCreationTimeUtc(string path);
///
public abstract string GetCurrentDirectory();
///
public abstract string[] GetDirectories(string path);
///
public abstract string[] GetDirectories(string path, string searchPattern);
///
public abstract string[] GetDirectories(string path, string searchPattern, SearchOption searchOption);
#if FEATURE_ENUMERATION_OPTIONS
///
public abstract string[] GetDirectories(string path, string searchPattern, EnumerationOptions enumerationOptions);
#endif
///
public abstract string GetDirectoryRoot(string path);
///
public abstract string[] GetFiles(string path);
///
public abstract string[] GetFiles(string path, string searchPattern);
///
public abstract string[] GetFiles(string path, string searchPattern, SearchOption searchOption);
#if FEATURE_ENUMERATION_OPTIONS
///
public abstract string[] GetFiles(string path, string searchPattern, EnumerationOptions enumerationOptions);
#endif
///
public abstract string[] GetFileSystemEntries(string path);
///
public abstract string[] GetFileSystemEntries(string path, string searchPattern);
///
public abstract string[] GetFileSystemEntries(string path, string searchPattern, SearchOption searchOption);
#if FEATURE_ENUMERATION_OPTIONS
///
public abstract string[] GetFileSystemEntries(string path, string searchPattern,
EnumerationOptions enumerationOptions);
#endif
///
public abstract DateTime GetLastAccessTime(string path);
///
public abstract DateTime GetLastAccessTimeUtc(string path);
///
public abstract DateTime GetLastWriteTime(string path);
///
public abstract DateTime GetLastWriteTimeUtc(string path);
///
public abstract string[] GetLogicalDrives();
///
public abstract IDirectoryInfo GetParent(string path);
///
public abstract void Move(string sourceDirName, string destDirName);
#if FEATURE_CREATE_SYMBOLIC_LINK
///
public abstract IFileSystemInfo ResolveLinkTarget(string linkPath, bool returnFinalTarget);
#endif
///
public abstract void SetCreationTime(string path, DateTime creationTime);
///
public abstract void SetCreationTimeUtc(string path, DateTime creationTimeUtc);
///
public abstract void SetCurrentDirectory(string path);
///
public abstract void SetLastAccessTime(string path, DateTime lastAccessTime);
///
public abstract void SetLastAccessTimeUtc(string path, DateTime lastAccessTimeUtc);
///
public abstract void SetLastWriteTime(string path, DateTime lastWriteTime);
///
public abstract void SetLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc);
///
public abstract IEnumerable EnumerateDirectories(string path);
///
public abstract IEnumerable EnumerateDirectories(string path, string searchPattern);
///
public abstract IEnumerable EnumerateDirectories(string path, string searchPattern, SearchOption searchOption);
#if FEATURE_ENUMERATION_OPTIONS
///
public abstract IEnumerable EnumerateDirectories(string path, string searchPattern, EnumerationOptions enumerationOptions);
#endif
///
public abstract IEnumerable EnumerateFiles(string path);
///
public abstract IEnumerable EnumerateFiles(string path, string searchPattern);
///
public abstract IEnumerable EnumerateFiles(string path, string searchPattern, SearchOption searchOption);
#if FEATURE_ENUMERATION_OPTIONS
///
public abstract IEnumerable EnumerateFiles(string path, string searchPattern, EnumerationOptions enumerationOptions);
#endif
///
public abstract IEnumerable EnumerateFileSystemEntries(string path);
///
public abstract IEnumerable EnumerateFileSystemEntries(string path, string searchPattern);
///
public abstract IEnumerable EnumerateFileSystemEntries(string path, string searchPattern, SearchOption searchOption);
#if FEATURE_ENUMERATION_OPTIONS
///
public abstract IEnumerable EnumerateFileSystemEntries(string path, string searchPattern, EnumerationOptions enumerationOptions);
#endif
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.Wrappers/DirectoryInfoAclExtensions.cs
================================================
using System.Runtime.Versioning;
using System.Security.AccessControl;
namespace System.IO.Abstractions;
///
/// ACL (access control list) extension methods for .
///
public static class DirectoryInfoAclExtensions
{
#if FEATURE_FILE_SYSTEM_ACL_EXTENSIONS
///
#else
///
#endif
[SupportedOSPlatform("windows")]
public static void Create(this IDirectoryInfo directoryInfo,
DirectorySecurity directorySecurity)
{
IFileSystemAclSupport aclSupport = directoryInfo as IFileSystemAclSupport;
if (aclSupport == null)
{
throw new NotSupportedException("The directory info does not support ACL extensions");
}
directoryInfo.Create();
aclSupport.SetAccessControl(directorySecurity);
}
#if FEATURE_FILE_SYSTEM_ACL_EXTENSIONS
///
#else
///
#endif
[SupportedOSPlatform("windows")]
public static DirectorySecurity GetAccessControl(
this IDirectoryInfo directoryInfo)
{
IFileSystemAclSupport aclSupport = directoryInfo as IFileSystemAclSupport;
var directorySecurity = aclSupport?.GetAccessControl() as DirectorySecurity;
if (aclSupport == null || directorySecurity == null)
{
throw new NotSupportedException("The directory info does not support ACL extensions");
}
return directorySecurity;
}
#if FEATURE_FILE_SYSTEM_ACL_EXTENSIONS
///
#else
///
#endif
[SupportedOSPlatform("windows")]
public static DirectorySecurity GetAccessControl(
this IDirectoryInfo directoryInfo,
AccessControlSections includeSections)
{
IFileSystemAclSupport aclSupport = directoryInfo as IFileSystemAclSupport;
var directorySecurity = aclSupport?.GetAccessControl((IFileSystemAclSupport.AccessControlSections) includeSections) as DirectorySecurity;
if (aclSupport == null || directorySecurity == null)
{
throw new NotSupportedException("The directory info does not support ACL extensions");
}
return directorySecurity;
}
#if FEATURE_FILE_SYSTEM_ACL_EXTENSIONS
///
#else
///
#endif
[SupportedOSPlatform("windows")]
public static void SetAccessControl(this IDirectoryInfo directoryInfo,
DirectorySecurity directorySecurity)
{
IFileSystemAclSupport aclSupport = directoryInfo as IFileSystemAclSupport;
if (aclSupport == null)
{
throw new NotSupportedException("The directory info does not support ACL extensions");
}
aclSupport.SetAccessControl(directorySecurity);
}
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.Wrappers/DirectoryInfoBase.cs
================================================
using System.Collections.Generic;
namespace System.IO.Abstractions;
///
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public abstract class DirectoryInfoBase : FileSystemInfoBase, IDirectoryInfo
{
///
/// Base class for calling methods of
///
protected DirectoryInfoBase(IFileSystem fileSystem) : base(fileSystem)
{
}
[Obsolete("This constructor only exists to support mocking libraries.", error: true)]
internal DirectoryInfoBase() { }
///
public abstract void Create();
///
public abstract IDirectoryInfo CreateSubdirectory(string path);
///
public abstract void Delete(bool recursive);
///
public abstract IEnumerable EnumerateDirectories();
///
public abstract IEnumerable EnumerateDirectories(string searchPattern);
///
public abstract IEnumerable EnumerateDirectories(string searchPattern, SearchOption searchOption);
#if FEATURE_ENUMERATION_OPTIONS
///
public abstract IEnumerable EnumerateDirectories(string searchPattern, EnumerationOptions enumerationOptions);
#endif
///
public abstract IEnumerable EnumerateFiles();
///
public abstract IEnumerable EnumerateFiles(string searchPattern);
///
public abstract IEnumerable EnumerateFiles(string searchPattern, SearchOption searchOption);
#if FEATURE_ENUMERATION_OPTIONS
///
public abstract IEnumerable EnumerateFiles(string searchPattern, EnumerationOptions enumerationOptions);
#endif
///
public abstract IEnumerable EnumerateFileSystemInfos();
///
public abstract IEnumerable EnumerateFileSystemInfos(string searchPattern);
///
public abstract IEnumerable EnumerateFileSystemInfos(string searchPattern, SearchOption searchOption);
#if FEATURE_ENUMERATION_OPTIONS
///
public abstract IEnumerable EnumerateFileSystemInfos(string searchPattern, EnumerationOptions enumerationOptions);
#endif
///
public abstract IDirectoryInfo[] GetDirectories();
///
public abstract IDirectoryInfo[] GetDirectories(string searchPattern);
///
public abstract IDirectoryInfo[] GetDirectories(string searchPattern, SearchOption searchOption);
#if FEATURE_ENUMERATION_OPTIONS
///
public abstract IDirectoryInfo[] GetDirectories(string searchPattern, EnumerationOptions enumerationOptions);
#endif
///
public abstract IFileInfo[] GetFiles();
///
public abstract IFileInfo[] GetFiles(string searchPattern);
///
public abstract IFileInfo[] GetFiles(string searchPattern, SearchOption searchOption);
#if FEATURE_ENUMERATION_OPTIONS
///
public abstract IFileInfo[] GetFiles(string searchPattern, EnumerationOptions enumerationOptions);
#endif
///
public abstract IFileSystemInfo[] GetFileSystemInfos();
///
public abstract IFileSystemInfo[] GetFileSystemInfos(string searchPattern);
///
public abstract IFileSystemInfo[] GetFileSystemInfos(string searchPattern, SearchOption searchOption);
#if FEATURE_ENUMERATION_OPTIONS
///
public abstract IFileSystemInfo[] GetFileSystemInfos(string searchPattern, EnumerationOptions enumerationOptions);
#endif
///
public abstract void MoveTo(string destDirName);
///
public abstract IDirectoryInfo Parent { get; }
///
public abstract IDirectoryInfo Root { get; }
///
/// Implicitly converts a to a .
///
public static implicit operator DirectoryInfoBase(DirectoryInfo directoryInfo)
{
if (directoryInfo == null)
{
return null;
}
return new DirectoryInfoWrapper(new FileSystem(), directoryInfo);
}
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.Wrappers/DirectoryInfoFactory.cs
================================================
namespace System.IO.Abstractions;
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
internal class DirectoryInfoFactory : IDirectoryInfoFactory
{
private readonly IFileSystem fileSystem;
///
/// Base factory class for creating a
///
public DirectoryInfoFactory(IFileSystem fileSystem)
{
this.fileSystem = fileSystem;
}
///
public IFileSystem FileSystem
=> fileSystem;
///
public IDirectoryInfo New(string path)
{
var realDirectoryInfo = new DirectoryInfo(path);
return new DirectoryInfoWrapper(fileSystem, realDirectoryInfo);
}
///
public IDirectoryInfo Wrap(DirectoryInfo directoryInfo)
{
if (directoryInfo == null)
{
return null;
}
return new DirectoryInfoWrapper(fileSystem, directoryInfo);
}
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.Wrappers/DirectoryInfoWrapper.cs
================================================
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Versioning;
using System.Security.AccessControl;
namespace System.IO.Abstractions;
///
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public class DirectoryInfoWrapper : DirectoryInfoBase, IFileSystemAclSupport
{
private readonly DirectoryInfo instance;
///
/// Wrapper class for calling methods of
///
public DirectoryInfoWrapper(IFileSystem fileSystem, DirectoryInfo instance) : base(fileSystem)
{
this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
}
#if FEATURE_CREATE_SYMBOLIC_LINK
///
public override void CreateAsSymbolicLink(string pathToTarget)
{
instance.CreateAsSymbolicLink(pathToTarget);
}
#endif
///
public override void Delete()
{
instance.Delete();
}
///
public override void Refresh()
{
instance.Refresh();
}
#if FEATURE_CREATE_SYMBOLIC_LINK
///
public override IFileSystemInfo ResolveLinkTarget(bool returnFinalTarget)
{
return instance.ResolveLinkTarget(returnFinalTarget)
.WrapFileSystemInfo(FileSystem);
}
#endif
///
public override FileAttributes Attributes
{
get { return instance.Attributes; }
set { instance.Attributes = value; }
}
///
public override DateTime CreationTime
{
get { return instance.CreationTime; }
set { instance.CreationTime = value; }
}
///
public override DateTime CreationTimeUtc
{
get { return instance.CreationTimeUtc; }
set { instance.CreationTimeUtc = value; }
}
///
public override bool Exists
{
get { return instance.Exists; }
}
///
public override string Extension
{
get { return instance.Extension; }
}
///
public override string FullName
{
get { return instance.FullName; }
}
///
public override DateTime LastAccessTime
{
get { return instance.LastAccessTime; }
set { instance.LastAccessTime = value; }
}
///
public override DateTime LastAccessTimeUtc
{
get { return instance.LastAccessTimeUtc; }
set { instance.LastAccessTimeUtc = value; }
}
///
public override DateTime LastWriteTime
{
get { return instance.LastWriteTime; }
set { instance.LastWriteTime = value; }
}
///
public override DateTime LastWriteTimeUtc
{
get { return instance.LastWriteTimeUtc; }
set { instance.LastWriteTimeUtc = value; }
}
#if FEATURE_FILE_SYSTEM_INFO_LINK_TARGET
///
public override string LinkTarget
{
get { return instance.LinkTarget; }
}
#endif
///
public override string Name
{
get { return instance.Name; }
}
///
public override void Create()
{
instance.Create();
}
///
public override IDirectoryInfo CreateSubdirectory(string path)
{
return new DirectoryInfoWrapper(FileSystem, instance.CreateSubdirectory(path));
}
///
public override void Delete(bool recursive)
{
instance.Delete(recursive);
}
///
public override IEnumerable EnumerateDirectories()
{
return instance.EnumerateDirectories().Select(directoryInfo => new DirectoryInfoWrapper(FileSystem, directoryInfo));
}
///
public override IEnumerable EnumerateDirectories(string searchPattern)
{
return instance.EnumerateDirectories(searchPattern).Select(directoryInfo => new DirectoryInfoWrapper(FileSystem, directoryInfo));
}
///
public override IEnumerable EnumerateDirectories(string searchPattern, SearchOption searchOption)
{
return instance.EnumerateDirectories(searchPattern, searchOption).Select(directoryInfo => new DirectoryInfoWrapper(FileSystem, directoryInfo));
}
#if FEATURE_ENUMERATION_OPTIONS
///
public override IEnumerable EnumerateDirectories(string searchPattern, EnumerationOptions enumerationOptions)
{
return instance.EnumerateDirectories(searchPattern, enumerationOptions).Select(directoryInfo => new DirectoryInfoWrapper(FileSystem, directoryInfo));
}
#endif
///
public override IEnumerable EnumerateFiles()
{
return instance.EnumerateFiles().Select(fileInfo => new FileInfoWrapper(FileSystem, fileInfo));
}
///
public override IEnumerable EnumerateFiles(string searchPattern)
{
return instance.EnumerateFiles(searchPattern).Select(fileInfo => new FileInfoWrapper(FileSystem, fileInfo));
}
///
public override IEnumerable EnumerateFiles(string searchPattern, SearchOption searchOption)
{
return instance.EnumerateFiles(searchPattern, searchOption).Select(fileInfo => new FileInfoWrapper(FileSystem, fileInfo));
}
#if FEATURE_ENUMERATION_OPTIONS
///
public override IEnumerable EnumerateFiles(string searchPattern, EnumerationOptions enumerationOptions)
{
return instance.EnumerateFiles(searchPattern, enumerationOptions).Select(fileInfo => new FileInfoWrapper(FileSystem, fileInfo));
}
#endif
///
public override IEnumerable EnumerateFileSystemInfos()
{
return instance.EnumerateFileSystemInfos().WrapFileSystemInfos(FileSystem);
}
///
public override IEnumerable EnumerateFileSystemInfos(string searchPattern)
{
return instance.EnumerateFileSystemInfos(searchPattern).WrapFileSystemInfos(FileSystem);
}
///
public override IEnumerable EnumerateFileSystemInfos(string searchPattern, SearchOption searchOption)
{
return instance.EnumerateFileSystemInfos(searchPattern, searchOption).WrapFileSystemInfos(FileSystem);
}
#if FEATURE_ENUMERATION_OPTIONS
///
public override IEnumerable EnumerateFileSystemInfos(string searchPattern, EnumerationOptions enumerationOptions)
{
return instance.EnumerateFileSystemInfos(searchPattern, enumerationOptions).WrapFileSystemInfos(FileSystem);
}
#endif
///
public override IDirectoryInfo[] GetDirectories()
{
return instance.GetDirectories().WrapDirectories(FileSystem);
}
///
public override IDirectoryInfo[] GetDirectories(string searchPattern)
{
return instance.GetDirectories(searchPattern).WrapDirectories(FileSystem);
}
///
public override IDirectoryInfo[] GetDirectories(string searchPattern, SearchOption searchOption)
{
return instance.GetDirectories(searchPattern, searchOption).WrapDirectories(FileSystem);
}
#if FEATURE_ENUMERATION_OPTIONS
///
public override IDirectoryInfo[] GetDirectories(string searchPattern, EnumerationOptions enumerationOptions)
{
return instance.GetDirectories(searchPattern, enumerationOptions).WrapDirectories(FileSystem);
}
#endif
///
public override IFileInfo[] GetFiles()
{
return instance.GetFiles().WrapFiles(FileSystem);
}
///
public override IFileInfo[] GetFiles(string searchPattern)
{
return instance.GetFiles(searchPattern).WrapFiles(FileSystem);
}
///
public override IFileInfo[] GetFiles(string searchPattern, SearchOption searchOption)
{
return instance.GetFiles(searchPattern, searchOption).WrapFiles(FileSystem);
}
#if FEATURE_ENUMERATION_OPTIONS
///
public override IFileInfo[] GetFiles(string searchPattern, EnumerationOptions enumerationOptions)
{
return instance.GetFiles(searchPattern, enumerationOptions).WrapFiles(FileSystem);
}
#endif
///
public override IFileSystemInfo[] GetFileSystemInfos()
{
return instance.GetFileSystemInfos().WrapFileSystemInfos(FileSystem);
}
///
public override IFileSystemInfo[] GetFileSystemInfos(string searchPattern)
{
return instance.GetFileSystemInfos(searchPattern).WrapFileSystemInfos(FileSystem);
}
///
public override IFileSystemInfo[] GetFileSystemInfos(string searchPattern, SearchOption searchOption)
{
return instance.GetFileSystemInfos(searchPattern, searchOption).WrapFileSystemInfos(FileSystem);
}
#if FEATURE_ENUMERATION_OPTIONS
///
public override IFileSystemInfo[] GetFileSystemInfos(string searchPattern, EnumerationOptions enumerationOptions)
{
return instance.GetFileSystemInfos(searchPattern, enumerationOptions).WrapFileSystemInfos(FileSystem);
}
#endif
///
public override void MoveTo(string destDirName)
{
instance.MoveTo(destDirName);
}
///
public override IDirectoryInfo Parent
{
get
{
if (instance.Parent == null)
{
return null;
}
else
{
return new DirectoryInfoWrapper(FileSystem, instance.Parent);
}
}
}
///
public override IDirectoryInfo Root
=> new DirectoryInfoWrapper(FileSystem, instance.Root);
///
public override string ToString()
{
return instance.ToString();
}
///
[SupportedOSPlatform("windows")]
public object GetAccessControl()
{
return instance.GetAccessControl();
}
///
[SupportedOSPlatform("windows")]
public object GetAccessControl(IFileSystemAclSupport.AccessControlSections includeSections)
{
return instance.GetAccessControl((AccessControlSections)includeSections);
}
///
[SupportedOSPlatform("windows")]
public void SetAccessControl(object value)
{
if (value is DirectorySecurity directorySecurity)
{
this.instance.SetAccessControl(directorySecurity);
}
else
{
throw new ArgumentException("value must be of type `FileSecurity`");
}
}
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.Wrappers/DirectoryWrapper.cs
================================================
using System.Collections.Generic;
using System.Runtime.Versioning;
namespace System.IO.Abstractions;
///
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public class DirectoryWrapper : DirectoryBase
{
///
public DirectoryWrapper(IFileSystem fileSystem) : base(fileSystem)
{
}
///
public override IDirectoryInfo CreateDirectory(string path)
{
var directoryInfo = new DirectoryInfo(path);
directoryInfo.Create();
return new DirectoryInfoWrapper(FileSystem, directoryInfo);
}
#if FEATURE_UNIX_FILE_MODE
///
[UnsupportedOSPlatform("windows")]
public override IDirectoryInfo CreateDirectory(string path, UnixFileMode unixCreateMode)
{
return new DirectoryInfoWrapper(FileSystem,
Directory.CreateDirectory(path, unixCreateMode));
}
#endif
#if FEATURE_CREATE_SYMBOLIC_LINK
///
public override IFileSystemInfo CreateSymbolicLink(string path, string pathToTarget)
{
return Directory.CreateSymbolicLink(path, pathToTarget)
.WrapFileSystemInfo(FileSystem);
}
#endif
#if FEATURE_CREATE_TEMP_SUBDIRECTORY
///
public override IDirectoryInfo CreateTempSubdirectory(string prefix = null)
{
return new DirectoryInfoWrapper(FileSystem,
Directory.CreateTempSubdirectory(prefix));
}
#endif
///
public override void Delete(string path)
{
Directory.Delete(path);
}
///
public override void Delete(string path, bool recursive)
{
Directory.Delete(path, recursive);
}
///
public override bool Exists(string path)
{
return Directory.Exists(path);
}
///
public override DateTime GetCreationTime(string path)
{
return Directory.GetCreationTime(path);
}
///
public override DateTime GetCreationTimeUtc(string path)
{
return Directory.GetCreationTimeUtc(path);
}
///
public override string GetCurrentDirectory()
{
return Directory.GetCurrentDirectory();
}
///
public override string[] GetDirectories(string path)
{
return Directory.GetDirectories(path);
}
///
public override string[] GetDirectories(string path, string searchPattern)
{
return Directory.GetDirectories(path, searchPattern);
}
///
public override string[] GetDirectories(string path, string searchPattern, SearchOption searchOption)
{
return Directory.GetDirectories(path, searchPattern, searchOption);
}
#if FEATURE_ENUMERATION_OPTIONS
///
public override string[] GetDirectories(string path, string searchPattern, EnumerationOptions enumerationOptions)
{
return Directory.GetDirectories(path, searchPattern, enumerationOptions);
}
#endif
///
public override string GetDirectoryRoot(string path)
{
return Directory.GetDirectoryRoot(path);
}
///
public override string[] GetFiles(string path)
{
return Directory.GetFiles(path);
}
///
public override string[] GetFiles(string path, string searchPattern)
{
return Directory.GetFiles(path, searchPattern);
}
///
public override string[] GetFiles(string path, string searchPattern, SearchOption searchOption)
{
return Directory.GetFiles(path, searchPattern, searchOption);
}
#if FEATURE_ENUMERATION_OPTIONS
///
public override string[] GetFiles(string path, string searchPattern, EnumerationOptions enumerationOptions)
{
return Directory.GetFiles(path, searchPattern, enumerationOptions);
}
#endif
///
public override string[] GetFileSystemEntries(string path)
{
return Directory.GetFileSystemEntries(path);
}
///
public override string[] GetFileSystemEntries(string path, string searchPattern)
{
return Directory.GetFileSystemEntries(path, searchPattern);
}
///
public override string[] GetFileSystemEntries(string path, string searchPattern, SearchOption searchOption)
{
return Directory.GetFileSystemEntries(path, searchPattern, searchOption);
}
#if FEATURE_ENUMERATION_OPTIONS
///
public override string[] GetFileSystemEntries(string path, string searchPattern, EnumerationOptions enumerationOptions)
{
return Directory.GetFileSystemEntries(path, searchPattern, enumerationOptions);
}
#endif
///
public override DateTime GetLastAccessTime(string path)
{
return Directory.GetLastAccessTime(path);
}
///
public override DateTime GetLastAccessTimeUtc(string path)
{
return Directory.GetLastAccessTimeUtc(path);
}
///
public override DateTime GetLastWriteTime(string path)
{
return Directory.GetLastWriteTime(path);
}
///
public override DateTime GetLastWriteTimeUtc(string path)
{
return Directory.GetLastWriteTimeUtc(path);
}
///
public override string[] GetLogicalDrives()
{
return Directory.GetLogicalDrives();
}
///
public override IDirectoryInfo GetParent(string path)
{
var parent = Directory.GetParent(path);
if (parent == null)
{
return null;
}
return new DirectoryInfoWrapper(FileSystem, parent);
}
///
public override void Move(string sourceDirName, string destDirName)
{
Directory.Move(sourceDirName, destDirName);
}
#if FEATURE_CREATE_SYMBOLIC_LINK
///
public override IFileSystemInfo ResolveLinkTarget(string linkPath, bool returnFinalTarget)
{
return Directory.ResolveLinkTarget(linkPath, returnFinalTarget)
.WrapFileSystemInfo(FileSystem);
}
#endif
///
public override void SetCreationTime(string path, DateTime creationTime)
{
Directory.SetCreationTime(path, creationTime);
}
///
public override void SetCreationTimeUtc(string path, DateTime creationTimeUtc)
{
Directory.SetCreationTimeUtc(path, creationTimeUtc);
}
///
public override void SetCurrentDirectory(string path)
{
Directory.SetCurrentDirectory(path);
}
///
public override void SetLastAccessTime(string path, DateTime lastAccessTime)
{
Directory.SetLastAccessTime(path, lastAccessTime);
}
///
public override void SetLastAccessTimeUtc(string path, DateTime lastAccessTimeUtc)
{
Directory.SetLastAccessTimeUtc(path, lastAccessTimeUtc);
}
///
public override void SetLastWriteTime(string path, DateTime lastWriteTime)
{
Directory.SetLastWriteTime(path, lastWriteTime);
}
///
public override void SetLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc)
{
Directory.SetLastWriteTimeUtc(path, lastWriteTimeUtc);
}
///
public override IEnumerable EnumerateDirectories(string path)
{
return Directory.EnumerateDirectories(path);
}
///
public override IEnumerable EnumerateDirectories(string path, string searchPattern)
{
return Directory.EnumerateDirectories(path, searchPattern);
}
///
public override IEnumerable EnumerateDirectories(string path, string searchPattern, SearchOption searchOption)
{
return Directory.EnumerateDirectories(path, searchPattern, searchOption);
}
#if FEATURE_ENUMERATION_OPTIONS
///
public override IEnumerable EnumerateDirectories(string path, string searchPattern, EnumerationOptions enumerationOptions)
{
return Directory.EnumerateDirectories(path, searchPattern, enumerationOptions);
}
#endif
///
public override IEnumerable EnumerateFiles(string path)
{
return Directory.EnumerateFiles(path);
}
///
public override IEnumerable EnumerateFiles(string path, string searchPattern)
{
return Directory.EnumerateFiles(path, searchPattern);
}
///
public override IEnumerable EnumerateFiles(string path, string searchPattern, SearchOption searchOption)
{
return Directory.EnumerateFiles(path, searchPattern, searchOption);
}
#if FEATURE_ENUMERATION_OPTIONS
///
public override IEnumerable EnumerateFiles(string path, string searchPattern, EnumerationOptions enumerationOptions)
{
return Directory.EnumerateFiles(path, searchPattern, enumerationOptions);
}
#endif
///
public override IEnumerable EnumerateFileSystemEntries(string path)
{
return Directory.EnumerateFileSystemEntries(path);
}
///
public override IEnumerable EnumerateFileSystemEntries(string path, string searchPattern)
{
return Directory.EnumerateFileSystemEntries(path, searchPattern);
}
///
public override IEnumerable EnumerateFileSystemEntries(string path, string searchPattern, SearchOption searchOption)
{
return Directory.EnumerateFileSystemEntries(path, searchPattern, searchOption);
}
#if FEATURE_ENUMERATION_OPTIONS
///
public override IEnumerable EnumerateFileSystemEntries(string path, string searchPattern, EnumerationOptions enumerationOptions)
{
return Directory.EnumerateFileSystemEntries(path, searchPattern, enumerationOptions);
}
#endif
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.Wrappers/DriveInfoBase.cs
================================================
namespace System.IO.Abstractions;
///
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public abstract class DriveInfoBase : IDriveInfo
{
///
/// Base class for calling methods of
///
protected DriveInfoBase(IFileSystem fileSystem)
{
FileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem));
}
[Obsolete("This constructor only exists to support mocking libraries.", error: true)]
internal DriveInfoBase() { }
///
/// Exposes the underlying filesystem implementation. This is useful for implementing extension methods.
///
public IFileSystem FileSystem { get; }
///
public abstract long AvailableFreeSpace { get; }
///
public abstract string DriveFormat { get; }
///
public abstract DriveType DriveType { get; }
///
public abstract bool IsReady { get; }
///
public abstract string Name { get; }
///
public abstract IDirectoryInfo RootDirectory { get; }
///
public abstract long TotalFreeSpace { get; }
///
public abstract long TotalSize { get; }
///
public abstract string VolumeLabel { get; set; }
///
/// Implicitly converts a to a .
///
public static implicit operator DriveInfoBase(DriveInfo driveInfo)
{
if (driveInfo == null)
{
return null;
}
return new DriveInfoWrapper(new FileSystem(), driveInfo);
}
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.Wrappers/DriveInfoFactory.cs
================================================
namespace System.IO.Abstractions;
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
internal class DriveInfoFactory : IDriveInfoFactory
{
private readonly IFileSystem fileSystem;
///
/// Base factory class for creating a
///
public DriveInfoFactory(IFileSystem fileSystem)
{
this.fileSystem = fileSystem;
}
///
public IFileSystem FileSystem
=> fileSystem;
///
public IDriveInfo[] GetDrives()
{
var driveInfos = DriveInfo.GetDrives();
var driveInfoWrappers = new DriveInfoBase[driveInfos.Length];
for (int index = 0; index < driveInfos.Length; index++)
{
var driveInfo = driveInfos[index];
driveInfoWrappers[index] = new DriveInfoWrapper(fileSystem, driveInfo);
}
return driveInfoWrappers;
}
///
public IDriveInfo New(string driveName)
{
var realDriveInfo = new DriveInfo(driveName);
return new DriveInfoWrapper(fileSystem, realDriveInfo);
}
///
public IDriveInfo Wrap(DriveInfo driveInfo)
{
if (driveInfo == null)
{
return null;
}
return new DriveInfoWrapper(fileSystem, driveInfo);
}
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.Wrappers/DriveInfoWrapper.cs
================================================
using System.Runtime.Versioning;
namespace System.IO.Abstractions;
///
/// The wrapper for a .
///
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public class DriveInfoWrapper : DriveInfoBase
{
///
/// The instance of the real .
///
private readonly DriveInfo instance;
///
/// Initializes a new instance of the class, which acts as a wrapper for a drive info.
///
/// The underlying IFileSystem.
/// The drive info.
public DriveInfoWrapper(IFileSystem fileSystem, DriveInfo instance) : base(fileSystem)
{
this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
}
///
/// Gets or sets the name of a drive, such as C:\.
///
/// The name of the drive.
///
/// This property is the name assigned to the drive, such as C:\ or E:\.
///
public override string Name
{
get { return instance.Name; }
}
///
/// Gets or sets the drive type, such as CD-ROM, removable, network, or fixed.
///
/// One of the enumeration values that specifies a drive type.
///
/// The DriveType property indicates whether a drive is one of the following: CDRom, Fixed, Network, NoRootDirectory, Ram, Removable, or Unknown.
/// These values are described in the DriveType enumeration.
///
public override DriveType DriveType
{
get { return instance.DriveType; }
}
///
/// Gets or sets the name of the file system, such as NTFS or FAT32.
///
///
/// Use DriveFormat to determine what formatting a drive uses.
///
/// The name of the file system on the specified drive.
/// Thrown if the access to the drive information is denied.
/// Thrown if the drive does not exist or is not mapped.
/// Thrown if an I/O error occurred (for example, a disk error or a drive was not ready).
public override string DriveFormat
{
get { return instance.DriveFormat; }
}
///
/// Gets or sets a value indicating whether a drive is ready.
///
///
/// if the drive is ready; if the drive is not ready.
///
///
/// IsReady indicates whether a drive is ready.
/// For example, it indicates whether a CD is in a CD drive or whether a removable storage device is ready for read/write operations.
/// If you do not test whether a drive is ready, and it is not ready, querying the drive using will raise an IOException.
/// Do not rely on IsReady to avoid catching exceptions from other members such as TotalSize, TotalFreeSpace, and .
/// Between the time that your code checks IsReady and then accesses one of the other properties (even if the access occurs immediately after the check),
/// a drive may have been disconnected or a disk may have been removed.
///
public override bool IsReady
{
get { return instance.IsReady; }
}
///
/// Gets or sets the amount of available free space on a drive, in bytes.
///
/// The amount of free space available on the drive, in bytes.
///
/// This property indicates the amount of free space available on the drive.
/// Note that this number may be different from the TotalFreeSpace number because this property takes into account disk quotas.
///
/// Thrown if the access to the drive information is denied.
/// Thrown if an I/O error occurred (for example, a disk error or a drive was not ready).
public override long AvailableFreeSpace
{
get { return instance.AvailableFreeSpace; }
}
///
/// Gets or sets the total amount of free space available on a drive, in bytes.
///
/// The total free space available on a drive, in bytes.
/// This property indicates the total amount of free space available on the drive, not just what is available to the current user.
/// Thrown if the access to the drive information is denied.
/// Thrown if the drive does not exist or is not mapped.
/// Thrown if an I/O error occurred (for example, a disk error or a drive was not ready).
public override long TotalFreeSpace
{
get { return instance.TotalFreeSpace; }
}
///
/// Gets or sets the total size of storage space on a drive, in bytes.
///
/// The total size of the drive, in bytes.
///
/// This property indicates the total size of the drive in bytes, not just what is available to the current user.
///
/// Thrown if the access to the drive information is denied.
/// Thrown if the drive does not exist or is not mapped.
/// Thrown if an I/O error occurred (for example, a disk error or a drive was not ready).
public override long TotalSize
{
get { return instance.TotalSize; }
}
///
/// Gets or sets the root directory of a drive.
///
/// An object that contains the root directory of the drive.
public override IDirectoryInfo RootDirectory
{
get { return new DirectoryInfoWrapper(FileSystem, instance.RootDirectory); }
}
///
/// Gets or sets the volume label of a drive.
///
/// The volume label.
///
/// The label length is determined by the operating system. For example, NTFS allows a volume label to be up to 32 characters long. Note that is a valid VolumeLabel.
///
/// Thrown if an I/O error occurred (for example, a disk error or a drive was not ready).
/// Thrown if the drive does not exist or is not mapped.
/// Thrown if the caller does not have the required permission.
///
/// Thrown if the volume label is being set on a network or CD-ROM drive
/// -or-
/// Access to the drive information is denied.
///
public override string VolumeLabel
{
get { return instance.VolumeLabel; }
[SupportedOSPlatform("windows")]
#pragma warning disable CA1416
set { instance.VolumeLabel = value; }
#pragma warning restore CA1416
}
///
public override string ToString()
{
return instance.ToString();
}
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.Wrappers/FileAclExtensions.cs
================================================
using System.Runtime.Versioning;
using System.Security.AccessControl;
namespace System.IO.Abstractions;
///
/// ACL (access control list) extension methods for .
///
public static class FileAclExtensions
{
#if FEATURE_FILE_SYSTEM_ACL_EXTENSIONS
///
#else
///
#endif
[SupportedOSPlatform("windows")]
public static FileSecurity GetAccessControl(
this IFile file, string path)
{
IFileInfo fileInfo = file.FileSystem.FileInfo.New(path);
return fileInfo.GetAccessControl();
}
#if FEATURE_FILE_SYSTEM_ACL_EXTENSIONS
///
#else
///
#endif
[SupportedOSPlatform("windows")]
public static FileSecurity GetAccessControl(
this IFile file,
string path,
AccessControlSections includeSections)
{
IFileInfo fileInfo = file.FileSystem.FileInfo.New(path);
return fileInfo.GetAccessControl(includeSections);
}
#if FEATURE_FILE_SYSTEM_ACL_EXTENSIONS
///
#else
///
#endif
[SupportedOSPlatform("windows")]
public static void SetAccessControl(this IFile file,
string path,
FileSecurity fileSecurity)
{
IFileInfo fileInfo = file.FileSystem.FileInfo.New(path);
fileInfo.SetAccessControl(fileSecurity);
}
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.Wrappers/FileBase.Async.cs
================================================
#if FEATURE_ASYNC_FILE
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace System.IO.Abstractions
{
partial class FileBase
{
#if FEATURE_FILE_SPAN
///
public abstract Task AppendAllBytesAsync(string path, byte[] bytes,
CancellationToken cancellationToken = default);
///
public abstract Task AppendAllBytesAsync(string path, ReadOnlyMemory bytes,
CancellationToken cancellationToken = default);
#endif
///
public abstract Task AppendAllLinesAsync(string path, IEnumerable contents, CancellationToken cancellationToken = default);
///
public abstract Task AppendAllLinesAsync(string path, IEnumerable contents, Encoding encoding, CancellationToken cancellationToken = default);
///
public abstract Task AppendAllTextAsync(String path, String contents, CancellationToken cancellationToken = default);
///
public abstract Task AppendAllTextAsync(String path, String contents, Encoding encoding, CancellationToken cancellationToken = default);
#if FEATURE_FILE_SPAN
///
public abstract Task AppendAllTextAsync(string path, ReadOnlyMemory contents,
CancellationToken cancellationToken = default);
///
public abstract Task AppendAllTextAsync(string path, ReadOnlyMemory contents, Encoding encoding,
CancellationToken cancellationToken = default);
#endif
///
public abstract Task ReadAllBytesAsync(string path, CancellationToken cancellationToken = default);
///
public abstract Task ReadAllLinesAsync(string path, CancellationToken cancellationToken = default);
///
public abstract Task ReadAllLinesAsync(string path, Encoding encoding, CancellationToken cancellationToken = default);
///
public abstract Task ReadAllTextAsync(string path, CancellationToken cancellationToken = default);
///
public abstract Task ReadAllTextAsync(string path, Encoding encoding, CancellationToken cancellationToken = default);
#if FEATURE_READ_LINES_ASYNC
///
public abstract IAsyncEnumerable ReadLinesAsync(string path,
CancellationToken cancellationToken = default);
///
public abstract IAsyncEnumerable ReadLinesAsync(string path, Encoding encoding,
CancellationToken cancellationToken = default);
#endif
///
public abstract Task WriteAllBytesAsync(string path, byte[] bytes, CancellationToken cancellationToken = default);
#if FEATURE_FILE_SPAN
///
public abstract Task WriteAllBytesAsync(string path, ReadOnlyMemory bytes,
CancellationToken cancellationToken = default);
#endif
///
public abstract Task WriteAllLinesAsync(string path, IEnumerable contents, CancellationToken cancellationToken = default);
///
public abstract Task WriteAllLinesAsync(string path, IEnumerable contents, Encoding encoding, CancellationToken cancellationToken = default);
///
public abstract Task WriteAllTextAsync(string path, string contents, CancellationToken cancellationToken = default);
///
public abstract Task WriteAllTextAsync(string path, string contents, Encoding encoding, CancellationToken cancellationToken = default);
#if FEATURE_FILE_SPAN
///
public abstract Task WriteAllTextAsync(string path, ReadOnlyMemory contents,
CancellationToken cancellationToken = default);
///
public abstract Task WriteAllTextAsync(string path, ReadOnlyMemory contents, Encoding encoding,
CancellationToken cancellationToken = default);
#endif
}
}
#endif
================================================
FILE: src/TestableIO.System.IO.Abstractions.Wrappers/FileBase.cs
================================================
using System.Collections.Generic;
using System.Text;
using Microsoft.Win32.SafeHandles;
namespace System.IO.Abstractions;
///
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public abstract partial class FileBase : IFile
{
///
/// Base class for calling static methods of
///
protected FileBase(IFileSystem fileSystem)
{
FileSystem = fileSystem;
}
[Obsolete("This constructor only exists to support mocking libraries.", error: true)]
internal FileBase() { }
///
/// Exposes the underlying filesystem implementation. This is useful for implementing extension methods.
///
public IFileSystem FileSystem { get; }
#if FEATURE_FILE_SPAN
///
public abstract void AppendAllBytes(string path, byte[] bytes);
///
public abstract void AppendAllBytes(string path, ReadOnlySpan bytes);
#endif
///
public abstract void AppendAllLines(string path, IEnumerable contents);
///
public abstract void AppendAllLines(string path, IEnumerable contents, Encoding encoding);
///
public abstract void AppendAllText(string path, string contents);
///
public abstract void AppendAllText(string path, string contents, Encoding encoding);
#if FEATURE_FILE_SPAN
///
public abstract void AppendAllText(string path, ReadOnlySpan contents);
///
public abstract void AppendAllText(string path, ReadOnlySpan contents, Encoding encoding);
#endif
///
public abstract StreamWriter AppendText(string path);
///
public abstract void Copy(string sourceFileName, string destFileName);
///
public abstract void Copy(string sourceFileName, string destFileName, bool overwrite);
///
public abstract FileSystemStream Create(string path);
///
public abstract FileSystemStream Create(string path, int bufferSize);
///
public abstract FileSystemStream Create(string path, int bufferSize, FileOptions options);
#if FEATURE_CREATE_SYMBOLIC_LINK
///
public abstract IFileSystemInfo CreateSymbolicLink(string path, string pathToTarget);
#endif
///
public abstract StreamWriter CreateText(string path);
///
public abstract void Decrypt(string path);
///
public abstract void Delete(string path);
///
public abstract void Encrypt(string path);
///
public abstract bool Exists(string path);
///
public abstract FileAttributes GetAttributes(string path);
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public abstract FileAttributes GetAttributes(SafeFileHandle fileHandle);
#endif
///
public abstract DateTime GetCreationTime(string path);
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public abstract DateTime GetCreationTime(SafeFileHandle fileHandle);
#endif
///
public abstract DateTime GetCreationTimeUtc(string path);
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public abstract DateTime GetCreationTimeUtc(SafeFileHandle fileHandle);
#endif
///
public abstract DateTime GetLastAccessTime(string path);
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public abstract DateTime GetLastAccessTime(SafeFileHandle fileHandle);
#endif
///
public abstract DateTime GetLastAccessTimeUtc(string path);
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public abstract DateTime GetLastAccessTimeUtc(SafeFileHandle fileHandle);
#endif
///
public abstract DateTime GetLastWriteTime(string path);
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public abstract DateTime GetLastWriteTime(SafeFileHandle fileHandle);
#endif
///
public abstract DateTime GetLastWriteTimeUtc(string path);
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public abstract DateTime GetLastWriteTimeUtc(SafeFileHandle fileHandle);
#endif
#if FEATURE_UNIX_FILE_MODE
///
public abstract UnixFileMode GetUnixFileMode(string path);
#endif
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public abstract UnixFileMode GetUnixFileMode(SafeFileHandle fileHandle);
#endif
///
public abstract void Move(string sourceFileName, string destFileName);
#if FEATURE_FILE_MOVE_WITH_OVERWRITE
///
public abstract void Move(string sourceFileName, string destFileName, bool overwrite);
#endif
///
public abstract FileSystemStream Open(string path, FileMode mode);
///
public abstract FileSystemStream Open(string path, FileMode mode, FileAccess access);
///
public abstract FileSystemStream Open(string path, FileMode mode, FileAccess access, FileShare share);
#if FEATURE_FILESTREAM_OPTIONS
///
public abstract FileSystemStream Open(string path, FileStreamOptions options);
#endif
///
public abstract FileSystemStream OpenRead(string path);
///
public abstract StreamReader OpenText(string path);
///
public abstract FileSystemStream OpenWrite(string path);
///
public abstract byte[] ReadAllBytes(string path);
///
public abstract string[] ReadAllLines(string path);
///
public abstract string[] ReadAllLines(string path, Encoding encoding);
///
public abstract string ReadAllText(string path);
///
public abstract string ReadAllText(string path, Encoding encoding);
///
public abstract IEnumerable ReadLines(string path);
///
public abstract IEnumerable ReadLines(string path, Encoding encoding);
///
public abstract void Replace(string sourceFileName, string destinationFileName, string destinationBackupFileName);
///
public abstract void Replace(string sourceFileName, string destinationFileName, string destinationBackupFileName, bool ignoreMetadataErrors);
#if FEATURE_CREATE_SYMBOLIC_LINK
///
public abstract IFileSystemInfo ResolveLinkTarget(string linkPath, bool returnFinalTarget);
#endif
///
public abstract void SetAttributes(string path, FileAttributes fileAttributes);
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public abstract void SetAttributes(SafeFileHandle fileHandle, FileAttributes fileAttributes);
#endif
///
public abstract void SetCreationTime(string path, DateTime creationTime);
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public abstract void SetCreationTime(SafeFileHandle fileHandle, DateTime creationTime);
#endif
///
public abstract void SetCreationTimeUtc(string path, DateTime creationTimeUtc);
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public abstract void SetCreationTimeUtc(SafeFileHandle fileHandle, DateTime creationTimeUtc);
#endif
///
public abstract void SetLastAccessTime(string path, DateTime lastAccessTime);
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public abstract void SetLastAccessTime(SafeFileHandle fileHandle, DateTime lastAccessTime);
#endif
///
public abstract void SetLastAccessTimeUtc(string path, DateTime lastAccessTimeUtc);
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public abstract void SetLastAccessTimeUtc(SafeFileHandle fileHandle, DateTime lastAccessTimeUtc);
#endif
///
public abstract void SetLastWriteTime(string path, DateTime lastWriteTime);
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public abstract void SetLastWriteTime(SafeFileHandle fileHandle, DateTime lastWriteTime);
#endif
///
public abstract void SetLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc);
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public abstract void SetLastWriteTimeUtc(SafeFileHandle fileHandle, DateTime lastWriteTimeUtc);
#endif
#if FEATURE_UNIX_FILE_MODE
///
public abstract void SetUnixFileMode(string path, UnixFileMode mode);
#endif
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public abstract void SetUnixFileMode(SafeFileHandle fileHandle, UnixFileMode mode);
#endif
///
public abstract void WriteAllBytes(string path, byte[] bytes);
#if FEATURE_FILE_SPAN
///
public abstract void WriteAllBytes(string path, ReadOnlySpan bytes);
#endif
///
public abstract void WriteAllLines(string path, IEnumerable contents);
///
public abstract void WriteAllLines(string path, IEnumerable contents, Encoding encoding);
///
public abstract void WriteAllLines(string path, string[] contents);
///
public abstract void WriteAllLines(string path, string[] contents, Encoding encoding);
///
public abstract void WriteAllText(string path, string contents);
///
public abstract void WriteAllText(string path, string contents, Encoding encoding);
#if FEATURE_FILE_SPAN
///
public abstract void WriteAllText(string path, ReadOnlySpan contents);
///
public abstract void WriteAllText(string path, ReadOnlySpan contents, Encoding encoding);
#endif
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.Wrappers/FileInfoAclExtensions.cs
================================================
using System.Runtime.Versioning;
using System.Security.AccessControl;
namespace System.IO.Abstractions;
///
/// ACL (access control list) extension methods for .
///
public static class FileInfoAclExtensions
{
#if FEATURE_FILE_SYSTEM_ACL_EXTENSIONS
///
#else
///
#endif
[SupportedOSPlatform("windows")]
public static FileSecurity GetAccessControl(
this IFileInfo fileInfo)
{
IFileSystemAclSupport aclSupport = fileInfo as IFileSystemAclSupport;
var fileSecurity = aclSupport?.GetAccessControl() as FileSecurity;
if (aclSupport == null || fileSecurity == null)
{
throw new NotSupportedException("The file info does not support ACL extensions");
}
return fileSecurity;
}
#if FEATURE_FILE_SYSTEM_ACL_EXTENSIONS
///
#else
///
#endif
[SupportedOSPlatform("windows")]
public static FileSecurity GetAccessControl(
this IFileInfo fileInfo,
AccessControlSections includeSections)
{
IFileSystemAclSupport aclSupport = fileInfo as IFileSystemAclSupport;
var fileSecurity = aclSupport?.GetAccessControl((IFileSystemAclSupport.AccessControlSections)includeSections) as FileSecurity;
if (aclSupport == null || fileSecurity == null)
{
throw new NotSupportedException("The file info does not support ACL extensions");
}
return fileSecurity;
}
#if FEATURE_FILE_SYSTEM_ACL_EXTENSIONS
///
#else
///
#endif
[SupportedOSPlatform("windows")]
public static void SetAccessControl(this IFileInfo fileInfo,
FileSecurity fileSecurity)
{
IFileSystemAclSupport aclSupport = fileInfo as IFileSystemAclSupport;
if (aclSupport == null)
{
throw new NotSupportedException("The file info does not support ACL extensions");
}
aclSupport.SetAccessControl(fileSecurity);
}
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.Wrappers/FileInfoBase.cs
================================================
namespace System.IO.Abstractions;
///
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public abstract class FileInfoBase : FileSystemInfoBase, IFileInfo
{
///
/// Base class for calling methods of
///
protected FileInfoBase(IFileSystem fileSystem) : base(fileSystem)
{
}
[Obsolete("This constructor only exists to support mocking libraries.", error: true)]
internal FileInfoBase() { }
///
public abstract StreamWriter AppendText();
///
public abstract IFileInfo CopyTo(string destFileName);
///
public abstract IFileInfo CopyTo(string destFileName, bool overwrite);
///
public abstract FileSystemStream Create();
///
public abstract StreamWriter CreateText();
///
public abstract void Decrypt();
///
public abstract void Encrypt();
///
public abstract void MoveTo(string destFileName);
#if FEATURE_FILE_MOVE_WITH_OVERWRITE
///
public abstract void MoveTo(string destFileName, bool overwrite);
#endif
///
public abstract FileSystemStream Open(FileMode mode);
///
public abstract FileSystemStream Open(FileMode mode, FileAccess access);
///
public abstract FileSystemStream Open(FileMode mode, FileAccess access, FileShare share);
#if FEATURE_FILESTREAM_OPTIONS
///
public abstract FileSystemStream Open(FileStreamOptions options);
#endif
///
public abstract FileSystemStream OpenRead();
///
public abstract StreamReader OpenText();
///
public abstract FileSystemStream OpenWrite();
///
public abstract IFileInfo Replace(string destinationFileName, string destinationBackupFileName);
///
public abstract IFileInfo Replace(string destinationFileName, string destinationBackupFileName, bool ignoreMetadataErrors);
///
public abstract IDirectoryInfo Directory { get; }
///
public abstract string DirectoryName { get; }
///
public abstract bool IsReadOnly { get; set; }
///
public abstract long Length { get; }
///
/// Implicitly converts a to a .
///
public static implicit operator FileInfoBase(FileInfo fileInfo)
{
if (fileInfo == null)
{
return null;
}
return new FileInfoWrapper(new FileSystem(), fileInfo);
}
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.Wrappers/FileInfoFactory.cs
================================================
namespace System.IO.Abstractions;
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
internal class FileInfoFactory : IFileInfoFactory
{
private readonly IFileSystem fileSystem;
///
/// Base factory class for creating a
///
public FileInfoFactory(IFileSystem fileSystem)
{
this.fileSystem = fileSystem;
}
///
public IFileSystem FileSystem
=> fileSystem;
///
public IFileInfo New(string fileName)
{
var realFileInfo = new FileInfo(fileName);
return new FileInfoWrapper(fileSystem, realFileInfo);
}
///
public IFileInfo Wrap(FileInfo fileInfo)
{
if (fileInfo == null)
{
return null;
}
return new FileInfoWrapper(fileSystem, fileInfo);
}
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.Wrappers/FileInfoWrapper.cs
================================================
using System.Runtime.Versioning;
using System.Security.AccessControl;
namespace System.IO.Abstractions;
///
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public class FileInfoWrapper : FileInfoBase, IFileSystemAclSupport
{
private readonly FileInfo instance;
///
/// Wrapper class for calling methods of
///
public FileInfoWrapper(IFileSystem fileSystem, FileInfo instance) : base(fileSystem)
{
this.instance = instance ?? throw new ArgumentNullException(nameof(instance));
}
#if FEATURE_CREATE_SYMBOLIC_LINK
///
public override void CreateAsSymbolicLink(string pathToTarget)
{
instance.CreateAsSymbolicLink(pathToTarget);
}
#endif
///
public override void Delete()
{
instance.Delete();
}
///
public override void Refresh()
{
instance.Refresh();
}
#if FEATURE_CREATE_SYMBOLIC_LINK
///
public override IFileSystemInfo ResolveLinkTarget(bool returnFinalTarget)
{
return instance.ResolveLinkTarget(returnFinalTarget)
.WrapFileSystemInfo(FileSystem);
}
#endif
///
public override FileAttributes Attributes
{
get { return instance.Attributes; }
set { instance.Attributes = value; }
}
///
public override DateTime CreationTime
{
get { return instance.CreationTime; }
set { instance.CreationTime = value; }
}
///
public override DateTime CreationTimeUtc
{
get { return instance.CreationTimeUtc; }
set { instance.CreationTimeUtc = value; }
}
///
public override bool Exists
{
get { return instance.Exists; }
}
///
public override string Extension
{
get { return instance.Extension; }
}
///
public override string FullName
{
get { return instance.FullName; }
}
///
public override DateTime LastAccessTime
{
get { return instance.LastAccessTime; }
set { instance.LastAccessTime = value; }
}
///
public override DateTime LastAccessTimeUtc
{
get { return instance.LastAccessTimeUtc; }
set { instance.LastAccessTimeUtc = value; }
}
///
public override DateTime LastWriteTime
{
get { return instance.LastWriteTime; }
set { instance.LastWriteTime = value; }
}
///
public override DateTime LastWriteTimeUtc
{
get { return instance.LastWriteTimeUtc; }
set { instance.LastWriteTimeUtc = value; }
}
#if FEATURE_FILE_SYSTEM_INFO_LINK_TARGET
///
public override string LinkTarget
{
get { return instance.LinkTarget; }
}
#endif
///
public override string Name
{
get { return instance.Name; }
}
///
public override StreamWriter AppendText()
{
return instance.AppendText();
}
///
public override IFileInfo CopyTo(string destFileName)
{
return new FileInfoWrapper(FileSystem, instance.CopyTo(destFileName));
}
///
public override IFileInfo CopyTo(string destFileName, bool overwrite)
{
return new FileInfoWrapper(FileSystem, instance.CopyTo(destFileName, overwrite));
}
///
public override FileSystemStream Create()
{
return new FileStreamWrapper(instance.Create());
}
///
public override StreamWriter CreateText()
{
return instance.CreateText();
}
///
[SupportedOSPlatform("windows")]
public override void Decrypt()
{
instance.Decrypt();
}
///
[SupportedOSPlatform("windows")]
public override void Encrypt()
{
instance.Encrypt();
}
///
public override void MoveTo(string destFileName)
{
instance.MoveTo(destFileName);
}
#if FEATURE_FILE_MOVE_WITH_OVERWRITE
///
public override void MoveTo(string destFileName, bool overwrite)
{
instance.MoveTo(destFileName, overwrite);
}
#endif
///
public override FileSystemStream Open(FileMode mode)
{
return new FileStreamWrapper(instance.Open(mode));
}
///
public override FileSystemStream Open(FileMode mode, FileAccess access)
{
return new FileStreamWrapper(instance.Open(mode, access));
}
///
public override FileSystemStream Open(FileMode mode, FileAccess access, FileShare share)
{
return new FileStreamWrapper(instance.Open(mode, access, share));
}
#if FEATURE_FILESTREAM_OPTIONS
///
public override FileSystemStream Open(FileStreamOptions options)
{
return new FileStreamWrapper(instance.Open(options));
}
#endif
///
public override FileSystemStream OpenRead()
{
return new FileStreamWrapper(instance.OpenRead());
}
///
public override StreamReader OpenText()
{
return instance.OpenText();
}
///
public override FileSystemStream OpenWrite()
{
return new FileStreamWrapper(instance.OpenWrite());
}
///
public override IFileInfo Replace(string destinationFileName, string destinationBackupFileName)
{
return new FileInfoWrapper(FileSystem, instance.Replace(destinationFileName, destinationBackupFileName));
}
///
public override IFileInfo Replace(string destinationFileName, string destinationBackupFileName, bool ignoreMetadataErrors)
{
return new FileInfoWrapper(FileSystem, instance.Replace(destinationFileName, destinationBackupFileName, ignoreMetadataErrors));
}
///
public override IDirectoryInfo Directory
{
get { return new DirectoryInfoWrapper(FileSystem, instance.Directory); }
}
///
public override string DirectoryName
{
get { return instance.DirectoryName; }
}
///
public override bool IsReadOnly
{
get { return instance.IsReadOnly; }
set { instance.IsReadOnly = value; }
}
///
public override long Length
{
get { return instance.Length; }
}
///
public override string ToString()
{
return instance.ToString();
}
///
[SupportedOSPlatform("windows")]
public object GetAccessControl()
{
return instance.GetAccessControl();
}
///
[SupportedOSPlatform("windows")]
public object GetAccessControl(IFileSystemAclSupport.AccessControlSections includeSections)
{
return instance.GetAccessControl((AccessControlSections)includeSections);
}
///
[SupportedOSPlatform("windows")]
public void SetAccessControl(object value)
{
if (value is FileSecurity fileSecurity)
{
this.instance.SetAccessControl(fileSecurity);
}
else
{
throw new ArgumentException("value must be of type `FileSecurity`");
}
}
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.Wrappers/FileStreamAclExtensions.cs
================================================
using System.Runtime.Versioning;
using System.Security.AccessControl;
namespace System.IO.Abstractions;
///
/// ACL (access control list) extension methods for .
///
public static class FileStreamAclExtensions
{
#if FEATURE_FILE_SYSTEM_ACL_EXTENSIONS
///
#else
///
#endif
[SupportedOSPlatform("windows")]
public static FileSecurity GetAccessControl(this FileSystemStream fileStream)
{
IFileSystemAclSupport aclSupport = fileStream as IFileSystemAclSupport;
var fileSecurity = aclSupport?.GetAccessControl() as FileSecurity;
if (aclSupport == null || fileSecurity == null)
{
throw new NotSupportedException("The file stream does not support ACL extensions");
}
return fileSecurity;
}
#if FEATURE_FILE_SYSTEM_ACL_EXTENSIONS
///
#else
///
#endif
[SupportedOSPlatform("windows")]
public static void SetAccessControl(this FileSystemStream fileStream,
FileSecurity fileSecurity)
{
IFileSystemAclSupport aclSupport = fileStream as IFileSystemAclSupport;
if (aclSupport == null)
{
throw new NotSupportedException("The file info does not support ACL extensions");
}
aclSupport.SetAccessControl(fileSecurity);
}
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.Wrappers/FileStreamFactory.cs
================================================
using Microsoft.Win32.SafeHandles;
namespace System.IO.Abstractions;
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
internal sealed class FileStreamFactory : IFileStreamFactory
{
///
/// Base factory class for creating a
///
public FileStreamFactory(IFileSystem fileSystem)
{
FileSystem = fileSystem;
}
///
public IFileSystem FileSystem { get; }
///
public FileSystemStream New(SafeFileHandle handle, FileAccess access)
=> new FileStreamWrapper(new FileStream(handle, access));
///
public FileSystemStream New(SafeFileHandle handle, FileAccess access, int bufferSize)
=> new FileStreamWrapper(new FileStream(handle, access, bufferSize));
///
public FileSystemStream New(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync)
=> new FileStreamWrapper(new FileStream(handle, access, bufferSize, isAsync));
///
public FileSystemStream New(string path, FileMode mode)
=> new FileStreamWrapper(new FileStream(path, mode));
///
public FileSystemStream New(string path, FileMode mode, FileAccess access)
=> new FileStreamWrapper(new FileStream(path, mode, access));
///
public FileSystemStream New(string path, FileMode mode, FileAccess access, FileShare share)
=> new FileStreamWrapper(new FileStream(path, mode, access, share));
///
public FileSystemStream New(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize)
=> new FileStreamWrapper(new FileStream(path, mode, access, share, bufferSize));
///
public FileSystemStream New(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool useAsync)
=> new FileStreamWrapper(new FileStream(path, mode, access, share, bufferSize, useAsync));
///
public FileSystemStream New(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize,
FileOptions options)
=> new FileStreamWrapper(new FileStream(path, mode, access, share, bufferSize, options));
#if FEATURE_FILESTREAM_OPTIONS
///
public FileSystemStream New(string path, FileStreamOptions options)
=> new FileStreamWrapper(new FileStream(path, options));
#endif
///
public FileSystemStream Wrap(FileStream fileStream)
=> new FileStreamWrapper(fileStream);
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.Wrappers/FileStreamWrapper.cs
================================================
using System.Runtime.Versioning;
using System.Security.AccessControl;
namespace System.IO.Abstractions;
internal sealed class FileStreamWrapper : FileSystemStream, IFileSystemAclSupport
{
private readonly FileStream fileStream;
public FileStreamWrapper(FileStream fileStream)
: base(fileStream, fileStream.Name, fileStream.IsAsync)
{
this.fileStream = fileStream;
}
///
[SupportedOSPlatform("windows")]
public object GetAccessControl()
{
return fileStream.GetAccessControl();
}
///
[SupportedOSPlatform("windows")]
public object GetAccessControl(IFileSystemAclSupport.AccessControlSections includeSections)
{
throw new NotSupportedException("GetAccessControl with includeSections is not supported for FileStreams");
}
///
[SupportedOSPlatform("windows")]
public void SetAccessControl(object value)
{
if (value is FileSecurity fileSecurity)
{
this.fileStream.SetAccessControl(fileSecurity);
}
else
{
throw new ArgumentException("value must be of type `FileSecurity`");
}
}
///
public override void Flush(bool flushToDisk)
=> fileStream.Flush(flushToDisk);
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.Wrappers/FileSystem.cs
================================================
namespace System.IO.Abstractions;
///
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public class FileSystem : FileSystemBase
{
///
public FileSystem()
{
DriveInfo = new DriveInfoFactory(this);
DirectoryInfo = new DirectoryInfoFactory(this);
FileInfo = new FileInfoFactory(this);
FileVersionInfo = new FileVersionInfoFactory(this);
Path = new PathWrapper(this);
File = new FileWrapper(this);
Directory = new DirectoryWrapper(this);
FileStream = new FileStreamFactory(this);
FileSystemWatcher = new FileSystemWatcherFactory(this);
}
///
public override IDirectory Directory { get; }
///
public override IFile File { get; }
///
public override IFileInfoFactory FileInfo { get; }
///
public override IFileVersionInfoFactory FileVersionInfo { get; }
///
public override IFileStreamFactory FileStream { get; }
///
public override IPath Path { get; }
///
public override IDirectoryInfoFactory DirectoryInfo { get; }
///
public override IDriveInfoFactory DriveInfo { get; }
///
public override IFileSystemWatcherFactory FileSystemWatcher { get; }
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.Wrappers/FileSystemBase.cs
================================================
namespace System.IO.Abstractions;
///
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public abstract class FileSystemBase : IFileSystem
{
///
public abstract IDirectory Directory { get; }
///
public abstract IFile File { get; }
///
public abstract IFileInfoFactory FileInfo { get; }
///
public abstract IFileVersionInfoFactory FileVersionInfo { get; }
///
public abstract IFileStreamFactory FileStream { get; }
///
public abstract IPath Path { get; }
///
public abstract IDirectoryInfoFactory DirectoryInfo { get; }
///
public abstract IDriveInfoFactory DriveInfo { get; }
///
public abstract IFileSystemWatcherFactory FileSystemWatcher { get; }
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.Wrappers/FileSystemInfoBase.cs
================================================
using System.Runtime.Versioning;
namespace System.IO.Abstractions;
///
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public abstract class FileSystemInfoBase : IFileSystemInfo
{
///
/// Base class for calling methods of
///
protected FileSystemInfoBase(IFileSystem fileSystem)
{
FileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem));
}
[Obsolete("This constructor only exists to support mocking libraries.", error: true)]
internal FileSystemInfoBase() { }
///
/// Exposes the underlying filesystem implementation. This is useful for implementing extension methods.
///
public IFileSystem FileSystem { get; }
#if FEATURE_CREATE_SYMBOLIC_LINK
///
public abstract void CreateAsSymbolicLink(string pathToTarget);
#endif
///
public abstract void Delete();
///
public abstract void Refresh();
#if FEATURE_CREATE_SYMBOLIC_LINK
///
public abstract IFileSystemInfo ResolveLinkTarget(bool returnFinalTarget);
#endif
///
public abstract FileAttributes Attributes { get; set; }
///
public abstract DateTime CreationTime { get; set; }
///
public abstract DateTime CreationTimeUtc { get; set; }
///
public abstract bool Exists { get; }
///
public abstract string Extension { get; }
///
public abstract string FullName { get; }
///
public abstract DateTime LastAccessTime { get; set; }
///
public abstract DateTime LastAccessTimeUtc { get; set; }
///
public abstract DateTime LastWriteTime { get; set; }
///
public abstract DateTime LastWriteTimeUtc { get; set; }
#if FEATURE_FILE_SYSTEM_INFO_LINK_TARGET
///
public abstract string LinkTarget { get; }
#endif
///
public abstract string Name { get; }
#if FEATURE_UNIX_FILE_MODE
///
public UnixFileMode UnixFileMode
{
get;
[UnsupportedOSPlatform("windows")] set;
}
#endif
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.Wrappers/FileSystemWatcherBase.cs
================================================
using System.ComponentModel;
namespace System.IO.Abstractions;
///
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public abstract class FileSystemWatcherBase : IFileSystemWatcher
{
///
public abstract IFileSystem FileSystem { get; }
///
public abstract bool IncludeSubdirectories { get; set; }
///
public abstract IContainer Container { get; }
///
public abstract bool EnableRaisingEvents { get; set; }
///
public abstract string Filter { get; set; }
#if FEATURE_FILE_SYSTEM_WATCHER_FILTERS
///
public abstract Collections.ObjectModel.Collection Filters { get; }
#endif
///
public abstract int InternalBufferSize { get; set; }
///
public abstract NotifyFilters NotifyFilter { get; set; }
///
public abstract string Path { get; set; }
///
public abstract ISite Site { get; set; }
///
public abstract ISynchronizeInvoke SynchronizingObject { get; set; }
///
public virtual event FileSystemEventHandler Changed;
///
public virtual event FileSystemEventHandler Created;
///
public virtual event FileSystemEventHandler Deleted;
///
public virtual event ErrorEventHandler Error;
///
public virtual event RenamedEventHandler Renamed;
///
public abstract void BeginInit();
///
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
///
public abstract void EndInit();
///
public abstract IWaitForChangedResult WaitForChanged(WatcherChangeTypes changeType);
///
public abstract IWaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, int timeout);
#if FEATURE_FILE_SYSTEM_WATCHER_WAIT_WITH_TIMESPAN
///
public abstract IWaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, TimeSpan timeout);
#endif
///
/// Implicitly converts a to a .
///
public static implicit operator FileSystemWatcherBase(FileSystemWatcher watcher)
{
if (watcher == null)
{
return null;
}
return new FileSystemWatcherWrapper(new FileSystem(), watcher);
}
///
/// Callback executed during
///
public virtual void Dispose(bool disposing)
{
// do nothing
}
///
/// Invokes the event.
///
protected void OnCreated(object sender, FileSystemEventArgs args)
{
Created?.Invoke(sender, args);
}
///
/// Invokes the event.
///
protected void OnChanged(object sender, FileSystemEventArgs args)
{
Changed?.Invoke(sender, args);
}
///
/// Invokes the event.
///
protected void OnDeleted(object sender, FileSystemEventArgs args)
{
Deleted?.Invoke(sender, args);
}
///
/// Invokes the event.
///
protected void OnRenamed(object sender, RenamedEventArgs args)
{
Renamed?.Invoke(sender, args);
}
///
/// Invokes the event.
///
protected void OnError(object sender, ErrorEventArgs args)
{
Error?.Invoke(sender, args);
}
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.Wrappers/FileSystemWatcherFactory.cs
================================================
namespace System.IO.Abstractions;
///
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public class FileSystemWatcherFactory : IFileSystemWatcherFactory
{
///
/// Base factory class for creating a
///
public FileSystemWatcherFactory(IFileSystem fileSystem)
{
FileSystem = fileSystem;
}
///
public IFileSystem FileSystem { get; }
///
public IFileSystemWatcher New()
=> new FileSystemWatcherWrapper(FileSystem);
///
public IFileSystemWatcher New(string path)
=> new FileSystemWatcherWrapper(FileSystem, path);
///
public IFileSystemWatcher New(string path, string filter)
=> new FileSystemWatcherWrapper(FileSystem, path, filter);
///
public IFileSystemWatcher Wrap(FileSystemWatcher fileSystemWatcher)
{
if (fileSystemWatcher == null)
{
return null;
}
return new FileSystemWatcherWrapper(FileSystem, fileSystemWatcher);
}
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.Wrappers/FileSystemWatcherWrapper.cs
================================================
using System.ComponentModel;
namespace System.IO.Abstractions;
///
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public class FileSystemWatcherWrapper : FileSystemWatcherBase
{
#if FEATURE_SERIALIZABLE
[NonSerialized]
#endif
private readonly FileSystemWatcher watcher;
///
public FileSystemWatcherWrapper(IFileSystem fileSystem)
: this(fileSystem, new FileSystemWatcher())
{
// do nothing
}
///
public FileSystemWatcherWrapper(IFileSystem fileSystem, string path)
: this(fileSystem, new FileSystemWatcher(path))
{
// do nothing
}
///
public FileSystemWatcherWrapper(IFileSystem fileSystem, string path, string filter)
: this(fileSystem, new FileSystemWatcher(path, filter))
{
// do nothing
}
///
public FileSystemWatcherWrapper(IFileSystem fileSystem, FileSystemWatcher watcher)
{
FileSystem = fileSystem;
this.watcher = watcher ?? throw new ArgumentNullException(nameof(watcher));
this.watcher.Created += OnCreated;
this.watcher.Changed += OnChanged;
this.watcher.Deleted += OnDeleted;
this.watcher.Error += OnError;
this.watcher.Renamed += OnRenamed;
}
///
public override IFileSystem FileSystem { get; }
///
public override bool IncludeSubdirectories
{
get { return watcher.IncludeSubdirectories; }
set { watcher.IncludeSubdirectories = value; }
}
///
public override IContainer Container
=> watcher.Container;
///
public override bool EnableRaisingEvents
{
get { return watcher.EnableRaisingEvents; }
set { watcher.EnableRaisingEvents = value; }
}
///
public override string Filter
{
get { return watcher.Filter; }
set { watcher.Filter = value; }
}
#if FEATURE_FILE_SYSTEM_WATCHER_FILTERS
///
public override Collections.ObjectModel.Collection Filters
{
get { return watcher.Filters; }
}
#endif
///
public override int InternalBufferSize
{
get { return watcher.InternalBufferSize; }
set { watcher.InternalBufferSize = value; }
}
///
public override NotifyFilters NotifyFilter
{
get { return watcher.NotifyFilter; }
set { watcher.NotifyFilter = value; }
}
///
public override string Path
{
get { return watcher.Path; }
set { watcher.Path = value; }
}
///
public override ISite Site
{
get { return watcher.Site; }
set { watcher.Site = value; }
}
///
public override ISynchronizeInvoke SynchronizingObject
{
get { return watcher.SynchronizingObject; }
set { watcher.SynchronizingObject = value; }
}
///
public override void BeginInit()
{
watcher.BeginInit();
}
///
public override void Dispose(bool disposing)
{
if (disposing)
{
watcher.Created -= OnCreated;
watcher.Changed -= OnChanged;
watcher.Deleted -= OnDeleted;
watcher.Error -= OnError;
watcher.Renamed -= OnRenamed;
watcher.Dispose();
}
base.Dispose(disposing);
}
///
public override void EndInit()
{
watcher.EndInit();
}
///
public override IWaitForChangedResult WaitForChanged(WatcherChangeTypes changeType)
{
return new WaitForChangedResultWrapper(watcher.WaitForChanged(changeType));
}
///
public override IWaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, int timeout)
{
return new WaitForChangedResultWrapper(watcher.WaitForChanged(changeType, timeout));
}
#if FEATURE_FILE_SYSTEM_WATCHER_WAIT_WITH_TIMESPAN
///
public override IWaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, TimeSpan timeout)
{
return new WaitForChangedResultWrapper(watcher.WaitForChanged(changeType, timeout));
}
#endif
private readonly struct WaitForChangedResultWrapper
: IWaitForChangedResult, IEquatable
{
private readonly WaitForChangedResult instance;
public WaitForChangedResultWrapper(WaitForChangedResult instance)
{
this.instance = instance;
}
///
public WatcherChangeTypes ChangeType
=> instance.ChangeType;
///
public string Name
=> instance.Name;
///
public string OldName
=> instance.OldName;
///
public bool TimedOut
=> instance.TimedOut;
///
public bool Equals(WaitForChangedResultWrapper other)
{
return instance.Equals(other.instance);
}
///
public override bool Equals(object obj)
{
return obj is WaitForChangedResultWrapper other
&& Equals(other);
}
///
public override int GetHashCode()
{
return instance.GetHashCode();
}
}
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.Wrappers/FileVersionInfoBase.cs
================================================
using System.Diagnostics;
namespace System.IO.Abstractions;
///
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public abstract class FileVersionInfoBase : IFileVersionInfo
{
///
public abstract string Comments { get; }
///
public abstract string CompanyName { get; }
///
public abstract int FileBuildPart { get; }
///
public abstract string FileDescription { get; }
///
public abstract int FileMajorPart { get; }
///
public abstract int FileMinorPart { get; }
///
public abstract string FileName { get; }
///
public abstract int FilePrivatePart { get; }
///
public abstract string FileVersion { get; }
///
public abstract string InternalName { get; }
///
public abstract bool IsDebug { get; }
///
public abstract bool IsPatched { get; }
///
public abstract bool IsPrivateBuild { get; }
///
public abstract bool IsPreRelease { get; }
///
public abstract bool IsSpecialBuild { get; }
///
public abstract string Language { get; }
///
public abstract string LegalCopyright { get; }
///
public abstract string LegalTrademarks { get; }
///
public abstract string OriginalFilename { get; }
///
public abstract string PrivateBuild { get; }
///
public abstract int ProductBuildPart { get; }
///
public abstract int ProductMajorPart { get; }
///
public abstract int ProductMinorPart { get; }
///
public abstract string ProductName { get; }
///
public abstract int ProductPrivatePart { get; }
///
public abstract string ProductVersion { get; }
///
public abstract string SpecialBuild { get; }
///
/// Implicitly converts a to a .
///
public static implicit operator FileVersionInfoBase(FileVersionInfo fileVersionInfo)
{
if (fileVersionInfo == null)
{
return null;
}
return new FileVersionInfoWrapper(fileVersionInfo);
}
///
public new abstract string ToString();
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.Wrappers/FileVersionInfoFactory.cs
================================================
namespace System.IO.Abstractions;
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
internal class FileVersionInfoFactory : IFileVersionInfoFactory
{
private readonly IFileSystem fileSystem;
///
/// Base factory class for creating a
///
public FileVersionInfoFactory(IFileSystem fileSystem)
{
this.fileSystem = fileSystem;
}
///
public IFileSystem FileSystem => fileSystem;
///
public IFileVersionInfo GetVersionInfo(string fileName)
{
Diagnostics.FileVersionInfo fileVersionInfo = Diagnostics.FileVersionInfo.GetVersionInfo(fileName);
return new FileVersionInfoWrapper(fileVersionInfo);
}
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.Wrappers/FileVersionInfoWrapper.cs
================================================
using System.Diagnostics;
namespace System.IO.Abstractions;
///
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public class FileVersionInfoWrapper : FileVersionInfoBase
{
private readonly FileVersionInfo instance;
///
public FileVersionInfoWrapper(FileVersionInfo fileVersionInfo)
{
instance = fileVersionInfo;
}
///
public override string Comments
{
get { return instance.Comments; }
}
///
public override string CompanyName
{
get { return instance.CompanyName; }
}
///
public override int FileBuildPart
{
get { return instance.FileBuildPart; }
}
///
public override string FileDescription
{
get { return instance.FileDescription; }
}
///
public override int FileMajorPart
{
get { return instance.FileMajorPart; }
}
///
public override int FileMinorPart
{
get { return instance.FileMinorPart; }
}
///
public override string FileName
{
get { return instance.FileName; }
}
///
public override int FilePrivatePart
{
get { return instance.FilePrivatePart; }
}
///
public override string FileVersion
{
get { return instance.FileVersion; }
}
///
public override string InternalName
{
get { return instance.InternalName; }
}
///
public override bool IsDebug
{
get { return instance.IsDebug; }
}
///
public override bool IsPatched
{
get { return instance.IsPatched; }
}
///
public override bool IsPrivateBuild
{
get { return instance.IsPrivateBuild; }
}
///
public override bool IsPreRelease
{
get { return instance.IsPreRelease; }
}
///
public override bool IsSpecialBuild
{
get { return instance.IsSpecialBuild; }
}
///
public override string Language
{
get { return instance.Language; }
}
///
public override string LegalCopyright
{
get { return instance.LegalCopyright; }
}
///
public override string LegalTrademarks
{
get { return instance.LegalTrademarks; }
}
///
public override string OriginalFilename
{
get { return instance.OriginalFilename; }
}
///
public override string PrivateBuild
{
get { return instance.PrivateBuild; }
}
///
public override int ProductBuildPart
{
get { return instance.ProductBuildPart; }
}
///
public override int ProductMajorPart
{
get { return instance.ProductMajorPart; }
}
///
public override int ProductMinorPart
{
get { return instance.ProductMinorPart; }
}
///
public override string ProductName
{
get { return instance.ProductName; }
}
///
public override int ProductPrivatePart
{
get { return instance.ProductPrivatePart; }
}
///
public override string ProductVersion
{
get { return instance.ProductVersion; }
}
///
public override string SpecialBuild
{
get { return instance.SpecialBuild; }
}
///
public override string ToString()
{
return instance.ToString();
}
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.Wrappers/FileWrapper.Async.cs
================================================
#if FEATURE_ASYNC_FILE
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace System.IO.Abstractions
{
partial class FileWrapper
{
#if FEATURE_FILE_SPAN
///
public override Task AppendAllBytesAsync(string path, byte[] bytes, CancellationToken cancellationToken = default)
{
return File.AppendAllBytesAsync(path, bytes, cancellationToken);
}
///
public override Task AppendAllBytesAsync(string path, ReadOnlyMemory bytes, CancellationToken cancellationToken = default)
{
return File.AppendAllBytesAsync(path, bytes, cancellationToken);
}
#endif
///
public override Task AppendAllLinesAsync(string path, IEnumerable contents, CancellationToken cancellationToken = default)
{
return File.AppendAllLinesAsync(path, contents, cancellationToken);
}
///
public override Task AppendAllLinesAsync(string path, IEnumerable contents, Encoding encoding, CancellationToken cancellationToken = default)
{
return File.AppendAllLinesAsync(path, contents, encoding, cancellationToken);
}
///
public override Task AppendAllTextAsync(string path, string contents, CancellationToken cancellationToken = default)
{
return File.AppendAllTextAsync(path, contents, cancellationToken);
}
///
public override Task AppendAllTextAsync(string path, string contents, Encoding encoding, CancellationToken cancellationToken = default)
{
return File.AppendAllTextAsync(path, contents, encoding, cancellationToken);
}
#if FEATURE_FILE_SPAN
///
public override Task AppendAllTextAsync(string path, ReadOnlyMemory contents, CancellationToken cancellationToken = default)
{
return File.AppendAllTextAsync(path, contents, cancellationToken);
}
///
public override Task AppendAllTextAsync(string path, ReadOnlyMemory contents, Encoding encoding,
CancellationToken cancellationToken = default)
{
return File.AppendAllTextAsync(path, contents, encoding, cancellationToken);
}
#endif
///
public override Task ReadAllBytesAsync(string path, CancellationToken cancellationToken = default)
{
return File.ReadAllBytesAsync(path, cancellationToken);
}
///
public override Task ReadAllLinesAsync(string path, CancellationToken cancellationToken = default)
{
return File.ReadAllLinesAsync(path, cancellationToken);
}
///
public override Task ReadAllLinesAsync(string path, Encoding encoding, CancellationToken cancellationToken = default)
{
return File.ReadAllLinesAsync(path, encoding, cancellationToken);
}
///
public override Task ReadAllTextAsync(string path, CancellationToken cancellationToken = default)
{
return File.ReadAllTextAsync(path, cancellationToken);
}
///
public override Task ReadAllTextAsync(string path, Encoding encoding, CancellationToken cancellationToken = default)
{
return File.ReadAllTextAsync(path, encoding, cancellationToken);
}
#if FEATURE_READ_LINES_ASYNC
///
public override IAsyncEnumerable ReadLinesAsync(string path,
CancellationToken cancellationToken = default)
=> File.ReadLinesAsync(path, cancellationToken);
///
public override IAsyncEnumerable ReadLinesAsync(string path, Encoding encoding,
CancellationToken cancellationToken = default)
=> File.ReadLinesAsync(path, encoding, cancellationToken);
#endif
///
public override Task WriteAllBytesAsync(string path, byte[] bytes, CancellationToken cancellationToken = default)
{
return File.WriteAllBytesAsync(path, bytes, cancellationToken);
}
#if FEATURE_FILE_SPAN
///
public override Task WriteAllBytesAsync(string path, ReadOnlyMemory bytes, CancellationToken cancellationToken = default)
{
return File.WriteAllBytesAsync(path, bytes, cancellationToken);
}
#endif
///
public override Task WriteAllLinesAsync(string path, IEnumerable contents, CancellationToken cancellationToken = default)
{
return File.WriteAllLinesAsync(path, contents, cancellationToken);
}
///
public override Task WriteAllLinesAsync(string path, IEnumerable contents, Encoding encoding, CancellationToken cancellationToken = default)
{
return File.WriteAllLinesAsync(path, contents, encoding, cancellationToken);
}
///
public override Task WriteAllTextAsync(string path, string contents, CancellationToken cancellationToken = default)
{
return File.WriteAllTextAsync(path, contents, cancellationToken);
}
///
public override Task WriteAllTextAsync(string path, string contents, Encoding encoding, CancellationToken cancellationToken = default)
{
return File.WriteAllTextAsync(path, contents, encoding, cancellationToken);
}
#if FEATURE_FILE_SPAN
///
public override Task WriteAllTextAsync(string path, ReadOnlyMemory contents, CancellationToken cancellationToken = default)
{
return File.WriteAllTextAsync(path, contents, cancellationToken);
}
///
public override Task WriteAllTextAsync(string path, ReadOnlyMemory contents, Encoding encoding,
CancellationToken cancellationToken = default)
{
return File.WriteAllTextAsync(path, contents, encoding, cancellationToken);
}
#endif
}
}
#endif
================================================
FILE: src/TestableIO.System.IO.Abstractions.Wrappers/FileWrapper.cs
================================================
using System.Collections.Generic;
using System.Runtime.Versioning;
using System.Text;
using Microsoft.Win32.SafeHandles;
namespace System.IO.Abstractions;
///
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public partial class FileWrapper : FileBase
{
///
public FileWrapper(IFileSystem fileSystem) : base(fileSystem)
{
}
#if FEATURE_FILE_SPAN
///
public override void AppendAllBytes(string path, byte[] bytes)
{
File.AppendAllBytes(path, bytes);
}
///
public override void AppendAllBytes(string path, ReadOnlySpan bytes)
{
File.AppendAllBytes(path, bytes);
}
#endif
///
public override void AppendAllLines(string path, IEnumerable contents)
{
File.AppendAllLines(path, contents);
}
///
public override void AppendAllLines(string path, IEnumerable contents, Encoding encoding)
{
File.AppendAllLines(path, contents, encoding);
}
///
public override void AppendAllText(string path, string contents)
{
File.AppendAllText(path, contents);
}
///
public override void AppendAllText(string path, string contents, Encoding encoding)
{
File.AppendAllText(path, contents, encoding);
}
#if FEATURE_FILE_SPAN
///
public override void AppendAllText(string path, ReadOnlySpan contents)
{
File.AppendAllText(path, contents);
}
///
public override void AppendAllText(string path, ReadOnlySpan contents, Encoding encoding)
{
File.AppendAllText(path, contents, encoding);
}
#endif
///
public override StreamWriter AppendText(string path)
{
return File.AppendText(path);
}
///
public override void Copy(string sourceFileName, string destFileName)
{
File.Copy(sourceFileName, destFileName);
}
///
public override void Copy(string sourceFileName, string destFileName, bool overwrite)
{
File.Copy(sourceFileName, destFileName, overwrite);
}
///
public override FileSystemStream Create(string path)
{
return new FileStreamWrapper(File.Create(path));
}
///
public override FileSystemStream Create(string path, int bufferSize)
{
return new FileStreamWrapper(File.Create(path, bufferSize));
}
///
public override FileSystemStream Create(string path, int bufferSize, FileOptions options)
{
return new FileStreamWrapper(File.Create(path, bufferSize, options));
}
#if FEATURE_CREATE_SYMBOLIC_LINK
///
public override IFileSystemInfo CreateSymbolicLink(string path, string pathToTarget)
{
return File.CreateSymbolicLink(path, pathToTarget)
.WrapFileSystemInfo(FileSystem);
}
#endif
///
public override StreamWriter CreateText(string path)
{
return File.CreateText(path);
}
///
[SupportedOSPlatform("windows")]
public override void Decrypt(string path)
{
File.Decrypt(path);
}
///
public override void Delete(string path)
{
File.Delete(path);
}
///
[SupportedOSPlatform("windows")]
public override void Encrypt(string path)
{
File.Encrypt(path);
}
///
public override bool Exists(string path)
{
return File.Exists(path);
}
///
public override FileAttributes GetAttributes(string path)
{
return File.GetAttributes(path);
}
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public override FileAttributes GetAttributes(SafeFileHandle fileHandle)
{
return File.GetAttributes(fileHandle);
}
#endif
///
public override DateTime GetCreationTime(string path)
{
return File.GetCreationTime(path);
}
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public override DateTime GetCreationTime(SafeFileHandle fileHandle)
{
return File.GetCreationTime(fileHandle);
}
#endif
///
public override DateTime GetCreationTimeUtc(string path)
{
return File.GetCreationTimeUtc(path);
}
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public override DateTime GetCreationTimeUtc(SafeFileHandle fileHandle)
{
return File.GetCreationTimeUtc(fileHandle);
}
#endif
///
public override DateTime GetLastAccessTime(string path)
{
return File.GetLastAccessTime(path);
}
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public override DateTime GetLastAccessTime(SafeFileHandle fileHandle)
{
return File.GetLastAccessTime(fileHandle);
}
#endif
///
public override DateTime GetLastAccessTimeUtc(string path)
{
return File.GetLastAccessTimeUtc(path);
}
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public override DateTime GetLastAccessTimeUtc(SafeFileHandle fileHandle)
{
return File.GetLastAccessTimeUtc(fileHandle);
}
#endif
///
public override DateTime GetLastWriteTime(string path)
{
return File.GetLastWriteTime(path);
}
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public override DateTime GetLastWriteTime(SafeFileHandle fileHandle)
{
return File.GetLastWriteTime(fileHandle);
}
#endif
///
public override DateTime GetLastWriteTimeUtc(string path)
{
return File.GetLastWriteTimeUtc(path);
}
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public override DateTime GetLastWriteTimeUtc(SafeFileHandle fileHandle)
{
return File.GetLastWriteTimeUtc(fileHandle);
}
#endif
#if FEATURE_UNIX_FILE_MODE
///
[UnsupportedOSPlatform("windows")]
public override UnixFileMode GetUnixFileMode(string path)
{
return File.GetUnixFileMode(path);
}
#endif
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
[UnsupportedOSPlatform("windows")]
public override UnixFileMode GetUnixFileMode(SafeFileHandle fileHandle)
{
return File.GetUnixFileMode(fileHandle);
}
#endif
///
public override void Move(string sourceFileName, string destFileName)
{
File.Move(sourceFileName, destFileName);
}
#if FEATURE_FILE_MOVE_WITH_OVERWRITE
///
public override void Move(string sourceFileName, string destFileName, bool overwrite)
{
File.Move(sourceFileName, destFileName, overwrite);
}
#endif
///
public override FileSystemStream Open(string path, FileMode mode)
{
return new FileStreamWrapper(File.Open(path, mode));
}
///
public override FileSystemStream Open(string path, FileMode mode, FileAccess access)
{
return new FileStreamWrapper(File.Open(path, mode, access));
}
///
public override FileSystemStream Open(string path, FileMode mode, FileAccess access, FileShare share)
{
return new FileStreamWrapper(File.Open(path, mode, access, share));
}
#if FEATURE_FILESTREAM_OPTIONS
///
public override FileSystemStream Open(string path, FileStreamOptions options)
{
return new FileStreamWrapper(File.Open(path, options));
}
#endif
///
public override FileSystemStream OpenRead(string path)
{
return new FileStreamWrapper(File.OpenRead(path));
}
///
public override StreamReader OpenText(string path)
{
return File.OpenText(path);
}
///
public override FileSystemStream OpenWrite(string path)
{
return new FileStreamWrapper(File.OpenWrite(path));
}
///
public override byte[] ReadAllBytes(string path)
{
return File.ReadAllBytes(path);
}
///
public override string[] ReadAllLines(string path)
{
return File.ReadAllLines(path);
}
///
public override string[] ReadAllLines(string path, Encoding encoding)
{
return File.ReadAllLines(path, encoding);
}
///
public override string ReadAllText(string path)
{
return File.ReadAllText(path);
}
///
public override string ReadAllText(string path, Encoding encoding)
{
return File.ReadAllText(path, encoding);
}
///
public override IEnumerable ReadLines(string path)
{
return File.ReadLines(path);
}
///
public override IEnumerable ReadLines(string path, Encoding encoding)
{
return File.ReadLines(path, encoding);
}
///
public override void Replace(string sourceFileName, string destinationFileName, string destinationBackupFileName)
{
File.Replace(sourceFileName, destinationFileName, destinationBackupFileName);
}
///
public override void Replace(string sourceFileName, string destinationFileName, string destinationBackupFileName, bool ignoreMetadataErrors)
{
File.Replace(sourceFileName, destinationFileName, destinationBackupFileName, ignoreMetadataErrors);
}
#if FEATURE_CREATE_SYMBOLIC_LINK
///
public override IFileSystemInfo ResolveLinkTarget(string linkPath, bool returnFinalTarget)
{
return File.ResolveLinkTarget(linkPath, returnFinalTarget)
.WrapFileSystemInfo(FileSystem);
}
#endif
///
public override void SetAttributes(string path, FileAttributes fileAttributes)
{
File.SetAttributes(path, fileAttributes);
}
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public override void SetAttributes(SafeFileHandle fileHandle, FileAttributes fileAttributes)
{
File.SetAttributes(fileHandle, fileAttributes);
}
#endif
///
public override void SetCreationTime(string path, DateTime creationTime)
{
File.SetCreationTime(path, creationTime);
}
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public override void SetCreationTime(SafeFileHandle fileHandle, DateTime creationTime)
{
File.SetCreationTime(fileHandle, creationTime);
}
#endif
///
public override void SetCreationTimeUtc(string path, DateTime creationTimeUtc)
{
File.SetCreationTimeUtc(path, creationTimeUtc);
}
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public override void SetCreationTimeUtc(SafeFileHandle fileHandle, DateTime creationTimeUtc)
{
File.SetCreationTimeUtc(fileHandle, creationTimeUtc);
}
#endif
///
public override void SetLastAccessTime(string path, DateTime lastAccessTime)
{
File.SetLastAccessTime(path, lastAccessTime);
}
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public override void SetLastAccessTime(SafeFileHandle fileHandle, DateTime lastAccessTime)
{
File.SetLastAccessTime(fileHandle, lastAccessTime);
}
#endif
///
public override void SetLastAccessTimeUtc(string path, DateTime lastAccessTimeUtc)
{
File.SetLastAccessTimeUtc(path, lastAccessTimeUtc);
}
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public override void SetLastAccessTimeUtc(SafeFileHandle fileHandle, DateTime lastAccessTimeUtc)
{
File.SetLastAccessTimeUtc(fileHandle, lastAccessTimeUtc);
}
#endif
///
public override void SetLastWriteTime(string path, DateTime lastWriteTime)
{
File.SetLastWriteTime(path, lastWriteTime);
}
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public override void SetLastWriteTime(SafeFileHandle fileHandle, DateTime lastWriteTime)
{
File.SetLastWriteTime(fileHandle, lastWriteTime);
}
#endif
///
public override void SetLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc)
{
File.SetLastWriteTimeUtc(path, lastWriteTimeUtc);
}
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
public override void SetLastWriteTimeUtc(SafeFileHandle fileHandle, DateTime lastWriteTimeUtc)
{
File.SetLastWriteTimeUtc(fileHandle, lastWriteTimeUtc);
}
#endif
#if FEATURE_UNIX_FILE_MODE
///
[UnsupportedOSPlatform("windows")]
public override void SetUnixFileMode(string path, UnixFileMode mode)
{
File.SetUnixFileMode(path, mode);
}
#endif
#if FEATURE_FILE_ATTRIBUTES_VIA_HANDLE
///
[UnsupportedOSPlatform("windows")]
public override void SetUnixFileMode(SafeFileHandle fileHandle, UnixFileMode mode)
{
File.SetUnixFileMode(fileHandle, mode);
}
#endif
///
/// Creates a new file, writes the specified byte array to the file, and then closes the file.
/// If the target file already exists, it is overwritten.
///
/// The file to write to.
/// The bytes to write to the file.
/// is a zero-length string, contains only white space, or contains one or more invalid characters as defined by .
/// is or contents is empty.
///
/// The specified path, file name, or both exceed the system-defined maximum length.
/// For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters.
///
/// The specified path is invalid (for example, it is on an unmapped drive).
/// An I/O error occurred while opening the file.
///
/// path specified a file that is read-only.
/// -or-
/// This operation is not supported on the current platform.
/// -or-
/// path specified a directory.
/// -or-
/// The caller does not have the required permission.
///
/// The file specified in was not found.
/// is in an invalid format.
/// The caller does not have the required permission.
///
/// Given a byte array and a file path, this method opens the specified file, writes the contents of the byte array to the file, and then closes the file.
///
public override void WriteAllBytes(string path, byte[] bytes)
{
File.WriteAllBytes(path, bytes);
}
#if FEATURE_FILE_SPAN
///
public override void WriteAllBytes(string path, ReadOnlySpan bytes)
{
File.WriteAllBytes(path, bytes);
}
#endif
///
/// Creates a new file, writes a collection of strings to the file, and then closes the file.
///
/// The file to write to.
/// The lines to write to the file.
/// is a zero-length string, contains only white space, or contains one or more invalid characters as defined by .
/// Either or is .
/// The specified path is invalid (for example, it is on an unmapped drive).
/// The file specified in was not found.
/// An I/O error occurred while opening the file.
///
/// The specified path, file name, or both exceed the system-defined maximum length.
/// For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters.
///
/// is in an invalid format.
/// The caller does not have the required permission.
///
/// specified a file that is read-only.
/// -or-
/// This operation is not supported on the current platform.
/// -or-
/// specified a directory.
/// -or-
/// The caller does not have the required permission.
///
///
///
/// If the target file already exists, it is overwritten.
///
///
/// You can use this method to create the contents for a collection class that takes an in its constructor, such as a , , or a class.
///
///
public override void WriteAllLines(string path, IEnumerable contents)
{
File.WriteAllLines(path, contents);
}
///
/// Creates a new file by using the specified encoding, writes a collection of strings to the file, and then closes the file.
///
/// The file to write to.
/// The lines to write to the file.
/// The character encoding to use.
/// is a zero-length string, contains only white space, or contains one or more invalid characters as defined by .
/// Either , , or is .
/// The specified path is invalid (for example, it is on an unmapped drive).
/// The file specified in was not found.
/// An I/O error occurred while opening the file.
///
/// The specified path, file name, or both exceed the system-defined maximum length.
/// For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters.
///
/// is in an invalid format.
/// The caller does not have the required permission.
///
/// specified a file that is read-only.
/// -or-
/// This operation is not supported on the current platform.
/// -or-
/// specified a directory.
/// -or-
/// The caller does not have the required permission.
///
///
///
/// If the target file already exists, it is overwritten.
///
///
/// You can use this method to create a file that contains the following:
///
/// -
/// The results of a LINQ to Objects query on the lines of a file, as obtained by using the ReadLines method.
///
/// -
/// The contents of a collection that implements an of strings.
///
///
///
///
public override void WriteAllLines(string path, IEnumerable contents, Encoding encoding)
{
File.WriteAllLines(path, contents, encoding);
}
///
/// Creates a new file, writes the specified string array to the file by using the specified encoding, and then closes the file.
///
/// The file to write to.
/// The string array to write to the file.
/// is a zero-length string, contains only white space, or contains one or more invalid characters as defined by .
/// Either or is .
///
/// The specified path, file name, or both exceed the system-defined maximum length.
/// For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters.
///
/// The specified path is invalid (for example, it is on an unmapped drive).
/// An I/O error occurred while opening the file.
///
/// specified a file that is read-only.
/// -or-
/// This operation is not supported on the current platform.
/// -or-
/// specified a directory.
/// -or-
/// The caller does not have the required permission.
///
/// The file specified in was not found.
/// is in an invalid format.
/// The caller does not have the required permission.
///
///
/// If the target file already exists, it is overwritten.
///
///
/// The default behavior of the WriteAllLines method is to write out data using UTF-8 encoding without a byte order mark (BOM). If it is necessary to include a UTF-8 identifier, such as a byte order mark, at the beginning of a file, use the method overload with encoding.
///
///
/// Given a string array and a file path, this method opens the specified file, writes the string array to the file using the specified encoding,
/// and then closes the file.
///
///
public override void WriteAllLines(string path, string[] contents)
{
File.WriteAllLines(path, contents);
}
///
/// Creates a new file, writes the specified string array to the file by using the specified encoding, and then closes the file.
///
/// The file to write to.
/// The string array to write to the file.
/// An object that represents the character encoding applied to the string array.
/// is a zero-length string, contains only white space, or contains one or more invalid characters as defined by .
/// Either or is .
///
/// The specified path, file name, or both exceed the system-defined maximum length.
/// For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters.
///
/// The specified path is invalid (for example, it is on an unmapped drive).
/// An I/O error occurred while opening the file.
///
/// specified a file that is read-only.
/// -or-
/// This operation is not supported on the current platform.
/// -or-
/// specified a directory.
/// -or-
/// The caller does not have the required permission.
///
/// The file specified in was not found.
/// is in an invalid format.
/// The caller does not have the required permission.
///
///
/// If the target file already exists, it is overwritten.
///
///
/// Given a string array and a file path, this method opens the specified file, writes the string array to the file using the specified encoding,
/// and then closes the file.
///
///
public override void WriteAllLines(string path, string[] contents, Encoding encoding)
{
File.WriteAllLines(path, contents, encoding);
}
///
/// Creates a new file, writes the specified string to the file using the specified encoding, and then closes the file. If the target file already exists, it is overwritten.
///
/// The file to write to.
/// The string to write to the file.
/// is a zero-length string, contains only white space, or contains one or more invalid characters as defined by .
/// is or contents is empty.
///
/// The specified path, file name, or both exceed the system-defined maximum length.
/// For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters.
///
/// The specified path is invalid (for example, it is on an unmapped drive).
/// An I/O error occurred while opening the file.
///
/// path specified a file that is read-only.
/// -or-
/// This operation is not supported on the current platform.
/// -or-
/// path specified a directory.
/// -or-
/// The caller does not have the required permission.
///
/// The file specified in was not found.
/// is in an invalid format.
/// The caller does not have the required permission.
///
/// This method uses UTF-8 encoding without a Byte-Order Mark (BOM), so using the method will return an empty byte array.
/// If it is necessary to include a UTF-8 identifier, such as a byte order mark, at the beginning of a file, use the method overload with encoding.
///
/// Given a string and a file path, this method opens the specified file, writes the string to the file, and then closes the file.
///
///
public override void WriteAllText(string path, string contents)
{
File.WriteAllText(path, contents);
}
///
/// Creates a new file, writes the specified string to the file using the specified encoding, and then closes the file. If the target file already exists, it is overwritten.
///
/// The file to write to.
/// The string to write to the file.
/// The encoding to apply to the string.
/// is a zero-length string, contains only white space, or contains one or more invalid characters as defined by .
/// is or contents is empty.
///
/// The specified path, file name, or both exceed the system-defined maximum length.
/// For example, on Windows-based platforms, paths must be less than 248 characters, and file names must be less than 260 characters.
///
/// The specified path is invalid (for example, it is on an unmapped drive).
/// An I/O error occurred while opening the file.
///
/// path specified a file that is read-only.
/// -or-
/// This operation is not supported on the current platform.
/// -or-
/// path specified a directory.
/// -or-
/// The caller does not have the required permission.
///
/// The file specified in was not found.
/// is in an invalid format.
/// The caller does not have the required permission.
///
/// Given a string and a file path, this method opens the specified file, writes the string to the file using the specified encoding, and then closes the file.
/// The file handle is guaranteed to be closed by this method, even if exceptions are raised.
///
public override void WriteAllText(string path, string contents, Encoding encoding)
{
File.WriteAllText(path, contents, encoding);
}
#if FEATURE_FILE_SPAN
///
public override void WriteAllText(string path, ReadOnlySpan contents)
{
File.WriteAllText(path, contents);
}
///
public override void WriteAllText(string path, ReadOnlySpan contents, Encoding encoding)
{
File.WriteAllText(path, contents, encoding);
}
#endif
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.Wrappers/PathBase.cs
================================================
namespace System.IO.Abstractions;
///
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public abstract class PathBase : IPath
{
///
/// Base class for calling static methods of
///
protected PathBase(IFileSystem fileSystem)
{
this.FileSystem = fileSystem;
}
[Obsolete("This constructor only exists to support mocking libraries.", error: true)]
internal PathBase() { }
///
/// Exposes the underlying filesystem implementation. This is useful for implementing extension methods.
///
public IFileSystem FileSystem { get; }
///
public abstract char AltDirectorySeparatorChar { get; }
///
public abstract char DirectorySeparatorChar { get; }
///
[Obsolete("Please use GetInvalidPathChars or GetInvalidFileNameChars instead.")]
public abstract char[] InvalidPathChars { get; }
///
public abstract char PathSeparator { get; }
///
public abstract char VolumeSeparatorChar { get; }
///
public abstract string ChangeExtension(string path, string extension);
///
public abstract string Combine(params string[] paths);
#if FEATURE_PATH_SPAN
///
public abstract string Combine(params ReadOnlySpan paths);
#endif
///
public abstract string Combine(string path1, string path2);
///
public abstract string Combine(string path1, string path2, string path3);
///
public abstract string Combine(string path1, string path2, string path3, string path4);
#if FEATURE_PATH_EXISTS
///
public abstract bool Exists(string path);
#endif
///
public abstract string GetDirectoryName(string path);
///
public abstract string GetExtension(string path);
///
public abstract string GetFileName(string path);
///
public abstract string GetFileNameWithoutExtension(string path);
///
public abstract string GetFullPath(string path);
#if FEATURE_ADVANCED_PATH_OPERATIONS
///
public abstract string GetFullPath(string path, string basePath);
#endif
///
public abstract char[] GetInvalidFileNameChars();
///
public abstract char[] GetInvalidPathChars();
///
public abstract string GetPathRoot(string path);
///
public abstract string GetRandomFileName();
///
public abstract string GetTempFileName();
///
public abstract string GetTempPath();
///
public abstract bool HasExtension(string path);
///
public abstract bool IsPathRooted(string path);
#if FEATURE_ADVANCED_PATH_OPERATIONS
///
public abstract bool IsPathFullyQualified(string path);
///
public abstract string GetRelativePath(string relativeTo, string path);
#endif
#if FEATURE_PATH_JOIN_WITH_SPAN
///
public abstract string Join(ReadOnlySpan path1, ReadOnlySpan path2);
///
public abstract string Join(ReadOnlySpan path1, ReadOnlySpan path2, ReadOnlySpan path3);
#if FEATURE_PATH_SPAN
///
public abstract string Join(params ReadOnlySpan paths);
#endif
///
public abstract bool TryJoin(ReadOnlySpan path1, ReadOnlySpan path2, ReadOnlySpan path3, Span destination, out int charsWritten);
///
public abstract bool TryJoin(ReadOnlySpan path1, ReadOnlySpan path2, Span destination, out int charsWritten);
#endif
#if FEATURE_ADVANCED_PATH_OPERATIONS
///
public abstract bool HasExtension(ReadOnlySpan path);
///
public abstract bool IsPathFullyQualified(ReadOnlySpan path);
///
public abstract bool IsPathRooted(ReadOnlySpan path);
///
public abstract ReadOnlySpan GetDirectoryName(ReadOnlySpan path);
///
public abstract ReadOnlySpan GetExtension(ReadOnlySpan path);
///
public abstract ReadOnlySpan GetFileName(ReadOnlySpan path);
///
public abstract ReadOnlySpan GetFileNameWithoutExtension(ReadOnlySpan path);
///
public abstract ReadOnlySpan GetPathRoot(ReadOnlySpan path);
#endif
#if FEATURE_PATH_JOIN_WITH_PARAMS
///
public abstract string Join(params string[] paths);
///
public abstract string Join(string path1, string path2);
///
public abstract string Join(string path1, string path2, string path3);
#endif
#if FEATURE_ENDS_IN_DIRECTORY_SEPARATOR
///
public abstract bool EndsInDirectorySeparator(ReadOnlySpan path);
///
public abstract bool EndsInDirectorySeparator(string path);
///
public abstract ReadOnlySpan TrimEndingDirectorySeparator(ReadOnlySpan path);
///
public abstract string TrimEndingDirectorySeparator(string path);
#endif
#if FEATURE_PATH_JOIN_WITH_FOUR_PATHS
///
public abstract string Join(ReadOnlySpan path1, ReadOnlySpan path2, ReadOnlySpan path3, ReadOnlySpan path4);
///
public abstract string Join(string path1, string path2, string path3, string path4);
#endif
}
================================================
FILE: src/TestableIO.System.IO.Abstractions.Wrappers/PathWrapper.cs
================================================
namespace System.IO.Abstractions;
///
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public class PathWrapper : PathBase
{
///
public PathWrapper(IFileSystem fileSystem) : base(fileSystem)
{
}
///
public override char AltDirectorySeparatorChar
{
get { return Path.AltDirectorySeparatorChar; }
}
///
public override char DirectorySeparatorChar
{
get { return Path.DirectorySeparatorChar; }
}
///
[Obsolete("Please use GetInvalidPathChars or GetInvalidFileNameChars instead.")]
public override char[] InvalidPathChars
{
get { return Path.InvalidPathChars; }
}
///
public override char PathSeparator
{
get { return Path.PathSeparator; }
}
///
public override char VolumeSeparatorChar
{
get { return Path.VolumeSeparatorChar; }
}
///
public override string ChangeExtension(string path, string extension)
{
return Path.ChangeExtension(path, extension);
}
///
public override string Combine(params string[] paths)
{
return Path.Combine(paths);
}
#if FEATURE_PATH_SPAN
///
public override string Combine(params ReadOnlySpan paths)
{
return Path.Combine(paths);
}
#endif
///