diff --git a/Directory.Packages.props b/Directory.Packages.props
index db2e2a7..2425cd9 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -7,7 +7,12 @@
+
+
+
+
+
diff --git a/Light.TemporaryStreams.sln b/Light.TemporaryStreams.sln
index e94d64b..bf0074f 100644
--- a/Light.TemporaryStreams.sln
+++ b/Light.TemporaryStreams.sln
@@ -25,8 +25,18 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{048D0C61-6CF0-43E6-B7DB-1FDD8F791D57}"
+ ProjectSection(SolutionItems) = preProject
+ src\Directory.Build.props = src\Directory.Build.props
+ EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{DA93B299-75F5-4A49-B2A6-4A1247047E5E}"
+ ProjectSection(SolutionItems) = preProject
+ tests\Directory.Build.props = tests\Directory.Build.props
+ EndProjectSection
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Light.TemporaryStreams", "src\Light.TemporaryStreams\Light.TemporaryStreams.csproj", "{61725DD8-D81C-4EC0-A0A1-63D96A87DAC1}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Light.TemporaryStreams.Tests", "tests\Light.TemporaryStreams.Tests\Light.TemporaryStreams.Tests.csproj", "{93CCDAD2-A16A-4BA3-A805-4FC7C4B518C8}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -62,6 +72,30 @@ Global
{4D6B4F48-1E4B-4ACA-9F32-829442DB5E56}.Release|x64.Build.0 = Release|Any CPU
{4D6B4F48-1E4B-4ACA-9F32-829442DB5E56}.Release|x86.ActiveCfg = Release|Any CPU
{4D6B4F48-1E4B-4ACA-9F32-829442DB5E56}.Release|x86.Build.0 = Release|Any CPU
+ {61725DD8-D81C-4EC0-A0A1-63D96A87DAC1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {61725DD8-D81C-4EC0-A0A1-63D96A87DAC1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {61725DD8-D81C-4EC0-A0A1-63D96A87DAC1}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {61725DD8-D81C-4EC0-A0A1-63D96A87DAC1}.Debug|x64.Build.0 = Debug|Any CPU
+ {61725DD8-D81C-4EC0-A0A1-63D96A87DAC1}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {61725DD8-D81C-4EC0-A0A1-63D96A87DAC1}.Debug|x86.Build.0 = Debug|Any CPU
+ {61725DD8-D81C-4EC0-A0A1-63D96A87DAC1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {61725DD8-D81C-4EC0-A0A1-63D96A87DAC1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {61725DD8-D81C-4EC0-A0A1-63D96A87DAC1}.Release|x64.ActiveCfg = Release|Any CPU
+ {61725DD8-D81C-4EC0-A0A1-63D96A87DAC1}.Release|x64.Build.0 = Release|Any CPU
+ {61725DD8-D81C-4EC0-A0A1-63D96A87DAC1}.Release|x86.ActiveCfg = Release|Any CPU
+ {61725DD8-D81C-4EC0-A0A1-63D96A87DAC1}.Release|x86.Build.0 = Release|Any CPU
+ {93CCDAD2-A16A-4BA3-A805-4FC7C4B518C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {93CCDAD2-A16A-4BA3-A805-4FC7C4B518C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {93CCDAD2-A16A-4BA3-A805-4FC7C4B518C8}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {93CCDAD2-A16A-4BA3-A805-4FC7C4B518C8}.Debug|x64.Build.0 = Debug|Any CPU
+ {93CCDAD2-A16A-4BA3-A805-4FC7C4B518C8}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {93CCDAD2-A16A-4BA3-A805-4FC7C4B518C8}.Debug|x86.Build.0 = Debug|Any CPU
+ {93CCDAD2-A16A-4BA3-A805-4FC7C4B518C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {93CCDAD2-A16A-4BA3-A805-4FC7C4B518C8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {93CCDAD2-A16A-4BA3-A805-4FC7C4B518C8}.Release|x64.ActiveCfg = Release|Any CPU
+ {93CCDAD2-A16A-4BA3-A805-4FC7C4B518C8}.Release|x64.Build.0 = Release|Any CPU
+ {93CCDAD2-A16A-4BA3-A805-4FC7C4B518C8}.Release|x86.ActiveCfg = Release|Any CPU
+ {93CCDAD2-A16A-4BA3-A805-4FC7C4B518C8}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -70,5 +104,7 @@ Global
{05D48EEC-A2AB-4143-9533-A633E7B25EA3} = {677E4EE1-7062-46AB-81FF-8D20E9316ED6}
{C86E6D31-A10A-4B61-B490-5A7AC4451BA5} = {048D0C61-6CF0-43E6-B7DB-1FDD8F791D57}
{4D6B4F48-1E4B-4ACA-9F32-829442DB5E56} = {DA93B299-75F5-4A49-B2A6-4A1247047E5E}
+ {61725DD8-D81C-4EC0-A0A1-63D96A87DAC1} = {048D0C61-6CF0-43E6-B7DB-1FDD8F791D57}
+ {93CCDAD2-A16A-4BA3-A805-4FC7C4B518C8} = {DA93B299-75F5-4A49-B2A6-4A1247047E5E}
EndGlobalSection
EndGlobal
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
new file mode 100644
index 0000000..4d31c2b
--- /dev/null
+++ b/src/Directory.Build.props
@@ -0,0 +1,13 @@
+
+
+ $([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))
+
+
+
+
+
+ true
+ true
+ Light.TemporaryStreams
+
+
diff --git a/src/Light.TemporaryStreams.Core/Hashing/HashingPlugin.cs b/src/Light.TemporaryStreams.Core/Hashing/HashingPlugin.cs
index 6e4257f..14ce42c 100644
--- a/src/Light.TemporaryStreams.Core/Hashing/HashingPlugin.cs
+++ b/src/Light.TemporaryStreams.Core/Hashing/HashingPlugin.cs
@@ -78,7 +78,8 @@ public async ValueTask DisposeAsync()
public ValueTask SetUpAsync(Stream innerStream, CancellationToken cancellationToken = default)
{
innerStream.MustNotBeNull();
- Stream currentStream = innerStream;
+ cancellationToken.ThrowIfCancellationRequested();
+ var currentStream = innerStream;
for (var i = 0; i < HashCalculators.Length; i++)
{
currentStream = HashCalculators[i].CreateWrappingCryptoStream(currentStream, leaveWrappedStreamOpen: true);
diff --git a/src/Light.TemporaryStreams.Core/Light.TemporaryStreams.Core.csproj b/src/Light.TemporaryStreams.Core/Light.TemporaryStreams.Core.csproj
index b96a5a5..17f6863 100644
--- a/src/Light.TemporaryStreams.Core/Light.TemporaryStreams.Core.csproj
+++ b/src/Light.TemporaryStreams.Core/Light.TemporaryStreams.Core.csproj
@@ -1,11 +1,5 @@
-
- true
- true
- Light.TemporaryStreams
-
-
diff --git a/src/Light.TemporaryStreams.Core/TemporaryStreamErrorHandlerProvider.cs b/src/Light.TemporaryStreams.Core/TemporaryStreamErrorHandlerProvider.cs
index 952a831..88ec810 100644
--- a/src/Light.TemporaryStreams.Core/TemporaryStreamErrorHandlerProvider.cs
+++ b/src/Light.TemporaryStreams.Core/TemporaryStreamErrorHandlerProvider.cs
@@ -9,7 +9,6 @@ public sealed class TemporaryStreamErrorHandlerProvider
{
private readonly Action? _errorHandler;
-
///
/// Initializes a new instance of .
///
diff --git a/src/Light.TemporaryStreams/Light.TemporaryStreams.csproj b/src/Light.TemporaryStreams/Light.TemporaryStreams.csproj
new file mode 100644
index 0000000..9deda95
--- /dev/null
+++ b/src/Light.TemporaryStreams/Light.TemporaryStreams.csproj
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Light.TemporaryStreams/MicrosoftLoggingExtensions.cs b/src/Light.TemporaryStreams/MicrosoftLoggingExtensions.cs
new file mode 100644
index 0000000..7a87b40
--- /dev/null
+++ b/src/Light.TemporaryStreams/MicrosoftLoggingExtensions.cs
@@ -0,0 +1,29 @@
+using System;
+using Microsoft.Extensions.Logging;
+
+namespace Light.TemporaryStreams;
+
+///
+/// Provides extension methods for that are specifically designed for use with the
+/// .
+///
+public static partial class MicrosoftLoggingExtensions
+{
+ ///
+ /// Logs an error message when an exception occurs while deleting a temporary stream.
+ ///
+ /// The logger to write the error message to.
+ /// The exception that occurred.
+ /// The file path of the temporary stream.
+ [LoggerMessage(
+ EventId = 1001,
+ Level = LogLevel.Error,
+ Message = "An error occurred while deleting the temporary stream '{TemporaryStreamFilePath}'"
+ )
+ ]
+ public static partial void LogErrorDeletingTemporaryStream(
+ this ILogger logger,
+ Exception exception,
+ string temporaryStreamFilePath
+ );
+}
diff --git a/src/Light.TemporaryStreams/ServiceCollectionExtensions.cs b/src/Light.TemporaryStreams/ServiceCollectionExtensions.cs
new file mode 100644
index 0000000..4fe0760
--- /dev/null
+++ b/src/Light.TemporaryStreams/ServiceCollectionExtensions.cs
@@ -0,0 +1,71 @@
+using System;
+using Light.GuardClauses;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+using Microsoft.Extensions.Logging;
+
+namespace Light.TemporaryStreams;
+
+///
+/// Provides extension methods for to register
+/// and corresponding dependencies.
+///
+public static class ServiceCollectionExtensions
+{
+ ///
+ /// Adds the to the specified .
+ ///
+ /// The to add the service to.
+ /// An optional delegate to create the .
+ ///
+ /// When true, the returns a delegate which logs exceptions
+ /// that occur during the deletion of temporary streams to Microsoft.Extensions.Logging. If false, the provider
+ /// will simply return null and logging will occur against the .NET Trace.
+ ///
+ /// The so that additional calls can be chained.
+ /// Thrown when is null.
+ public static IServiceCollection AddTemporaryStreamService(
+ this IServiceCollection services,
+ Func? createOptions = null,
+ bool integrateIntoMicrosoftExtensionsLogging = true
+ )
+ {
+ services.MustNotBeNull();
+ if (createOptions is null)
+ {
+ services.TryAddSingleton();
+ }
+ else
+ {
+ services.TryAddSingleton(createOptions);
+ }
+
+ if (integrateIntoMicrosoftExtensionsLogging)
+ {
+ services.TryAddSingleton(
+ sp =>
+ {
+ var loggerFactory = sp.GetRequiredService();
+ var logger = loggerFactory.CreateLogger(nameof(TemporaryStreamService));
+ return new TemporaryStreamErrorHandlerProvider(
+ (stream, exception) =>
+ logger.LogErrorDeletingTemporaryStream(
+ exception,
+ stream.GetUnderlyingFilePath()
+ )
+ );
+ }
+ );
+ }
+ else
+ {
+ services.TryAddSingleton(
+ _ => new TemporaryStreamErrorHandlerProvider(null)
+ );
+ }
+
+ services.TryAddSingleton();
+ services.TryAddSingleton(sp => sp.GetRequiredService());
+ return services;
+ }
+}
diff --git a/src/Light.TemporaryStreams/packages.lock.json b/src/Light.TemporaryStreams/packages.lock.json
new file mode 100644
index 0000000..74dee5a
--- /dev/null
+++ b/src/Light.TemporaryStreams/packages.lock.json
@@ -0,0 +1,40 @@
+{
+ "version": 2,
+ "dependencies": {
+ "net8.0": {
+ "Microsoft.Extensions.DependencyInjection.Abstractions": {
+ "type": "Direct",
+ "requested": "[8.0.0, )",
+ "resolved": "8.0.0",
+ "contentHash": "cjWrLkJXK0rs4zofsK4bSdg+jhDLTaxrkXu4gS6Y7MAlCvRyNNgwY/lJi5RDlQOnSZweHqoyvgvbdvQsRIW+hg=="
+ },
+ "Microsoft.Extensions.Logging.Abstractions": {
+ "type": "Direct",
+ "requested": "[8.0.0, )",
+ "resolved": "8.0.0",
+ "contentHash": "arDBqTgFCyS0EvRV7O3MZturChstm50OJ0y9bDJvAcmEPJm0FFpFyjU/JLYyStNGGey081DvnQYlncNX5SJJGA==",
+ "dependencies": {
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0"
+ }
+ },
+ "Microsoft.NET.ILLink.Tasks": {
+ "type": "Direct",
+ "requested": "[8.0.17, )",
+ "resolved": "8.0.17",
+ "contentHash": "x5/y4l8AtshpBOrCZdlE4txw8K3e3s9meBFeZeR3l8hbbku2V7kK6ojhXvrbjg1rk3G+JqL1BI26gtgc1ZrdUw=="
+ },
+ "light.temporarystreams.core": {
+ "type": "Project",
+ "dependencies": {
+ "Light.GuardClauses": "[13.0.0, )"
+ }
+ },
+ "Light.GuardClauses": {
+ "type": "CentralTransitive",
+ "requested": "[13.0.0, )",
+ "resolved": "13.0.0",
+ "contentHash": "mhlWUk0o+XibJgYuPlO9vQQpMmh4EAeDRFAac7pP6W3zmpjEeyUVGOiw+mdCxX469QQ2rkMYjWyHoDlZv599/w=="
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props
new file mode 100644
index 0000000..b6f38ce
--- /dev/null
+++ b/tests/Directory.Build.props
@@ -0,0 +1,23 @@
+
+
+ $([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))
+
+
+
+
+
+ Exe
+ true
+ false
+ true
+ true
+ Light.TemporaryStreams
+
+
+
+
+
+
+
+
+
diff --git a/tests/Light.TemporaryStreams.Core.Tests/Hashing/CopyToHashCalculatorTests.cs b/tests/Light.TemporaryStreams.Core.Tests/Hashing/CopyToHashCalculatorTests.cs
index 9593db8..e02053f 100644
--- a/tests/Light.TemporaryStreams.Core.Tests/Hashing/CopyToHashCalculatorTests.cs
+++ b/tests/Light.TemporaryStreams.Core.Tests/Hashing/CopyToHashCalculatorTests.cs
@@ -35,7 +35,7 @@ public static async Task HashArray_ShouldThrow_WhenObtainHashFromAlgorithmWasNot
[Fact]
public static async Task ObtainHashFromAlgorithm_ShouldThrow_WhenNothingWasWrittenToUnderlyingCryptoStream()
{
- await using CopyToHashCalculator calculator = SHA3_512.Create();
+ await using CopyToHashCalculator calculator = SHA1.Create();
// ReSharper disable once AccessToDisposedClosure -- delegate called before calculator is disposed of
var act = () => calculator.ObtainHashFromAlgorithm();
diff --git a/tests/Light.TemporaryStreams.Core.Tests/Light.TemporaryStreams.Core.Tests.csproj b/tests/Light.TemporaryStreams.Core.Tests/Light.TemporaryStreams.Core.Tests.csproj
index fd61100..786fc4a 100644
--- a/tests/Light.TemporaryStreams.Core.Tests/Light.TemporaryStreams.Core.Tests.csproj
+++ b/tests/Light.TemporaryStreams.Core.Tests/Light.TemporaryStreams.Core.Tests.csproj
@@ -1,21 +1,5 @@
-
- Exe
- true
- false
- true
- true
- Light.TemporaryStreams
-
-
-
-
-
-
-
-
-
diff --git a/tests/Light.TemporaryStreams.Tests/Light.TemporaryStreams.Tests.csproj b/tests/Light.TemporaryStreams.Tests/Light.TemporaryStreams.Tests.csproj
new file mode 100644
index 0000000..21f0d65
--- /dev/null
+++ b/tests/Light.TemporaryStreams.Tests/Light.TemporaryStreams.Tests.csproj
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/Light.TemporaryStreams.Tests/ServiceCollectionExtensionsTests.cs b/tests/Light.TemporaryStreams.Tests/ServiceCollectionExtensionsTests.cs
new file mode 100644
index 0000000..a5ddd11
--- /dev/null
+++ b/tests/Light.TemporaryStreams.Tests/ServiceCollectionExtensionsTests.cs
@@ -0,0 +1,92 @@
+using System;
+using System.Threading.Tasks;
+using FluentAssertions;
+using Light.GuardClauses;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Neovolve.Logging.Xunit;
+using Xunit;
+using Xunit.Sdk;
+
+namespace Light.TemporaryStreams;
+
+public sealed class ServiceCollectionExtensionsTests
+{
+ private readonly ITestOutputHelper _output;
+
+ public ServiceCollectionExtensionsTests(ITestOutputHelper output) => _output = output;
+
+ [Fact]
+ public async Task AddTemporaryStreamService_ShouldRegisterAllNecessaryTypes_WhenNoConfigurationProvided()
+ {
+ await using var serviceProvider = new ServiceCollection()
+ .AddLogging(builder => builder.AddXunit(_output))
+ .AddTemporaryStreamService()
+ .BuildServiceProvider();
+
+ serviceProvider.GetService().Should().NotBeNull();
+ serviceProvider.GetService().Should().NotBeNull();
+ serviceProvider.GetService().Should().NotBeNull()
+ .And.Be(new TemporaryStreamServiceOptions());
+ var errorHandlerProvider = serviceProvider.GetRequiredService();
+ var errorHandler = errorHandlerProvider.GetErrorHandlerDelegate();
+ errorHandler.Should().NotBeNull();
+ }
+
+ [Fact]
+ public async Task AddTemporaryStreamService_ShouldNotIncorporateMicrosoftLogging_WhenCorrespondingArgumentIsFalse()
+ {
+ await using var serviceProvider = new ServiceCollection()
+ .AddTemporaryStreamService(integrateIntoMicrosoftExtensionsLogging: false)
+ .BuildServiceProvider();
+
+ serviceProvider.GetService().Should().NotBeNull();
+ serviceProvider.GetService().Should().NotBeNull();
+ serviceProvider.GetService().Should().NotBeNull()
+ .And.Be(new TemporaryStreamServiceOptions());
+ var errorHandlerProvider = serviceProvider.GetRequiredService();
+ var errorHandler = errorHandlerProvider.GetErrorHandlerDelegate();
+ errorHandler.Should().BeNull();
+ }
+
+ [Fact]
+ public async Task AddTemporaryStreamService_ShouldNotRegisterDefaultOptions_WhenDelegateIsProvided()
+ {
+ await using var serviceProvider = new ServiceCollection()
+ .AddLogging(builder => builder.AddXunit(_output))
+ .AddTemporaryStreamService(_ => new TemporaryStreamServiceOptions { FileThresholdInBytes = 0 })
+ .BuildServiceProvider();
+
+ serviceProvider.GetService().Should().NotBeNull();
+ serviceProvider.GetService().Should().NotBeNull();
+ serviceProvider.GetService().Should().NotBeNull()
+ .And.NotBe(new TemporaryStreamServiceOptions());
+ }
+
+ [Fact]
+ public async Task MicrosoftErrorHandler_ShouldLogErrorMessage_WhenExceptionIsReported()
+ {
+ using var logger = _output.BuildLogger();
+ await using var serviceProvider = new ServiceCollection()
+ .AddSingleton(new LoggerFactoryStub(logger))
+ .AddTemporaryStreamService()
+ .BuildServiceProvider();
+
+ var errorHandler =
+ serviceProvider.GetRequiredService().GetErrorHandlerDelegate();
+ await using var temporaryStream =
+ serviceProvider.GetRequiredService().CreateTemporaryStream(100_000);
+ var exception = new XunitException("Just for testing purposes");
+ errorHandler.MustNotBeNull().Invoke(temporaryStream, exception);
+ logger.Entries.Should().HaveCount(1);
+ }
+
+ private sealed class LoggerFactoryStub(ICacheLogger logger) : ILoggerFactory
+ {
+ public void Dispose() { }
+
+ public ILogger CreateLogger(string categoryName) => logger;
+
+ public void AddProvider(ILoggerProvider provider) => throw new NotSupportedException();
+ }
+}
diff --git a/tests/Light.TemporaryStreams.Tests/packages.lock.json b/tests/Light.TemporaryStreams.Tests/packages.lock.json
new file mode 100644
index 0000000..371e013
--- /dev/null
+++ b/tests/Light.TemporaryStreams.Tests/packages.lock.json
@@ -0,0 +1,298 @@
+{
+ "version": 2,
+ "dependencies": {
+ "net8.0": {
+ "FluentAssertions": {
+ "type": "Direct",
+ "requested": "[7.2.0, 7.2.0]",
+ "resolved": "7.2.0",
+ "contentHash": "k94gV49Otru4e9nKtj36KpA9UkjuAGKPmhfM0oqyI+rrtxhSrgaeEhILR0AbJ9iNaoagAeQtawPz6njQOC6WQA==",
+ "dependencies": {
+ "System.Configuration.ConfigurationManager": "6.0.0"
+ }
+ },
+ "Microsoft.Extensions.DependencyInjection": {
+ "type": "Direct",
+ "requested": "[8.0.1, )",
+ "resolved": "8.0.1",
+ "contentHash": "BmANAnR5Xd4Oqw7yQ75xOAYODybZQRzdeNucg7kS5wWKd2PNnMdYtJ2Vciy0QLylRmv42DGl5+AFL9izA6F1Rw==",
+ "dependencies": {
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2"
+ }
+ },
+ "Microsoft.Extensions.Logging": {
+ "type": "Direct",
+ "requested": "[8.0.1, )",
+ "resolved": "8.0.1",
+ "contentHash": "4x+pzsQEbqxhNf1QYRr5TDkLP9UsLT3A6MdRKDDEgrW7h1ljiEPgTNhKYUhNCCAaVpQECVQ+onA91PTPnIp6Lw==",
+ "dependencies": {
+ "Microsoft.Extensions.DependencyInjection": "8.0.1",
+ "Microsoft.Extensions.Logging.Abstractions": "8.0.2",
+ "Microsoft.Extensions.Options": "8.0.2"
+ }
+ },
+ "Microsoft.NET.Test.Sdk": {
+ "type": "Direct",
+ "requested": "[17.14.1, )",
+ "resolved": "17.14.1",
+ "contentHash": "HJKqKOE+vshXra2aEHpi2TlxYX7Z9VFYkr+E5rwEvHC8eIXiyO+K9kNm8vmNom3e2rA56WqxU+/N9NJlLGXsJQ==",
+ "dependencies": {
+ "Microsoft.CodeCoverage": "17.14.1",
+ "Microsoft.TestPlatform.TestHost": "17.14.1"
+ }
+ },
+ "Neovolve.Logging.Xunit.v3": {
+ "type": "Direct",
+ "requested": "[7.1.0, )",
+ "resolved": "7.1.0",
+ "contentHash": "qv7m92tiB9thOhzWawD7XjkV08ifPFg+tMAXh8GmL+1F4k8lqJNpdIPzDBM0ZkPwqFR4wkZx4bFi8z+YoyUqNg==",
+ "dependencies": {
+ "Microsoft.Extensions.Logging": "7.0.0",
+ "System.Text.Json": "6.0.11",
+ "xunit.v3.extensibility.core": "1.0.0"
+ }
+ },
+ "xunit.runner.visualstudio": {
+ "type": "Direct",
+ "requested": "[3.1.1, )",
+ "resolved": "3.1.1",
+ "contentHash": "gNu2zhnuwjq5vQlU4S7yK/lfaKZDLmtcu+vTjnhfTlMAUYn+Hmgu8IIX0UCwWepYkk+Szx03DHx1bDnc9Fd+9w=="
+ },
+ "xunit.v3": {
+ "type": "Direct",
+ "requested": "[2.0.3, )",
+ "resolved": "2.0.3",
+ "contentHash": "Zx2r5xnUgyAIhI3cvfKaAr7Mj0Enn0zFhaMoTJ2hRYEPJlqlllUkTUvBBYalZQvpXGub/UQWq1nY5erD7Zv1cA==",
+ "dependencies": {
+ "xunit.analyzers": "1.22.0",
+ "xunit.v3.assert": "[2.0.3]",
+ "xunit.v3.core": "[2.0.3]"
+ }
+ },
+ "Microsoft.Bcl.AsyncInterfaces": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "UcSjPsst+DfAdJGVDsu346FX0ci0ah+lw3WRtn18NUwEqRt70HaOQ7lI72vy3+1LxtqI3T5GWwV39rQSrCzAeg=="
+ },
+ "Microsoft.CodeCoverage": {
+ "type": "Transitive",
+ "resolved": "17.14.1",
+ "contentHash": "pmTrhfFIoplzFVbhVwUquT+77CbGH+h4/3mBpdmIlYtBi9nAB+kKI6dN3A/nV4DFi3wLLx/BlHIPK+MkbQ6Tpg=="
+ },
+ "Microsoft.Extensions.Options": {
+ "type": "Transitive",
+ "resolved": "8.0.2",
+ "contentHash": "dWGKvhFybsaZpGmzkGCbNNwBD1rVlWzrZKANLW/CcbFJpCEceMCGzT7zZwHOGBCbwM0SzBuceMj5HN1LKV1QqA==",
+ "dependencies": {
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0",
+ "Microsoft.Extensions.Primitives": "8.0.0"
+ }
+ },
+ "Microsoft.Extensions.Primitives": {
+ "type": "Transitive",
+ "resolved": "8.0.0",
+ "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g=="
+ },
+ "Microsoft.Testing.Extensions.TrxReport.Abstractions": {
+ "type": "Transitive",
+ "resolved": "1.6.3",
+ "contentHash": "0MdowM+3IDVWE5VBzVe9NvxsE4caSbM3fO+jlWVzEBr/Vnc3BWx+uV/Ex0dLLpkxkeUKH2gGWTNLb39rw3DDqw==",
+ "dependencies": {
+ "Microsoft.Testing.Platform": "1.6.3"
+ }
+ },
+ "Microsoft.Testing.Platform": {
+ "type": "Transitive",
+ "resolved": "1.6.3",
+ "contentHash": "DqMZukaPo+vKzColfqd1I5qZebfISZT6ND70AOem/dYQmHsaMN0xg/JG7E0e80rwfxL7wAA4ylSg8j6KJf1Tuw=="
+ },
+ "Microsoft.Testing.Platform.MSBuild": {
+ "type": "Transitive",
+ "resolved": "1.6.3",
+ "contentHash": "PXSYI5Iae29GM5636zOL8PlQD1YyOa9cfzfYLR43hrLjjK7RDJgMTvgAet3oZLgDTvz6pbzABZvhx+S/W5m8YA==",
+ "dependencies": {
+ "Microsoft.Testing.Platform": "1.6.3"
+ }
+ },
+ "Microsoft.TestPlatform.ObjectModel": {
+ "type": "Transitive",
+ "resolved": "17.14.1",
+ "contentHash": "xTP1W6Mi6SWmuxd3a+jj9G9UoC850WGwZUps1Wah9r1ZxgXhdJfj1QqDLJkFjHDCvN42qDL2Ps5KjQYWUU0zcQ==",
+ "dependencies": {
+ "System.Reflection.Metadata": "8.0.0"
+ }
+ },
+ "Microsoft.TestPlatform.TestHost": {
+ "type": "Transitive",
+ "resolved": "17.14.1",
+ "contentHash": "d78LPzGKkJwsJXAQwsbJJ7LE7D1wB+rAyhHHAaODF+RDSQ0NgMjDFkSA1Djw18VrxO76GlKAjRUhl+H8NL8Z+Q==",
+ "dependencies": {
+ "Microsoft.TestPlatform.ObjectModel": "17.14.1",
+ "Newtonsoft.Json": "13.0.3"
+ }
+ },
+ "Microsoft.Win32.SystemEvents": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "hqTM5628jSsQiv+HGpiq3WKBl2c8v1KZfby2J6Pr7pEPlK9waPdgEO6b8A/+/xn/yZ9ulv8HuqK71ONy2tg67A=="
+ },
+ "Newtonsoft.Json": {
+ "type": "Transitive",
+ "resolved": "13.0.3",
+ "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ=="
+ },
+ "System.Collections.Immutable": {
+ "type": "Transitive",
+ "resolved": "8.0.0",
+ "contentHash": "AurL6Y5BA1WotzlEvVaIDpqzpIPvYnnldxru8oXJU2yFxFUy3+pNXjXd1ymO+RA0rq0+590Q8gaz2l3Sr7fmqg=="
+ },
+ "System.Configuration.ConfigurationManager": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "7T+m0kDSlIPTHIkPMIu6m6tV6qsMqJpvQWW2jIc2qi7sn40qxFo0q+7mEQAhMPXZHMKnWrnv47ntGlM/ejvw3g==",
+ "dependencies": {
+ "System.Security.Cryptography.ProtectedData": "6.0.0",
+ "System.Security.Permissions": "6.0.0"
+ }
+ },
+ "System.Drawing.Common": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "NfuoKUiP2nUWwKZN6twGqXioIe1zVD0RIj2t976A+czLHr2nY454RwwXs6JU9Htc6mwqL6Dn/nEL3dpVf2jOhg==",
+ "dependencies": {
+ "Microsoft.Win32.SystemEvents": "6.0.0"
+ }
+ },
+ "System.Reflection.Metadata": {
+ "type": "Transitive",
+ "resolved": "8.0.0",
+ "contentHash": "ptvgrFh7PvWI8bcVqG5rsA/weWM09EnthFHR5SCnS6IN+P4mj6rE1lBDC4U8HL9/57htKAqy4KQ3bBj84cfYyQ==",
+ "dependencies": {
+ "System.Collections.Immutable": "8.0.0"
+ }
+ },
+ "System.Security.AccessControl": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "AUADIc0LIEQe7MzC+I0cl0rAT8RrTAKFHl53yHjEUzNVIaUlhFY11vc2ebiVJzVBuOzun6F7FBA+8KAbGTTedQ=="
+ },
+ "System.Security.Cryptography.ProtectedData": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "rp1gMNEZpvx9vP0JW0oHLxlf8oSiQgtno77Y4PLUBjSiDYoD77Y8uXHr1Ea5XG4/pIKhqAdxZ8v8OTUtqo9PeQ=="
+ },
+ "System.Security.Permissions": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "T/uuc7AklkDoxmcJ7LGkyX1CcSviZuLCa4jg3PekfJ7SU0niF0IVTXwUiNVP9DSpzou2PpxJ+eNY2IfDM90ZCg==",
+ "dependencies": {
+ "System.Security.AccessControl": "6.0.0",
+ "System.Windows.Extensions": "6.0.0"
+ }
+ },
+ "System.Text.Json": {
+ "type": "Transitive",
+ "resolved": "6.0.11",
+ "contentHash": "xqC1HIbJMBFhrpYs76oYP+NAskNVjc6v73HqLal7ECRDPIp4oRU5pPavkD//vNactCn9DA2aaald/I5N+uZ5/g=="
+ },
+ "System.Windows.Extensions": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "IXoJOXIqc39AIe+CIR7koBtRGMiCt/LPM3lI+PELtDIy9XdyeSrwXFdWV9dzJ2Awl0paLWUaknLxFQ5HpHZUog==",
+ "dependencies": {
+ "System.Drawing.Common": "6.0.0"
+ }
+ },
+ "xunit.analyzers": {
+ "type": "Transitive",
+ "resolved": "1.22.0",
+ "contentHash": "MXTmnlJLQPSJfgxibeFgpUP5sj+3649BWkf3WgeUhAVUrI3nXzWE+BGH4OPfWkG2Q5x0SzIKgegikNhnfMgaXQ=="
+ },
+ "xunit.v3.assert": {
+ "type": "Transitive",
+ "resolved": "2.0.3",
+ "contentHash": "6uTT9zLyg7YtP8fAZqOFcCpeGXtsh6K1+QVHHJl/aoQObr2qHvhGMuZbm5UYf4z49ytpQt6NzP6Iq4uWEBBh4g=="
+ },
+ "xunit.v3.common": {
+ "type": "Transitive",
+ "resolved": "2.0.3",
+ "contentHash": "u3kTB9Fqo6X8keccsW4Zkn87nQNxMNZGm1rjJjCC0yThKP77N8H52Fx6CaochjGeGzEc2w9CUDu7C2ygu61bYg==",
+ "dependencies": {
+ "Microsoft.Bcl.AsyncInterfaces": "6.0.0"
+ }
+ },
+ "xunit.v3.core": {
+ "type": "Transitive",
+ "resolved": "2.0.3",
+ "contentHash": "KN2xOHNVGXI6AsPKatV9urqVs2YIGQ+2klY6HLys/oQauBVoS2W2+BRZz1rnwoJ+5aK+2+gfe1rLKi8iInT9Ow==",
+ "dependencies": {
+ "Microsoft.Testing.Platform.MSBuild": "1.6.3",
+ "xunit.v3.extensibility.core": "[2.0.3]",
+ "xunit.v3.runner.inproc.console": "[2.0.3]"
+ }
+ },
+ "xunit.v3.extensibility.core": {
+ "type": "Transitive",
+ "resolved": "2.0.3",
+ "contentHash": "1uxfWR72veq0b5/3ghEA2zRAkdqmnA9ahCxWr1VEb2q6MBNKx6aHsAL85PyY172f1dsvUMqh7BSelaHsR5Df5w==",
+ "dependencies": {
+ "xunit.v3.common": "[2.0.3]"
+ }
+ },
+ "xunit.v3.runner.common": {
+ "type": "Transitive",
+ "resolved": "2.0.3",
+ "contentHash": "Q+43ogqiJlWOG3l6Me7iNTm68CvVdX16OsHDoQcKCUEvWXHyiSRGBkcmMAT3OVENH0wS69QHi0q4mb3VsAbclA==",
+ "dependencies": {
+ "xunit.v3.common": "[2.0.3]"
+ }
+ },
+ "xunit.v3.runner.inproc.console": {
+ "type": "Transitive",
+ "resolved": "2.0.3",
+ "contentHash": "S65IrpHbt9nHotyfs6MnjNsc8bZUMBLHHJcyky4ywDNKx+k07sjwaGb+otLQA4Z6FPCGIPrL7u0ThQm76FxBZw==",
+ "dependencies": {
+ "Microsoft.Testing.Extensions.TrxReport.Abstractions": "1.6.3",
+ "Microsoft.Testing.Platform": "1.6.3",
+ "xunit.v3.extensibility.core": "[2.0.3]",
+ "xunit.v3.runner.common": "[2.0.3]"
+ }
+ },
+ "light.temporarystreams": {
+ "type": "Project",
+ "dependencies": {
+ "Light.TemporaryStreams.Core": "[1.0.0, )",
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "[8.0.0, )",
+ "Microsoft.Extensions.Logging.Abstractions": "[8.0.0, )"
+ }
+ },
+ "light.temporarystreams.core": {
+ "type": "Project",
+ "dependencies": {
+ "Light.GuardClauses": "[13.0.0, )"
+ }
+ },
+ "Light.GuardClauses": {
+ "type": "CentralTransitive",
+ "requested": "[13.0.0, )",
+ "resolved": "13.0.0",
+ "contentHash": "mhlWUk0o+XibJgYuPlO9vQQpMmh4EAeDRFAac7pP6W3zmpjEeyUVGOiw+mdCxX469QQ2rkMYjWyHoDlZv599/w=="
+ },
+ "Microsoft.Extensions.DependencyInjection.Abstractions": {
+ "type": "CentralTransitive",
+ "requested": "[8.0.0, )",
+ "resolved": "8.0.2",
+ "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg=="
+ },
+ "Microsoft.Extensions.Logging.Abstractions": {
+ "type": "CentralTransitive",
+ "requested": "[8.0.0, )",
+ "resolved": "8.0.2",
+ "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
+ "dependencies": {
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/Light.TemporaryStreams.Tests/xunit.runner.json b/tests/Light.TemporaryStreams.Tests/xunit.runner.json
new file mode 100644
index 0000000..c2f8426
--- /dev/null
+++ b/tests/Light.TemporaryStreams.Tests/xunit.runner.json
@@ -0,0 +1,3 @@
+{
+ "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json"
+}