From fa0a9fc41124e2288926d5b82ed6f09a04c70263 Mon Sep 17 00:00:00 2001 From: aicia-bot Date: Wed, 21 Oct 2026 18:36:02 +0200 Subject: [PATCH 01/32] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20upgrade=20package=20?= =?UTF-8?q?versions=20for=20net11=20baseline?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bump Codebelt bootstrapper, BenchmarkDotNet extension and coverlet package versions, drop the net9 package group and pin the net11 release candidate Microsoft.Extensions packages for the new target framework. --- Directory.Packages.props | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 904ca31..0200f90 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -6,15 +6,15 @@ - - - - + + + + - + @@ -30,14 +30,6 @@ - - - - - - - - @@ -46,4 +38,12 @@ + + + + + + + + \ No newline at end of file From a515a456460317fbc83c510404f1550403873bee Mon Sep 17 00:00:00 2001 From: aicia-bot Date: Wed, 21 Oct 2026 18:36:07 +0200 Subject: [PATCH 02/32] =?UTF-8?q?=F0=9F=94=A7=20retarget=20projects=20from?= =?UTF-8?q?=20net9=20to=20net11?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Source, test and app projects now target net11.0 alongside net10.0, and net9.0 is dropped from the target framework lists so the solution tracks the current .NET previews. --- Directory.Build.props | 8 ++++---- ...xtensions.Xunit.Hosting.BootstrapperConsole.App.csproj | 2 +- ...ns.Xunit.Hosting.BootstrapperMinimalConsole.App.csproj | 2 +- ...nsions.Xunit.Hosting.BootstrapperMinimalWeb.App.csproj | 2 +- ...ons.Xunit.Hosting.BootstrapperMinimalWorker.App.csproj | 2 +- ...lt.Extensions.Xunit.Hosting.BootstrapperWeb.App.csproj | 2 +- ...Extensions.Xunit.Hosting.BootstrapperWorker.App.csproj | 2 +- ...elt.Extensions.Xunit.Hosting.ClassicProgram.App.csproj | 2 +- .../Codebelt.Extensions.Xunit.Hosting.Program.App.csproj | 2 +- .../Codebelt.Extensions.Xunit.App.csproj | 2 +- .../Codebelt.Extensions.Xunit.Hosting.AspNetCore.csproj | 2 +- ...nsions.Xunit.Hosting.AspNetCore.FunctionalTests.csproj | 2 +- ...ebelt.Extensions.Xunit.Hosting.AspNetCore.Tests.csproj | 8 ++++---- ...debelt.Extensions.Xunit.Hosting.FunctionalTests.csproj | 4 ++-- 14 files changed, 21 insertions(+), 21 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index bca8c4c..f470452 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -20,7 +20,7 @@ - net10.0;net9.0;netstandard2.0 + net11.0;net10.0;netstandard2.0 Copyright © Geekle 2024-2026. All rights reserved. gimlichael Geekle @@ -56,11 +56,11 @@ - net10.0;net9.0 + net11.0;net10.0 - net10.0;net9.0;net48 + net11.0;net10.0;net48 @@ -103,7 +103,7 @@ - net10.0;net9.0;netstandard2.0 + net11.0;net10.0;netstandard2.0 false false false diff --git a/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperConsole.App/Codebelt.Extensions.Xunit.Hosting.BootstrapperConsole.App.csproj b/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperConsole.App/Codebelt.Extensions.Xunit.Hosting.BootstrapperConsole.App.csproj index 84750d0..9dc92dc 100644 --- a/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperConsole.App/Codebelt.Extensions.Xunit.Hosting.BootstrapperConsole.App.csproj +++ b/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperConsole.App/Codebelt.Extensions.Xunit.Hosting.BootstrapperConsole.App.csproj @@ -1,7 +1,7 @@ - net10.0;net9.0 + net11.0;net10.0 Exe false false diff --git a/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalConsole.App/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalConsole.App.csproj b/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalConsole.App/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalConsole.App.csproj index 84750d0..9dc92dc 100644 --- a/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalConsole.App/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalConsole.App.csproj +++ b/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalConsole.App/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalConsole.App.csproj @@ -1,7 +1,7 @@ - net10.0;net9.0 + net11.0;net10.0 Exe false false diff --git a/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWeb.App/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWeb.App.csproj b/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWeb.App/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWeb.App.csproj index f3a06e4..08f75d1 100644 --- a/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWeb.App/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWeb.App.csproj +++ b/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWeb.App/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWeb.App.csproj @@ -1,7 +1,7 @@ - net10.0;net9.0 + net11.0;net10.0 false Exe false diff --git a/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWorker.App/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWorker.App.csproj b/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWorker.App/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWorker.App.csproj index 2de2a06..e548c59 100644 --- a/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWorker.App/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWorker.App.csproj +++ b/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWorker.App/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWorker.App.csproj @@ -1,7 +1,7 @@ - net10.0;net9.0 + net11.0;net10.0 Exe false false diff --git a/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperWeb.App/Codebelt.Extensions.Xunit.Hosting.BootstrapperWeb.App.csproj b/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperWeb.App/Codebelt.Extensions.Xunit.Hosting.BootstrapperWeb.App.csproj index f3a06e4..08f75d1 100644 --- a/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperWeb.App/Codebelt.Extensions.Xunit.Hosting.BootstrapperWeb.App.csproj +++ b/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperWeb.App/Codebelt.Extensions.Xunit.Hosting.BootstrapperWeb.App.csproj @@ -1,7 +1,7 @@ - net10.0;net9.0 + net11.0;net10.0 false Exe false diff --git a/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperWorker.App/Codebelt.Extensions.Xunit.Hosting.BootstrapperWorker.App.csproj b/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperWorker.App/Codebelt.Extensions.Xunit.Hosting.BootstrapperWorker.App.csproj index 2de2a06..e548c59 100644 --- a/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperWorker.App/Codebelt.Extensions.Xunit.Hosting.BootstrapperWorker.App.csproj +++ b/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperWorker.App/Codebelt.Extensions.Xunit.Hosting.BootstrapperWorker.App.csproj @@ -1,7 +1,7 @@ - net10.0;net9.0 + net11.0;net10.0 Exe false false diff --git a/app/Codebelt.Extensions.Xunit.Hosting.ClassicProgram.App/Codebelt.Extensions.Xunit.Hosting.ClassicProgram.App.csproj b/app/Codebelt.Extensions.Xunit.Hosting.ClassicProgram.App/Codebelt.Extensions.Xunit.Hosting.ClassicProgram.App.csproj index 61b3e41..ed77a50 100644 --- a/app/Codebelt.Extensions.Xunit.Hosting.ClassicProgram.App/Codebelt.Extensions.Xunit.Hosting.ClassicProgram.App.csproj +++ b/app/Codebelt.Extensions.Xunit.Hosting.ClassicProgram.App/Codebelt.Extensions.Xunit.Hosting.ClassicProgram.App.csproj @@ -1,7 +1,7 @@ - net10.0;net9.0 + net11.0;net10.0 false Exe false diff --git a/app/Codebelt.Extensions.Xunit.Hosting.Program.App/Codebelt.Extensions.Xunit.Hosting.Program.App.csproj b/app/Codebelt.Extensions.Xunit.Hosting.Program.App/Codebelt.Extensions.Xunit.Hosting.Program.App.csproj index 61b3e41..ed77a50 100644 --- a/app/Codebelt.Extensions.Xunit.Hosting.Program.App/Codebelt.Extensions.Xunit.Hosting.Program.App.csproj +++ b/app/Codebelt.Extensions.Xunit.Hosting.Program.App/Codebelt.Extensions.Xunit.Hosting.Program.App.csproj @@ -1,7 +1,7 @@ - net10.0;net9.0 + net11.0;net10.0 false Exe false diff --git a/src/Codebelt.Extensions.Xunit.App/Codebelt.Extensions.Xunit.App.csproj b/src/Codebelt.Extensions.Xunit.App/Codebelt.Extensions.Xunit.App.csproj index 0cffd8e..59310f3 100644 --- a/src/Codebelt.Extensions.Xunit.App/Codebelt.Extensions.Xunit.App.csproj +++ b/src/Codebelt.Extensions.Xunit.App/Codebelt.Extensions.Xunit.App.csproj @@ -1,7 +1,7 @@  - net10.0;net9.0 + net11.0;net10.0 250bdf91-e7c7-4cb4-a39d-e1a5374c5602 diff --git a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/Codebelt.Extensions.Xunit.Hosting.AspNetCore.csproj b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/Codebelt.Extensions.Xunit.Hosting.AspNetCore.csproj index 3d0dd7c..28be837 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/Codebelt.Extensions.Xunit.Hosting.AspNetCore.csproj +++ b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/Codebelt.Extensions.Xunit.Hosting.AspNetCore.csproj @@ -1,7 +1,7 @@  - net10.0;net9.0 + net11.0;net10.0 200bdf91-e7c7-4cb4-a39d-e1a5374c5602 false diff --git a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.FunctionalTests/Codebelt.Extensions.Xunit.Hosting.AspNetCore.FunctionalTests.csproj b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.FunctionalTests/Codebelt.Extensions.Xunit.Hosting.AspNetCore.FunctionalTests.csproj index 3b29551..bd663c3 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.FunctionalTests/Codebelt.Extensions.Xunit.Hosting.AspNetCore.FunctionalTests.csproj +++ b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.FunctionalTests/Codebelt.Extensions.Xunit.Hosting.AspNetCore.FunctionalTests.csproj @@ -1,7 +1,7 @@ - net10.0;net9.0 + net11.0;net10.0 Codebelt.Extensions.Xunit.Hosting.AspNetCore diff --git a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests.csproj b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests.csproj index 7a8c104..87084a6 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests.csproj +++ b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests.csproj @@ -1,7 +1,7 @@  - net10.0;net9.0 + net11.0;net10.0 Codebelt.Extensions.Xunit.Hosting.AspNetCore @@ -10,8 +10,8 @@ - - - + + + diff --git a/test/Codebelt.Extensions.Xunit.Hosting.FunctionalTests/Codebelt.Extensions.Xunit.Hosting.FunctionalTests.csproj b/test/Codebelt.Extensions.Xunit.Hosting.FunctionalTests/Codebelt.Extensions.Xunit.Hosting.FunctionalTests.csproj index 45198ca..da7f673 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.FunctionalTests/Codebelt.Extensions.Xunit.Hosting.FunctionalTests.csproj +++ b/test/Codebelt.Extensions.Xunit.Hosting.FunctionalTests/Codebelt.Extensions.Xunit.Hosting.FunctionalTests.csproj @@ -1,7 +1,7 @@ - + - net10.0;net9.0 + net11.0;net10.0 Codebelt.Extensions.Xunit.Hosting From 4364f33b549f2882170f39748ee6854f9c3e2f7a Mon Sep 17 00:00:00 2001 From: aicia-bot Date: Wed, 21 Oct 2026 18:36:11 +0200 Subject: [PATCH 03/32] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20switch=20conditional?= =?UTF-8?q?=20compilation=20from=20net9=20to=20net10?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With net9 removed from the target frameworks, the NET9_0_OR_GREATER guards are moved to NET10_0_OR_GREATER so the Lock type, async disposal and default service provider validation are used on every target that supports them. --- .../ManagedWebHostFixture.cs | 2 +- src/Codebelt.Extensions.Xunit.Hosting/HostFixture.cs | 6 +++--- .../Internal/DeferredHostBuilder.cs | 2 +- .../Internal/HostBuilderFactory.cs | 2 +- src/Codebelt.Extensions.Xunit.Hosting/ManagedHostFixture.cs | 2 +- src/Codebelt.Extensions.Xunit/Test.cs | 2 +- .../MinimalWebHostTestTest.cs | 2 +- .../WebHostTestTest.cs | 2 +- .../Assets/AsyncDisposable.cs | 2 +- .../Assets/UnmanagedDisposable.cs | 4 ++-- test/Codebelt.Extensions.Xunit.Tests/DisposableTest.cs | 2 +- 11 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/ManagedWebHostFixture.cs b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/ManagedWebHostFixture.cs index c7c8332..c35479f 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/ManagedWebHostFixture.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/ManagedWebHostFixture.cs @@ -81,7 +81,7 @@ public override void ConfigureHost(Test hostTest) .UseSetting(HostDefaults.ApplicationKey, hostTest.CallerType.Assembly.GetName().Name); }); -#if NET9_0_OR_GREATER +#if NET10_0_OR_GREATER hb.UseDefaultServiceProvider(o => { o.ValidateOnBuild = true; diff --git a/src/Codebelt.Extensions.Xunit.Hosting/HostFixture.cs b/src/Codebelt.Extensions.Xunit.Hosting/HostFixture.cs index 2563adb..1646886 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting/HostFixture.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting/HostFixture.cs @@ -13,7 +13,7 @@ namespace Codebelt.Extensions.Xunit.Hosting; /// public abstract class HostFixture : IHostFixture, IAsyncLifetime { -#if NET9_0_OR_GREATER +#if NET10_0_OR_GREATER private readonly Lock _lock = new(); #else private readonly object _lock = new(); @@ -43,7 +43,7 @@ protected HostFixture() { } - /// + /// /// Determines whether the specified contains one or more of the specified target . /// /// The to validate. @@ -119,7 +119,7 @@ protected virtual void OnDisposeManagedResources() /// /// Called when this object is being disposed by . /// -#if NET9_0_OR_GREATER +#if NET10_0_OR_GREATER protected virtual async ValueTask OnDisposeManagedResourcesAsync() { if (Host is IAsyncDisposable asyncDisposable) diff --git a/src/Codebelt.Extensions.Xunit.Hosting/Internal/DeferredHostBuilder.cs b/src/Codebelt.Extensions.Xunit.Hosting/Internal/DeferredHostBuilder.cs index 0305a11..60c5007 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting/Internal/DeferredHostBuilder.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting/Internal/DeferredHostBuilder.cs @@ -152,7 +152,7 @@ protected virtual void Dispose(bool disposing) public async ValueTask DisposeAsync() { -#if NET9_0_OR_GREATER +#if NET10_0_OR_GREATER await _startedRegistration.DisposeAsync().ConfigureAwait(false); #else _startedRegistration.Dispose(); diff --git a/src/Codebelt.Extensions.Xunit.Hosting/Internal/HostBuilderFactory.cs b/src/Codebelt.Extensions.Xunit.Hosting/Internal/HostBuilderFactory.cs index 3ce773d..2d81b42 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting/Internal/HostBuilderFactory.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting/Internal/HostBuilderFactory.cs @@ -9,7 +9,7 @@ public static IHost Build(IHostBuilder hostBuilder, Action configu { configureHost?.Invoke(hostBuilder); -#if NET9_0_OR_GREATER +#if NET10_0_OR_GREATER hostBuilder.UseDefaultServiceProvider(o => { o.ValidateOnBuild = true; diff --git a/src/Codebelt.Extensions.Xunit.Hosting/ManagedHostFixture.cs b/src/Codebelt.Extensions.Xunit.Hosting/ManagedHostFixture.cs index 5aa7bd1..3c8664a 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting/ManagedHostFixture.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting/ManagedHostFixture.cs @@ -65,7 +65,7 @@ public virtual void ConfigureHost(Test hostTest) }); }); -#if NET9_0_OR_GREATER +#if NET10_0_OR_GREATER hb.UseDefaultServiceProvider(o => { o.ValidateOnBuild = true; diff --git a/src/Codebelt.Extensions.Xunit/Test.cs b/src/Codebelt.Extensions.Xunit/Test.cs index fc173f9..318a8c9 100644 --- a/src/Codebelt.Extensions.Xunit/Test.cs +++ b/src/Codebelt.Extensions.Xunit/Test.cs @@ -13,7 +13,7 @@ namespace Codebelt.Extensions.Xunit; /// public abstract class Test : ITest, IAsyncLifetime { -#if NET9_0_OR_GREATER +#if NET10_0_OR_GREATER private readonly Lock _lock = new(); #else private readonly object _lock = new(); diff --git a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/MinimalWebHostTestTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/MinimalWebHostTestTest.cs index 7a15b44..937978a 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/MinimalWebHostTestTest.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/MinimalWebHostTestTest.cs @@ -50,7 +50,7 @@ public async Task ShouldHaveResultOfBoolMiddlewareInBody() Assert.False(options.Value.F); } -#if NET9_0_OR_GREATER +#if NET10_0_OR_GREATER [Fact] public void ShouldThrowInvalidOperationException_BecauseOneOfTheServicesIsScoped() { diff --git a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/WebHostTestTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/WebHostTestTest.cs index c78fd1a..d02d9b5 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/WebHostTestTest.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/WebHostTestTest.cs @@ -49,7 +49,7 @@ public async Task ShouldHaveResultOfBoolMiddlewareInBody() Assert.False(options.Value.F); } -#if NET9_0_OR_GREATER +#if NET10_0_OR_GREATER [Fact] public void ShouldThrowInvalidOperationException_BecauseOneOfTheServicesIsScoped() { diff --git a/test/Codebelt.Extensions.Xunit.Tests/Assets/AsyncDisposable.cs b/test/Codebelt.Extensions.Xunit.Tests/Assets/AsyncDisposable.cs index 53f1c74..d03741c 100644 --- a/test/Codebelt.Extensions.Xunit.Tests/Assets/AsyncDisposable.cs +++ b/test/Codebelt.Extensions.Xunit.Tests/Assets/AsyncDisposable.cs @@ -7,7 +7,7 @@ namespace Codebelt.Extensions.Xunit.Assets; public class AsyncDisposable : Test { IDisposable _disposableResource = new MemoryStream(); -#if NET9_0_OR_GREATER +#if NET10_0_OR_GREATER IAsyncDisposable _asyncDisposableResource = new MemoryStream(); #else IAsyncDisposable _asyncDisposableResource = new WemoryStream(); diff --git a/test/Codebelt.Extensions.Xunit.Tests/Assets/UnmanagedDisposable.cs b/test/Codebelt.Extensions.Xunit.Tests/Assets/UnmanagedDisposable.cs index d5b88d1..5936461 100644 --- a/test/Codebelt.Extensions.Xunit.Tests/Assets/UnmanagedDisposable.cs +++ b/test/Codebelt.Extensions.Xunit.Tests/Assets/UnmanagedDisposable.cs @@ -28,7 +28,7 @@ public delegate IntPtr CreateFileDelegate(string lpFileName, public UnmanagedDisposable() { -#if NET9_0_OR_GREATER +#if NET10_0_OR_GREATER if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { if (NativeLibrary.TryLoad("kernel32.dll", GetType().Assembly, DllImportSearchPath.System32, out _libHandle)) @@ -102,7 +102,7 @@ protected override void OnDisposeManagedResources() protected override void OnDisposeUnmanagedResources() { -#if NET9_0_OR_GREATER +#if NET10_0_OR_GREATER if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { if (_handle != IntPtr.Zero) diff --git a/test/Codebelt.Extensions.Xunit.Tests/DisposableTest.cs b/test/Codebelt.Extensions.Xunit.Tests/DisposableTest.cs index 723b971..b2f8270 100644 --- a/test/Codebelt.Extensions.Xunit.Tests/DisposableTest.cs +++ b/test/Codebelt.Extensions.Xunit.Tests/DisposableTest.cs @@ -11,7 +11,7 @@ public DisposableTest(ITestOutputHelper output) : base(output) { } -#if NET9_0_OR_GREATER +#if NET10_0_OR_GREATER [Fact] public async Task AsyncDisposable_VerifyThatAssetIsBeingDisposed() { From f1ad53974cf56d5ae98de2eaa67bbf8a21f1bc9c Mon Sep 17 00:00:00 2001 From: aicia-bot Date: Wed, 21 Oct 2026 18:36:14 +0200 Subject: [PATCH 04/32] =?UTF-8?q?=F0=9F=93=9D=20update=20tfm=20guidance=20?= =?UTF-8?q?for=20net10=20baseline?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AGENTS.md now points the conditional compilation example at NET10_0_OR_GREATER and names net10.0 and net11.0 as the APIs to guard, matching the target frameworks used by the source projects. --- AGENTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 4fd549f..a1e5bc7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -23,7 +23,7 @@ A .NET library providing extensions for xUnit v3 testing framework. Supports mul - Benchmark TFMs: `net10.0;net9.0;netstandard2.0`. - Central package management via `Directory.Packages.props` (`ManagePackageVersionsCentrally=true`). - CI runs on Linux (ubuntu-24.04) and Windows (windows-2025), both X64 and ARM64. -- TFM compatibility is mandatory: proposals and code changes must work for all source TFMs. Do not assume `net9.0`/`net10.0` APIs exist in `netstandard2.0`; use conditional compilation (`#if NET9_0_OR_GREATER`) or compatible fallbacks where needed. +- TFM compatibility is mandatory: proposals and code changes must work for all source TFMs. Do not assume `net10.0`/`net11.0` APIs exist in `netstandard2.0`; use conditional compilation (`#if NET10_0_OR_GREATER`) or compatible fallbacks where needed. ## Build Commands From 88aabd82675df0f34cc253ea5ba2d0cfc38d7bdc Mon Sep 17 00:00:00 2001 From: aicia-bot Date: Wed, 21 Oct 2026 18:36:15 +0200 Subject: [PATCH 05/32] =?UTF-8?q?=F0=9F=91=B7=20enable=20preview=20sdk=20i?= =?UTF-8?q?n=20ci=20pipeline?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each reusable build, pack, test, SonarCloud and CodeQL job now sets include-preview so CI can build and test against the net11 preview SDK that the projects now target. --- .github/workflows/ci-pipeline.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/ci-pipeline.yml b/.github/workflows/ci-pipeline.yml index da800c8..827ea32 100644 --- a/.github/workflows/ci-pipeline.yml +++ b/.github/workflows/ci-pipeline.yml @@ -59,6 +59,7 @@ jobs: configuration: [Debug, Release] uses: codebeltnet/jobs-dotnet-build/.github/workflows/default.yml@v3 with: + include-preview: true configuration: ${{ matrix.configuration }} strong-name-key-filename: ${{ needs.init.outputs.strong-name-key-filename }} build-switches: ${{ needs.init.outputs.build-switches }} @@ -74,6 +75,7 @@ jobs: configuration: [Debug, Release] uses: codebeltnet/jobs-dotnet-pack/.github/workflows/default.yml@v3 with: + include-preview: true configuration: ${{ matrix.configuration }} version: ${{ needs.build.outputs.version }} download-build-artifact-pattern: build-${{ matrix.configuration }}-X64 @@ -88,6 +90,7 @@ jobs: arch: [X64, ARM64] uses: codebeltnet/jobs-dotnet-test/.github/workflows/default.yml@v3 with: + include-preview: true runs-on: ${{ matrix.arch == 'ARM64' && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }} configuration: ${{ matrix.configuration }} build-switches: -p:SkipSignAssembly=true @@ -105,6 +108,7 @@ jobs: arch: [X64, ARM64] uses: codebeltnet/jobs-dotnet-test/.github/workflows/default.yml@v3 with: + include-preview: true runs-on: ${{ matrix.arch == 'ARM64' && 'windows-11-arm' || 'windows-2025' }} configuration: ${{ matrix.configuration }} build-switches: -p:SkipSignAssembly=true @@ -123,6 +127,7 @@ jobs: configuration: [Debug, Release] uses: codebeltnet/jobs-dotnet-test/.github/workflows/default.yml@v3 with: + include-preview: true runs-on: ${{ matrix.arch == 'ARM64' && 'macos-26' || 'macos-26-intel' }} configuration: ${{ matrix.configuration }} build-switches: -p:SkipSignAssembly=true @@ -180,6 +185,7 @@ jobs: needs: [init, build, test_qualitygate] uses: codebeltnet/jobs-sonarcloud/.github/workflows/default.yml@v3 with: + include-preview: true organization: geekle projectKey: xunit version: ${{ needs.build.outputs.version }} @@ -199,6 +205,8 @@ jobs: name: call-codeql needs: [init, build, test_qualitygate] uses: codebeltnet/jobs-codeql/.github/workflows/default.yml@v3 + with: + include-preview: true permissions: security-events: write From cbb544d146329c7c42aacffe5d8a6682e8cd8f97 Mon Sep 17 00:00:00 2001 From: aicia-bot Date: Wed, 21 Oct 2026 18:36:15 +0200 Subject: [PATCH 06/32] =?UTF-8?q?=F0=9F=91=B7=20derive=20service=20update?= =?UTF-8?q?=20version=20from=20git=20tags?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The service update workflow now takes the latest stable vMAJOR.MINOR.PATCH tag as the baseline instead of the first CHANGELOG.md heading, and it reuses a pending TBD entry for the next version. Tags are fetched explicitly so the lookup works on a shallow checkout. --- .github/workflows/service-update.yml | 93 ++++++++++++++++++++++++---- 1 file changed, 81 insertions(+), 12 deletions(-) diff --git a/.github/workflows/service-update.yml b/.github/workflows/service-update.yml index f9a235e..1f69586 100644 --- a/.github/workflows/service-update.yml +++ b/.github/workflows/service-update.yml @@ -31,6 +31,7 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 + fetch-tags: true - name: Resolve trigger inputs id: trigger @@ -42,13 +43,68 @@ jobs: - name: Determine new version for this repo id: newver + shell: bash run: | - CURRENT=$(grep -oP '(?<=## \[)[\d.]+(?=\])' CHANGELOG.md | head -1) - NEW=$(echo "$CURRENT" | awk -F. '{printf "%s.%s.%d", $1, $2, $3+1}') - BRANCH="v${NEW}/service-update" - echo "current=$CURRENT" >> $GITHUB_OUTPUT - echo "new=$NEW" >> $GITHUB_OUTPUT - echo "branch=$BRANCH" >> $GITHUB_OUTPUT + python3 - <<'PY' + import os + import re + import subprocess + import sys + + tag_pattern = re.compile(r"^v(\d+)\.(\d+)\.(\d+)$") + tags = subprocess.check_output(["git", "tag", "--list"], text=True).splitlines() + stable_tags = [] + for tag in tags: + match = tag_pattern.fullmatch(tag) + if match: + version = tuple(int(part) for part in match.groups()) + stable_tags.append((version, tag)) + + if not stable_tags: + print("::error::No stable vMAJOR.MINOR.PATCH Git tag was found.") + sys.exit(1) + + _, latest_tag = max(stable_tags, key=lambda item: item[0]) + latest_version = latest_tag[1:] + major, minor, patch = (int(part) for part in latest_version.split(".")) + next_version = f"{major}.{minor}.{patch + 1}" + + with open("CHANGELOG.md", encoding="utf-8") as changelog_file: + changelog = changelog_file.read() + + heading = re.search(r"(?m)^## \[(\d+\.\d+\.\d+)\] - ([^\r\n]+)[ \t]*$", changelog) + if not heading: + print("::error::CHANGELOG.md has no versioned heading to compare with Git tags.") + sys.exit(1) + + current_version, current_status = heading.groups() + current_status = current_status.strip() + if current_status.upper() != "TBD" and not re.fullmatch(r"\d{4}-\d{2}-\d{2}", current_status): + print(f"::error::Unsupported CHANGELOG.md version status: {current_status}") + sys.exit(1) + + if current_version == next_version and current_status.upper() == "TBD": + # Reuse an already pending next release, such as [5.0.13] - TBD. + new_version = next_version + elif current_version == latest_version and current_status.upper() != "TBD": + # The changelog is at the latest released tag; start the next patch release. + new_version = next_version + else: + print( + f"::error::CHANGELOG.md starts at {current_version} ({current_status}), " + f"but the latest stable Git tag is {latest_tag}. Expected either " + f"[{latest_version}] with a release date or [{next_version}] - TBD." + ) + sys.exit(1) + + branch = f"v{new_version}/service-update" + print(f"Latest stable Git tag: {latest_tag}") + print(f"Latest CHANGELOG.md version: {current_version} ({current_status})") + print(f"Service update version: {new_version}") + with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as output: + output.write(f"current={current_version}\n") + output.write(f"new={new_version}\n") + output.write(f"branch={branch}\n") - name: Generate codebelt-aicia token id: app-token @@ -81,14 +137,27 @@ jobs: from datetime import date new_ver = os.environ['NEW_VERSION'] today = date.today().isoformat() - entry = f"## [{new_ver}] - {today}\n\nThis is a service update that focuses on package dependencies.\n\n" - with open("CHANGELOG.md") as f: + service_note = "This release also includes a service update focused on package dependencies." + with open("CHANGELOG.md", encoding="utf-8") as f: content = f.read() - idx = content.find("## [") - content = (content[:idx] + entry + content[idx:]) if idx != -1 else (content + entry) - with open("CHANGELOG.md", "w") as f: + heading = re.search(r"(?m)^## \[(\d+\.\d+\.\d+)\] - (TBD|\d{4}-\d{2}-\d{2})[ \t]*$", content) + if heading and heading.group(1) == new_ver: + if heading.group(2) == "TBD": + content = content[:heading.start(2)] + today + content[heading.end(2):] + heading = re.search(r"(?m)^## \[(\d+\.\d+\.\d+)\] - \d{4}-\d{2}-\d{2}[ \t]*$", content) + section_end = re.search(r"(?m)^## \[", content[heading.end():]) + end = heading.end() + section_end.start() if section_end else len(content) + if service_note not in content[heading.end():end]: + remainder = content[heading.end():] + content = content[:heading.end()] + "\n\n" + service_note + "\n\n" + remainder.lstrip("\r\n") + print(f"Updated existing CHANGELOG entry for v{new_ver}") + else: + entry = f"## [{new_ver}] - {today}\n\nThis is a service update that focuses on package dependencies.\n\n" + idx = content.find("## [") + content = (content[:idx] + entry + content[idx:]) if idx != -1 else (content + entry) + print(f"Added CHANGELOG entry for v{new_ver}") + with open("CHANGELOG.md", "w", encoding="utf-8") as f: f.write(content) - print(f"CHANGELOG updated for v{new_ver}") EOF env: NEW_VERSION: ${{ steps.newver.outputs.new }} From 2b2f294d030a10841d2e2533ee8c356ade611ca6 Mon Sep 17 00:00:00 2001 From: aicia-bot Date: Wed, 21 Oct 2026 19:17:12 +0200 Subject: [PATCH 07/32] =?UTF-8?q?=F0=9F=91=B7=20derive=20release=20availab?= =?UTF-8?q?ility=20from=20docfx=20includes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The release-note workflow now reads the Availability line from the shared docfx include files instead of a hardcoded fallback string, so the published TFM list stays in one place. --- .github/workflows/service-update.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/service-update.yml b/.github/workflows/service-update.yml index 1f69586..bd60702 100644 --- a/.github/workflows/service-update.yml +++ b/.github/workflows/service-update.yml @@ -125,7 +125,13 @@ jobs: NEW="${{ steps.newver.outputs.new }}" for f in .nuget/*/PackageReleaseNotes.txt; do [ -f "$f" ] || continue - TFM=$(grep -m1 "^Availability:" "$f" | sed 's/Availability: //' || echo ".NET 10, .NET 9 and .NET Standard 2.0") + case "$f" in + .nuget/Codebelt.Extensions.Xunit.App/*|.nuget/Codebelt.Extensions.Xunit.Hosting.AspNetCore/*) + AVAILABILITY=.docfx/includes/availability-modern.md ;; + *) + AVAILABILITY=.docfx/includes/availability-default.md ;; + esac + TFM=$(sed -n 's/^Availability: //p' "$AVAILABILITY") ENTRY="Version: ${NEW}\nAvailability: ${TFM}\n\n# ALM\n- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs)\n\n" { printf "$ENTRY"; cat "$f"; } > "$f.tmp" && mv "$f.tmp" "$f" done From 5d7740246fe65bd121af3194248ae3aa1b6d93ca Mon Sep 17 00:00:00 2001 From: aicia-bot Date: Wed, 21 Oct 2026 19:17:15 +0200 Subject: [PATCH 08/32] =?UTF-8?q?=F0=9F=93=9A=20update=20docfx=20availabil?= =?UTF-8?q?ity=20for=20.net=2011?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The shared availability includes and the docfx landing page now reflect .NET 11 as the current target framework, replacing the .NET 9 references. --- .docfx/includes/availability-default.md | 2 +- .docfx/includes/availability-modern.md | 2 +- .docfx/index.md | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.docfx/includes/availability-default.md b/.docfx/includes/availability-default.md index fa28d0e..64a956a 100644 --- a/.docfx/includes/availability-default.md +++ b/.docfx/includes/availability-default.md @@ -1 +1 @@ -Availability: .NET 10, .NET 9 and .NET Standard 2.0 \ No newline at end of file +Availability: .NET 11, .NET 10 and .NET Standard 2.0 diff --git a/.docfx/includes/availability-modern.md b/.docfx/includes/availability-modern.md index af5cf4b..a13a3f6 100644 --- a/.docfx/includes/availability-modern.md +++ b/.docfx/includes/availability-modern.md @@ -1 +1 @@ -Availability: .NET 10 and .NET 9 \ No newline at end of file +Availability: .NET 11 and .NET 10 diff --git a/.docfx/index.md b/.docfx/index.md index 95995dc..7a1d981 100644 --- a/.docfx/index.md +++ b/.docfx/index.md @@ -6,7 +6,9 @@ title: Extensions for xUnit by Codebelt # Extensions for xUnit by Codebelt -Originally, this project was part of **Cuemon for .NET**, but with the forthcoming release of .NET 9, it was split into its own project. +Originally part of **Cuemon for .NET**, this project was split into its own project ahead of the .NET 9 release. + +The current target frameworks are .NET 10 and .NET 11, with .NET Standard 2.0 available where applicable. This project is now part of [Extensions for xUnit by Codebelt](https://github.com/codebeltnet/xunit). From 01d72295c11a33f78c877034fb8f83aa7602507b Mon Sep 17 00:00:00 2001 From: aicia-bot Date: Wed, 21 Oct 2026 19:17:17 +0200 Subject: [PATCH 09/32] =?UTF-8?q?=F0=9F=92=AC=20update=20contributing=20gu?= =?UTF-8?q?ide=20for=20.net=2011=20sdk?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Contributors are now pointed at a .NET 11 SDK and the current target frameworks, so the guide matches what the repository builds against. --- .github/CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index d00cf00..47cc0bb 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -5,7 +5,7 @@ This repository is part of the Codebelt .NET library estate. The instructions be ## Before you start - Read the repository `README.md` and open an issue before starting a non-trivial feature or behavioral change. -- Use an installed .NET SDK that can build the target frameworks listed below. This repository currently targets: **net10.0, net48, net9.0, netstandard2.0**. +- Use a .NET 11 SDK to build this repository. The current target frameworks are **net11.0, net10.0, netstandard2.0**, with **net48** additionally used for tests on Windows. - The solution is `Codebelt.Extensions.Xunit.slnx`. Central package versions are maintained in `Directory.Packages.props`. - The shared build behavior is in `Directory.Build.props` and `Directory.Build.targets`; repository-specific TFMs, package references and metadata remain local to this library. From d847863279d0b420fdbc2a4f4ae73c62556192ef Mon Sep 17 00:00:00 2001 From: aicia-bot Date: Wed, 21 Oct 2026 19:17:20 +0200 Subject: [PATCH 10/32] =?UTF-8?q?=F0=9F=93=9D=20update=20package=20readmes?= =?UTF-8?q?=20for=20.net=2011?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The root README and the NuGet package READMEs list .NET 10 and .NET 11 as the supported modern targets, replacing the outdated .NET 9 mention. --- .nuget/Codebelt.Extensions.Xunit.App/README.md | 2 +- .nuget/Codebelt.Extensions.Xunit.Hosting.AspNetCore/README.md | 2 +- .nuget/Codebelt.Extensions.Xunit.Hosting/README.md | 2 +- .nuget/Codebelt.Extensions.Xunit/README.md | 2 +- README.md | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.nuget/Codebelt.Extensions.Xunit.App/README.md b/.nuget/Codebelt.Extensions.Xunit.App/README.md index cb86185..f42f222 100644 --- a/.nuget/Codebelt.Extensions.Xunit.App/README.md +++ b/.nuget/Codebelt.Extensions.Xunit.App/README.md @@ -3,7 +3,7 @@ An open-source project (MIT license) that targets and complements the [xUnit.net](https://xunit.net/) test platform. It provides a uniform and convenient way of doing unit test for all project types in .NET. Your versatile xUnit companion for: -- Modern development with `.NET 9` and `.NET 10`, +- Modern development with `.NET 10` and `.NET 11`, - Cross-platform libraries with `.NET Standard 2` (where applicable), - Legacy applications on `.NET Framework 4.6.2` and newer. diff --git a/.nuget/Codebelt.Extensions.Xunit.Hosting.AspNetCore/README.md b/.nuget/Codebelt.Extensions.Xunit.Hosting.AspNetCore/README.md index b66624b..1d3506c 100644 --- a/.nuget/Codebelt.Extensions.Xunit.Hosting.AspNetCore/README.md +++ b/.nuget/Codebelt.Extensions.Xunit.Hosting.AspNetCore/README.md @@ -3,7 +3,7 @@ An open-source project (MIT license) that targets and complements the [xUnit.net](https://xunit.net/) test platform. It provides a uniform and convenient way of doing unit test for all project types in .NET. Your versatile xUnit companion for: -- Modern development with `.NET 9` and `.NET 10`, +- Modern development with `.NET 10` and `.NET 11`, - Cross-platform libraries with `.NET Standard 2` (where applicable), - Legacy applications on `.NET Framework 4.6.2` and newer. diff --git a/.nuget/Codebelt.Extensions.Xunit.Hosting/README.md b/.nuget/Codebelt.Extensions.Xunit.Hosting/README.md index 3953423..43e009f 100644 --- a/.nuget/Codebelt.Extensions.Xunit.Hosting/README.md +++ b/.nuget/Codebelt.Extensions.Xunit.Hosting/README.md @@ -3,7 +3,7 @@ An open-source project (MIT license) that targets and complements the [xUnit.net](https://xunit.net/) test platform. It provides a uniform and convenient way of doing unit test for all project types in .NET. Your versatile xUnit companion for: -- Modern development with `.NET 9` and `.NET 10`, +- Modern development with `.NET 10` and `.NET 11`, - Cross-platform libraries with `.NET Standard 2` (where applicable), - Legacy applications on `.NET Framework 4.6.2` and newer. diff --git a/.nuget/Codebelt.Extensions.Xunit/README.md b/.nuget/Codebelt.Extensions.Xunit/README.md index 9d13aa3..a016c47 100644 --- a/.nuget/Codebelt.Extensions.Xunit/README.md +++ b/.nuget/Codebelt.Extensions.Xunit/README.md @@ -3,7 +3,7 @@ An open-source project (MIT license) that targets and complements the [xUnit.net](https://xunit.net/) test platform. It provides a uniform and convenient way of doing unit test for all project types in .NET. Your versatile xUnit companion for: -- Modern development with `.NET 9` and `.NET 10`, +- Modern development with `.NET 10` and `.NET 11`, - Cross-platform libraries with `.NET Standard 2` (where applicable), - Legacy applications on `.NET Framework 4.6.2` and newer. diff --git a/README.md b/README.md index 10fe938..b61977f 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ An open-source project (MIT license) that targets and complements the [xUnit.net](https://xunit.net/) test platform. It provides a uniform and convenient way of doing unit test for all project types in .NET. Your versatile xUnit companion for: -- Modern development with `.NET 9` and `.NET 10`, +- Modern development with `.NET 10` and `.NET 11`, - Cross-platform libraries with `.NET Standard 2` (where applicable), - Legacy applications on `.NET Framework 4.6.2` and newer. From 3d46452135169eede4fc8985bb1123a1fc9bc14b Mon Sep 17 00:00:00 2001 From: aicia-bot Date: Wed, 21 Oct 2026 19:17:23 +0200 Subject: [PATCH 11/32] =?UTF-8?q?=F0=9F=93=9D=20update=20agent=20guide=20f?= =?UTF-8?q?or=20.net=2011=20toolchain?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The agent guide now describes the .NET 11 SDK, source TFMs, and test TFMs, so automated contributors build and test against the same targets as the project. --- AGENTS.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index a1e5bc7..05cdb94 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,7 +4,7 @@ Guide for AI agents working on this .NET xUnit extensions library. ## Project Overview -A .NET library providing extensions for xUnit v3 testing framework. Supports multi-targeting: `net10.0`, `net9.0`, `netstandard2.0` (source) and `net48` (tests on Windows). +A .NET library providing extensions for xUnit v3 testing framework. Supports multi-targeting: `net11.0`, `net10.0`, `netstandard2.0` (source) and `net48` (tests on Windows). ## Repository Layout @@ -17,10 +17,10 @@ A .NET library providing extensions for xUnit v3 testing framework. Supports mul ## Toolchain -- .NET SDK with `LangVersion=latest`. -- Source TFMs: `net10.0;net9.0;netstandard2.0`. -- Test TFMs: `net10.0;net9.0` on Linux; adds `net48` on Windows. -- Benchmark TFMs: `net10.0;net9.0;netstandard2.0`. +- .NET 11 SDK with `LangVersion=latest`. +- Source TFMs: `net11.0;net10.0;netstandard2.0` (App and AspNetCore target only `net11.0;net10.0`). +- Test TFMs: `net11.0;net10.0` on Linux; adds `net48` on Windows where applicable. +- Benchmark TFMs: `net11.0;net10.0;netstandard2.0`. - Central package management via `Directory.Packages.props` (`ManagePackageVersionsCentrally=true`). - CI runs on Linux (ubuntu-24.04) and Windows (windows-2025), both X64 and ARM64. - TFM compatibility is mandatory: proposals and code changes must work for all source TFMs. Do not assume `net10.0`/`net11.0` APIs exist in `netstandard2.0`; use conditional compilation (`#if NET10_0_OR_GREATER`) or compatible fallbacks where needed. From a3ef339db018ab74bb0e72bf3d672b5a3edb74e3 Mon Sep 17 00:00:00 2001 From: aicia-bot Date: Thu, 22 Oct 2026 18:13:30 +0200 Subject: [PATCH 12/32] =?UTF-8?q?=F0=9F=93=A6=20add=2013.0.0=20package=20r?= =?UTF-8?q?elease=20notes=20for=20net11=20support?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The packable projects now target .NET 11, so each package's release notes record the new target framework set and the removal of .NET 9 under version 13.0.0. --- .../Codebelt.Extensions.Xunit.App/PackageReleaseNotes.txt | 6 ++++++ .../PackageReleaseNotes.txt | 6 ++++++ .../PackageReleaseNotes.txt | 6 ++++++ .nuget/Codebelt.Extensions.Xunit/PackageReleaseNotes.txt | 6 ++++++ 4 files changed, 24 insertions(+) diff --git a/.nuget/Codebelt.Extensions.Xunit.App/PackageReleaseNotes.txt b/.nuget/Codebelt.Extensions.Xunit.App/PackageReleaseNotes.txt index ade76fa..ad94394 100644 --- a/.nuget/Codebelt.Extensions.Xunit.App/PackageReleaseNotes.txt +++ b/.nuget/Codebelt.Extensions.Xunit.App/PackageReleaseNotes.txt @@ -1,3 +1,9 @@ +Version: 13.0.0 +Availability: .NET 11 and .NET 10 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) and removing .NET 9 (net9.0), so the package now targets .NET 11 and .NET 10 + Version: 12.0.2 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Codebelt.Extensions.Xunit.Hosting.AspNetCore/PackageReleaseNotes.txt b/.nuget/Codebelt.Extensions.Xunit.Hosting.AspNetCore/PackageReleaseNotes.txt index 1ec735f..91f64f6 100644 --- a/.nuget/Codebelt.Extensions.Xunit.Hosting.AspNetCore/PackageReleaseNotes.txt +++ b/.nuget/Codebelt.Extensions.Xunit.Hosting.AspNetCore/PackageReleaseNotes.txt @@ -1,3 +1,9 @@ +Version: 13.0.0 +Availability: .NET 11 and .NET 10 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) and removing .NET 9 (net9.0), so the package now targets .NET 11 and .NET 10 + Version: 12.0.2 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Codebelt.Extensions.Xunit.Hosting/PackageReleaseNotes.txt b/.nuget/Codebelt.Extensions.Xunit.Hosting/PackageReleaseNotes.txt index e5f3377..1e65b39 100644 --- a/.nuget/Codebelt.Extensions.Xunit.Hosting/PackageReleaseNotes.txt +++ b/.nuget/Codebelt.Extensions.Xunit.Hosting/PackageReleaseNotes.txt @@ -1,3 +1,9 @@ +Version: 13.0.0 +Availability: .NET 11, .NET 10 and .NET Standard 2.0 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) and removing .NET 9 (net9.0), so the package now targets .NET 11, .NET 10 and .NET Standard 2.0 + Version: 12.0.2 Availability: .NET 10, .NET 9 and .NET Standard 2.0 diff --git a/.nuget/Codebelt.Extensions.Xunit/PackageReleaseNotes.txt b/.nuget/Codebelt.Extensions.Xunit/PackageReleaseNotes.txt index abf8bc4..664a441 100644 --- a/.nuget/Codebelt.Extensions.Xunit/PackageReleaseNotes.txt +++ b/.nuget/Codebelt.Extensions.Xunit/PackageReleaseNotes.txt @@ -1,3 +1,9 @@ +Version: 13.0.0 +Availability: .NET 11, .NET 10 and .NET Standard 2.0 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) and removing .NET 9 (net9.0), so the package now targets .NET 11, .NET 10 and .NET Standard 2.0 + Version: 12.0.2 Availability: .NET 10, .NET 9 and .NET Standard 2.0 From e0f27e3fd4d4f285f5b455923bb02699720dbe22 Mon Sep 17 00:00:00 2001 From: aicia-bot Date: Thu, 22 Oct 2026 18:13:31 +0200 Subject: [PATCH 13/32] =?UTF-8?q?=F0=9F=92=AC=20add=2013.0.0=20changelog?= =?UTF-8?q?=20entry=20for=20net11=20baseline?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The changelog summarizes the 13.0.0 major release for humans reading release status: .NET 11 is added, .NET 9 is removed, and the package, test, CI, and documentation baselines move to .NET 11 and .NET 10. --- CHANGELOG.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index aea0743..bb8d5a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,31 @@ For more details, please refer to `PackageReleaseNotes.txt` on a per assembly ba > [!NOTE] > Changelog entries prior to version 8.4.0 was migrated from previous versions of Cuemon.Extensions.Xunit, Cuemon.Extensions.Xunit.Hosting, and Cuemon.Extensions.Xunit.Hosting.AspNetCore. +## [13.0.0] - 2026-10-22 + +This is a major release that adds .NET 11 as a target framework and removes .NET 9, so consumers still targeting .NET 9 are no longer covered by this version. The remaining changes refresh package pins, test and benchmark targets, CI, and documentation to the new .NET 11 and .NET 10 baseline. + +### Added + +- .NET 11 (`net11.0`) as a target framework for Codebelt.Extensions.Xunit, Codebelt.Extensions.Xunit.App, Codebelt.Extensions.Xunit.Hosting, and Codebelt.Extensions.Xunit.Hosting.AspNetCore. + +### Changed + +- Test projects, benchmark projects, and bootstrapper reference applications now target `net11.0` alongside `net10.0`, +- Upgraded Codebelt.Bootstrapper.Console, Codebelt.Bootstrapper.Web, and Codebelt.Bootstrapper.Worker from 5.2.2 to 5.2.3, +- Upgraded Codebelt.Extensions.BenchmarkDotNet.Console from 1.3.4 to 1.3.5, +- Upgraded Codebelt.Coverlet.MTP from 10.1.0 to 10.1.1, +- Pinned Microsoft.Extensions.Configuration, Microsoft.Extensions.Configuration.EnvironmentVariables, Microsoft.Extensions.Configuration.FileExtensions, Microsoft.Extensions.Configuration.Json, Microsoft.Extensions.Hosting, and Microsoft.AspNetCore.TestHost to 11.0.0-rc.2.26504.105 for .NET 11, +- Package READMEs, the root README, the contributing guide, `AGENTS.md`, and the DocFX landing page and availability includes now list .NET 11 and .NET 10 instead of .NET 10 and .NET 9, +- CI pipeline build, pack, test, SonarCloud, and CodeQL jobs enable the preview SDK so they can build against .NET 11, +- Service update workflow derives its baseline version from the latest stable `vMAJOR.MINOR.PATCH` Git tag instead of the first CHANGELOG.md heading, +- Release-note workflow reads the Availability line from the shared DocFX include files instead of a hardcoded fallback string. + +### Removed + +- `net9.0` target framework from the Codebelt.Extensions.Xunit, Codebelt.Extensions.Xunit.App, Codebelt.Extensions.Xunit.Hosting, and Codebelt.Extensions.Xunit.Hosting.AspNetCore packages and from the test projects, +- .NET 9 version group of Microsoft.Extensions.Configuration, Microsoft.Extensions.Configuration.EnvironmentVariables, Microsoft.Extensions.Configuration.FileExtensions, Microsoft.Extensions.Configuration.Json, Microsoft.Extensions.Hosting, and Microsoft.AspNetCore.TestHost at 9.0.21 in `Directory.Packages.props`; these packages remain pinned for .NET 10. + ## [12.0.2] - 2026-10-15 This is a patch release that updates package dependencies across all supported target frameworks and consolidates the agent guidance for this repository into `AGENTS.md`. @@ -568,6 +593,7 @@ This major release is first and foremost focused on ironing out any wrinkles tha +[13.0.0]: https://github.com/codebeltnet/xunit/compare/v12.0.2...v13.0.0 [12.0.2]: https://github.com/codebeltnet/xunit/compare/v12.0.1...v12.0.2 [12.0.1]: https://github.com/codebeltnet/xunit/compare/v12.0.0...v12.0.1 [12.0.0]: https://github.com/codebeltnet/xunit/compare/v11.2.1...v12.0.0 From cd1c6c1f87325d17e1304277389f3b744f6fa66f Mon Sep 17 00:00:00 2001 From: gimlichael Date: Tue, 27 Oct 2026 15:46:38 +0100 Subject: [PATCH 14/32] =?UTF-8?q?=F0=9F=94=A7=20enforce=20code=20style=20a?= =?UTF-8?q?nd=20sonar=20analyzers=20at=20build=20time?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Turn on EnforceCodeStyleInBuild for source projects and reference SonarAnalyzer.CSharp centrally so style issues surface during build. --- .editorconfig | 72 ++++++++++++++++++++++++---------------- Directory.Build.props | 2 ++ Directory.Packages.props | 1 + 3 files changed, 47 insertions(+), 28 deletions(-) diff --git a/.editorconfig b/.editorconfig index 6d0e3c9..9bc0265 100644 --- a/.editorconfig +++ b/.editorconfig @@ -13,45 +13,45 @@ tab_width = 2 # This style rule concern the use of the range operator, which is available in C# 8.0 and later. # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0057 -[*.{cs,vb}] +[*.cs] dotnet_diagnostic.IDE0057.severity = none # This style rule concerns the use of switch expressions versus switch statements. # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0066 -[*.{cs,vb}] +[*.cs] dotnet_diagnostic.IDE0066.severity = none # Performance rules # https://docs.microsoft.com/da-dk/dotnet/fundamentals/code-analysis/quality-rules/performance-warnings -[*.{cs,vb}] +[*.cs] dotnet_analyzer_diagnostic.category-Performance.severity = none # Because many of the suggestions by performance analyzers are not compatible with .NET Standard 2.0 # This style rule concerns the use of using statements without curly braces, also known as using declarations. This alternative syntax was introduced in C# 8.0. # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0063 -[*.{cs,vb}] +[*.cs] dotnet_diagnostic.IDE0063.severity = none # This style rule concerns with simplification of interpolated strings to improve code readability. It recommends removal of certain explicit method calls, such as ToString(), when the same method would be implicitly invoked by the compiler if the explicit method call is removed. # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0071 -[*.{cs,vb}] +[*.cs] dotnet_diagnostic.IDE0071.severity = none # S3267: Loops should be simplified with "LINQ" expressions -# https://rules.sonarsource.com/csharp/RSPEC-3267 +# https://sonarcloud.io/organizations/sonarcloud/rules?open=csharpsquid:S3267&rule_key=csharpsquid:S3267 dotnet_diagnostic.S3267.severity = none # CA1859: Use concrete types when possible for improved performance # This is a violation of Framework Design Guidelines. # https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1859 -[*.{cs,vb}] +[*.cs] dotnet_diagnostic.CA1859.severity = none # IDE0008: Use explicit type # https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0007-ide0008 -[*.{cs,vb}] +[*.cs] dotnet_diagnostic.IDE0008.severity = none -[*.{cs,vb}] +[*.cs] indent_style = space indent_size = 4 @@ -74,75 +74,75 @@ indent_size = 2 # IDE0078: Use pattern matching # https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0078 -[*.{cs,vb}] +[*.cs] dotnet_diagnostic.IDE0078.severity = none # IDE0290: Use primary constructor # https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0290 -[*.{cs,vb}] +[*.cs] dotnet_diagnostic.IDE0290.severity = none # IDE0305: Use collection expression for fluent # https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0305 -[*.{cs,vb}] +[*.cs] dotnet_diagnostic.IDE0305.severity = none # IDE0011: Add braces # https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0011 -[*.{cs,vb}] +[*.cs] dotnet_diagnostic.IDE0011.severity = none # IDE0028: Use collection initializers or expressions # https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0028 -[*.{cs,vb}] +[*.cs] dotnet_diagnostic.IDE0028.severity = none # IDE0039: Use collection expression for array # https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0300 -[*.{cs,vb}] +[*.cs] dotnet_diagnostic.IDE0300.severity = none # IDE0031: Use collection expression for empty # https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0301 -[*.{cs,vb}] +[*.cs] dotnet_diagnostic.IDE0301.severity = none # IDE0046: Use conditional expression for return # https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0046 -[*.{cs,vb}] +[*.cs] dotnet_diagnostic.IDE0046.severity = none # IDE0047: Parentheses preferences # https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0047-ide0048 -[*.{cs,vb}] +[*.cs] dotnet_diagnostic.IDE0047.severity = none # CA1716: Identifiers should not match keywords # https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1716 -[*.{cs,vb}] +[*.cs] dotnet_diagnostic.CA1716.severity = none # CA1720: Identifiers should not contain type names # https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1720 -[*.{cs,vb}] +[*.cs] dotnet_diagnostic.CA1720.severity = none # CA1846: Prefer AsSpan over Substring # https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1846 # Excluded while TFMs include netstandard2.0 -[*.{cs,vb}] +[*.cs] dotnet_diagnostic.CA1846.severity = none # CA1847: Use String.Contains(char) instead of String.Contains(string) with single characters # https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1847 # Excluded while TFMs include netstandard2.0 -[*.{cs,vb}] +[*.cs] dotnet_diagnostic.CA1847.severity = none # CA1865-CA1867: Use 'string.Method(char)' instead of 'string.Method(string)' for string with single char # https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1865-ca1867 # Excluded while TFMs include netstandard2.0 -[*.{cs,vb}] +[*.cs] dotnet_diagnostic.CA1865.severity = none dotnet_diagnostic.CA1866.severity = none dotnet_diagnostic.CA1867.severity = none @@ -150,27 +150,43 @@ dotnet_diagnostic.CA1867.severity = none # CA2263: Prefer generic overload when type is known # https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2263 # Excluded while TFMs include netstandard2.0 -[*.{cs,vb}] +[*.cs] dotnet_diagnostic.CA2263.severity = none # CA2249: Consider using String.Contains instead of String.IndexOf # https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2249 # Excluded while TFMs include netstandard2.0 -[*.{cs,vb}] +[*.cs] dotnet_diagnostic.CA2249.severity = none # IDE0022: Use expression body for methods # https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0022 -[*.{cs,vb}] +[*.cs] dotnet_diagnostic.IDE0022.severity = none # IDE0032: Use auto-property # https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0032 -[*.{cs,vb}] +[*.cs] dotnet_diagnostic.IDE0032.severity = none # Order modifiers # https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0036 # Excluded becuase of inconsistency with other analyzers -[*.{cs,vb}] +[*.cs] dotnet_diagnostic.IDE0036.severity = none + +# Use collection expression for new +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0306 +# Excluded becuase of inconsistency with other analyzers +[*.cs] +dotnet_diagnostic.IDE0306.severity = none + +# CA1016: Mark assemblies with AssemblyVersionAttribute +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1016 +[*.cs] +dotnet_diagnostic.CA1016.severity = none + +# Sonar rule csharpsquid:S8970 +# https://sonarcloud.io/organizations/sonarcloud/rules?open=csharpsquid:S8970&rule_key=csharpsquid:S8970 +[*.cs] +dotnet_diagnostic.S8970.severity = none diff --git a/Directory.Build.props b/Directory.Build.props index f470452..7c01032 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -43,6 +43,7 @@ latest Recommended 7035,CA2260 + true @@ -51,6 +52,7 @@ + diff --git a/Directory.Packages.props b/Directory.Packages.props index 0200f90..f73adb9 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -25,6 +25,7 @@ + From decad9774260b7e34680a8e8dd54c4563e414d47 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Tue, 27 Oct 2026 15:46:49 +0100 Subject: [PATCH 15/32] =?UTF-8?q?=F0=9F=93=A6=20bump=20cuemon,=20microsoft?= =?UTF-8?q?=20extensions=20and=20.net=2011=20rc=20packages?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Align package pins with the current .NET 11 baseline and Cuemon 10.8.2. --- Directory.Packages.props | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index f73adb9..a6b509f 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -10,9 +10,9 @@ - - - + + + @@ -28,23 +28,23 @@ - + - - - - - - + + + + + + - - - - - - + + + + + + \ No newline at end of file From 82a4c935c239f477ab93bc8ca73eb25cddfd2f97 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Tue, 27 Oct 2026 15:46:49 +0100 Subject: [PATCH 16/32] =?UTF-8?q?=F0=9F=94=A7=20force=20lf=20line=20ending?= =?UTF-8?q?s=20via=20gitattributes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Normalize text files to LF so line endings stay consistent across Windows and Linux checkouts. --- .gitattributes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index 1ff0c42..6f3fcbd 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,7 +1,7 @@ ############################################################################### # Set default behavior to automatically normalize line endings. ############################################################################### -* text=auto +* text=auto eol=lf ############################################################################### # Set default behavior for command prompt diff. From 416905846a9c56a230b55d3a36c4bd3fcb86cdeb Mon Sep 17 00:00:00 2001 From: gimlichael Date: Tue, 27 Oct 2026 15:46:50 +0100 Subject: [PATCH 17/32] =?UTF-8?q?=F0=9F=94=A7=20move=20ci=20and=20test=20e?= =?UTF-8?q?nvironments=20to=20ubuntu=2026.04?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Run CI on Ubuntu 26.04 and split the Docker test environment into net10.0 and net11.0. --- .github/workflows/ci-pipeline.yml | 8 ++++---- testenvironments.json | 11 ++++++++--- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci-pipeline.yml b/.github/workflows/ci-pipeline.yml index 827ea32..1e2aa22 100644 --- a/.github/workflows/ci-pipeline.yml +++ b/.github/workflows/ci-pipeline.yml @@ -23,7 +23,7 @@ permissions: jobs: init: name: initialize - runs-on: ubuntu-24.04 + runs-on: ubuntu-26.04 outputs: run-mac-tests: ${{ steps.vars.outputs.run-mac-tests }} run-privileged-jobs: ${{ steps.vars.outputs.run-privileged-jobs }} @@ -63,7 +63,7 @@ jobs: configuration: ${{ matrix.configuration }} strong-name-key-filename: ${{ needs.init.outputs.strong-name-key-filename }} build-switches: ${{ needs.init.outputs.build-switches }} - runs-on: ${{ matrix.arch == 'ARM64' && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }} + runs-on: ${{ matrix.arch == 'ARM64' && 'ubuntu-26.04-arm' || 'ubuntu-26.04' }} upload-build-artifact-name: build-${{ matrix.configuration }}-${{ matrix.arch }} secrets: inherit @@ -91,7 +91,7 @@ jobs: uses: codebeltnet/jobs-dotnet-test/.github/workflows/default.yml@v3 with: include-preview: true - runs-on: ${{ matrix.arch == 'ARM64' && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }} + runs-on: ${{ matrix.arch == 'ARM64' && 'ubuntu-26.04-arm' || 'ubuntu-26.04' }} configuration: ${{ matrix.configuration }} build-switches: -p:SkipSignAssembly=true restore: true # we need to restore the packages for the test project @@ -139,7 +139,7 @@ jobs: if: ${{ always() }} name: test-qualitygate needs: [init, test_linux, test_windows, test_mac] - runs-on: ubuntu-24.04 + runs-on: ubuntu-26.04 steps: - name: Evaluate test results shell: bash diff --git a/testenvironments.json b/testenvironments.json index 587b2ef..6371963 100644 --- a/testenvironments.json +++ b/testenvironments.json @@ -4,12 +4,17 @@ { "name": "WSL-Ubuntu", "type": "wsl", - "wslDistribution": "Ubuntu-24.04" + "wslDistribution": "Ubuntu-26.04" }, { - "name": "Docker-Ubuntu", + "name": "Docker-Ubuntu (net10.0)", "type": "docker", - "dockerImage": "codebeltnet/ubuntu-testrunner:8-9-10-11" + "dockerImage": "codebeltnet/ubuntu-testrunner:10" + }, + { + "name": "Docker-Ubuntu (net11.0)", + "type": "docker", + "dockerImage": "codebeltnet/ubuntu-testrunner:11" } ] } From 8b00a26c441c3a9424d4496da2acb268e0ddc59e Mon Sep 17 00:00:00 2001 From: gimlichael Date: Tue, 27 Oct 2026 15:46:51 +0100 Subject: [PATCH 18/32] =?UTF-8?q?=F0=9F=94=A7=20include=20global=20nowarn?= =?UTF-8?q?=20in=20app=20project?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keep the project-wide NoWarn list instead of replacing it. --- .../Codebelt.Extensions.Xunit.App.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Codebelt.Extensions.Xunit.App/Codebelt.Extensions.Xunit.App.csproj b/src/Codebelt.Extensions.Xunit.App/Codebelt.Extensions.Xunit.App.csproj index 59310f3..301a7cf 100644 --- a/src/Codebelt.Extensions.Xunit.App/Codebelt.Extensions.Xunit.App.csproj +++ b/src/Codebelt.Extensions.Xunit.App/Codebelt.Extensions.Xunit.App.csproj @@ -9,7 +9,7 @@ false false Provides a convenient set of default API additions for building .NET test projects based on xUnit. - NU5128 + $(NoWarn);NU5128 From 261e69944feecc8d6e688e6af4c2cfa030520a70 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Tue, 27 Oct 2026 15:49:05 +0100 Subject: [PATCH 19/32] =?UTF-8?q?=F0=9F=93=9D=20correct=2013.0.0=20changel?= =?UTF-8?q?og=20release=20date=20to=20go-live=20date?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Set the 13.0.0 release date to 2026-11-10, the planned go-live date. --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb8d5a9..21b36e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ For more details, please refer to `PackageReleaseNotes.txt` on a per assembly ba > [!NOTE] > Changelog entries prior to version 8.4.0 was migrated from previous versions of Cuemon.Extensions.Xunit, Cuemon.Extensions.Xunit.Hosting, and Cuemon.Extensions.Xunit.Hosting.AspNetCore. -## [13.0.0] - 2026-10-22 +## [13.0.0] - 2026-11-10 This is a major release that adds .NET 11 as a target framework and removes .NET 9, so consumers still targeting .NET 9 are no longer covered by this version. The remaining changes refresh package pins, test and benchmark targets, CI, and documentation to the new .NET 11 and .NET 10 baseline. From 5b4471440b5459a523e96cdb1718e5a5ab86a6d6 Mon Sep 17 00:00:00 2001 From: aicia-bot Date: Tue, 27 Oct 2026 16:43:06 +0100 Subject: [PATCH 20/32] =?UTF-8?q?=F0=9F=93=9D=20document=20lock=20usage=20?= =?UTF-8?q?rules=20in=20agents=20guidance?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit IDE0330 is no longer disabled, so the agent guidance now tells contributors to use System.Threading.Lock behind a NET10_0_OR_GREATER guard with a plain object fallback for multi-targeted source projects. --- AGENTS.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 05cdb94..490461f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -108,9 +108,10 @@ dotnet test --project test/Codebelt.Extensions.Xunit.Tests --framework net10.0 - | IDE0022 | expression-bodied methods | style consistency | | IDE0300/0301/0028/0305 | collection expressions | netstandard2.0 compat | | CA1846/1847/1865-1867 | Span/char overloads | netstandard2.0 compat | -| IDE0330 | `System.Threading.Lock` | requires net9.0+ | | Performance category | various | netstandard2.0 compat | +IDE0330 is not disabled. For dedicated lock fields in multi-targeted source projects, use `System.Threading.Lock` under `#if NET10_0_OR_GREATER` and a dedicated `object` in the fallback for `netstandard2.0`. Keep the `lock` expression statically typed as the selected field type; do not mix `Lock` and `Monitor` synchronization for the same protected state. + ### Namespaces - **CRITICAL**: Test namespaces MUST match the System Under Test (SUT) exactly From 789e95bef0ba3ad36e3258b88f2c36d2c70b0bd6 Mon Sep 17 00:00:00 2001 From: aicia-bot Date: Tue, 27 Oct 2026 16:43:07 +0100 Subject: [PATCH 21/32] =?UTF-8?q?=F0=9F=94=A7=20configure=20build=20warnin?= =?UTF-8?q?gs=20nuget=20audit=20and=20mtp=20runner?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The shared build props now emit warnings consistently, disable NuGet audit for the offline build, and run test projects on the Microsoft Testing Platform runner. --- Directory.Build.props | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 7c01032..16732d1 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -67,19 +67,17 @@ false + Exe false false false true - 0 none - NU1701,NU1903,NETSDK1206 - false - Exe - - - + NU1701,NU1902,NU1903 0 + false + false + true From 2551ac036a207357320c8c20081f24b7ade55ff6 Mon Sep 17 00:00:00 2001 From: aicia-bot Date: Tue, 27 Oct 2026 16:43:07 +0100 Subject: [PATCH 22/32] =?UTF-8?q?=F0=9F=94=A7=20suppress=20sonar=20finding?= =?UTF-8?q?s=20with=20documented=20justifications?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Sonar analyzer flags a few intentional patterns, such as unused type parameters that bind a fixture to its entry point. Each suppression carries a justification so the intent stays visible. --- .../GlobalSuppressions.cs | 25 ++++++------ .../GlobalSuppressions.cs | 40 ++++++++++--------- 2 files changed, 34 insertions(+), 31 deletions(-) diff --git a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/GlobalSuppressions.cs b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/GlobalSuppressions.cs index 97a14ca..3da47bf 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/GlobalSuppressions.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/GlobalSuppressions.cs @@ -1,12 +1,13 @@ -// This file is used by Code Analysis to maintain SuppressMessage -// attributes that are applied to this project. -// Project-level suppressions either have no target or are given -// a specific target and scoped to a namespace, type, member, etc. - -using System.Diagnostics.CodeAnalysis; - -[assembly: SuppressMessage("Blocker Code Smell", "S3427:Method overloads with default parameter values should not overlap", Justification = "Avoid bumping major version by providing an extra overloaded member as optional argument.", Scope = "member", Target = "~M:Codebelt.Extensions.Xunit.Hosting.AspNetCore.WebHostTestFactory.Create(System.Action{Microsoft.Extensions.DependencyInjection.IServiceCollection},System.Action{Microsoft.AspNetCore.Builder.IApplicationBuilder},System.Action{Microsoft.Extensions.Hosting.IHostBuilder},Codebelt.Extensions.Xunit.Hosting.AspNetCore.IWebHostFixture)~Codebelt.Extensions.Xunit.Hosting.AspNetCore.IWebHostTest")] -[assembly: SuppressMessage("Blocker Code Smell", "S3427:Method overloads with default parameter values should not overlap", Justification = "Avoid bumping major version by providing an extra overloaded member as optional argument.", Scope = "member", Target = "~M:Codebelt.Extensions.Xunit.Hosting.AspNetCore.WebHostTestFactory.CreateWithHostBuilderContext(System.Action{Microsoft.Extensions.Hosting.HostBuilderContext,Microsoft.Extensions.DependencyInjection.IServiceCollection},System.Action{Microsoft.Extensions.Hosting.HostBuilderContext,Microsoft.AspNetCore.Builder.IApplicationBuilder},System.Action{Microsoft.Extensions.Hosting.IHostBuilder},Codebelt.Extensions.Xunit.Hosting.AspNetCore.IWebHostFixture)~Codebelt.Extensions.Xunit.Hosting.AspNetCore.IWebHostTest")] -[assembly: SuppressMessage("Blocker Code Smell", "S3427:Method overloads with default parameter values should not overlap", Justification = "Avoid bumping major version by providing an extra overloaded member as optional argument.", Scope = "member", Target = "~M:Codebelt.Extensions.Xunit.Hosting.AspNetCore.WebHostTestFactory.RunAsync(System.Action{Microsoft.Extensions.DependencyInjection.IServiceCollection},System.Action{Microsoft.AspNetCore.Builder.IApplicationBuilder},System.Action{Microsoft.Extensions.Hosting.IHostBuilder},System.Func{System.Net.Http.HttpClient,System.Threading.Tasks.Task{System.Net.Http.HttpResponseMessage}},Codebelt.Extensions.Xunit.Hosting.AspNetCore.IWebHostFixture)~System.Threading.Tasks.Task{System.Net.Http.HttpResponseMessage}")] -[assembly: SuppressMessage("Blocker Code Smell", "S3427:Method overloads with default parameter values should not overlap", Justification = "Avoid bumping major version by providing an extra overloaded member as optional argument.", Scope = "member", Target = "~M:Codebelt.Extensions.Xunit.Hosting.AspNetCore.WebHostTestFactory.RunWithHostBuilderContextAsync(System.Action{Microsoft.Extensions.Hosting.HostBuilderContext,Microsoft.Extensions.DependencyInjection.IServiceCollection},System.Action{Microsoft.Extensions.Hosting.HostBuilderContext,Microsoft.AspNetCore.Builder.IApplicationBuilder},System.Action{Microsoft.Extensions.Hosting.IHostBuilder},System.Func{System.Net.Http.HttpClient,System.Threading.Tasks.Task{System.Net.Http.HttpResponseMessage}},Codebelt.Extensions.Xunit.Hosting.AspNetCore.IWebHostFixture)~System.Threading.Tasks.Task{System.Net.Http.HttpResponseMessage}")] -[assembly: SuppressMessage("Performance", "CA1859:Use concrete types when possible for improved performance", Justification = "Nitpick.", Scope = "member", Target = "~M:Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http.FakeHttpContextAccessor.MakeGreeting(System.String)~System.IO.Stream")] +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Blocker Code Smell", "S3427:Method overloads with default parameter values should not overlap", Justification = "Avoid bumping major version by providing an extra overloaded member as optional argument.", Scope = "member", Target = "~M:Codebelt.Extensions.Xunit.Hosting.AspNetCore.WebHostTestFactory.Create(System.Action{Microsoft.Extensions.DependencyInjection.IServiceCollection},System.Action{Microsoft.AspNetCore.Builder.IApplicationBuilder},System.Action{Microsoft.Extensions.Hosting.IHostBuilder},Codebelt.Extensions.Xunit.Hosting.AspNetCore.IWebHostFixture)~Codebelt.Extensions.Xunit.Hosting.AspNetCore.IWebHostTest")] +[assembly: SuppressMessage("Blocker Code Smell", "S3427:Method overloads with default parameter values should not overlap", Justification = "Avoid bumping major version by providing an extra overloaded member as optional argument.", Scope = "member", Target = "~M:Codebelt.Extensions.Xunit.Hosting.AspNetCore.WebHostTestFactory.CreateWithHostBuilderContext(System.Action{Microsoft.Extensions.Hosting.HostBuilderContext,Microsoft.Extensions.DependencyInjection.IServiceCollection},System.Action{Microsoft.Extensions.Hosting.HostBuilderContext,Microsoft.AspNetCore.Builder.IApplicationBuilder},System.Action{Microsoft.Extensions.Hosting.IHostBuilder},Codebelt.Extensions.Xunit.Hosting.AspNetCore.IWebHostFixture)~Codebelt.Extensions.Xunit.Hosting.AspNetCore.IWebHostTest")] +[assembly: SuppressMessage("Blocker Code Smell", "S3427:Method overloads with default parameter values should not overlap", Justification = "Avoid bumping major version by providing an extra overloaded member as optional argument.", Scope = "member", Target = "~M:Codebelt.Extensions.Xunit.Hosting.AspNetCore.WebHostTestFactory.RunAsync(System.Action{Microsoft.Extensions.DependencyInjection.IServiceCollection},System.Action{Microsoft.AspNetCore.Builder.IApplicationBuilder},System.Action{Microsoft.Extensions.Hosting.IHostBuilder},System.Func{System.Net.Http.HttpClient,System.Threading.Tasks.Task{System.Net.Http.HttpResponseMessage}},Codebelt.Extensions.Xunit.Hosting.AspNetCore.IWebHostFixture)~System.Threading.Tasks.Task{System.Net.Http.HttpResponseMessage}")] +[assembly: SuppressMessage("Blocker Code Smell", "S3427:Method overloads with default parameter values should not overlap", Justification = "Avoid bumping major version by providing an extra overloaded member as optional argument.", Scope = "member", Target = "~M:Codebelt.Extensions.Xunit.Hosting.AspNetCore.WebHostTestFactory.RunWithHostBuilderContextAsync(System.Action{Microsoft.Extensions.Hosting.HostBuilderContext,Microsoft.Extensions.DependencyInjection.IServiceCollection},System.Action{Microsoft.Extensions.Hosting.HostBuilderContext,Microsoft.AspNetCore.Builder.IApplicationBuilder},System.Action{Microsoft.Extensions.Hosting.IHostBuilder},System.Func{System.Net.Http.HttpClient,System.Threading.Tasks.Task{System.Net.Http.HttpResponseMessage}},Codebelt.Extensions.Xunit.Hosting.AspNetCore.IWebHostFixture)~System.Threading.Tasks.Task{System.Net.Http.HttpResponseMessage}")] +[assembly: SuppressMessage("Performance", "CA1859:Use concrete types when possible for improved performance", Justification = "Nitpick.", Scope = "member", Target = "~M:Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http.FakeHttpContextAccessor.MakeGreeting(System.String)~System.IO.Stream")] +[assembly: SuppressMessage("Major Code Smell", "S2326:Unused type parameters should be removed", Justification = "TEntryPoint associates the fixture with the application's entry point at compile time; the default implementation uses its assembly to initialize the application.", Scope = "type", Target = "~T:Codebelt.Extensions.Xunit.Hosting.AspNetCore.IWebApplicationFixture`1")] diff --git a/src/Codebelt.Extensions.Xunit.Hosting/GlobalSuppressions.cs b/src/Codebelt.Extensions.Xunit.Hosting/GlobalSuppressions.cs index a73e32e..370b3b9 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting/GlobalSuppressions.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting/GlobalSuppressions.cs @@ -1,19 +1,21 @@ -// This file is used by Code Analysis to maintain SuppressMessage -// attributes that are applied to this project. -// Project-level suppressions either have no target or are given -// a specific target and scoped to a namespace, type, member, etc. - -using System.Diagnostics.CodeAnalysis; - -[assembly: SuppressMessage("Major Code Smell", "S3881:\"IDisposable\" should be implemented correctly", Justification = "This is an implementation of the IDisposable interface tailored to avoid wrong implementations.", Scope = "type", Target = "~T:Codebelt.Extensions.Xunit.Hosting.HostFixture")] -[assembly: SuppressMessage("Major Code Smell", "S3971:\"GC.SuppressFinalize\" should not be called", Justification = "False-Positive due to IAsyncDisposable living side-by-side with IDisposable.", Scope = "member", Target = "~M:Codebelt.Extensions.Xunit.Hosting.HostFixture.DisposeAsync~System.Threading.Tasks.ValueTask")] -[assembly: SuppressMessage("Blocker Code Smell", "S3427:Method overloads with default parameter values should not overlap", Justification = "Avoid bumping major version by providing an extra overloaded member as optional argument.", Scope = "member", Target = "~M:Codebelt.Extensions.Xunit.Hosting.HostTestFactory.Create(System.Action{Microsoft.Extensions.DependencyInjection.IServiceCollection},System.Action{Microsoft.Extensions.Hosting.IHostBuilder},Codebelt.Extensions.Xunit.Hosting.IGenericHostFixture)~Codebelt.Extensions.Xunit.Hosting.IHostTest")] -[assembly: SuppressMessage("Blocker Code Smell", "S3427:Method overloads with default parameter values should not overlap", Justification = "Avoid bumping major version by providing an extra overloaded member as optional argument.", Scope = "member", Target = "~M:Codebelt.Extensions.Xunit.Hosting.HostTestFactory.CreateWithHostBuilderContext(System.Action{Microsoft.Extensions.Hosting.HostBuilderContext,Microsoft.Extensions.DependencyInjection.IServiceCollection},System.Action{Microsoft.Extensions.Hosting.IHostBuilder},Codebelt.Extensions.Xunit.Hosting.IGenericHostFixture)~Codebelt.Extensions.Xunit.Hosting.IHostTest")] -[assembly: SuppressMessage("Maintainability", "CA1510:Use ArgumentNullException throw helper", Justification = "Wont fix. Multi-TFMs. Only supported with >= .NET 7.", Scope = "member", Target = "~M:Codebelt.Extensions.Xunit.Hosting.HostTest`1.#ctor(System.Boolean,`0,Xunit.ITestOutputHelper,System.Type)")] -[assembly: SuppressMessage("Maintainability", "CA1510:Use ArgumentNullException throw helper", Justification = "Wont fix. Multi-TFMs. Only supported with >= .NET 7.", Scope = "member", Target = "~M:Codebelt.Extensions.Xunit.Hosting.LoggerExtensions.GetTestStore(Microsoft.Extensions.Logging.ILogger,System.String)~Codebelt.Extensions.Xunit.ITestStore{Codebelt.Extensions.Xunit.Hosting.XunitTestLoggerEntry}")] -[assembly: SuppressMessage("Maintainability", "CA1510:Use ArgumentNullException throw helper", Justification = "Wont fix. Multi-TFMs. Only supported with >= .NET 7.", Scope = "member", Target = "~M:Codebelt.Extensions.Xunit.Hosting.ManagedHostFixture.ConfigureHost(Codebelt.Extensions.Xunit.Test)")] -[assembly: SuppressMessage("Maintainability", "CA1510:Use ArgumentNullException throw helper", Justification = "Wont fix. Multi-TFMs. Only supported with >= .NET 7.", Scope = "member", Target = "~M:Codebelt.Extensions.Xunit.Hosting.ManagedMinimalHostFixture.ConfigureHost(Codebelt.Extensions.Xunit.Test)")] -[assembly: SuppressMessage("Maintainability", "CA1510:Use ArgumentNullException throw helper", Justification = "Wont fix. Multi-TFMs. Only supported with >= .NET 7.", Scope = "member", Target = "~M:Codebelt.Extensions.Xunit.Hosting.MinimalHostTest`1.#ctor(System.Boolean,`0,Xunit.ITestOutputHelper,System.Type)")] -[assembly: SuppressMessage("Maintainability", "CA1510:Use ArgumentNullException throw helper", Justification = "Wont fix. Multi-TFMs. Only supported with >= .NET 7.", Scope = "member", Target = "~M:Codebelt.Extensions.Xunit.Hosting.ServiceCollectionExtensions.AddXunitTestLogging(Microsoft.Extensions.DependencyInjection.IServiceCollection,Microsoft.Extensions.Logging.LogLevel)~Microsoft.Extensions.DependencyInjection.IServiceCollection")] -[assembly: SuppressMessage("Maintainability", "CA1510:Use ArgumentNullException throw helper", Justification = "Wont fix. Multi-TFMs. Only supported with >= .NET 7.", Scope = "member", Target = "~M:Codebelt.Extensions.Xunit.Hosting.ServiceCollectionExtensions.AddXunitTestLogging(Microsoft.Extensions.DependencyInjection.IServiceCollection,Xunit.ITestOutputHelper,Microsoft.Extensions.Logging.LogLevel)~Microsoft.Extensions.DependencyInjection.IServiceCollection")] -[assembly: SuppressMessage("Maintainability", "CA1510:Use ArgumentNullException throw helper", Justification = "Wont fix. Multi-TFMs. Only supported with >= .NET 7.", Scope = "member", Target = "~M:Codebelt.Extensions.Xunit.Hosting.ServiceCollectionExtensions.AddXunitTestLoggingOutputHelperAccessor``1(Microsoft.Extensions.DependencyInjection.IServiceCollection)~Microsoft.Extensions.DependencyInjection.IServiceCollection")] +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Major Code Smell", "S3881:\"IDisposable\" should be implemented correctly", Justification = "This is an implementation of the IDisposable interface tailored to avoid wrong implementations.", Scope = "type", Target = "~T:Codebelt.Extensions.Xunit.Hosting.HostFixture")] +[assembly: SuppressMessage("Major Code Smell", "S3971:\"GC.SuppressFinalize\" should not be called", Justification = "False-Positive due to IAsyncDisposable living side-by-side with IDisposable.", Scope = "member", Target = "~M:Codebelt.Extensions.Xunit.Hosting.HostFixture.DisposeAsync~System.Threading.Tasks.ValueTask")] +[assembly: SuppressMessage("Major Code Smell", "S2326:Unused type parameters should be removed", Justification = "TEntryPoint associates the fixture with the application's entry point at compile time; the default implementation uses its assembly to initialize the application.", Scope = "type", Target = "~T:Codebelt.Extensions.Xunit.Hosting.IApplicationFixture`1")] +[assembly: SuppressMessage("Major Code Smell", "S1144:Unused private types or members should be removed", Justification = "The inherited method implements IDeferredHost.ReleaseEntrypoint and is called through that interface.", Scope = "member", Target = "~M:Codebelt.Extensions.Xunit.Hosting.Internal.DeferredHostBuilder.DeferredHost.ReleaseEntrypoint")] +[assembly: SuppressMessage("Blocker Code Smell", "S3427:Method overloads with default parameter values should not overlap", Justification = "Avoid bumping major version by providing an extra overloaded member as optional argument.", Scope = "member", Target = "~M:Codebelt.Extensions.Xunit.Hosting.HostTestFactory.Create(System.Action{Microsoft.Extensions.DependencyInjection.IServiceCollection},System.Action{Microsoft.Extensions.Hosting.IHostBuilder},Codebelt.Extensions.Xunit.Hosting.IGenericHostFixture)~Codebelt.Extensions.Xunit.Hosting.IHostTest")] +[assembly: SuppressMessage("Blocker Code Smell", "S3427:Method overloads with default parameter values should not overlap", Justification = "Avoid bumping major version by providing an extra overloaded member as optional argument.", Scope = "member", Target = "~M:Codebelt.Extensions.Xunit.Hosting.HostTestFactory.CreateWithHostBuilderContext(System.Action{Microsoft.Extensions.Hosting.HostBuilderContext,Microsoft.Extensions.DependencyInjection.IServiceCollection},System.Action{Microsoft.Extensions.Hosting.IHostBuilder},Codebelt.Extensions.Xunit.Hosting.IGenericHostFixture)~Codebelt.Extensions.Xunit.Hosting.IHostTest")] +[assembly: SuppressMessage("Maintainability", "CA1510:Use ArgumentNullException throw helper", Justification = "Wont fix. Multi-TFMs. Only supported with >= .NET 7.", Scope = "member", Target = "~M:Codebelt.Extensions.Xunit.Hosting.HostTest`1.#ctor(System.Boolean,`0,Xunit.ITestOutputHelper,System.Type)")] +[assembly: SuppressMessage("Maintainability", "CA1510:Use ArgumentNullException throw helper", Justification = "Wont fix. Multi-TFMs. Only supported with >= .NET 7.", Scope = "member", Target = "~M:Codebelt.Extensions.Xunit.Hosting.LoggerExtensions.GetTestStore(Microsoft.Extensions.Logging.ILogger,System.String)~Codebelt.Extensions.Xunit.ITestStore{Codebelt.Extensions.Xunit.Hosting.XunitTestLoggerEntry}")] +[assembly: SuppressMessage("Maintainability", "CA1510:Use ArgumentNullException throw helper", Justification = "Wont fix. Multi-TFMs. Only supported with >= .NET 7.", Scope = "member", Target = "~M:Codebelt.Extensions.Xunit.Hosting.ManagedHostFixture.ConfigureHost(Codebelt.Extensions.Xunit.Test)")] +[assembly: SuppressMessage("Maintainability", "CA1510:Use ArgumentNullException throw helper", Justification = "Wont fix. Multi-TFMs. Only supported with >= .NET 7.", Scope = "member", Target = "~M:Codebelt.Extensions.Xunit.Hosting.ManagedMinimalHostFixture.ConfigureHost(Codebelt.Extensions.Xunit.Test)")] +[assembly: SuppressMessage("Maintainability", "CA1510:Use ArgumentNullException throw helper", Justification = "Wont fix. Multi-TFMs. Only supported with >= .NET 7.", Scope = "member", Target = "~M:Codebelt.Extensions.Xunit.Hosting.MinimalHostTest`1.#ctor(System.Boolean,`0,Xunit.ITestOutputHelper,System.Type)")] +[assembly: SuppressMessage("Maintainability", "CA1510:Use ArgumentNullException throw helper", Justification = "Wont fix. Multi-TFMs. Only supported with >= .NET 7.", Scope = "member", Target = "~M:Codebelt.Extensions.Xunit.Hosting.ServiceCollectionExtensions.AddXunitTestLogging(Microsoft.Extensions.DependencyInjection.IServiceCollection,Microsoft.Extensions.Logging.LogLevel)~Microsoft.Extensions.DependencyInjection.IServiceCollection")] +[assembly: SuppressMessage("Maintainability", "CA1510:Use ArgumentNullException throw helper", Justification = "Wont fix. Multi-TFMs. Only supported with >= .NET 7.", Scope = "member", Target = "~M:Codebelt.Extensions.Xunit.Hosting.ServiceCollectionExtensions.AddXunitTestLogging(Microsoft.Extensions.DependencyInjection.IServiceCollection,Xunit.ITestOutputHelper,Microsoft.Extensions.Logging.LogLevel)~Microsoft.Extensions.DependencyInjection.IServiceCollection")] +[assembly: SuppressMessage("Maintainability", "CA1510:Use ArgumentNullException throw helper", Justification = "Wont fix. Multi-TFMs. Only supported with >= .NET 7.", Scope = "member", Target = "~M:Codebelt.Extensions.Xunit.Hosting.ServiceCollectionExtensions.AddXunitTestLoggingOutputHelperAccessor``1(Microsoft.Extensions.DependencyInjection.IServiceCollection)~Microsoft.Extensions.DependencyInjection.IServiceCollection")] From 85dd507e511eba6855661052efb3c166469ea4fb Mon Sep 17 00:00:00 2001 From: aicia-bot Date: Tue, 27 Oct 2026 16:43:07 +0100 Subject: [PATCH 23/32] =?UTF-8?q?=F0=9F=90=9B=20keep=20deferred=20host=20c?= =?UTF-8?q?ancellation=20token=20and=20abort=20handling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cancelling a deferred host now propagates the original cancellation token instead of a bare cancel, and stopping the entry point after the host is captured no longer surfaces as an error. A null host factory check that could never fail was removed. --- .../Internal/DeferredHostBuilder.cs | 2 +- .../Internal/DeferredHostFactory.cs | 7 +------ .../Internal/ProgramHostFactoryResolver.cs | 6 ++---- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/Codebelt.Extensions.Xunit.Hosting/Internal/DeferredHostBuilder.cs b/src/Codebelt.Extensions.Xunit.Hosting/Internal/DeferredHostBuilder.cs index 60c5007..0414f19 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting/Internal/DeferredHostBuilder.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting/Internal/DeferredHostBuilder.cs @@ -169,7 +169,7 @@ public async ValueTask DisposeAsync() public async Task StartAsync(CancellationToken cancellationToken = default) { - using var registration = cancellationToken.Register(() => _hostStarted.TrySetCanceled()); + using var registration = cancellationToken.Register(() => _hostStarted.TrySetCanceled(cancellationToken)); await _hostStarted.Task.ConfigureAwait(false); } diff --git a/src/Codebelt.Extensions.Xunit.Hosting/Internal/DeferredHostFactory.cs b/src/Codebelt.Extensions.Xunit.Hosting/Internal/DeferredHostFactory.cs index 6e88043..6b2fe71 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting/Internal/DeferredHostFactory.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting/Internal/DeferredHostFactory.cs @@ -21,12 +21,7 @@ public static IHost Create(Assembly assembly, Action configureHost }); }); - var hostFactory = ProgramHostFactoryResolver.ResolveHostFactory(assembly, stopApplication, deferredHostBuilder.ConfigureHostBuilder, deferredHostBuilder.EntryPointCompleted); - if (hostFactory == null) - { - throw new InvalidOperationException($"The entry point assembly '{assembly.GetName().Name}' does not expose a supported application host."); - } - + var hostFactory = ProgramHostFactoryResolver.ResolveHostFactory(assembly, stopApplication, deferredHostBuilder.ConfigureHostBuilder, deferredHostBuilder.EntryPointCompleted) ?? throw new InvalidOperationException($"The entry point assembly '{assembly.GetName().Name}' does not expose a supported application host."); deferredHostBuilder.SetHostFactory(hostFactory); return HostBuilderFactory.Build(deferredHostBuilder, configureHost); } diff --git a/src/Codebelt.Extensions.Xunit.Hosting/Internal/ProgramHostFactoryResolver.cs b/src/Codebelt.Extensions.Xunit.Hosting/Internal/ProgramHostFactoryResolver.cs index 7ab286b..8ce19d6 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting/Internal/ProgramHostFactoryResolver.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting/Internal/ProgramHostFactoryResolver.cs @@ -158,9 +158,11 @@ private void InvokeEntryPoint() } catch (TargetInvocationException ex) when (ex.InnerException?.GetType().Name == nameof(HostAbortedException)) { + // Expected when stopping the entry point after capturing the host. } catch (HostAbortedException) { + // Expected when stopping the entry point after capturing the host. } catch (TargetInvocationException ex) { @@ -178,10 +180,6 @@ private void InvokeEntryPoint() _entryPointCompleted?.Invoke(exception); } } - - private sealed class HostAbortedException : Exception - { - } } internal sealed class HostCapture From c663260ff99a0c84f926a9ddb65b807efb88a442 Mon Sep 17 00:00:00 2001 From: aicia-bot Date: Tue, 27 Oct 2026 16:43:07 +0100 Subject: [PATCH 24/32] =?UTF-8?q?=F0=9F=90=9B=20serialize=20deferred=20hos?= =?UTF-8?q?t=20startup=20with=20a=20dedicated=20lock?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Concurrent access to a deferred host could start it more than once. A dedicated lock now guards startup, using System.Threading.Lock on net10.0 and later and a plain object on older targets. --- .../HostTest.cs | 217 +++++++++--------- 1 file changed, 111 insertions(+), 106 deletions(-) diff --git a/src/Codebelt.Extensions.Xunit.Hosting/HostTest.cs b/src/Codebelt.Extensions.Xunit.Hosting/HostTest.cs index 802cd48..fae0c68 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting/HostTest.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting/HostTest.cs @@ -1,44 +1,49 @@ using System; +using System.Threading; using Codebelt.Extensions.Xunit.Hosting.Internal; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Xunit; - -namespace Codebelt.Extensions.Xunit.Hosting; - -/// -/// Represents the non-generic base class from where its generic equivalent should derive. -/// -/// -/// -/// + +namespace Codebelt.Extensions.Xunit.Hosting; + +/// +/// Represents the non-generic base class from where its generic equivalent should derive. +/// +/// +/// +/// public abstract class HostTest : Test, IHostTest { +#if NET10_0_OR_GREATER + private readonly Lock _hostLock = new(); +#else private readonly object _hostLock = new(); +#endif private IHost _host; private bool _hostStartAttempted; - /// - /// Initializes a new instance of the class. - /// - /// An implementation of the interface. - /// The of caller that ends up invoking this instance. - protected HostTest(ITestOutputHelper output = null, Type callerType = null) : base(output, callerType) - { - } - - /// - /// Adds and to this instance. - /// - /// The initialized by the . - /// The initialized by the . - public virtual void Configure(IConfiguration configuration, IHostEnvironment environment) - { - Configuration = configuration; - Environment = environment; - } - + /// + /// Initializes a new instance of the class. + /// + /// An implementation of the interface. + /// The of caller that ends up invoking this instance. + protected HostTest(ITestOutputHelper output = null, Type callerType = null) : base(output, callerType) + { + } + + /// + /// Adds and to this instance. + /// + /// The initialized by the . + /// The initialized by the . + public virtual void Configure(IConfiguration configuration, IHostEnvironment environment) + { + Configuration = configuration; + Environment = environment; + } + /// /// Gets the initialized by the . /// @@ -78,83 +83,83 @@ protected set } } } - - /// - /// Gets the initialized by the . - /// - /// The initialized by the . - public IConfiguration Configuration { get; protected set; } - - /// - /// Gets the initialized by the . - /// - /// The initialized by the . - public IHostEnvironment Environment { get; protected set; } -} - -/// -/// Represents a base class from which all implementations of unit testing, that uses Microsoft Dependency Injection, should derive. -/// -/// The type of the object that implements the interface. -/// -/// -/// -/// The class needed to be designed in this rather complex way, as this is the only way that xUnit supports a shared context. The need for shared context is theoretical at best, but it does opt-in for Scoped instances. -public abstract class HostTest : HostTest, IClassFixture where T : class, IGenericHostFixture -{ - /// - /// Initializes a new instance of the class. - /// - /// An implementation of the interface. - /// An implementation of the interface. - /// The of caller that ends up invoking this instance. - /// - /// is null. - /// - protected HostTest(T hostFixture, ITestOutputHelper output = null, Type callerType = null) : this(false, hostFixture, output, callerType) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// A value indicating whether to skip the host fixture initialization. - /// An implementation of the interface. - /// An implementation of the interface. - /// The of caller that ends up invoking this instance. - /// - /// is null. - /// - protected HostTest(bool skipHostFixtureInitialization, T hostFixture, ITestOutputHelper output = null, Type callerType = null) : base(output, callerType) + + /// + /// Gets the initialized by the . + /// + /// The initialized by the . + public IConfiguration Configuration { get; protected set; } + + /// + /// Gets the initialized by the . + /// + /// The initialized by the . + public IHostEnvironment Environment { get; protected set; } +} + +/// +/// Represents a base class from which all implementations of unit testing, that uses Microsoft Dependency Injection, should derive. +/// +/// The type of the object that implements the interface. +/// +/// +/// +/// The class needed to be designed in this rather complex way, as this is the only way that xUnit supports a shared context. The need for shared context is theoretical at best, but it does opt-in for Scoped instances. +public abstract class HostTest : HostTest, IClassFixture where T : class, IGenericHostFixture +{ + /// + /// Initializes a new instance of the class. + /// + /// An implementation of the interface. + /// An implementation of the interface. + /// The of caller that ends up invoking this instance. + /// + /// is null. + /// + protected HostTest(T hostFixture, ITestOutputHelper output = null, Type callerType = null) : this(false, hostFixture, output, callerType) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// A value indicating whether to skip the host fixture initialization. + /// An implementation of the interface. + /// An implementation of the interface. + /// The of caller that ends up invoking this instance. + /// + /// is null. + /// + protected HostTest(bool skipHostFixtureInitialization, T hostFixture, ITestOutputHelper output = null, Type callerType = null) : base(output, callerType) { #if NETSTANDARD2_0 - if (hostFixture == null) { throw new ArgumentNullException(nameof(hostFixture)); } -#else + if (hostFixture == null) { throw new ArgumentNullException(nameof(hostFixture)); } +#else ArgumentNullException.ThrowIfNull(hostFixture); -#endif - if (skipHostFixtureInitialization) { return; } - if (!hostFixture.HasValidState()) - { - hostFixture.ConfigureHostCallback = ConfigureHost; - hostFixture.ConfigureCallback = Configure; - hostFixture.ConfigureServicesCallback = ConfigureServices; - hostFixture.ConfigureHost(this); - } - Host = hostFixture.Host; - Configure(hostFixture.Configuration, hostFixture.Environment); - } - - /// - /// Provides a way to override the defaults set up by . - /// - /// The that initializes an instance of . - protected virtual void ConfigureHost(IHostBuilder hb) - { - } - - /// - /// Adds services to the container. - /// - /// The collection of service descriptors. - public abstract void ConfigureServices(IServiceCollection services); -} +#endif + if (skipHostFixtureInitialization) { return; } + if (!hostFixture.HasValidState()) + { + hostFixture.ConfigureHostCallback = ConfigureHost; + hostFixture.ConfigureCallback = Configure; + hostFixture.ConfigureServicesCallback = ConfigureServices; + hostFixture.ConfigureHost(this); + } + Host = hostFixture.Host; + Configure(hostFixture.Configuration, hostFixture.Environment); + } + + /// + /// Provides a way to override the defaults set up by . + /// + /// The that initializes an instance of . + protected virtual void ConfigureHost(IHostBuilder hb) + { + } + + /// + /// Adds services to the container. + /// + /// The collection of service descriptors. + public abstract void ConfigureServices(IServiceCollection services); +} From bb87f77b3b99a967eeedd4f612a027d113556cab Mon Sep 17 00:00:00 2001 From: aicia-bot Date: Tue, 27 Oct 2026 16:43:08 +0100 Subject: [PATCH 25/32] =?UTF-8?q?=E2=9C=85=20add=20tests=20for=20deferred?= =?UTF-8?q?=20host=20cancellation=20lock=20and=20abort?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New tests cover cancellation of a deferred host, the abort path when stopping an entry point, and concurrent lock-protected startup. The hosting test project now references the minimal console bootstrapper so these scenarios run end to end. --- .../ApplicationHostFactoryTest.cs | 48 ++++++++++++++ .../DeferredHostCancellationTest.cs | 65 +++++++++++++++++++ ...belt.Extensions.Xunit.Hosting.Tests.csproj | 4 ++ .../HostTestLockTest.cs | 55 ++++++++++++++++ 4 files changed, 172 insertions(+) create mode 100644 test/Codebelt.Extensions.Xunit.Hosting.FunctionalTests/ApplicationHostFactoryTest.cs create mode 100644 test/Codebelt.Extensions.Xunit.Hosting.FunctionalTests/DeferredHostCancellationTest.cs create mode 100644 test/Codebelt.Extensions.Xunit.Hosting.Tests/HostTestLockTest.cs diff --git a/test/Codebelt.Extensions.Xunit.Hosting.FunctionalTests/ApplicationHostFactoryTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.FunctionalTests/ApplicationHostFactoryTest.cs new file mode 100644 index 0000000..1c62fe8 --- /dev/null +++ b/test/Codebelt.Extensions.Xunit.Hosting.FunctionalTests/ApplicationHostFactoryTest.cs @@ -0,0 +1,48 @@ +using System; +using System.Runtime.ExceptionServices; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Xunit; +using BootstrapperMinimalConsoleProgram = Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalConsole.App.Program; +using BootstrapperMinimalConsoleState = Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalConsole.App.BootstrapperMinimalConsoleState; + +namespace Codebelt.Extensions.Xunit.Hosting; + +public class ApplicationHostFactoryTest : Test +{ + public ApplicationHostFactoryTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public async Task ShouldUseFrameworkHostAbortedException_WhenStoppingEntrypointAfterHostIsBuilt() + { + var context = new AsyncLocal { Value = true }; + var observedException = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + EventHandler observer = (_, args) => + { + if (context.Value && args.Exception.GetType().Name == nameof(HostAbortedException)) + { + observedException.TrySetResult(args.Exception); + } + }; + + AppDomain.CurrentDomain.FirstChanceException += observer; + try + { + using var host = ApplicationHostFactory.Create(null); + + await host.StartAsync().WaitAsync(TimeSpan.FromSeconds(10)); + var exception = await observedException.Task.WaitAsync(TimeSpan.FromSeconds(10)); + + Assert.IsType(exception); + Assert.False(host.Services.GetRequiredService().EntrypointStarted); + } + finally + { + AppDomain.CurrentDomain.FirstChanceException -= observer; + } + } +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.FunctionalTests/DeferredHostCancellationTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.FunctionalTests/DeferredHostCancellationTest.cs new file mode 100644 index 0000000..adc519b --- /dev/null +++ b/test/Codebelt.Extensions.Xunit.Hosting.FunctionalTests/DeferredHostCancellationTest.cs @@ -0,0 +1,65 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Xunit; +using BootstrapperMinimalConsoleProgram = Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalConsole.App.Program; + +namespace Codebelt.Extensions.Xunit.Hosting; + +public class DeferredHostCancellationTest : Test +{ + public DeferredHostCancellationTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public async Task ShouldPreserveCancellationToken_WhenWaitingForEntrypointStartupIsCanceled() + { + var startup = new BlockingStartupService(); + using var host = ApplicationHostFactory.Create( + builder => builder.ConfigureServices(services => services.AddSingleton(startup)), false); + using var cancellation = new CancellationTokenSource(); + + try + { + await startup.Starting.Task.WaitAsync(TimeSpan.FromSeconds(10)); + var start = host.StartAsync(cancellation.Token); + cancellation.Cancel(); + + var exception = await Assert.ThrowsAnyAsync(() => start); + Assert.Equal(cancellation.Token, exception.CancellationToken); + } + finally + { + var started = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var stopped = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var lifetime = host.Services.GetRequiredService(); + using var startedRegistration = lifetime.ApplicationStarted.Register(() => started.TrySetResult(true)); + using var stoppedRegistration = lifetime.ApplicationStopped.Register(() => stopped.TrySetResult(true)); + startup.Continue.TrySetResult(true); + await started.Task.WaitAsync(TimeSpan.FromSeconds(10)); + lifetime.StopApplication(); + await stopped.Task.WaitAsync(TimeSpan.FromSeconds(10)); + } + } + + private sealed class BlockingStartupService : IHostedService + { + public TaskCompletionSource Starting { get; } = new(TaskCreationOptions.RunContinuationsAsynchronously); + + public TaskCompletionSource Continue { get; } = new(TaskCreationOptions.RunContinuationsAsynchronously); + + public async Task StartAsync(CancellationToken cancellationToken) + { + Starting.TrySetResult(true); + await Continue.Task; + } + + public Task StopAsync(CancellationToken cancellationToken) + { + return Task.CompletedTask; + } + } +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.Tests/Codebelt.Extensions.Xunit.Hosting.Tests.csproj b/test/Codebelt.Extensions.Xunit.Hosting.Tests/Codebelt.Extensions.Xunit.Hosting.Tests.csproj index b66b6c1..d78b9b5 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.Tests/Codebelt.Extensions.Xunit.Hosting.Tests.csproj +++ b/test/Codebelt.Extensions.Xunit.Hosting.Tests/Codebelt.Extensions.Xunit.Hosting.Tests.csproj @@ -23,4 +23,8 @@ + + + + diff --git a/test/Codebelt.Extensions.Xunit.Hosting.Tests/HostTestLockTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.Tests/HostTestLockTest.cs new file mode 100644 index 0000000..17973ac --- /dev/null +++ b/test/Codebelt.Extensions.Xunit.Hosting.Tests/HostTestLockTest.cs @@ -0,0 +1,55 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; +using Xunit; +#if NET10_0_OR_GREATER +using Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalConsole.App; +using BootstrapperMinimalConsoleProgram = Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalConsole.App.Program; +#endif + +namespace Codebelt.Extensions.Xunit.Hosting; + +public class HostTestLockTest : Test +{ + public HostTestLockTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void ShouldReturnNull_WhenHostIsNotAssigned() + { + using var test = new HostTestProbe(); + + Assert.Null(test.Host); + } + +#if NET10_0_OR_GREATER + [Fact] + public async ValueTask ShouldStartDeferredHost_WhenAccessedConcurrently() + { + using var test = new HostTestProbe(); + using var fixture = new ManagedApplicationFixture(); + fixture.ConfigureCallback = (_, _) => { }; + fixture.ConfigureHost(test); + var host = fixture.Host; + var state = (BootstrapperMinimalConsoleState)host.Services.GetService(typeof(BootstrapperMinimalConsoleState)); + Assert.NotNull(state); + Assert.False(state.EntrypointStarted); + test.SetHost(host); + + var results = await Task.WhenAll(Enumerable.Range(0, 8).Select(_ => Task.Run(() => test.Host))); + + Assert.All(results, result => Assert.Same(host, result)); + Assert.True(state.EntrypointStarted); + } +#endif + + private sealed class HostTestProbe : HostTest + { + public void SetHost(IHost host) + { + Host = host; + } + } +} From 7f53b356c7b46ac91d3cbf9658bced2fae46ad06 Mon Sep 17 00:00:00 2001 From: aicia-bot Date: Tue, 27 Oct 2026 16:43:09 +0100 Subject: [PATCH 26/32] =?UTF-8?q?=F0=9F=8E=A8=20normalize=20encoding=20and?= =?UTF-8?q?=20line=20endings=20across=20the=20solution?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Files were saved with a byte order mark and CRLF line endings, which conflicts with the LF rule in gitattributes. Stripping the BOM and converting to LF brings every file in line with the repository convention and removes noisy diffs. --- .../BootstrapperConsoleMarker.cs | 14 +- .../BootstrapperMinimalConsoleMarker.cs | 14 +- .../Program.cs | 3 +- .../BootstrapperMinimalWebMarker.cs | 22 +- .../BootstrapperMinimalWorkerMarker.cs | 22 +- .../BootstrapperMinimalWorkerService.cs | 26 +- .../BootstrapperWebMarker.cs | 22 +- .../BootstrapperWorkerMarker.cs | 22 +- .../BootstrapperWorkerService.cs | 26 +- .../ClassicProgramMarker.cs | 22 +- .../ProgramCustomization.cs | 22 +- .../ProgramMarker.cs | 22 +- .../BlockingManagedWebHostFixture.cs | 46 +- .../HostBuilderApplicationExtensions.cs | 50 +- .../Http/FakeHttpContextAccessor.cs | 140 ++-- .../Http/Features/FakeHttpRequestFeature.cs | 44 +- .../Http/Features/FakeHttpResponseFeature.cs | 88 +-- .../HttpClientExtensions.cs | 56 +- .../IPipelineTest.cs | 32 +- .../IWebHostFixture.cs | 34 +- .../IWebHostTest.cs | 20 +- .../IWebMinimalHostFixture.cs | 36 +- .../Internal/MinimalWebHostTest.cs | 142 ++-- .../Internal/WebHostTest.cs | 152 ++--- .../ManagedWebHostFixture.cs | 220 +++--- .../ManagedWebMinimalHostFixture.cs | 152 ++--- .../MinimalWebHostTest.cs | 130 ++-- .../MinimalWebHostTestFactory.cs | 146 ++-- .../Properties/AssemblyInfo.cs | 8 +- .../SelfManagedWebHostFixture.cs | 40 +- .../SelfManagedWebMinimalHostFixture.cs | 40 +- .../ServiceCollectionExtensions.cs | 88 +-- .../Tweaker.cs | 22 +- .../WebHostFixtureExtensions.cs | 44 +- .../WebHostTest.cs | 122 ++-- .../WebHostTestFactory.cs | 146 ++-- .../WebMinimalHostFixtureExtensions.cs | 46 +- .../GenericHostFixtureExtensions.cs | 44 +- .../HostFixture.cs | 394 +++++------ .../HostTestFactory.cs | 70 +- .../IConfigurationTest.cs | 32 +- .../IEnvironmentTest.cs | 30 +- .../IGenericHostFixture.cs | 62 +- .../IHostFixture.cs | 54 +- .../IHostTest.cs | 24 +- .../IMinimalHostFixture.cs | 52 +- .../Internal/HostTest.cs | 114 ++-- .../Internal/MinimalHostTest.cs | 104 +-- .../LoggerExtensions.cs | 170 ++--- .../ManagedHostFixture.cs | 188 ++--- .../ManagedMinimalHostFixture.cs | 122 ++-- .../MinimalHostFixtureExtensions.cs | 46 +- .../MinimalHostTest.cs | 152 ++--- .../MinimalHostTestFactory.cs | 70 +- .../Properties/AssemblyInfo.cs | 8 +- .../SelfManagedHostFixture.cs | 40 +- .../SelfManagedMinimalHostFixture.cs | 40 +- .../ServiceCollectionExtensions.cs | 228 +++---- .../ServiceProviderExtensions.cs | 46 +- .../Tweaker.cs | 22 +- .../XunitTestLogger.cs | 152 ++--- .../XunitTestLoggerEntry.cs | 98 +-- .../XunitTestLoggerProvider.cs | 112 +-- .../DelimitedString.cs | 56 +- .../GlobalSuppressions.cs | 22 +- src/Codebelt.Extensions.Xunit/ITest.cs | 32 +- .../ITestOutputHelperAccessor.cs | 30 +- src/Codebelt.Extensions.Xunit/ITestStore.cs | 74 +- .../InMemoryTestStore.cs | 130 ++-- .../Properties/AssemblyInfo.cs | 8 +- .../StringExtensions.cs | 86 +-- src/Codebelt.Extensions.Xunit/Test.cs | 414 +++++------ .../TestOutputHelperAccessor.cs | 62 +- .../TestOutputHelperExtensions.cs | 120 ++-- .../WildcardOptions.cs | 108 +-- ...otstrapperMinimalWebApplicationTestTest.cs | 48 +- .../BootstrapperWebApplicationTestTest.cs | 50 +- .../DeferredInvalidWebApplicationTest.cs | 22 +- .../DeferredModernWebApplicationTest.cs | 24 +- .../Assets/BoolMiddleware.cs | 78 +-- .../Assets/BoolOptions.cs | 36 +- .../Assets/FakeController.cs | 36 +- .../Assets/InvalidHostTest.cs | 20 +- .../Assets/ScopedCorrelation.cs | 14 +- .../Assets/SingletonCorrelation.cs | 14 +- .../Assets/TransientCorrelation.cs | 14 +- .../Assets/ValidHostTest.cs | 42 +- .../BlockingManagedWebHostFixtureTest.cs | 36 +- .../HostBuilderApplicationExtensionsTest.cs | 62 +- .../Features/FakeHttpResponseFeatureTest.cs | 96 +-- .../ManagedWebHostFixtureTest.cs | 126 ++-- .../MinimalMvcWebHostTestTest.cs | 114 ++-- .../MinimalWebHostTestFactoryTest.cs | 270 ++++---- .../MinimalWebHostTestTest.cs | 328 ++++----- .../MvcWebHostTestTest.cs | 112 +-- .../SelfManagedWebHostFixtureTest.cs | 36 +- .../SelfManagedWebMinimalHostFixtureTest.cs | 44 +- .../ServiceCollectionExtensionsTest.cs | 88 +-- .../WebHostTestFactoryTest.cs | 346 +++++----- .../WebHostTestTest.cs | 326 ++++----- .../BootstrapperConsoleApplicationTestTest.cs | 34 +- ...trapperMinimalWorkerApplicationTestTest.cs | 42 +- .../BootstrapperWorkerApplicationTestTest.cs | 42 +- .../Assets/InvalidHostTest.cs | 20 +- .../Assets/MinimalValidHostTest.cs | 22 +- .../Assets/ScopedCorrelation.cs | 14 +- .../Assets/SingletonCorrelation.cs | 14 +- .../Assets/TransientCorrelation.cs | 14 +- .../Assets/ValidHostTest.cs | 34 +- .../HostTestFactoryTest.cs | 100 +-- .../HostTestTest.cs | 232 +++---- .../ManagedHostFixtureTest.cs | 118 ++-- .../MinimalHostFixtureTest.cs | 112 +-- .../MinimalHostTestFactoryTest.cs | 88 +-- .../MinimalHostTestTest.cs | 196 +++--- .../ServiceCollectionExtensions.cs | 320 ++++----- .../Assets/AsyncDisposable.cs | 74 +- .../Assets/ManagedDisposable.cs | 50 +- .../Assets/UnmanagedDisposable.cs | 294 ++++---- .../Assets/WemoryStream.cs | 38 +- .../DisposableTest.cs | 186 ++--- .../InMemoryTestStoreTest.cs | 608 ++++++++--------- .../StringExtensionsTest.cs | 68 +- .../TestOutputHelperExtensionsTest.cs | 500 +++++++------- .../TestTest.cs | 644 +++++++++--------- tooling/benchmark-runner/Program.cs | 54 +- .../DelimitedStringBenchmark.cs | 126 ++-- .../InMemoryTestStoreBenchmark.cs | 2 +- .../TestBenchmark.cs | 148 ++-- 129 files changed, 6237 insertions(+), 6236 deletions(-) diff --git a/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperConsole.App/BootstrapperConsoleMarker.cs b/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperConsole.App/BootstrapperConsoleMarker.cs index dd6ac36..e3c472e 100644 --- a/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperConsole.App/BootstrapperConsoleMarker.cs +++ b/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperConsole.App/BootstrapperConsoleMarker.cs @@ -1,10 +1,10 @@ -namespace Codebelt.Extensions.Xunit.Hosting.BootstrapperConsole.App; - -public sealed class BootstrapperConsoleMarker -{ - public BootstrapperConsoleMarker(string value) - { - Value = value; +namespace Codebelt.Extensions.Xunit.Hosting.BootstrapperConsole.App; + +public sealed class BootstrapperConsoleMarker +{ + public BootstrapperConsoleMarker(string value) + { + Value = value; } public string Value { get; } diff --git a/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalConsole.App/BootstrapperMinimalConsoleMarker.cs b/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalConsole.App/BootstrapperMinimalConsoleMarker.cs index 82dd16f..109a31b 100644 --- a/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalConsole.App/BootstrapperMinimalConsoleMarker.cs +++ b/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalConsole.App/BootstrapperMinimalConsoleMarker.cs @@ -1,10 +1,10 @@ -namespace Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalConsole.App; - -public sealed class BootstrapperMinimalConsoleMarker -{ - public BootstrapperMinimalConsoleMarker(string value) - { - Value = value; +namespace Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalConsole.App; + +public sealed class BootstrapperMinimalConsoleMarker +{ + public BootstrapperMinimalConsoleMarker(string value) + { + Value = value; } public string Value { get; } diff --git a/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalConsole.App/Program.cs b/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalConsole.App/Program.cs index ed3d519..af8889e 100644 --- a/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalConsole.App/Program.cs +++ b/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalConsole.App/Program.cs @@ -26,7 +26,8 @@ public override async Task RunAsync(IServiceProvider serviceProvider, Cancellati try { await Task.Delay(Timeout.Infinite, cancellationToken).ConfigureAwait(false); - } catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested) + } + catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested) { } } diff --git a/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWeb.App/BootstrapperMinimalWebMarker.cs b/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWeb.App/BootstrapperMinimalWebMarker.cs index e376b50..c968603 100644 --- a/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWeb.App/BootstrapperMinimalWebMarker.cs +++ b/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWeb.App/BootstrapperMinimalWebMarker.cs @@ -1,11 +1,11 @@ -namespace Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWeb.App; - -public sealed class BootstrapperMinimalWebMarker -{ - public BootstrapperMinimalWebMarker(string value) - { - Value = value; - } - - public string Value { get; } -} +namespace Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWeb.App; + +public sealed class BootstrapperMinimalWebMarker +{ + public BootstrapperMinimalWebMarker(string value) + { + Value = value; + } + + public string Value { get; } +} diff --git a/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWorker.App/BootstrapperMinimalWorkerMarker.cs b/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWorker.App/BootstrapperMinimalWorkerMarker.cs index 91950c6..086471e 100644 --- a/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWorker.App/BootstrapperMinimalWorkerMarker.cs +++ b/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWorker.App/BootstrapperMinimalWorkerMarker.cs @@ -1,11 +1,11 @@ -namespace Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWorker.App; - -public sealed class BootstrapperMinimalWorkerMarker -{ - public BootstrapperMinimalWorkerMarker(string value) - { - Value = value; - } - - public string Value { get; } -} +namespace Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWorker.App; + +public sealed class BootstrapperMinimalWorkerMarker +{ + public BootstrapperMinimalWorkerMarker(string value) + { + Value = value; + } + + public string Value { get; } +} diff --git a/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWorker.App/BootstrapperMinimalWorkerService.cs b/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWorker.App/BootstrapperMinimalWorkerService.cs index b693bf9..f59955c 100644 --- a/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWorker.App/BootstrapperMinimalWorkerService.cs +++ b/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWorker.App/BootstrapperMinimalWorkerService.cs @@ -1,13 +1,13 @@ -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Hosting; - -namespace Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWorker.App; - -public sealed class BootstrapperMinimalWorkerService : BackgroundService -{ - protected override Task ExecuteAsync(CancellationToken stoppingToken) - { - return Task.CompletedTask; - } -} +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; + +namespace Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWorker.App; + +public sealed class BootstrapperMinimalWorkerService : BackgroundService +{ + protected override Task ExecuteAsync(CancellationToken stoppingToken) + { + return Task.CompletedTask; + } +} diff --git a/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperWeb.App/BootstrapperWebMarker.cs b/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperWeb.App/BootstrapperWebMarker.cs index 79f2e82..a408302 100644 --- a/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperWeb.App/BootstrapperWebMarker.cs +++ b/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperWeb.App/BootstrapperWebMarker.cs @@ -1,11 +1,11 @@ -namespace Codebelt.Extensions.Xunit.Hosting.BootstrapperWeb.App; - -public sealed class BootstrapperWebMarker -{ - public BootstrapperWebMarker(string value) - { - Value = value; - } - - public string Value { get; } -} +namespace Codebelt.Extensions.Xunit.Hosting.BootstrapperWeb.App; + +public sealed class BootstrapperWebMarker +{ + public BootstrapperWebMarker(string value) + { + Value = value; + } + + public string Value { get; } +} diff --git a/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperWorker.App/BootstrapperWorkerMarker.cs b/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperWorker.App/BootstrapperWorkerMarker.cs index 9475bba..c57c77f 100644 --- a/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperWorker.App/BootstrapperWorkerMarker.cs +++ b/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperWorker.App/BootstrapperWorkerMarker.cs @@ -1,11 +1,11 @@ -namespace Codebelt.Extensions.Xunit.Hosting.BootstrapperWorker.App; - -public sealed class BootstrapperWorkerMarker -{ - public BootstrapperWorkerMarker(string value) - { - Value = value; - } - - public string Value { get; } -} +namespace Codebelt.Extensions.Xunit.Hosting.BootstrapperWorker.App; + +public sealed class BootstrapperWorkerMarker +{ + public BootstrapperWorkerMarker(string value) + { + Value = value; + } + + public string Value { get; } +} diff --git a/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperWorker.App/BootstrapperWorkerService.cs b/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperWorker.App/BootstrapperWorkerService.cs index 38bb4ab..9fba794 100644 --- a/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperWorker.App/BootstrapperWorkerService.cs +++ b/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperWorker.App/BootstrapperWorkerService.cs @@ -1,13 +1,13 @@ -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Hosting; - -namespace Codebelt.Extensions.Xunit.Hosting.BootstrapperWorker.App; - -public sealed class BootstrapperWorkerService : BackgroundService -{ - protected override Task ExecuteAsync(CancellationToken stoppingToken) - { - return Task.CompletedTask; - } -} +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; + +namespace Codebelt.Extensions.Xunit.Hosting.BootstrapperWorker.App; + +public sealed class BootstrapperWorkerService : BackgroundService +{ + protected override Task ExecuteAsync(CancellationToken stoppingToken) + { + return Task.CompletedTask; + } +} diff --git a/app/Codebelt.Extensions.Xunit.Hosting.ClassicProgram.App/ClassicProgramMarker.cs b/app/Codebelt.Extensions.Xunit.Hosting.ClassicProgram.App/ClassicProgramMarker.cs index cc157f7..26433bb 100644 --- a/app/Codebelt.Extensions.Xunit.Hosting.ClassicProgram.App/ClassicProgramMarker.cs +++ b/app/Codebelt.Extensions.Xunit.Hosting.ClassicProgram.App/ClassicProgramMarker.cs @@ -1,11 +1,11 @@ -namespace Codebelt.Extensions.Xunit.Hosting.ClassicProgram.App; - -public sealed class ClassicProgramMarker -{ - public ClassicProgramMarker(string value) - { - Value = value; - } - - public string Value { get; } -} +namespace Codebelt.Extensions.Xunit.Hosting.ClassicProgram.App; + +public sealed class ClassicProgramMarker +{ + public ClassicProgramMarker(string value) + { + Value = value; + } + + public string Value { get; } +} diff --git a/app/Codebelt.Extensions.Xunit.Hosting.Program.App/ProgramCustomization.cs b/app/Codebelt.Extensions.Xunit.Hosting.Program.App/ProgramCustomization.cs index f9a3b9e..5553d90 100644 --- a/app/Codebelt.Extensions.Xunit.Hosting.Program.App/ProgramCustomization.cs +++ b/app/Codebelt.Extensions.Xunit.Hosting.Program.App/ProgramCustomization.cs @@ -1,11 +1,11 @@ -namespace Codebelt.Extensions.Xunit.Hosting.Program.App; - -public sealed class ProgramCustomization -{ - public ProgramCustomization(string value) - { - Value = value; - } - - public string Value { get; } -} +namespace Codebelt.Extensions.Xunit.Hosting.Program.App; + +public sealed class ProgramCustomization +{ + public ProgramCustomization(string value) + { + Value = value; + } + + public string Value { get; } +} diff --git a/app/Codebelt.Extensions.Xunit.Hosting.Program.App/ProgramMarker.cs b/app/Codebelt.Extensions.Xunit.Hosting.Program.App/ProgramMarker.cs index 58eec85..5ec9d6b 100644 --- a/app/Codebelt.Extensions.Xunit.Hosting.Program.App/ProgramMarker.cs +++ b/app/Codebelt.Extensions.Xunit.Hosting.Program.App/ProgramMarker.cs @@ -1,11 +1,11 @@ -namespace Codebelt.Extensions.Xunit.Hosting.Program.App; - -public sealed class ProgramMarker -{ - public ProgramMarker(string value) - { - Value = value; - } - - public string Value { get; } -} +namespace Codebelt.Extensions.Xunit.Hosting.Program.App; + +public sealed class ProgramMarker +{ + public ProgramMarker(string value) + { + Value = value; + } + + public string Value { get; } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/BlockingManagedWebHostFixture.cs b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/BlockingManagedWebHostFixture.cs index 303adb4..610dbc3 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/BlockingManagedWebHostFixture.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/BlockingManagedWebHostFixture.cs @@ -1,23 +1,23 @@ -using System.Threading.Tasks; -using Microsoft.Extensions.Hosting; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; - -/// -/// Extends the default implementation of the interface to be synchronous e.g., blocking where exceptions can be captured. -/// -/// -public sealed class BlockingManagedWebHostFixture : ManagedWebHostFixture -{ - /// - /// Initializes a new instance of the class. - /// - public BlockingManagedWebHostFixture() - { - AsyncHostRunnerCallback = (host, _) => - { - host.Start(); - return Task.CompletedTask; - }; - } -} +using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; + +/// +/// Extends the default implementation of the interface to be synchronous e.g., blocking where exceptions can be captured. +/// +/// +public sealed class BlockingManagedWebHostFixture : ManagedWebHostFixture +{ + /// + /// Initializes a new instance of the class. + /// + public BlockingManagedWebHostFixture() + { + AsyncHostRunnerCallback = (host, _) => + { + host.Start(); + return Task.CompletedTask; + }; + } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/HostBuilderApplicationExtensions.cs b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/HostBuilderApplicationExtensions.cs index a1aa158..f6d52e9 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/HostBuilderApplicationExtensions.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/HostBuilderApplicationExtensions.cs @@ -1,25 +1,25 @@ -using System; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.Hosting; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; - -/// -/// Provides extension methods for . -/// -public static class HostBuilderApplicationExtensions -{ - /// - /// Converts an to an . - /// - /// The to convert. - /// The instance. - /// - /// is not a . - /// - public static IHostBuilder ToHostBuilder(this IHostApplicationBuilder builder) - { - if (builder is WebApplicationBuilder webAppBuilder) { return webAppBuilder.Host; } - throw new ArgumentException($"The provided IHostApplicationBuilder is not a {nameof(WebApplicationBuilder)}.", nameof(builder)); - } -} +using System; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Hosting; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; + +/// +/// Provides extension methods for . +/// +public static class HostBuilderApplicationExtensions +{ + /// + /// Converts an to an . + /// + /// The to convert. + /// The instance. + /// + /// is not a . + /// + public static IHostBuilder ToHostBuilder(this IHostApplicationBuilder builder) + { + if (builder is WebApplicationBuilder webAppBuilder) { return webAppBuilder.Host; } + throw new ArgumentException($"The provided IHostApplicationBuilder is not a {nameof(WebApplicationBuilder)}.", nameof(builder)); + } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/Http/FakeHttpContextAccessor.cs b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/Http/FakeHttpContextAccessor.cs index 49fd808..40a081b 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/Http/FakeHttpContextAccessor.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/Http/FakeHttpContextAccessor.cs @@ -1,70 +1,70 @@ -using System; -using System.IO; -using System.Text; -using Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http.Features; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Features; -using Microsoft.Extensions.DependencyInjection; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http; - -/// -/// Provides a unit test implementation of . -/// -/// -public class FakeHttpContextAccessor : IHttpContextAccessor -{ - /// - /// Initializes a new instance of the class. - /// - /// An optional for resolving services. - public FakeHttpContextAccessor(IServiceScopeFactory factory = null) - { - var context = new DefaultHttpContext(); - var fc = new FeatureCollection(); - fc.Set(new RequestServicesFeature(context, factory)); - fc.Set(new FakeHttpResponseFeature()); - fc.Set(new FakeHttpRequestFeature()); - fc.Set(new StreamResponseBodyFeature(MakeGreeting("Hello awesome developers!"))); - context.Uninitialize(); - context.Initialize(fc); - HttpContext = context; - } - - private static Stream MakeGreeting(string greeting) - { - Stream interim = null; - Stream result = null; - try - { - interim = new MemoryStream(); - - using (var sw = new StreamWriter(interim, Encoding.UTF8, leaveOpen: true)) - { - sw.Write(greeting); - sw.Flush(); - } - - interim.Flush(); - interim.Position = 0; - - result = interim; - interim = null; - } - catch (Exception ex) - { - throw new InvalidOperationException("There is an error in the Stream being written.", ex); - } - finally - { - interim?.Dispose(); - } - return result; - } - - /// - /// Gets or sets the HTTP context. - /// - /// The HTTP context. - public HttpContext HttpContext { get; set; } -} +using System; +using System.IO; +using System.Text; +using Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http.Features; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; +using Microsoft.Extensions.DependencyInjection; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http; + +/// +/// Provides a unit test implementation of . +/// +/// +public class FakeHttpContextAccessor : IHttpContextAccessor +{ + /// + /// Initializes a new instance of the class. + /// + /// An optional for resolving services. + public FakeHttpContextAccessor(IServiceScopeFactory factory = null) + { + var context = new DefaultHttpContext(); + var fc = new FeatureCollection(); + fc.Set(new RequestServicesFeature(context, factory)); + fc.Set(new FakeHttpResponseFeature()); + fc.Set(new FakeHttpRequestFeature()); + fc.Set(new StreamResponseBodyFeature(MakeGreeting("Hello awesome developers!"))); + context.Uninitialize(); + context.Initialize(fc); + HttpContext = context; + } + + private static Stream MakeGreeting(string greeting) + { + Stream interim = null; + Stream result = null; + try + { + interim = new MemoryStream(); + + using (var sw = new StreamWriter(interim, Encoding.UTF8, leaveOpen: true)) + { + sw.Write(greeting); + sw.Flush(); + } + + interim.Flush(); + interim.Position = 0; + + result = interim; + interim = null; + } + catch (Exception ex) + { + throw new InvalidOperationException("There is an error in the Stream being written.", ex); + } + finally + { + interim?.Dispose(); + } + return result; + } + + /// + /// Gets or sets the HTTP context. + /// + /// The HTTP context. + public HttpContext HttpContext { get; set; } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/Http/Features/FakeHttpRequestFeature.cs b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/Http/Features/FakeHttpRequestFeature.cs index 1698a7c..cfcff47 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/Http/Features/FakeHttpRequestFeature.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/Http/Features/FakeHttpRequestFeature.cs @@ -1,22 +1,22 @@ -using System.Net.Http; -using Microsoft.AspNetCore.Http.Features; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http.Features; - -/// -/// Represents a way to support some default values for Request context.. -/// -/// -public class FakeHttpRequestFeature : HttpRequestFeature -{ - /// - /// Initializes a new instance of the class. - /// - public FakeHttpRequestFeature() - { - Method = HttpMethod.Get.ToString(); - Path = "/"; - Scheme = "http"; - Protocol = "HTTP/1.1"; - } -} +using System.Net.Http; +using Microsoft.AspNetCore.Http.Features; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http.Features; + +/// +/// Represents a way to support some default values for Request context.. +/// +/// +public class FakeHttpRequestFeature : HttpRequestFeature +{ + /// + /// Initializes a new instance of the class. + /// + public FakeHttpRequestFeature() + { + Method = HttpMethod.Get.ToString(); + Path = "/"; + Scheme = "http"; + Protocol = "HTTP/1.1"; + } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/Http/Features/FakeHttpResponseFeature.cs b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/Http/Features/FakeHttpResponseFeature.cs index e0469ce..c604c2c 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/Http/Features/FakeHttpResponseFeature.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/Http/Features/FakeHttpResponseFeature.cs @@ -1,44 +1,44 @@ -using System; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Features; -using Microsoft.Net.Http.Headers; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http.Features; - -/// -/// Represents a way to trigger . -/// -/// -public class FakeHttpResponseFeature : HttpResponseFeature -{ - private bool _hasStarted; - - /// - /// Initializes a new instance of the class. - /// - public FakeHttpResponseFeature() - { - Headers.Append(HeaderNames.Date, DateTime.UtcNow.ToString("R")); - } - - /// - /// Registers a callback to be invoked just before the response starts. This is the last chance to modify the , , or . - /// - /// The callback to invoke when starting the response. - /// The state to pass into the callback. - public override void OnStarting(Func callback, object state) - { - if (_hasStarted) { return; } - _hasStarted = true; - callback?.Invoke(state); - } - - /// - /// Indicates if the response has started. If true, the , - /// , and are now immutable, and - /// OnStarting should no longer be called. - /// - /// true if this instance has started; otherwise, false. - public override bool HasStarted => _hasStarted; -} +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; +using Microsoft.Net.Http.Headers; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http.Features; + +/// +/// Represents a way to trigger . +/// +/// +public class FakeHttpResponseFeature : HttpResponseFeature +{ + private bool _hasStarted; + + /// + /// Initializes a new instance of the class. + /// + public FakeHttpResponseFeature() + { + Headers.Append(HeaderNames.Date, DateTime.UtcNow.ToString("R")); + } + + /// + /// Registers a callback to be invoked just before the response starts. This is the last chance to modify the , , or . + /// + /// The callback to invoke when starting the response. + /// The state to pass into the callback. + public override void OnStarting(Func callback, object state) + { + if (_hasStarted) { return; } + _hasStarted = true; + callback?.Invoke(state); + } + + /// + /// Indicates if the response has started. If true, the , + /// , and are now immutable, and + /// OnStarting should no longer be called. + /// + /// true if this instance has started; otherwise, false. + public override bool HasStarted => _hasStarted; +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/HttpClientExtensions.cs b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/HttpClientExtensions.cs index 74cb697..39b16c1 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/HttpClientExtensions.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/HttpClientExtensions.cs @@ -1,28 +1,28 @@ -using System; -using System.Net.Http; -using System.Threading.Tasks; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; - -/// -/// Provides extension methods for the class. -/// -public static class HttpClientExtensions -{ - /// - /// Provides a convenient way to return a from a using the specified . - /// - /// The to extend. - /// The function delegate that creates a from the . Default is a GET request to the root URL ("/"). - /// A that represents the asynchronous operation. The task result contains the generated by the . - /// - /// cannot be null. - /// - /// Designed to be used in conjunction with and . - public static async Task ToHttpResponseMessageAsync(this HttpClient client, Func> responseFactory = null) - { - ArgumentNullException.ThrowIfNull(client); - responseFactory ??= c => c.GetAsync("/"); - return await responseFactory(client).ConfigureAwait(false); - } -} +using System; +using System.Net.Http; +using System.Threading.Tasks; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; + +/// +/// Provides extension methods for the class. +/// +public static class HttpClientExtensions +{ + /// + /// Provides a convenient way to return a from a using the specified . + /// + /// The to extend. + /// The function delegate that creates a from the . Default is a GET request to the root URL ("/"). + /// A that represents the asynchronous operation. The task result contains the generated by the . + /// + /// cannot be null. + /// + /// Designed to be used in conjunction with and . + public static async Task ToHttpResponseMessageAsync(this HttpClient client, Func> responseFactory = null) + { + ArgumentNullException.ThrowIfNull(client); + responseFactory ??= c => c.GetAsync("/"); + return await responseFactory(client).ConfigureAwait(false); + } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/IPipelineTest.cs b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/IPipelineTest.cs index 163dcb7..11377af 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/IPipelineTest.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/IPipelineTest.cs @@ -1,16 +1,16 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.Hosting; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; - -/// -/// Represents the members needed for ASP.NET Core pipeline testing. -/// -public interface IPipelineTest -{ - /// - /// Gets the initialized by the . - /// - /// The initialized by the . - IApplicationBuilder Application { get; } -} +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Hosting; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; + +/// +/// Represents the members needed for ASP.NET Core pipeline testing. +/// +public interface IPipelineTest +{ + /// + /// Gets the initialized by the . + /// + /// The initialized by the . + IApplicationBuilder Application { get; } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/IWebHostFixture.cs b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/IWebHostFixture.cs index fa0131a..3c205b6 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/IWebHostFixture.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/IWebHostFixture.cs @@ -1,17 +1,17 @@ -using System; -using Microsoft.AspNetCore.Builder; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; - -/// -/// Provides a way to use Microsoft Dependency Injection in unit tests tailored for ASP.NET Core. -/// -/// -public interface IWebHostFixture : IGenericHostFixture, IPipelineTest -{ - /// - /// Gets or sets the delegate that configures the HTTP request pipeline. - /// - /// The delegate that configures the HTTP request pipeline. - Action ConfigureApplicationCallback { get; set; } -} +using System; +using Microsoft.AspNetCore.Builder; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; + +/// +/// Provides a way to use Microsoft Dependency Injection in unit tests tailored for ASP.NET Core. +/// +/// +public interface IWebHostFixture : IGenericHostFixture, IPipelineTest +{ + /// + /// Gets or sets the delegate that configures the HTTP request pipeline. + /// + /// The delegate that configures the HTTP request pipeline. + Action ConfigureApplicationCallback { get; set; } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/IWebHostTest.cs b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/IWebHostTest.cs index fca9af9..ca48545 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/IWebHostTest.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/IWebHostTest.cs @@ -1,10 +1,10 @@ -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; - -/// -/// Represents the members needed for ASP.NET Core (including but not limited to MVC, Razor and related) testing. -/// -/// -/// -public interface IWebHostTest : IHostTest, IPipelineTest -{ -} +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; + +/// +/// Represents the members needed for ASP.NET Core (including but not limited to MVC, Razor and related) testing. +/// +/// +/// +public interface IWebHostTest : IHostTest, IPipelineTest +{ +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/IWebMinimalHostFixture.cs b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/IWebMinimalHostFixture.cs index 23ae0b5..d8083cf 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/IWebMinimalHostFixture.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/IWebMinimalHostFixture.cs @@ -1,18 +1,18 @@ -using System; -using Microsoft.AspNetCore.Builder; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; - -/// -/// Provides a way to use Microsoft Dependency Injection in unit tests (minimal style). -/// -/// -/// -public interface IWebMinimalHostFixture : IMinimalHostFixture, IPipelineTest -{ - /// - /// Gets or sets the delegate that configures the HTTP request pipeline. - /// - /// The delegate that configures the HTTP request pipeline. - Action ConfigureApplicationCallback { get; set; } -} +using System; +using Microsoft.AspNetCore.Builder; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; + +/// +/// Provides a way to use Microsoft Dependency Injection in unit tests (minimal style). +/// +/// +/// +public interface IWebMinimalHostFixture : IMinimalHostFixture, IPipelineTest +{ + /// + /// Gets or sets the delegate that configures the HTTP request pipeline. + /// + /// The delegate that configures the HTTP request pipeline. + Action ConfigureApplicationCallback { get; set; } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/Internal/MinimalWebHostTest.cs b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/Internal/MinimalWebHostTest.cs index f8287cf..97c4c3d 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/Internal/MinimalWebHostTest.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/Internal/MinimalWebHostTest.cs @@ -1,71 +1,71 @@ -using System; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore.Internal; - -internal sealed class MinimalWebHostTest : MinimalWebHostTest -{ - private readonly Action _pipelineConfigurator; - private readonly Action _serviceConfigurator; - private readonly Action _pipelineConfiguratorWithContext; - private readonly Action _serviceConfiguratorWithContext; - private readonly Action _hostConfigurator; - private HostBuilderContext _hostBuilderContext; - - internal MinimalWebHostTest(Action serviceConfigurator, Action pipelineConfigurator, Action hostConfigurator, IWebMinimalHostFixture hostFixture) : base(true, hostFixture, callerType: pipelineConfigurator?.Target?.GetType() ?? serviceConfigurator?.Target?.GetType() ?? hostConfigurator?.Target?.GetType()) - { - _serviceConfigurator = serviceConfigurator; - _pipelineConfigurator = pipelineConfigurator; - _hostConfigurator = hostConfigurator; - InitializeHostFixture(hostFixture); - } - - internal MinimalWebHostTest(Action serviceConfigurator, Action pipelineConfigurator, Action hostConfigurator, IWebMinimalHostFixture hostFixture) : base(true, hostFixture, callerType: pipelineConfigurator?.Target?.GetType() ?? serviceConfigurator?.Target?.GetType() ?? hostConfigurator?.Target?.GetType()) - { - _serviceConfiguratorWithContext = serviceConfigurator; - _pipelineConfiguratorWithContext = pipelineConfigurator; - _hostConfigurator = hostConfigurator; - InitializeHostFixture(hostFixture); - } - - private void InitializeHostFixture(IWebMinimalHostFixture hostFixture) - { - if (!hostFixture.HasValidState()) - { - hostFixture.ConfigureHostCallback = ConfigureHost; - hostFixture.ConfigureCallback = Configure; - hostFixture.ConfigureApplicationCallback = ConfigureApplication; - hostFixture.ConfigureHost(this); - } - Host = hostFixture.Host; - Application = hostFixture.Application; - Configure(hostFixture.Configuration, hostFixture.Environment); - } - - public override void ConfigureApplication(IApplicationBuilder app) - { - _pipelineConfigurator?.Invoke(app); - _pipelineConfiguratorWithContext?.Invoke(Tweaker.Adjust(_hostBuilderContext, hbc => - { - hbc.Configuration = Configuration; - hbc.HostingEnvironment = Environment; - return hbc; - }), app); - } - - protected override void ConfigureHost(IHostApplicationBuilder hb) - { - _hostBuilderContext = new HostBuilderContext(hb.Properties); - _hostConfigurator?.Invoke(hb); - _serviceConfigurator?.Invoke(hb.Services); - _serviceConfiguratorWithContext?.Invoke(Tweaker.Adjust(_hostBuilderContext, hbc => - { - hbc.Configuration = hb.Configuration; - hbc.HostingEnvironment = hb.Environment; - return hbc; - }), hb.Services); - hb.Services.AddFakeHttpContextAccessor(); - } -} +using System; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore.Internal; + +internal sealed class MinimalWebHostTest : MinimalWebHostTest +{ + private readonly Action _pipelineConfigurator; + private readonly Action _serviceConfigurator; + private readonly Action _pipelineConfiguratorWithContext; + private readonly Action _serviceConfiguratorWithContext; + private readonly Action _hostConfigurator; + private HostBuilderContext _hostBuilderContext; + + internal MinimalWebHostTest(Action serviceConfigurator, Action pipelineConfigurator, Action hostConfigurator, IWebMinimalHostFixture hostFixture) : base(true, hostFixture, callerType: pipelineConfigurator?.Target?.GetType() ?? serviceConfigurator?.Target?.GetType() ?? hostConfigurator?.Target?.GetType()) + { + _serviceConfigurator = serviceConfigurator; + _pipelineConfigurator = pipelineConfigurator; + _hostConfigurator = hostConfigurator; + InitializeHostFixture(hostFixture); + } + + internal MinimalWebHostTest(Action serviceConfigurator, Action pipelineConfigurator, Action hostConfigurator, IWebMinimalHostFixture hostFixture) : base(true, hostFixture, callerType: pipelineConfigurator?.Target?.GetType() ?? serviceConfigurator?.Target?.GetType() ?? hostConfigurator?.Target?.GetType()) + { + _serviceConfiguratorWithContext = serviceConfigurator; + _pipelineConfiguratorWithContext = pipelineConfigurator; + _hostConfigurator = hostConfigurator; + InitializeHostFixture(hostFixture); + } + + private void InitializeHostFixture(IWebMinimalHostFixture hostFixture) + { + if (!hostFixture.HasValidState()) + { + hostFixture.ConfigureHostCallback = ConfigureHost; + hostFixture.ConfigureCallback = Configure; + hostFixture.ConfigureApplicationCallback = ConfigureApplication; + hostFixture.ConfigureHost(this); + } + Host = hostFixture.Host; + Application = hostFixture.Application; + Configure(hostFixture.Configuration, hostFixture.Environment); + } + + public override void ConfigureApplication(IApplicationBuilder app) + { + _pipelineConfigurator?.Invoke(app); + _pipelineConfiguratorWithContext?.Invoke(Tweaker.Adjust(_hostBuilderContext, hbc => + { + hbc.Configuration = Configuration; + hbc.HostingEnvironment = Environment; + return hbc; + }), app); + } + + protected override void ConfigureHost(IHostApplicationBuilder hb) + { + _hostBuilderContext = new HostBuilderContext(hb.Properties); + _hostConfigurator?.Invoke(hb); + _serviceConfigurator?.Invoke(hb.Services); + _serviceConfiguratorWithContext?.Invoke(Tweaker.Adjust(_hostBuilderContext, hbc => + { + hbc.Configuration = hb.Configuration; + hbc.HostingEnvironment = hb.Environment; + return hbc; + }), hb.Services); + hb.Services.AddFakeHttpContextAccessor(); + } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/Internal/WebHostTest.cs b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/Internal/WebHostTest.cs index 7b56f5f..1d3405f 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/Internal/WebHostTest.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/Internal/WebHostTest.cs @@ -1,76 +1,76 @@ -using System; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore.Internal; - -internal sealed class WebHostTest : WebHostTest -{ - private readonly Action _pipelineConfigurator; - private readonly Action _serviceConfigurator; - private readonly Action _pipelineConfiguratorWithContext; - private readonly Action _serviceConfiguratorWithContext; - private readonly Action _hostConfigurator; - private HostBuilderContext _hostBuilderContext; - - internal WebHostTest(Action serviceConfigurator, Action pipelineConfigurator, Action hostConfigurator, IWebHostFixture hostFixture) : base(true, hostFixture, callerType: pipelineConfigurator?.Target?.GetType() ?? serviceConfigurator?.Target?.GetType() ?? hostConfigurator?.Target?.GetType()) - { - _serviceConfigurator = serviceConfigurator; - _pipelineConfigurator = pipelineConfigurator; - _hostConfigurator = hostConfigurator; - InitializeHostFixture(hostFixture); - } - - internal WebHostTest(Action serviceConfigurator, Action pipelineConfigurator, Action hostConfigurator, IWebHostFixture hostFixture) : base(true, hostFixture, callerType: pipelineConfigurator?.Target?.GetType() ?? serviceConfigurator?.Target?.GetType() ?? hostConfigurator?.Target?.GetType()) - { - _serviceConfiguratorWithContext = serviceConfigurator; - _pipelineConfiguratorWithContext = pipelineConfigurator; - _hostConfigurator = hostConfigurator; - InitializeHostFixture(hostFixture); - } - - private void InitializeHostFixture(IWebHostFixture hostFixture) - { - if (!hostFixture.HasValidState()) - { - hostFixture.ConfigureHostCallback = ConfigureHost; - hostFixture.ConfigureCallback = Configure; - hostFixture.ConfigureServicesCallback = ConfigureServices; - hostFixture.ConfigureApplicationCallback = ConfigureApplication; - hostFixture.ConfigureHost(this); - } - Host = hostFixture.Host; - Application = hostFixture.Application; - Configure(hostFixture.Configuration, hostFixture.Environment); - } - - public override void ConfigureApplication(IApplicationBuilder app) - { - _pipelineConfigurator?.Invoke(app); - _pipelineConfiguratorWithContext?.Invoke(Tweaker.Adjust(_hostBuilderContext, hbc => - { - hbc.Configuration = Configuration; - hbc.HostingEnvironment = Environment; - return hbc; - }), app); - } - - protected override void ConfigureHost(IHostBuilder hb) - { - _hostBuilderContext = new HostBuilderContext(hb.Properties); - _hostConfigurator?.Invoke(hb); - } - - public override void ConfigureServices(IServiceCollection services) - { - _serviceConfigurator?.Invoke(services); - _serviceConfiguratorWithContext?.Invoke(Tweaker.Adjust(_hostBuilderContext, hbc => - { - hbc.Configuration = Configuration; - hbc.HostingEnvironment = Environment; - return hbc; - }), services); - services.AddFakeHttpContextAccessor(); - } -} +using System; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore.Internal; + +internal sealed class WebHostTest : WebHostTest +{ + private readonly Action _pipelineConfigurator; + private readonly Action _serviceConfigurator; + private readonly Action _pipelineConfiguratorWithContext; + private readonly Action _serviceConfiguratorWithContext; + private readonly Action _hostConfigurator; + private HostBuilderContext _hostBuilderContext; + + internal WebHostTest(Action serviceConfigurator, Action pipelineConfigurator, Action hostConfigurator, IWebHostFixture hostFixture) : base(true, hostFixture, callerType: pipelineConfigurator?.Target?.GetType() ?? serviceConfigurator?.Target?.GetType() ?? hostConfigurator?.Target?.GetType()) + { + _serviceConfigurator = serviceConfigurator; + _pipelineConfigurator = pipelineConfigurator; + _hostConfigurator = hostConfigurator; + InitializeHostFixture(hostFixture); + } + + internal WebHostTest(Action serviceConfigurator, Action pipelineConfigurator, Action hostConfigurator, IWebHostFixture hostFixture) : base(true, hostFixture, callerType: pipelineConfigurator?.Target?.GetType() ?? serviceConfigurator?.Target?.GetType() ?? hostConfigurator?.Target?.GetType()) + { + _serviceConfiguratorWithContext = serviceConfigurator; + _pipelineConfiguratorWithContext = pipelineConfigurator; + _hostConfigurator = hostConfigurator; + InitializeHostFixture(hostFixture); + } + + private void InitializeHostFixture(IWebHostFixture hostFixture) + { + if (!hostFixture.HasValidState()) + { + hostFixture.ConfigureHostCallback = ConfigureHost; + hostFixture.ConfigureCallback = Configure; + hostFixture.ConfigureServicesCallback = ConfigureServices; + hostFixture.ConfigureApplicationCallback = ConfigureApplication; + hostFixture.ConfigureHost(this); + } + Host = hostFixture.Host; + Application = hostFixture.Application; + Configure(hostFixture.Configuration, hostFixture.Environment); + } + + public override void ConfigureApplication(IApplicationBuilder app) + { + _pipelineConfigurator?.Invoke(app); + _pipelineConfiguratorWithContext?.Invoke(Tweaker.Adjust(_hostBuilderContext, hbc => + { + hbc.Configuration = Configuration; + hbc.HostingEnvironment = Environment; + return hbc; + }), app); + } + + protected override void ConfigureHost(IHostBuilder hb) + { + _hostBuilderContext = new HostBuilderContext(hb.Properties); + _hostConfigurator?.Invoke(hb); + } + + public override void ConfigureServices(IServiceCollection services) + { + _serviceConfigurator?.Invoke(services); + _serviceConfiguratorWithContext?.Invoke(Tweaker.Adjust(_hostBuilderContext, hbc => + { + hbc.Configuration = Configuration; + hbc.HostingEnvironment = Environment; + return hbc; + }), services); + services.AddFakeHttpContextAccessor(); + } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/ManagedWebHostFixture.cs b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/ManagedWebHostFixture.cs index c35479f..309322d 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/ManagedWebHostFixture.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/ManagedWebHostFixture.cs @@ -1,110 +1,110 @@ -using System; -using System.IO; -using System.Threading; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Hosting.StaticWebAssets; -using Microsoft.AspNetCore.TestHost; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; - -/// -/// Provides a default implementation of the interface. -/// -/// -/// -public class ManagedWebHostFixture : ManagedHostFixture, IWebHostFixture -{ - /// - /// Initializes a new instance of the class. - /// - public ManagedWebHostFixture() - { - } - - /// - /// Creates and configures the of this instance. - /// - /// The object that inherits from . - /// was added to support those cases where the caller is required in the host configuration. - /// - /// is null. - /// - /// - /// is not assignable from . - /// - public override void ConfigureHost(Test hostTest) - { - ArgumentNullException.ThrowIfNull(hostTest); - if (!HasTypes(hostTest.GetType(), typeof(WebHostTest<>))) { throw new ArgumentOutOfRangeException(nameof(hostTest), typeof(WebHostTest<>), $"{nameof(hostTest)} is not assignable from WebHostTest."); } - if (!this.HasValidState()) { return; } // had to include this due to dual-call this method (one uncontrolled from xUnit library reflection magic; second controlled from this library) - - var hb = new HostBuilder() - .ConfigureWebHost(webBuilder => - { - webBuilder - .UseTestServer(o => o.PreserveExecutionContext = true) - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseEnvironment("Development") - .ConfigureAppConfiguration((context, config) => - { - config.AddJsonFile("appsettings.json", true, true) - .AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json", true, true) - .AddEnvironmentVariables(); - - StaticWebAssetsLoader.UseStaticWebAssets(context.HostingEnvironment, context.Configuration); - - ConfigureCallback(config.Build(), context.HostingEnvironment); - }) - .ConfigureLogging((context, logging) => - { - logging.AddConfiguration(context.Configuration.GetSection("Logging")); - logging.AddConsole(); - logging.AddDebug(); - logging.AddEventSourceLogger(); - }) - .ConfigureServices((context, services) => - { - Configuration = context.Configuration; - Environment = context.HostingEnvironment; - ConfigureServicesCallback(services); - }) - .Configure(app => - { - ConfigureApplicationCallback.Invoke(app); - Application = app; - } - ) - .UseSetting(HostDefaults.ApplicationKey, hostTest.CallerType.Assembly.GetName().Name); - }); - -#if NET10_0_OR_GREATER - hb.UseDefaultServiceProvider(o => - { - o.ValidateOnBuild = true; - o.ValidateScopes = true; - }); -#endif - - ConfigureHostCallback(hb); - - Host = hb.Build(); - - AsyncHostRunnerCallback(Host, CancellationToken.None); - } - - /// - /// Gets or sets the delegate that configures the HTTP request pipeline. - /// - /// The delegate that configures the HTTP request pipeline. - public Action ConfigureApplicationCallback { get; set; } - - /// - /// Gets the initialized by the . - /// - /// The initialized by the . - public IApplicationBuilder Application { get; protected set; } -} +using System; +using System.IO; +using System.Threading; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Hosting.StaticWebAssets; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; + +/// +/// Provides a default implementation of the interface. +/// +/// +/// +public class ManagedWebHostFixture : ManagedHostFixture, IWebHostFixture +{ + /// + /// Initializes a new instance of the class. + /// + public ManagedWebHostFixture() + { + } + + /// + /// Creates and configures the of this instance. + /// + /// The object that inherits from . + /// was added to support those cases where the caller is required in the host configuration. + /// + /// is null. + /// + /// + /// is not assignable from . + /// + public override void ConfigureHost(Test hostTest) + { + ArgumentNullException.ThrowIfNull(hostTest); + if (!HasTypes(hostTest.GetType(), typeof(WebHostTest<>))) { throw new ArgumentOutOfRangeException(nameof(hostTest), typeof(WebHostTest<>), $"{nameof(hostTest)} is not assignable from WebHostTest."); } + if (!this.HasValidState()) { return; } // had to include this due to dual-call this method (one uncontrolled from xUnit library reflection magic; second controlled from this library) + + var hb = new HostBuilder() + .ConfigureWebHost(webBuilder => + { + webBuilder + .UseTestServer(o => o.PreserveExecutionContext = true) + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseEnvironment("Development") + .ConfigureAppConfiguration((context, config) => + { + config.AddJsonFile("appsettings.json", true, true) + .AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json", true, true) + .AddEnvironmentVariables(); + + StaticWebAssetsLoader.UseStaticWebAssets(context.HostingEnvironment, context.Configuration); + + ConfigureCallback(config.Build(), context.HostingEnvironment); + }) + .ConfigureLogging((context, logging) => + { + logging.AddConfiguration(context.Configuration.GetSection("Logging")); + logging.AddConsole(); + logging.AddDebug(); + logging.AddEventSourceLogger(); + }) + .ConfigureServices((context, services) => + { + Configuration = context.Configuration; + Environment = context.HostingEnvironment; + ConfigureServicesCallback(services); + }) + .Configure(app => + { + ConfigureApplicationCallback.Invoke(app); + Application = app; + } + ) + .UseSetting(HostDefaults.ApplicationKey, hostTest.CallerType.Assembly.GetName().Name); + }); + +#if NET10_0_OR_GREATER + hb.UseDefaultServiceProvider(o => + { + o.ValidateOnBuild = true; + o.ValidateScopes = true; + }); +#endif + + ConfigureHostCallback(hb); + + Host = hb.Build(); + + AsyncHostRunnerCallback(Host, CancellationToken.None); + } + + /// + /// Gets or sets the delegate that configures the HTTP request pipeline. + /// + /// The delegate that configures the HTTP request pipeline. + public Action ConfigureApplicationCallback { get; set; } + + /// + /// Gets the initialized by the . + /// + /// The initialized by the . + public IApplicationBuilder Application { get; protected set; } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/ManagedWebMinimalHostFixture.cs b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/ManagedWebMinimalHostFixture.cs index 29c14f8..5266512 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/ManagedWebMinimalHostFixture.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/ManagedWebMinimalHostFixture.cs @@ -1,76 +1,76 @@ -using System; -using System.Threading; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.TestHost; -using Microsoft.Extensions.Hosting; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; - -/// -/// Provides a default implementation of the interface. -/// -/// -/// -/// This is the "modern" minimal style implementation of . -public class ManagedWebMinimalHostFixture : ManagedMinimalHostFixture, IWebMinimalHostFixture -{ - /// - /// Initializes a new instance of the class. - /// - public ManagedWebMinimalHostFixture() - { - } - - /// - /// Creates and configures the of this instance. - /// - /// The object that inherits from . - /// was added to support those cases where the caller is required in the host configuration. - /// - /// is null. - /// - /// - /// is not assignable from . - /// - public override void ConfigureHost(Test hostTest) - { - ArgumentNullException.ThrowIfNull(hostTest); - if (!HasTypes(hostTest.GetType(), typeof(MinimalWebHostTest<>))) { throw new ArgumentOutOfRangeException(nameof(hostTest), typeof(MinimalWebHostTest<>), $"{nameof(hostTest)} is not assignable from MinimalWebHostTest."); } - - var hb = WebApplication.CreateBuilder(new WebApplicationOptions() - { - EnvironmentName = "Development", - ApplicationName = hostTest.CallerType.Assembly.GetName().Name - }); - - hb.WebHost.UseTestServer(o => o.PreserveExecutionContext = true); - - Configuration = hb.Configuration; - Environment = hb.Environment; - - ConfigureCallback(Configuration, Environment); - - ConfigureHostCallback(hb); - - var webApplication = hb.Build(); - - ConfigureApplicationCallback(webApplication); - Application = webApplication; - - Host = webApplication; - - AsyncHostRunnerCallback(Host, CancellationToken.None); - } - - /// - /// Gets or sets the delegate that configures the HTTP request pipeline. - /// - /// The delegate that configures the HTTP request pipeline. - public Action ConfigureApplicationCallback { get; set; } - - /// - /// Gets the initialized by the . - /// - /// The initialized by the . - public IApplicationBuilder Application { get; protected set; } -} +using System; +using System.Threading; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.Hosting; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; + +/// +/// Provides a default implementation of the interface. +/// +/// +/// +/// This is the "modern" minimal style implementation of . +public class ManagedWebMinimalHostFixture : ManagedMinimalHostFixture, IWebMinimalHostFixture +{ + /// + /// Initializes a new instance of the class. + /// + public ManagedWebMinimalHostFixture() + { + } + + /// + /// Creates and configures the of this instance. + /// + /// The object that inherits from . + /// was added to support those cases where the caller is required in the host configuration. + /// + /// is null. + /// + /// + /// is not assignable from . + /// + public override void ConfigureHost(Test hostTest) + { + ArgumentNullException.ThrowIfNull(hostTest); + if (!HasTypes(hostTest.GetType(), typeof(MinimalWebHostTest<>))) { throw new ArgumentOutOfRangeException(nameof(hostTest), typeof(MinimalWebHostTest<>), $"{nameof(hostTest)} is not assignable from MinimalWebHostTest."); } + + var hb = WebApplication.CreateBuilder(new WebApplicationOptions() + { + EnvironmentName = "Development", + ApplicationName = hostTest.CallerType.Assembly.GetName().Name + }); + + hb.WebHost.UseTestServer(o => o.PreserveExecutionContext = true); + + Configuration = hb.Configuration; + Environment = hb.Environment; + + ConfigureCallback(Configuration, Environment); + + ConfigureHostCallback(hb); + + var webApplication = hb.Build(); + + ConfigureApplicationCallback(webApplication); + Application = webApplication; + + Host = webApplication; + + AsyncHostRunnerCallback(Host, CancellationToken.None); + } + + /// + /// Gets or sets the delegate that configures the HTTP request pipeline. + /// + /// The delegate that configures the HTTP request pipeline. + public Action ConfigureApplicationCallback { get; set; } + + /// + /// Gets the initialized by the . + /// + /// The initialized by the . + public IApplicationBuilder Application { get; protected set; } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/MinimalWebHostTest.cs b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/MinimalWebHostTest.cs index c08b789..f591ca6 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/MinimalWebHostTest.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/MinimalWebHostTest.cs @@ -1,65 +1,65 @@ -using System; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.Hosting; -using Xunit; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; - -/// -/// Represents a base class from which all implementations of unit testing, that uses Microsoft Dependency Injection and depends on ASP.NET Core (minimal style), should derive. -/// -/// The type of the object that implements the interface. -/// -/// -/// -/// The class needed to be designed in this rather complex way, as this is the only way that xUnit supports a shared context. The need for shared context is theoretical at best, but it does opt-in for Scoped instances. -public abstract class MinimalWebHostTest : MinimalHostTest, IWebHostTest, IClassFixture where T : class, IWebMinimalHostFixture -{ - /// - /// Initializes a new instance of the class. - /// - /// An implementation of the interface. - /// An implementation of the interface. - /// The of caller that ends up invoking this instance. - protected MinimalWebHostTest(T hostFixture, ITestOutputHelper output = null, Type callerType = null) : this(false, hostFixture, output, callerType) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// A value indicating whether to skip the host fixture initialization. - /// An implementation of the interface. - /// An implementation of the interface. - /// The of caller that ends up invoking this instance. - /// - /// is null. - /// - protected MinimalWebHostTest(bool skipHostFixtureInitialization, T hostFixture, ITestOutputHelper output = null, Type callerType = null) : base(output, callerType) - { - ArgumentNullException.ThrowIfNull(hostFixture); - if (skipHostFixtureInitialization) { return; } - if (!hostFixture.HasValidState()) - { - hostFixture.ConfigureCallback = Configure; - hostFixture.ConfigureHostCallback = ConfigureHost; - hostFixture.ConfigureApplicationCallback = ConfigureApplication; - hostFixture.ConfigureHost(this); - } - Host = hostFixture.Host; - Application = hostFixture.Application; - Configure(hostFixture.Configuration, hostFixture.Environment); - } - - /// - /// Gets the initialized by the . - /// - /// The initialized by the . - public IApplicationBuilder Application { get; protected set; } - - /// - /// Configures the HTTP request pipeline. - /// - /// The type that provides the mechanisms to configure the HTTP request pipeline. - public abstract void ConfigureApplication(IApplicationBuilder app); -} +using System; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Hosting; +using Xunit; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; + +/// +/// Represents a base class from which all implementations of unit testing, that uses Microsoft Dependency Injection and depends on ASP.NET Core (minimal style), should derive. +/// +/// The type of the object that implements the interface. +/// +/// +/// +/// The class needed to be designed in this rather complex way, as this is the only way that xUnit supports a shared context. The need for shared context is theoretical at best, but it does opt-in for Scoped instances. +public abstract class MinimalWebHostTest : MinimalHostTest, IWebHostTest, IClassFixture where T : class, IWebMinimalHostFixture +{ + /// + /// Initializes a new instance of the class. + /// + /// An implementation of the interface. + /// An implementation of the interface. + /// The of caller that ends up invoking this instance. + protected MinimalWebHostTest(T hostFixture, ITestOutputHelper output = null, Type callerType = null) : this(false, hostFixture, output, callerType) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// A value indicating whether to skip the host fixture initialization. + /// An implementation of the interface. + /// An implementation of the interface. + /// The of caller that ends up invoking this instance. + /// + /// is null. + /// + protected MinimalWebHostTest(bool skipHostFixtureInitialization, T hostFixture, ITestOutputHelper output = null, Type callerType = null) : base(output, callerType) + { + ArgumentNullException.ThrowIfNull(hostFixture); + if (skipHostFixtureInitialization) { return; } + if (!hostFixture.HasValidState()) + { + hostFixture.ConfigureCallback = Configure; + hostFixture.ConfigureHostCallback = ConfigureHost; + hostFixture.ConfigureApplicationCallback = ConfigureApplication; + hostFixture.ConfigureHost(this); + } + Host = hostFixture.Host; + Application = hostFixture.Application; + Configure(hostFixture.Configuration, hostFixture.Environment); + } + + /// + /// Gets the initialized by the . + /// + /// The initialized by the . + public IApplicationBuilder Application { get; protected set; } + + /// + /// Configures the HTTP request pipeline. + /// + /// The type that provides the mechanisms to configure the HTTP request pipeline. + public abstract void ConfigureApplication(IApplicationBuilder app); +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/MinimalWebHostTestFactory.cs b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/MinimalWebHostTestFactory.cs index ee843b1..b67e439 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/MinimalWebHostTestFactory.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/MinimalWebHostTestFactory.cs @@ -1,73 +1,73 @@ -using System; -using System.Net.Http; -using System.Threading.Tasks; -using Codebelt.Extensions.Xunit.Hosting.AspNetCore.Internal; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.TestHost; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; - -/// -/// Provides a set of static methods for ASP.NET Core (including, but not limited to MVC, Razor and related) unit testing (minimal style). -/// -/// . -public static class MinimalWebHostTestFactory -{ - /// - /// Creates and returns an implementation. - /// - /// The which may be configured. - /// The which may be configured. - /// The which may be configured. - /// An optional implementation to use instead of the default instance. - /// An instance of an implementation. - public static IWebHostTest Create(Action serviceSetup = null, Action pipelineSetup = null, Action hostSetup = null, IWebMinimalHostFixture hostFixture = null) - { - return new MinimalWebHostTest(serviceSetup, pipelineSetup, hostSetup, hostFixture ?? new ManagedWebMinimalHostFixture()); - } - - /// - /// Creates and returns an implementation. - /// - /// The which may be configured. - /// The which may be configured. - /// The which may be configured. - /// An optional implementation to use instead of the default instance. - /// An instance of an implementation. - public static IWebHostTest CreateWithHostBuilderContext(Action serviceSetup = null, Action pipelineSetup = null, Action hostSetup = null, IWebMinimalHostFixture hostFixture = null) - { - return new MinimalWebHostTest(serviceSetup, pipelineSetup, hostSetup, hostFixture ?? new ManagedWebMinimalHostFixture()); - } - - /// - /// Runs a middleware and returns an for making HTTP requests to the test server. - /// - /// The which may be configured. - /// The which may be configured. - /// The which may be configured. - /// The function delegate that creates a from the . Default is a GET request to the root URL ("/"). - /// An optional implementation to use instead of the default instance. - /// A that represents the asynchronous operation. The task result contains the for the test server. - public static async Task RunAsync(Action serviceSetup = null, Action pipelineSetup = null, Action hostSetup = null, Func> responseFactory = null, IWebMinimalHostFixture hostFixture = null) - { - using var client = Create(serviceSetup, pipelineSetup, hostSetup, hostFixture).Host.GetTestClient(); - return await client.ToHttpResponseMessageAsync(responseFactory).ConfigureAwait(false); - } - - /// - /// Runs a filter/middleware test. - /// - /// The which may be configured. - /// The which may be configured. - /// The which may be configured. - /// The function delegate that creates a from the . Default is a GET request to the root URL ("/"). - /// An optional implementation to use instead of the default instance. - /// A that represents the asynchronous operation. The task result contains the for the test server. - public static async Task RunWithHostBuilderContextAsync(Action serviceSetup = null, Action pipelineSetup = null, Action hostSetup = null, Func> responseFactory = null, IWebMinimalHostFixture hostFixture = null) - { - using var client = CreateWithHostBuilderContext(serviceSetup, pipelineSetup, hostSetup, hostFixture).Host.GetTestClient(); - return await client.ToHttpResponseMessageAsync(responseFactory).ConfigureAwait(false); - } -} +using System; +using System.Net.Http; +using System.Threading.Tasks; +using Codebelt.Extensions.Xunit.Hosting.AspNetCore.Internal; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; + +/// +/// Provides a set of static methods for ASP.NET Core (including, but not limited to MVC, Razor and related) unit testing (minimal style). +/// +/// . +public static class MinimalWebHostTestFactory +{ + /// + /// Creates and returns an implementation. + /// + /// The which may be configured. + /// The which may be configured. + /// The which may be configured. + /// An optional implementation to use instead of the default instance. + /// An instance of an implementation. + public static IWebHostTest Create(Action serviceSetup = null, Action pipelineSetup = null, Action hostSetup = null, IWebMinimalHostFixture hostFixture = null) + { + return new MinimalWebHostTest(serviceSetup, pipelineSetup, hostSetup, hostFixture ?? new ManagedWebMinimalHostFixture()); + } + + /// + /// Creates and returns an implementation. + /// + /// The which may be configured. + /// The which may be configured. + /// The which may be configured. + /// An optional implementation to use instead of the default instance. + /// An instance of an implementation. + public static IWebHostTest CreateWithHostBuilderContext(Action serviceSetup = null, Action pipelineSetup = null, Action hostSetup = null, IWebMinimalHostFixture hostFixture = null) + { + return new MinimalWebHostTest(serviceSetup, pipelineSetup, hostSetup, hostFixture ?? new ManagedWebMinimalHostFixture()); + } + + /// + /// Runs a middleware and returns an for making HTTP requests to the test server. + /// + /// The which may be configured. + /// The which may be configured. + /// The which may be configured. + /// The function delegate that creates a from the . Default is a GET request to the root URL ("/"). + /// An optional implementation to use instead of the default instance. + /// A that represents the asynchronous operation. The task result contains the for the test server. + public static async Task RunAsync(Action serviceSetup = null, Action pipelineSetup = null, Action hostSetup = null, Func> responseFactory = null, IWebMinimalHostFixture hostFixture = null) + { + using var client = Create(serviceSetup, pipelineSetup, hostSetup, hostFixture).Host.GetTestClient(); + return await client.ToHttpResponseMessageAsync(responseFactory).ConfigureAwait(false); + } + + /// + /// Runs a filter/middleware test. + /// + /// The which may be configured. + /// The which may be configured. + /// The which may be configured. + /// The function delegate that creates a from the . Default is a GET request to the root URL ("/"). + /// An optional implementation to use instead of the default instance. + /// A that represents the asynchronous operation. The task result contains the for the test server. + public static async Task RunWithHostBuilderContextAsync(Action serviceSetup = null, Action pipelineSetup = null, Action hostSetup = null, Func> responseFactory = null, IWebMinimalHostFixture hostFixture = null) + { + using var client = CreateWithHostBuilderContext(serviceSetup, pipelineSetup, hostSetup, hostFixture).Host.GetTestClient(); + return await client.ToHttpResponseMessageAsync(responseFactory).ConfigureAwait(false); + } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/Properties/AssemblyInfo.cs b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/Properties/AssemblyInfo.cs index 5d2cd22..24478c5 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/Properties/AssemblyInfo.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/Properties/AssemblyInfo.cs @@ -1,4 +1,4 @@ -using System.Runtime.InteropServices; - -[assembly: ComVisible(false)] -[assembly: Guid("eecbaeec-0593-4e91-9ef3-417e986bc341")] \ No newline at end of file +using System.Runtime.InteropServices; + +[assembly: ComVisible(false)] +[assembly: Guid("eecbaeec-0593-4e91-9ef3-417e986bc341")] diff --git a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/SelfManagedWebHostFixture.cs b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/SelfManagedWebHostFixture.cs index 5b90e9a..babf574 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/SelfManagedWebHostFixture.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/SelfManagedWebHostFixture.cs @@ -1,20 +1,20 @@ -using System.Threading.Tasks; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; - -/// -/// Represents a self-managed implementation of the class. -/// -public sealed class SelfManagedWebHostFixture : ManagedWebHostFixture -{ - /// - /// Initializes a new instance of the class. - /// - /// - /// This constructor sets the to a no-op asynchronous delegate. - /// - public SelfManagedWebHostFixture() - { - AsyncHostRunnerCallback = (_, __) => Task.CompletedTask; - } -} +using System.Threading.Tasks; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; + +/// +/// Represents a self-managed implementation of the class. +/// +public sealed class SelfManagedWebHostFixture : ManagedWebHostFixture +{ + /// + /// Initializes a new instance of the class. + /// + /// + /// This constructor sets the to a no-op asynchronous delegate. + /// + public SelfManagedWebHostFixture() + { + AsyncHostRunnerCallback = (_, __) => Task.CompletedTask; + } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/SelfManagedWebMinimalHostFixture.cs b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/SelfManagedWebMinimalHostFixture.cs index 8cefce7..6dcc317 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/SelfManagedWebMinimalHostFixture.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/SelfManagedWebMinimalHostFixture.cs @@ -1,20 +1,20 @@ -using System.Threading.Tasks; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; - -/// -/// Represents a self-managed implementation of the class. -/// -public sealed class SelfManagedWebMinimalHostFixture : ManagedWebMinimalHostFixture -{ - /// - /// Initializes a new instance of the class. - /// - /// - /// This constructor sets the to a no-op asynchronous delegate. - /// - public SelfManagedWebMinimalHostFixture() - { - AsyncHostRunnerCallback = (_, __) => Task.CompletedTask; - } -} +using System.Threading.Tasks; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; + +/// +/// Represents a self-managed implementation of the class. +/// +public sealed class SelfManagedWebMinimalHostFixture : ManagedWebMinimalHostFixture +{ + /// + /// Initializes a new instance of the class. + /// + /// + /// This constructor sets the to a no-op asynchronous delegate. + /// + public SelfManagedWebMinimalHostFixture() + { + AsyncHostRunnerCallback = (_, __) => Task.CompletedTask; + } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/ServiceCollectionExtensions.cs b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/ServiceCollectionExtensions.cs index 9e1db52..57b3593 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/ServiceCollectionExtensions.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/ServiceCollectionExtensions.cs @@ -1,44 +1,44 @@ -using System; -using System.ComponentModel; -using Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; - -/// -/// Extension methods for the interface. -/// -public static class ServiceCollectionExtensions -{ - /// - /// Adds a unit test optimized implementation for the service. - /// - /// The to extend. - /// The lifetime of the service. Default is . - /// A reference to after the operation has completed. - public static IServiceCollection AddFakeHttpContextAccessor(this IServiceCollection services, ServiceLifetime lifetime = ServiceLifetime.Singleton) - { - switch (lifetime) - { - case ServiceLifetime.Transient: - services.TryAddTransient(FakeHttpContextAccessorFactory); - break; - case ServiceLifetime.Scoped: - services.TryAddScoped(FakeHttpContextAccessorFactory); - break; - case ServiceLifetime.Singleton: - services.TryAddSingleton(FakeHttpContextAccessorFactory); - break; - default: - throw new InvalidEnumArgumentException(nameof(lifetime), (int)lifetime, typeof(ServiceLifetime)); - } - return services; - } - - private static IHttpContextAccessor FakeHttpContextAccessorFactory(IServiceProvider provider) - { - return new FakeHttpContextAccessor(provider.GetRequiredService()); - } -} +using System; +using System.ComponentModel; +using Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; + +/// +/// Extension methods for the interface. +/// +public static class ServiceCollectionExtensions +{ + /// + /// Adds a unit test optimized implementation for the service. + /// + /// The to extend. + /// The lifetime of the service. Default is . + /// A reference to after the operation has completed. + public static IServiceCollection AddFakeHttpContextAccessor(this IServiceCollection services, ServiceLifetime lifetime = ServiceLifetime.Singleton) + { + switch (lifetime) + { + case ServiceLifetime.Transient: + services.TryAddTransient(FakeHttpContextAccessorFactory); + break; + case ServiceLifetime.Scoped: + services.TryAddScoped(FakeHttpContextAccessorFactory); + break; + case ServiceLifetime.Singleton: + services.TryAddSingleton(FakeHttpContextAccessorFactory); + break; + default: + throw new InvalidEnumArgumentException(nameof(lifetime), (int)lifetime, typeof(ServiceLifetime)); + } + return services; + } + + private static IHttpContextAccessor FakeHttpContextAccessorFactory(IServiceProvider provider) + { + return new FakeHttpContextAccessor(provider.GetRequiredService()); + } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/Tweaker.cs b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/Tweaker.cs index 116c425..2b6644c 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/Tweaker.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/Tweaker.cs @@ -1,11 +1,11 @@ -using System; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; - -internal static class Tweaker -{ - internal static T Adjust(T value, Func converter) - { - return converter == null ? value : converter.Invoke(value); - } -} +using System; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; + +internal static class Tweaker +{ + internal static T Adjust(T value, Func converter) + { + return converter == null ? value : converter.Invoke(value); + } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/WebHostFixtureExtensions.cs b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/WebHostFixtureExtensions.cs index c589bfa..288fa50 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/WebHostFixtureExtensions.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/WebHostFixtureExtensions.cs @@ -1,22 +1,22 @@ -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; - -/// -/// Extension methods for the interface. -/// -public static class WebHostFixtureExtensions -{ - /// - /// Determines whether the specified has a valid state. - /// - /// The to check. - /// true if the specified has a valid state; otherwise, false. - /// - /// A valid state is defined as having non-null values for the following properties: - /// , and . - /// - public static bool HasValidState(this IWebHostFixture hostFixture) - { - var hasValidState = ((IGenericHostFixture)hostFixture).HasValidState(); - return hasValidState && hostFixture.ConfigureApplicationCallback != null; - } -} +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; + +/// +/// Extension methods for the interface. +/// +public static class WebHostFixtureExtensions +{ + /// + /// Determines whether the specified has a valid state. + /// + /// The to check. + /// true if the specified has a valid state; otherwise, false. + /// + /// A valid state is defined as having non-null values for the following properties: + /// , and . + /// + public static bool HasValidState(this IWebHostFixture hostFixture) + { + var hasValidState = ((IGenericHostFixture)hostFixture).HasValidState(); + return hasValidState && hostFixture.ConfigureApplicationCallback != null; + } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/WebHostTest.cs b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/WebHostTest.cs index 7a89a13..6dad06e 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/WebHostTest.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/WebHostTest.cs @@ -1,61 +1,61 @@ -using System; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.Hosting; -using Xunit; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; - -/// -/// Represents a base class from which all implementations of unit testing, that uses Microsoft Dependency Injection and depends on ASP.NET Core, should derive. -/// -/// The type of the object that implements the interface. -/// -/// -public abstract class WebHostTest : HostTest, IWebHostTest where T : class, IWebHostFixture -{ - /// - /// Initializes a new instance of the class. - /// - /// An implementation of the interface. - /// An implementation of the interface. - /// The of caller that ends up invoking this instance. - protected WebHostTest(T hostFixture, ITestOutputHelper output = null, Type callerType = null) : this(false, hostFixture, output, callerType) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// A value indicating whether to skip the host fixture initialization. - /// An implementation of the interface. - /// An implementation of the interface. - /// The of caller that ends up invoking this instance. - protected WebHostTest(bool skipHostFixtureInitialization, T hostFixture, ITestOutputHelper output = null, Type callerType = null) : base(skipHostFixtureInitialization, hostFixture, output, callerType) - { - ArgumentNullException.ThrowIfNull(hostFixture); - if (skipHostFixtureInitialization) { return; } - if (!hostFixture.HasValidState()) - { - hostFixture.ConfigureHostCallback = ConfigureHost; - hostFixture.ConfigureCallback = Configure; - hostFixture.ConfigureServicesCallback = ConfigureServices; - hostFixture.ConfigureApplicationCallback = ConfigureApplication; - hostFixture.ConfigureHost(this); - } - Host = hostFixture.Host; - Application = hostFixture.Application; - Configure(hostFixture.Configuration, hostFixture.Environment); - } - - /// - /// Gets the initialized by the . - /// - /// The initialized by the . - public IApplicationBuilder Application { get; protected set; } - - /// - /// Configures the HTTP request pipeline. - /// - /// The type that provides the mechanisms to configure the HTTP request pipeline. - public abstract void ConfigureApplication(IApplicationBuilder app); -} +using System; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Hosting; +using Xunit; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; + +/// +/// Represents a base class from which all implementations of unit testing, that uses Microsoft Dependency Injection and depends on ASP.NET Core, should derive. +/// +/// The type of the object that implements the interface. +/// +/// +public abstract class WebHostTest : HostTest, IWebHostTest where T : class, IWebHostFixture +{ + /// + /// Initializes a new instance of the class. + /// + /// An implementation of the interface. + /// An implementation of the interface. + /// The of caller that ends up invoking this instance. + protected WebHostTest(T hostFixture, ITestOutputHelper output = null, Type callerType = null) : this(false, hostFixture, output, callerType) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// A value indicating whether to skip the host fixture initialization. + /// An implementation of the interface. + /// An implementation of the interface. + /// The of caller that ends up invoking this instance. + protected WebHostTest(bool skipHostFixtureInitialization, T hostFixture, ITestOutputHelper output = null, Type callerType = null) : base(skipHostFixtureInitialization, hostFixture, output, callerType) + { + ArgumentNullException.ThrowIfNull(hostFixture); + if (skipHostFixtureInitialization) { return; } + if (!hostFixture.HasValidState()) + { + hostFixture.ConfigureHostCallback = ConfigureHost; + hostFixture.ConfigureCallback = Configure; + hostFixture.ConfigureServicesCallback = ConfigureServices; + hostFixture.ConfigureApplicationCallback = ConfigureApplication; + hostFixture.ConfigureHost(this); + } + Host = hostFixture.Host; + Application = hostFixture.Application; + Configure(hostFixture.Configuration, hostFixture.Environment); + } + + /// + /// Gets the initialized by the . + /// + /// The initialized by the . + public IApplicationBuilder Application { get; protected set; } + + /// + /// Configures the HTTP request pipeline. + /// + /// The type that provides the mechanisms to configure the HTTP request pipeline. + public abstract void ConfigureApplication(IApplicationBuilder app); +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/WebHostTestFactory.cs b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/WebHostTestFactory.cs index 4904d93..309e823 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/WebHostTestFactory.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/WebHostTestFactory.cs @@ -1,73 +1,73 @@ -using System; -using System.Net.Http; -using System.Threading.Tasks; -using Codebelt.Extensions.Xunit.Hosting.AspNetCore.Internal; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.TestHost; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; - -/// -/// Provides a set of static methods for ASP.NET Core (including, but not limited to MVC, Razor and related) unit testing. -/// -/// . -public static class WebHostTestFactory -{ - /// - /// Creates and returns an implementation. - /// - /// The which may be configured. - /// The which may be configured. - /// The which may be configured. - /// An optional implementation to use instead of the default instance. - /// An instance of an implementation. - public static IWebHostTest Create(Action serviceSetup = null, Action pipelineSetup = null, Action hostSetup = null, IWebHostFixture hostFixture = null) - { - return new WebHostTest(serviceSetup, pipelineSetup, hostSetup, hostFixture ?? new ManagedWebHostFixture()); - } - - /// - /// Creates and returns an implementation. - /// - /// The which may be configured. - /// The which may be configured. - /// The which may be configured. - /// An optional implementation to use instead of the default instance. - /// An instance of an implementation. - public static IWebHostTest CreateWithHostBuilderContext(Action serviceSetup = null, Action pipelineSetup = null, Action hostSetup = null, IWebHostFixture hostFixture = null) - { - return new WebHostTest(serviceSetup, pipelineSetup, hostSetup, hostFixture ?? new ManagedWebHostFixture()); - } - - /// - /// Runs a middleware and returns an for making HTTP requests to the test server. - /// - /// The which may be configured. - /// The which may be configured. - /// The which may be configured. - /// The function delegate that creates a from the . Default is a GET request to the root URL ("/"). - /// An optional implementation to use instead of the default instance. - /// A that represents the asynchronous operation. The task result contains the for the test server. - public static async Task RunAsync(Action serviceSetup = null, Action pipelineSetup = null, Action hostSetup = null, Func> responseFactory = null, IWebHostFixture hostFixture = null) - { - using var client = Create(serviceSetup, pipelineSetup, hostSetup, hostFixture).Host.GetTestClient(); - return await client.ToHttpResponseMessageAsync(responseFactory).ConfigureAwait(false); - } - - /// - /// Runs a filter/middleware test. - /// - /// The which may be configured. - /// The which may be configured. - /// The which may be configured. - /// The function delegate that creates a from the . Default is a GET request to the root URL ("/"). - /// An optional implementation to use instead of the default instance. - /// A that represents the asynchronous operation. The task result contains the for the test server. - public static async Task RunWithHostBuilderContextAsync(Action serviceSetup = null, Action pipelineSetup = null, Action hostSetup = null, Func> responseFactory = null, IWebHostFixture hostFixture = null) - { - using var client = CreateWithHostBuilderContext(serviceSetup, pipelineSetup, hostSetup, hostFixture).Host.GetTestClient(); - return await client.ToHttpResponseMessageAsync(responseFactory).ConfigureAwait(false); - } -} +using System; +using System.Net.Http; +using System.Threading.Tasks; +using Codebelt.Extensions.Xunit.Hosting.AspNetCore.Internal; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; + +/// +/// Provides a set of static methods for ASP.NET Core (including, but not limited to MVC, Razor and related) unit testing. +/// +/// . +public static class WebHostTestFactory +{ + /// + /// Creates and returns an implementation. + /// + /// The which may be configured. + /// The which may be configured. + /// The which may be configured. + /// An optional implementation to use instead of the default instance. + /// An instance of an implementation. + public static IWebHostTest Create(Action serviceSetup = null, Action pipelineSetup = null, Action hostSetup = null, IWebHostFixture hostFixture = null) + { + return new WebHostTest(serviceSetup, pipelineSetup, hostSetup, hostFixture ?? new ManagedWebHostFixture()); + } + + /// + /// Creates and returns an implementation. + /// + /// The which may be configured. + /// The which may be configured. + /// The which may be configured. + /// An optional implementation to use instead of the default instance. + /// An instance of an implementation. + public static IWebHostTest CreateWithHostBuilderContext(Action serviceSetup = null, Action pipelineSetup = null, Action hostSetup = null, IWebHostFixture hostFixture = null) + { + return new WebHostTest(serviceSetup, pipelineSetup, hostSetup, hostFixture ?? new ManagedWebHostFixture()); + } + + /// + /// Runs a middleware and returns an for making HTTP requests to the test server. + /// + /// The which may be configured. + /// The which may be configured. + /// The which may be configured. + /// The function delegate that creates a from the . Default is a GET request to the root URL ("/"). + /// An optional implementation to use instead of the default instance. + /// A that represents the asynchronous operation. The task result contains the for the test server. + public static async Task RunAsync(Action serviceSetup = null, Action pipelineSetup = null, Action hostSetup = null, Func> responseFactory = null, IWebHostFixture hostFixture = null) + { + using var client = Create(serviceSetup, pipelineSetup, hostSetup, hostFixture).Host.GetTestClient(); + return await client.ToHttpResponseMessageAsync(responseFactory).ConfigureAwait(false); + } + + /// + /// Runs a filter/middleware test. + /// + /// The which may be configured. + /// The which may be configured. + /// The which may be configured. + /// The function delegate that creates a from the . Default is a GET request to the root URL ("/"). + /// An optional implementation to use instead of the default instance. + /// A that represents the asynchronous operation. The task result contains the for the test server. + public static async Task RunWithHostBuilderContextAsync(Action serviceSetup = null, Action pipelineSetup = null, Action hostSetup = null, Func> responseFactory = null, IWebHostFixture hostFixture = null) + { + using var client = CreateWithHostBuilderContext(serviceSetup, pipelineSetup, hostSetup, hostFixture).Host.GetTestClient(); + return await client.ToHttpResponseMessageAsync(responseFactory).ConfigureAwait(false); + } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/WebMinimalHostFixtureExtensions.cs b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/WebMinimalHostFixtureExtensions.cs index de3562f..33a9471 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/WebMinimalHostFixtureExtensions.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/WebMinimalHostFixtureExtensions.cs @@ -1,23 +1,23 @@ -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; - -/// -/// Extension methods for the interface. -/// -public static class WebMinimalHostFixtureExtensions -{ - /// - /// Determines whether the specified has a valid state. - /// - /// The to check. - /// true if the specified has a valid state; otherwise, false. - /// - /// A valid state is defined as having non-null values for the following properties: - /// , , - /// , and . - /// - public static bool HasValidState(this IWebMinimalHostFixture hostFixture) - { - var hasValidState = ((IMinimalHostFixture)hostFixture).HasValidState(); - return hasValidState && hostFixture.ConfigureApplicationCallback != null && hostFixture.Application != null; - } -} +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; + +/// +/// Extension methods for the interface. +/// +public static class WebMinimalHostFixtureExtensions +{ + /// + /// Determines whether the specified has a valid state. + /// + /// The to check. + /// true if the specified has a valid state; otherwise, false. + /// + /// A valid state is defined as having non-null values for the following properties: + /// , , + /// , and . + /// + public static bool HasValidState(this IWebMinimalHostFixture hostFixture) + { + var hasValidState = ((IMinimalHostFixture)hostFixture).HasValidState(); + return hasValidState && hostFixture.ConfigureApplicationCallback != null && hostFixture.Application != null; + } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting/GenericHostFixtureExtensions.cs b/src/Codebelt.Extensions.Xunit.Hosting/GenericHostFixtureExtensions.cs index 32f110a..377d50e 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting/GenericHostFixtureExtensions.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting/GenericHostFixtureExtensions.cs @@ -1,22 +1,22 @@ -namespace Codebelt.Extensions.Xunit.Hosting; - -/// -/// Extension methods for the interface. -/// -public static class GenericHostFixtureExtensions -{ - /// - /// Determines whether the specified has a valid state. - /// - /// The to check. - /// true if the specified has a valid state; otherwise, false. - /// - /// A valid state is defined as having non-null values for the following properties: - /// and . - /// - public static bool HasValidState(this IGenericHostFixture hostFixture) - { - return hostFixture.ConfigureServicesCallback != null && - hostFixture.ConfigureHostCallback != null; - } -} +namespace Codebelt.Extensions.Xunit.Hosting; + +/// +/// Extension methods for the interface. +/// +public static class GenericHostFixtureExtensions +{ + /// + /// Determines whether the specified has a valid state. + /// + /// The to check. + /// true if the specified has a valid state; otherwise, false. + /// + /// A valid state is defined as having non-null values for the following properties: + /// and . + /// + public static bool HasValidState(this IGenericHostFixture hostFixture) + { + return hostFixture.ConfigureServicesCallback != null && + hostFixture.ConfigureHostCallback != null; + } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting/HostFixture.cs b/src/Codebelt.Extensions.Xunit.Hosting/HostFixture.cs index 1646886..cee8a4c 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting/HostFixture.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting/HostFixture.cs @@ -1,197 +1,197 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Hosting; -using Xunit; - -namespace Codebelt.Extensions.Xunit.Hosting; - -/// -/// Represents the base class from which all implementations of xUnit fixture concept should derive. -/// -/// -public abstract class HostFixture : IHostFixture, IAsyncLifetime -{ -#if NET10_0_OR_GREATER - private readonly Lock _lock = new(); -#else - private readonly object _lock = new(); -#endif - - private Func _asyncHostRunnerCallback = async (host, cancellationToken) => - { - if (SynchronizationContext.Current == null) - { - await host.StartAsync(cancellationToken).ConfigureAwait(false); - } - else - { - Task.Run(async () => - { - await host.StartAsync(cancellationToken).ConfigureAwait(false); - }).GetAwaiter().GetResult(); - } - - // this was done to reduce the risk of deadlocks (https://stackoverflow.com/questions/50918647/why-does-this-xunit-test-deadlock-on-a-single-cpu-vm/50953607#50953607) - }; - - /// - /// Initializes a new instance of the class. - /// - protected HostFixture() - { - } - - /// - /// Determines whether the specified contains one or more of the specified target . - /// - /// The to validate. - /// The target types to be matched against. - /// true if the contains one or more of the specified target types; otherwise, false. - protected static bool HasTypes(Type type, params Type[] types) - { - foreach (var tt in types) - { - var st = type; - while (st != null) - { - if (st.IsGenericType && tt == st.GetGenericTypeDefinition()) { return true; } - if (st == tt) { return true; } - st = st.BaseType; - } - } - return false; - } - - /// - /// Gets or sets the delegate responsible for running the . - /// - /// The delegate responsible for running the . - /// - /// cannot be null. - /// - protected Func AsyncHostRunnerCallback - { - get => _asyncHostRunnerCallback; - set => _asyncHostRunnerCallback = value ?? throw new ArgumentNullException(nameof(value), "The host runner delegate cannot be null."); - } - - /// - /// Gets or sets the delegate that initializes the test class. - /// - /// The delegate that initializes the test class. - /// Mimics the Startup convention. - public Action ConfigureCallback { get; set; } - - /// - /// Gets or sets the initialized by this instance. - /// - /// The initialized by this instance. - public IHost Host { get; protected set; } - - /// - /// Gets the initialized by this instance. - /// - /// The initialized by this instance. - public IConfiguration Configuration { get; protected set; } - - /// - /// Gets the initialized by this instance. - /// - /// The initialized by this instance. - public IHostEnvironment Environment { get; protected set; } - - /// - /// Gets a value indicating whether this object is disposed. - /// - /// true if this object is disposed; otherwise, false. - public bool Disposed { get; private set; } - - /// - /// Called when this object is being disposed by either or having disposing set to true and is false. - /// - protected virtual void OnDisposeManagedResources() - { - Host?.Dispose(); - } - - /// - /// Called when this object is being disposed by . - /// -#if NET10_0_OR_GREATER - protected virtual async ValueTask OnDisposeManagedResourcesAsync() - { - if (Host is IAsyncDisposable asyncDisposable) - { - await asyncDisposable.DisposeAsync(); - } - else - { - Host?.Dispose(); - } - } -#else - protected virtual ValueTask OnDisposeManagedResourcesAsync() - { - OnDisposeManagedResources(); - return default; - } -#endif - - /// - /// Called when this object is being disposed by either or and is false. - /// - protected virtual void OnDisposeUnmanagedResources() - { - } - - /// - /// Releases all resources used by the object. - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Releases the unmanaged resources used by the object and optionally releases the managed resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected void Dispose(bool disposing) - { - if (Disposed) { return; } - lock (_lock) - { - if (Disposed) { return; } - if (disposing) - { - OnDisposeManagedResources(); - } - OnDisposeUnmanagedResources(); - Disposed = true; - } - } - - /// - /// Asynchronously releases the resources used by the . - /// - /// A that represents the asynchronous dispose operation. - /// https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-disposeasync#the-disposeasync-method - public async ValueTask DisposeAsync() - { - await OnDisposeManagedResourcesAsync().ConfigureAwait(false); - Dispose(false); - GC.SuppressFinalize(this); - } - - /// - /// Called immediately after the class has been created, before it is used. - /// - /// A that represents the asynchronous operation. - public virtual ValueTask InitializeAsync() - { - return default; - } -} +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; +using Xunit; + +namespace Codebelt.Extensions.Xunit.Hosting; + +/// +/// Represents the base class from which all implementations of xUnit fixture concept should derive. +/// +/// +public abstract class HostFixture : IHostFixture, IAsyncLifetime +{ +#if NET10_0_OR_GREATER + private readonly Lock _lock = new(); +#else + private readonly object _lock = new(); +#endif + + private Func _asyncHostRunnerCallback = async (host, cancellationToken) => + { + if (SynchronizationContext.Current == null) + { + await host.StartAsync(cancellationToken).ConfigureAwait(false); + } + else + { + Task.Run(async () => + { + await host.StartAsync(cancellationToken).ConfigureAwait(false); + }).GetAwaiter().GetResult(); + } + + // this was done to reduce the risk of deadlocks (https://stackoverflow.com/questions/50918647/why-does-this-xunit-test-deadlock-on-a-single-cpu-vm/50953607#50953607) + }; + + /// + /// Initializes a new instance of the class. + /// + protected HostFixture() + { + } + + /// + /// Determines whether the specified contains one or more of the specified target . + /// + /// The to validate. + /// The target types to be matched against. + /// true if the contains one or more of the specified target types; otherwise, false. + protected static bool HasTypes(Type type, params Type[] types) + { + foreach (var tt in types) + { + var st = type; + while (st != null) + { + if (st.IsGenericType && tt == st.GetGenericTypeDefinition()) { return true; } + if (st == tt) { return true; } + st = st.BaseType; + } + } + return false; + } + + /// + /// Gets or sets the delegate responsible for running the . + /// + /// The delegate responsible for running the . + /// + /// cannot be null. + /// + protected Func AsyncHostRunnerCallback + { + get => _asyncHostRunnerCallback; + set => _asyncHostRunnerCallback = value ?? throw new ArgumentNullException(nameof(value), "The host runner delegate cannot be null."); + } + + /// + /// Gets or sets the delegate that initializes the test class. + /// + /// The delegate that initializes the test class. + /// Mimics the Startup convention. + public Action ConfigureCallback { get; set; } + + /// + /// Gets or sets the initialized by this instance. + /// + /// The initialized by this instance. + public IHost Host { get; protected set; } + + /// + /// Gets the initialized by this instance. + /// + /// The initialized by this instance. + public IConfiguration Configuration { get; protected set; } + + /// + /// Gets the initialized by this instance. + /// + /// The initialized by this instance. + public IHostEnvironment Environment { get; protected set; } + + /// + /// Gets a value indicating whether this object is disposed. + /// + /// true if this object is disposed; otherwise, false. + public bool Disposed { get; private set; } + + /// + /// Called when this object is being disposed by either or having disposing set to true and is false. + /// + protected virtual void OnDisposeManagedResources() + { + Host?.Dispose(); + } + + /// + /// Called when this object is being disposed by . + /// +#if NET10_0_OR_GREATER + protected virtual async ValueTask OnDisposeManagedResourcesAsync() + { + if (Host is IAsyncDisposable asyncDisposable) + { + await asyncDisposable.DisposeAsync(); + } + else + { + Host?.Dispose(); + } + } +#else + protected virtual ValueTask OnDisposeManagedResourcesAsync() + { + OnDisposeManagedResources(); + return default; + } +#endif + + /// + /// Called when this object is being disposed by either or and is false. + /// + protected virtual void OnDisposeUnmanagedResources() + { + } + + /// + /// Releases all resources used by the object. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Releases the unmanaged resources used by the object and optionally releases the managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected void Dispose(bool disposing) + { + if (Disposed) { return; } + lock (_lock) + { + if (Disposed) { return; } + if (disposing) + { + OnDisposeManagedResources(); + } + OnDisposeUnmanagedResources(); + Disposed = true; + } + } + + /// + /// Asynchronously releases the resources used by the . + /// + /// A that represents the asynchronous dispose operation. + /// https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-disposeasync#the-disposeasync-method + public async ValueTask DisposeAsync() + { + await OnDisposeManagedResourcesAsync().ConfigureAwait(false); + Dispose(false); + GC.SuppressFinalize(this); + } + + /// + /// Called immediately after the class has been created, before it is used. + /// + /// A that represents the asynchronous operation. + public virtual ValueTask InitializeAsync() + { + return default; + } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting/HostTestFactory.cs b/src/Codebelt.Extensions.Xunit.Hosting/HostTestFactory.cs index fb4dd21..07edc0c 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting/HostTestFactory.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting/HostTestFactory.cs @@ -1,35 +1,35 @@ -using System; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; - -namespace Codebelt.Extensions.Xunit.Hosting; - -/// -/// Provides a set of static methods for unit testing. -/// -public static class HostTestFactory -{ - /// - /// Creates and returns an implementation. - /// - /// The which may be configured. - /// The which may be configured. - /// An optional implementation to use instead of the default instance. - /// An instance of an implementation. - public static IHostTest Create(Action serviceSetup = null, Action hostSetup = null, IGenericHostFixture hostFixture = null) - { - return new Internal.HostTest(serviceSetup, hostSetup, hostFixture ?? new ManagedHostFixture()); - } - - /// - /// Creates and returns an implementation. - /// - /// The which may be configured. - /// The which may be configured. - /// An optional implementation to use instead of the default instance. - /// An instance of an implementation. - public static IHostTest CreateWithHostBuilderContext(Action serviceSetup = null, Action hostSetup = null, IGenericHostFixture hostFixture = null) - { - return new Internal.HostTest(serviceSetup, hostSetup, hostFixture ?? new ManagedHostFixture()); - } -} +using System; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace Codebelt.Extensions.Xunit.Hosting; + +/// +/// Provides a set of static methods for unit testing. +/// +public static class HostTestFactory +{ + /// + /// Creates and returns an implementation. + /// + /// The which may be configured. + /// The which may be configured. + /// An optional implementation to use instead of the default instance. + /// An instance of an implementation. + public static IHostTest Create(Action serviceSetup = null, Action hostSetup = null, IGenericHostFixture hostFixture = null) + { + return new Internal.HostTest(serviceSetup, hostSetup, hostFixture ?? new ManagedHostFixture()); + } + + /// + /// Creates and returns an implementation. + /// + /// The which may be configured. + /// The which may be configured. + /// An optional implementation to use instead of the default instance. + /// An instance of an implementation. + public static IHostTest CreateWithHostBuilderContext(Action serviceSetup = null, Action hostSetup = null, IGenericHostFixture hostFixture = null) + { + return new Internal.HostTest(serviceSetup, hostSetup, hostFixture ?? new ManagedHostFixture()); + } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting/IConfigurationTest.cs b/src/Codebelt.Extensions.Xunit.Hosting/IConfigurationTest.cs index e925a28..bf26bc3 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting/IConfigurationTest.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting/IConfigurationTest.cs @@ -1,16 +1,16 @@ -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Hosting; - -namespace Codebelt.Extensions.Xunit.Hosting; - -/// -/// Represents the members needed for DI testing with support for Configuration. -/// -public interface IConfigurationTest -{ - /// - /// Gets the initialized by the . - /// - /// The initialized by the . - IConfiguration Configuration { get; } -} +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; + +namespace Codebelt.Extensions.Xunit.Hosting; + +/// +/// Represents the members needed for DI testing with support for Configuration. +/// +public interface IConfigurationTest +{ + /// + /// Gets the initialized by the . + /// + /// The initialized by the . + IConfiguration Configuration { get; } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting/IEnvironmentTest.cs b/src/Codebelt.Extensions.Xunit.Hosting/IEnvironmentTest.cs index 6801a74..aae9179 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting/IEnvironmentTest.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting/IEnvironmentTest.cs @@ -1,15 +1,15 @@ -using Microsoft.Extensions.Hosting; - -namespace Codebelt.Extensions.Xunit.Hosting; - -/// -/// Represents the members needed for DI testing with support for . -/// -public interface IEnvironmentTest -{ - /// - /// Gets the initialized by the . - /// - /// The initialized by the . - IHostEnvironment Environment { get; } -} +using Microsoft.Extensions.Hosting; + +namespace Codebelt.Extensions.Xunit.Hosting; + +/// +/// Represents the members needed for DI testing with support for . +/// +public interface IEnvironmentTest +{ + /// + /// Gets the initialized by the . + /// + /// The initialized by the . + IHostEnvironment Environment { get; } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting/IGenericHostFixture.cs b/src/Codebelt.Extensions.Xunit.Hosting/IGenericHostFixture.cs index 457c579..db50169 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting/IGenericHostFixture.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting/IGenericHostFixture.cs @@ -1,31 +1,31 @@ -using System; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; - -namespace Codebelt.Extensions.Xunit.Hosting; - -/// -/// Provides a way to use Microsoft Dependency Injection in unit tests. -/// -/// -public interface IGenericHostFixture : IHostFixture -{ - /// - /// Gets or sets the delegate that adds services to the container. - /// - /// The delegate that adds services to the container. - Action ConfigureServicesCallback { get; set; } - - /// - /// Gets or sets the delegate that provides a way to override the defaults set up by . - /// - /// The delegate that provides a way to override the . - Action ConfigureHostCallback { get; set; } - - /// - /// Creates and configures the of this . - /// - /// The object that inherits from . - /// was added to support those cases where the caller is required in the host configuration. - void ConfigureHost(Test hostTest); -} +using System; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace Codebelt.Extensions.Xunit.Hosting; + +/// +/// Provides a way to use Microsoft Dependency Injection in unit tests. +/// +/// +public interface IGenericHostFixture : IHostFixture +{ + /// + /// Gets or sets the delegate that adds services to the container. + /// + /// The delegate that adds services to the container. + Action ConfigureServicesCallback { get; set; } + + /// + /// Gets or sets the delegate that provides a way to override the defaults set up by . + /// + /// The delegate that provides a way to override the . + Action ConfigureHostCallback { get; set; } + + /// + /// Creates and configures the of this . + /// + /// The object that inherits from . + /// was added to support those cases where the caller is required in the host configuration. + void ConfigureHost(Test hostTest); +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting/IHostFixture.cs b/src/Codebelt.Extensions.Xunit.Hosting/IHostFixture.cs index 92b247f..e5a69ca 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting/IHostFixture.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting/IHostFixture.cs @@ -1,27 +1,27 @@ -using System; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Hosting; - -namespace Codebelt.Extensions.Xunit.Hosting; - -/// -/// Provides a way to support app and lifetime management in unit tests. -/// -/// -/// -/// -/// -public interface IHostFixture : IConfigurationTest, IEnvironmentTest, IDisposable, IAsyncDisposable -{ - /// - /// Gets the initialized by either the or . - /// - /// The initialized by the or . - IHost Host { get; } - - /// - /// Gets or sets the delegate that adds configuration and environment information to a . - /// - /// The delegate that adds configuration and environment information to a . - Action ConfigureCallback { get; set; } -} +using System; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; + +namespace Codebelt.Extensions.Xunit.Hosting; + +/// +/// Provides a way to support app and lifetime management in unit tests. +/// +/// +/// +/// +/// +public interface IHostFixture : IConfigurationTest, IEnvironmentTest, IDisposable, IAsyncDisposable +{ + /// + /// Gets the initialized by either the or . + /// + /// The initialized by the or . + IHost Host { get; } + + /// + /// Gets or sets the delegate that adds configuration and environment information to a . + /// + /// The delegate that adds configuration and environment information to a . + Action ConfigureCallback { get; set; } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting/IHostTest.cs b/src/Codebelt.Extensions.Xunit.Hosting/IHostTest.cs index 5e242bf..7f79b4f 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting/IHostTest.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting/IHostTest.cs @@ -1,15 +1,15 @@ -using Microsoft.Extensions.Hosting; - -namespace Codebelt.Extensions.Xunit.Hosting; - -/// -/// Represents the members needed for bare-bone DI testing with support for . -/// -/// -/// -/// -public interface IHostTest : IConfigurationTest, IEnvironmentTest, ITest -{ +using Microsoft.Extensions.Hosting; + +namespace Codebelt.Extensions.Xunit.Hosting; + +/// +/// Represents the members needed for bare-bone DI testing with support for . +/// +/// +/// +/// +public interface IHostTest : IConfigurationTest, IEnvironmentTest, ITest +{ /// /// Gets the initialized by the . /// diff --git a/src/Codebelt.Extensions.Xunit.Hosting/IMinimalHostFixture.cs b/src/Codebelt.Extensions.Xunit.Hosting/IMinimalHostFixture.cs index 1c95f83..5c59dea 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting/IMinimalHostFixture.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting/IMinimalHostFixture.cs @@ -1,26 +1,26 @@ -using Microsoft.Extensions.Hosting; -using System; - -namespace Codebelt.Extensions.Xunit.Hosting; - -/// -/// Provides a way to use Microsoft Dependency Injection in unit tests (minimal style). -/// -/// -/// -/// -public interface IMinimalHostFixture : IHostFixture -{ - /// - /// Gets or sets the delegate that provides a way to override the defaults set up by . - /// - /// The delegate that provides a way to override the . - Action ConfigureHostCallback { get; set; } - - /// - /// Creates and configures the of this . - /// - /// The object that inherits from . - /// was added to support those cases where the caller is required in the host configuration. - void ConfigureHost(Test hostTest); -} +using Microsoft.Extensions.Hosting; +using System; + +namespace Codebelt.Extensions.Xunit.Hosting; + +/// +/// Provides a way to use Microsoft Dependency Injection in unit tests (minimal style). +/// +/// +/// +/// +public interface IMinimalHostFixture : IHostFixture +{ + /// + /// Gets or sets the delegate that provides a way to override the defaults set up by . + /// + /// The delegate that provides a way to override the . + Action ConfigureHostCallback { get; set; } + + /// + /// Creates and configures the of this . + /// + /// The object that inherits from . + /// was added to support those cases where the caller is required in the host configuration. + void ConfigureHost(Test hostTest); +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting/Internal/HostTest.cs b/src/Codebelt.Extensions.Xunit.Hosting/Internal/HostTest.cs index 87268ba..2328a4e 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting/Internal/HostTest.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting/Internal/HostTest.cs @@ -1,57 +1,57 @@ -using System; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; - -namespace Codebelt.Extensions.Xunit.Hosting.Internal; - -internal sealed class HostTest : HostTest -{ - private readonly Action _serviceConfigurator; - private readonly Action _serviceConfiguratorWithContext; - private readonly Action _hostConfigurator; - private HostBuilderContext _hostBuilderContext; - - internal HostTest(Action serviceConfigurator, Action hostConfigurator, IGenericHostFixture hostFixture) : base(true, hostFixture, callerType: serviceConfigurator?.Target?.GetType() ?? hostConfigurator?.Target?.GetType()) - { - _serviceConfigurator = serviceConfigurator; - _hostConfigurator = hostConfigurator; - InitializeHostFixture(hostFixture); - } - - internal HostTest(Action serviceConfigurator, Action hostConfigurator, IGenericHostFixture hostFixture) : base(true, hostFixture, callerType: serviceConfigurator?.Target?.GetType() ?? hostConfigurator?.Target?.GetType()) - { - _serviceConfiguratorWithContext = serviceConfigurator; - _hostConfigurator = hostConfigurator; - InitializeHostFixture(hostFixture); - } - - private void InitializeHostFixture(IGenericHostFixture hostFixture) - { - if (!hostFixture.HasValidState()) - { - hostFixture.ConfigureHostCallback = ConfigureHost; - hostFixture.ConfigureCallback = Configure; - hostFixture.ConfigureServicesCallback = ConfigureServices; - hostFixture.ConfigureHost(this); - } - Host = hostFixture.Host; - Configure(hostFixture.Configuration, hostFixture.Environment); - } - - protected override void ConfigureHost(IHostBuilder hb) - { - _hostBuilderContext = new HostBuilderContext(hb.Properties); - _hostConfigurator?.Invoke(hb); - } - - public override void ConfigureServices(IServiceCollection services) - { - _serviceConfigurator?.Invoke(services); - _serviceConfiguratorWithContext?.Invoke(Tweaker.Adjust(_hostBuilderContext, hbc => - { - hbc.Configuration = Configuration; - hbc.HostingEnvironment = Environment; - return hbc; - }), services); - } -} +using System; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace Codebelt.Extensions.Xunit.Hosting.Internal; + +internal sealed class HostTest : HostTest +{ + private readonly Action _serviceConfigurator; + private readonly Action _serviceConfiguratorWithContext; + private readonly Action _hostConfigurator; + private HostBuilderContext _hostBuilderContext; + + internal HostTest(Action serviceConfigurator, Action hostConfigurator, IGenericHostFixture hostFixture) : base(true, hostFixture, callerType: serviceConfigurator?.Target?.GetType() ?? hostConfigurator?.Target?.GetType()) + { + _serviceConfigurator = serviceConfigurator; + _hostConfigurator = hostConfigurator; + InitializeHostFixture(hostFixture); + } + + internal HostTest(Action serviceConfigurator, Action hostConfigurator, IGenericHostFixture hostFixture) : base(true, hostFixture, callerType: serviceConfigurator?.Target?.GetType() ?? hostConfigurator?.Target?.GetType()) + { + _serviceConfiguratorWithContext = serviceConfigurator; + _hostConfigurator = hostConfigurator; + InitializeHostFixture(hostFixture); + } + + private void InitializeHostFixture(IGenericHostFixture hostFixture) + { + if (!hostFixture.HasValidState()) + { + hostFixture.ConfigureHostCallback = ConfigureHost; + hostFixture.ConfigureCallback = Configure; + hostFixture.ConfigureServicesCallback = ConfigureServices; + hostFixture.ConfigureHost(this); + } + Host = hostFixture.Host; + Configure(hostFixture.Configuration, hostFixture.Environment); + } + + protected override void ConfigureHost(IHostBuilder hb) + { + _hostBuilderContext = new HostBuilderContext(hb.Properties); + _hostConfigurator?.Invoke(hb); + } + + public override void ConfigureServices(IServiceCollection services) + { + _serviceConfigurator?.Invoke(services); + _serviceConfiguratorWithContext?.Invoke(Tweaker.Adjust(_hostBuilderContext, hbc => + { + hbc.Configuration = Configuration; + hbc.HostingEnvironment = Environment; + return hbc; + }), services); + } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting/Internal/MinimalHostTest.cs b/src/Codebelt.Extensions.Xunit.Hosting/Internal/MinimalHostTest.cs index b920ed8..71e08d1 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting/Internal/MinimalHostTest.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting/Internal/MinimalHostTest.cs @@ -1,52 +1,52 @@ -using System; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; - -namespace Codebelt.Extensions.Xunit.Hosting.Internal; - -internal sealed class MinimalHostTest : MinimalHostTest -{ - private readonly Action _serviceConfigurator; - private readonly Action _serviceConfiguratorWithContext; - private readonly Action _hostConfigurator; - private HostBuilderContext _hostBuilderContext; - - internal MinimalHostTest(Action serviceConfigurator, Action hostConfigurator, IMinimalHostFixture hostFixture) : base(true, hostFixture, callerType: serviceConfigurator?.Target?.GetType() ?? hostConfigurator?.Target?.GetType()) - { - _serviceConfigurator = serviceConfigurator; - _hostConfigurator = hostConfigurator; - InitializeHostFixture(hostFixture); - } - - internal MinimalHostTest(Action serviceConfigurator, Action hostConfigurator, IMinimalHostFixture hostFixture) : base(true, hostFixture, callerType: serviceConfigurator?.Target?.GetType() ?? hostConfigurator?.Target?.GetType()) - { - _serviceConfiguratorWithContext = serviceConfigurator; - _hostConfigurator = hostConfigurator; - InitializeHostFixture(hostFixture); - } - - private void InitializeHostFixture(IMinimalHostFixture hostFixture) - { - if (!hostFixture.HasValidState()) - { - hostFixture.ConfigureCallback = Configure; - hostFixture.ConfigureHostCallback = ConfigureHost; - hostFixture.ConfigureHost(this); - } - Host = hostFixture.Host; - Configure(hostFixture.Configuration, hostFixture.Environment); - } - - protected override void ConfigureHost(IHostApplicationBuilder hb) - { - _hostBuilderContext = new HostBuilderContext(hb.Properties); - _hostConfigurator?.Invoke(hb); - _serviceConfigurator?.Invoke(hb.Services); - _serviceConfiguratorWithContext?.Invoke(Tweaker.Adjust(_hostBuilderContext, hbc => - { - hbc.Configuration = hb.Configuration; - hbc.HostingEnvironment = hb.Environment; - return hbc; - }), hb.Services); - } -} +using System; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace Codebelt.Extensions.Xunit.Hosting.Internal; + +internal sealed class MinimalHostTest : MinimalHostTest +{ + private readonly Action _serviceConfigurator; + private readonly Action _serviceConfiguratorWithContext; + private readonly Action _hostConfigurator; + private HostBuilderContext _hostBuilderContext; + + internal MinimalHostTest(Action serviceConfigurator, Action hostConfigurator, IMinimalHostFixture hostFixture) : base(true, hostFixture, callerType: serviceConfigurator?.Target?.GetType() ?? hostConfigurator?.Target?.GetType()) + { + _serviceConfigurator = serviceConfigurator; + _hostConfigurator = hostConfigurator; + InitializeHostFixture(hostFixture); + } + + internal MinimalHostTest(Action serviceConfigurator, Action hostConfigurator, IMinimalHostFixture hostFixture) : base(true, hostFixture, callerType: serviceConfigurator?.Target?.GetType() ?? hostConfigurator?.Target?.GetType()) + { + _serviceConfiguratorWithContext = serviceConfigurator; + _hostConfigurator = hostConfigurator; + InitializeHostFixture(hostFixture); + } + + private void InitializeHostFixture(IMinimalHostFixture hostFixture) + { + if (!hostFixture.HasValidState()) + { + hostFixture.ConfigureCallback = Configure; + hostFixture.ConfigureHostCallback = ConfigureHost; + hostFixture.ConfigureHost(this); + } + Host = hostFixture.Host; + Configure(hostFixture.Configuration, hostFixture.Environment); + } + + protected override void ConfigureHost(IHostApplicationBuilder hb) + { + _hostBuilderContext = new HostBuilderContext(hb.Properties); + _hostConfigurator?.Invoke(hb); + _serviceConfigurator?.Invoke(hb.Services); + _serviceConfiguratorWithContext?.Invoke(Tweaker.Adjust(_hostBuilderContext, hbc => + { + hbc.Configuration = hb.Configuration; + hbc.HostingEnvironment = hb.Environment; + return hbc; + }), hb.Services); + } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting/LoggerExtensions.cs b/src/Codebelt.Extensions.Xunit.Hosting/LoggerExtensions.cs index ac007f6..d72aced 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting/LoggerExtensions.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting/LoggerExtensions.cs @@ -1,87 +1,87 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using Microsoft.Extensions.Logging; - -namespace Codebelt.Extensions.Xunit.Hosting; - -/// -/// Extension methods for the interface. -/// -public static class LoggerExtensions -{ - /// - /// Returns the associated that is provided when settings up services from or related. - /// - /// The from which to retrieve the . - /// The category name for messages produced by the -or- null for messages produced by all loggers. - /// Returns an implementation of with all logged entries expressed as . - /// - /// cannot be null. - /// - /// - /// does not contain a test store. - /// - /// - /// does not contain a test store for the specified . - /// - public static ITestStore GetTestStore(this ILogger logger, string categoryName = null) - { - if (logger == null) { throw new ArgumentNullException(nameof(logger)); } - var internalLogger = GetInternalLogger(logger); - if (internalLogger != null) - { - var store = FindTestStore(internalLogger, categoryName); - if (store != null) { return store; } - } - throw new ArgumentException($"Logger does not contain a test store; did you remember to call {nameof(ServiceCollectionExtensions.AddXunitTestLogging)} before calling this method?", nameof(logger)); +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Microsoft.Extensions.Logging; + +namespace Codebelt.Extensions.Xunit.Hosting; + +/// +/// Extension methods for the interface. +/// +public static class LoggerExtensions +{ + /// + /// Returns the associated that is provided when settings up services from or related. + /// + /// The from which to retrieve the . + /// The category name for messages produced by the -or- null for messages produced by all loggers. + /// Returns an implementation of with all logged entries expressed as . + /// + /// cannot be null. + /// + /// + /// does not contain a test store. + /// + /// + /// does not contain a test store for the specified . + /// + public static ITestStore GetTestStore(this ILogger logger, string categoryName = null) + { + if (logger == null) { throw new ArgumentNullException(nameof(logger)); } + var internalLogger = GetInternalLogger(logger); + if (internalLogger != null) + { + var store = FindTestStore(internalLogger, categoryName); + if (store != null) { return store; } + } + throw new ArgumentException($"Logger does not contain a test store; did you remember to call {nameof(ServiceCollectionExtensions.AddXunitTestLogging)} before calling this method?", nameof(logger)); } - /// - /// Returns the associated that is provided when settings up services from or related. - /// - /// The from which to retrieve the . - /// Returns an implementation of with all logged entries expressed as . - /// - /// cannot be null. - /// - /// - /// does not contain a test store. - /// - public static ITestStore GetTestStore(this ILogger logger) - { - return GetTestStore(logger, typeof(T).FullName); - } - - private static object GetInternalLogger(ILogger logger) - { - var loggerType = logger.GetType(); - return loggerType.GetRuntimeFields().SingleOrDefault(fi => fi.Name == "_logger")?.GetValue(logger); - } - - private static ITestStore FindTestStore(object internalLogger, string categoryName) - { - var internalLoggerType = internalLogger.GetType(); - var internalLoggers = internalLoggerType.GetRuntimeProperties().SingleOrDefault(pi => pi.Name == "Loggers")?.GetValue(internalLogger); - if (internalLoggers == null) { return null; } - foreach (var loggerInformation in (IEnumerable)internalLoggers) - { - var store = TryGetTestStore(loggerInformation, categoryName); - if (store != null) { return store; } - } - return null; - } - - private static ITestStore TryGetTestStore(object loggerInformation, string categoryName) - { - var loggerInformationType = loggerInformation.GetType(); - var providerType = loggerInformationType.GetProperty("ProviderType")?.GetValue(loggerInformation) as Type; - if (providerType != typeof(XunitTestLoggerProvider)) { return null; } - if (loggerInformationType.GetProperty("Logger")?.GetValue(loggerInformation) is not XunitTestLogger xunitTestLogger) { return null; } - return categoryName == null - ? xunitTestLogger.Provider - : xunitTestLogger.Provider[categoryName]; - } -} + /// + /// Returns the associated that is provided when settings up services from or related. + /// + /// The from which to retrieve the . + /// Returns an implementation of with all logged entries expressed as . + /// + /// cannot be null. + /// + /// + /// does not contain a test store. + /// + public static ITestStore GetTestStore(this ILogger logger) + { + return GetTestStore(logger, typeof(T).FullName); + } + + private static object GetInternalLogger(ILogger logger) + { + var loggerType = logger.GetType(); + return loggerType.GetRuntimeFields().SingleOrDefault(fi => fi.Name == "_logger")?.GetValue(logger); + } + + private static ITestStore FindTestStore(object internalLogger, string categoryName) + { + var internalLoggerType = internalLogger.GetType(); + var internalLoggers = internalLoggerType.GetRuntimeProperties().SingleOrDefault(pi => pi.Name == "Loggers")?.GetValue(internalLogger); + if (internalLoggers == null) { return null; } + foreach (var loggerInformation in (IEnumerable)internalLoggers) + { + var store = TryGetTestStore(loggerInformation, categoryName); + if (store != null) { return store; } + } + return null; + } + + private static ITestStore TryGetTestStore(object loggerInformation, string categoryName) + { + var loggerInformationType = loggerInformation.GetType(); + var providerType = loggerInformationType.GetProperty("ProviderType")?.GetValue(loggerInformation) as Type; + if (providerType != typeof(XunitTestLoggerProvider)) { return null; } + if (loggerInformationType.GetProperty("Logger")?.GetValue(loggerInformation) is not XunitTestLogger xunitTestLogger) { return null; } + return categoryName == null + ? xunitTestLogger.Provider + : xunitTestLogger.Provider[categoryName]; + } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting/ManagedHostFixture.cs b/src/Codebelt.Extensions.Xunit.Hosting/ManagedHostFixture.cs index 3c8664a..66c70a6 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting/ManagedHostFixture.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting/ManagedHostFixture.cs @@ -1,94 +1,94 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Threading; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; - -namespace Codebelt.Extensions.Xunit.Hosting; - -/// -/// Provides a default implementation of the interface. -/// -/// -/// -public class ManagedHostFixture : HostFixture, IGenericHostFixture -{ - /// - /// Initializes a new instance of the class. - /// - public ManagedHostFixture() - { - } - - /// - /// Creates and configures the of this instance. - /// - /// The object that inherits from . - /// was added to support those cases where the caller is required in the host configuration. - /// - /// is null. - /// - /// - /// is not assignable from . - /// - public virtual void ConfigureHost(Test hostTest) - { - if (hostTest == null) { throw new ArgumentNullException(nameof(hostTest)); } - if (!HasTypes(hostTest.GetType(), typeof(HostTest<>))) { throw new ArgumentOutOfRangeException(nameof(hostTest), typeof(HostTest<>), $"{nameof(hostTest)} is not assignable from HostTest."); } - if (!this.HasValidState()) { return; } // had to include this due to dual-call this method (one uncontrolled from xUnit library reflection magic; second controlled from this library) - - var hb = new HostBuilder() - .UseContentRoot(Directory.GetCurrentDirectory()) - .UseEnvironment("Development") - .ConfigureAppConfiguration((context, config) => - { - config - .AddJsonFile("appsettings.json", true, true) - .AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json", true, true) - .AddEnvironmentVariables(); - - ConfigureCallback(config.Build(), context.HostingEnvironment); - }) - .ConfigureServices((context, services) => - { - Configuration = context.Configuration; - Environment = context.HostingEnvironment; - ConfigureServicesCallback(services); - }) - .ConfigureHostConfiguration(builder => - { - builder.AddInMemoryCollection(new Dictionary - { - { HostDefaults.ApplicationKey, hostTest.CallerType.Assembly.GetName().Name } - }); - }); - -#if NET10_0_OR_GREATER - hb.UseDefaultServiceProvider(o => - { - o.ValidateOnBuild = true; - o.ValidateScopes = true; - }); -#endif - - ConfigureHostCallback(hb); - - Host = hb.Build(); - - AsyncHostRunnerCallback(Host, CancellationToken.None); - } - - /// - /// Gets or sets the delegate that initializes the host builder. - /// - /// The delegate that initializes the host builder. - public Action ConfigureHostCallback { get; set; } - - /// - /// Gets or sets the delegate that adds services to the container. - /// - /// The delegate that adds services to the container. - public Action ConfigureServicesCallback { get; set; } -} +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace Codebelt.Extensions.Xunit.Hosting; + +/// +/// Provides a default implementation of the interface. +/// +/// +/// +public class ManagedHostFixture : HostFixture, IGenericHostFixture +{ + /// + /// Initializes a new instance of the class. + /// + public ManagedHostFixture() + { + } + + /// + /// Creates and configures the of this instance. + /// + /// The object that inherits from . + /// was added to support those cases where the caller is required in the host configuration. + /// + /// is null. + /// + /// + /// is not assignable from . + /// + public virtual void ConfigureHost(Test hostTest) + { + if (hostTest == null) { throw new ArgumentNullException(nameof(hostTest)); } + if (!HasTypes(hostTest.GetType(), typeof(HostTest<>))) { throw new ArgumentOutOfRangeException(nameof(hostTest), typeof(HostTest<>), $"{nameof(hostTest)} is not assignable from HostTest."); } + if (!this.HasValidState()) { return; } // had to include this due to dual-call this method (one uncontrolled from xUnit library reflection magic; second controlled from this library) + + var hb = new HostBuilder() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseEnvironment("Development") + .ConfigureAppConfiguration((context, config) => + { + config + .AddJsonFile("appsettings.json", true, true) + .AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json", true, true) + .AddEnvironmentVariables(); + + ConfigureCallback(config.Build(), context.HostingEnvironment); + }) + .ConfigureServices((context, services) => + { + Configuration = context.Configuration; + Environment = context.HostingEnvironment; + ConfigureServicesCallback(services); + }) + .ConfigureHostConfiguration(builder => + { + builder.AddInMemoryCollection(new Dictionary + { + { HostDefaults.ApplicationKey, hostTest.CallerType.Assembly.GetName().Name } + }); + }); + +#if NET10_0_OR_GREATER + hb.UseDefaultServiceProvider(o => + { + o.ValidateOnBuild = true; + o.ValidateScopes = true; + }); +#endif + + ConfigureHostCallback(hb); + + Host = hb.Build(); + + AsyncHostRunnerCallback(Host, CancellationToken.None); + } + + /// + /// Gets or sets the delegate that initializes the host builder. + /// + /// The delegate that initializes the host builder. + public Action ConfigureHostCallback { get; set; } + + /// + /// Gets or sets the delegate that adds services to the container. + /// + /// The delegate that adds services to the container. + public Action ConfigureServicesCallback { get; set; } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting/ManagedMinimalHostFixture.cs b/src/Codebelt.Extensions.Xunit.Hosting/ManagedMinimalHostFixture.cs index b6319d2..3ea4408 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting/ManagedMinimalHostFixture.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting/ManagedMinimalHostFixture.cs @@ -1,61 +1,61 @@ -using System; -using System.Threading; -using Microsoft.Extensions.Hosting; - -namespace Codebelt.Extensions.Xunit.Hosting; - -/// -/// Provides a default implementation of the interface. -/// -/// -/// -/// This is the "modern" minimal style implementation of . -public class ManagedMinimalHostFixture : HostFixture, IMinimalHostFixture -{ - /// - /// Initializes a new instance of the class. - /// - public ManagedMinimalHostFixture() - { - } - - /// - /// Creates and configures the of this instance. - /// - /// The object that inherits from . - /// was added to support those cases where the caller is required in the host configuration. - /// - /// is null. - /// - /// - /// is not assignable from . - /// - public virtual void ConfigureHost(Test hostTest) - { - if (hostTest == null) { throw new ArgumentNullException(nameof(hostTest)); } - if (!HasTypes(hostTest.GetType(), typeof(MinimalHostTest<>))) { throw new ArgumentOutOfRangeException(nameof(hostTest), typeof(MinimalHostTest<>), $"{nameof(hostTest)} is not assignable from MinimalHostTest."); } - - var hb = Microsoft.Extensions.Hosting.Host.CreateApplicationBuilder(new HostApplicationBuilderSettings() - { - EnvironmentName = "Development", - ApplicationName = hostTest.CallerType.Assembly.GetName().Name, - }); - - Configuration = hb.Configuration; - Environment = hb.Environment; - - ConfigureCallback(Configuration, Environment); - - ConfigureHostCallback(hb); - - Host = hb.Build(); - - AsyncHostRunnerCallback(Host, CancellationToken.None); - } - - /// - /// Gets or sets the delegate that initializes the host application builder. - /// - /// The delegate that initializes the host application builder. - public Action ConfigureHostCallback { get; set; } -} +using System; +using System.Threading; +using Microsoft.Extensions.Hosting; + +namespace Codebelt.Extensions.Xunit.Hosting; + +/// +/// Provides a default implementation of the interface. +/// +/// +/// +/// This is the "modern" minimal style implementation of . +public class ManagedMinimalHostFixture : HostFixture, IMinimalHostFixture +{ + /// + /// Initializes a new instance of the class. + /// + public ManagedMinimalHostFixture() + { + } + + /// + /// Creates and configures the of this instance. + /// + /// The object that inherits from . + /// was added to support those cases where the caller is required in the host configuration. + /// + /// is null. + /// + /// + /// is not assignable from . + /// + public virtual void ConfigureHost(Test hostTest) + { + if (hostTest == null) { throw new ArgumentNullException(nameof(hostTest)); } + if (!HasTypes(hostTest.GetType(), typeof(MinimalHostTest<>))) { throw new ArgumentOutOfRangeException(nameof(hostTest), typeof(MinimalHostTest<>), $"{nameof(hostTest)} is not assignable from MinimalHostTest."); } + + var hb = Microsoft.Extensions.Hosting.Host.CreateApplicationBuilder(new HostApplicationBuilderSettings() + { + EnvironmentName = "Development", + ApplicationName = hostTest.CallerType.Assembly.GetName().Name, + }); + + Configuration = hb.Configuration; + Environment = hb.Environment; + + ConfigureCallback(Configuration, Environment); + + ConfigureHostCallback(hb); + + Host = hb.Build(); + + AsyncHostRunnerCallback(Host, CancellationToken.None); + } + + /// + /// Gets or sets the delegate that initializes the host application builder. + /// + /// The delegate that initializes the host application builder. + public Action ConfigureHostCallback { get; set; } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting/MinimalHostFixtureExtensions.cs b/src/Codebelt.Extensions.Xunit.Hosting/MinimalHostFixtureExtensions.cs index b27a293..5be67d9 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting/MinimalHostFixtureExtensions.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting/MinimalHostFixtureExtensions.cs @@ -1,23 +1,23 @@ -namespace Codebelt.Extensions.Xunit.Hosting; - -/// -/// Extension methods for the interface. -/// -public static class MinimalHostFixtureExtensions -{ - /// - /// Determines whether the specified has a valid state. - /// - /// The to check. - /// true if the specified has a valid state; otherwise, false. - /// - /// A valid state is defined as having non-null values for the following properties: - /// , and . - /// - public static bool HasValidState(this IMinimalHostFixture hostFixture) - { - return hostFixture.Host != null && - hostFixture.ConfigureHostCallback != null && - hostFixture.ConfigureCallback != null; - } -} +namespace Codebelt.Extensions.Xunit.Hosting; + +/// +/// Extension methods for the interface. +/// +public static class MinimalHostFixtureExtensions +{ + /// + /// Determines whether the specified has a valid state. + /// + /// The to check. + /// true if the specified has a valid state; otherwise, false. + /// + /// A valid state is defined as having non-null values for the following properties: + /// , and . + /// + public static bool HasValidState(this IMinimalHostFixture hostFixture) + { + return hostFixture.Host != null && + hostFixture.ConfigureHostCallback != null && + hostFixture.ConfigureCallback != null; + } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting/MinimalHostTest.cs b/src/Codebelt.Extensions.Xunit.Hosting/MinimalHostTest.cs index 6a4d73b..49a30c5 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting/MinimalHostTest.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting/MinimalHostTest.cs @@ -1,81 +1,81 @@ -using System; -using Microsoft.Extensions.Hosting; -using Xunit; - -namespace Codebelt.Extensions.Xunit.Hosting; - -/// -/// Represents the non-generic base class from where its generic equivalent should derive. -/// -/// -public abstract class MinimalHostTest : HostTest -{ - /// - /// Initializes a new instance of the class. - /// - /// An implementation of the interface. - /// The of caller that ends up invoking this instance. - protected MinimalHostTest(ITestOutputHelper output = null, Type callerType = null) : base(output, callerType) - { +using System; +using Microsoft.Extensions.Hosting; +using Xunit; + +namespace Codebelt.Extensions.Xunit.Hosting; + +/// +/// Represents the non-generic base class from where its generic equivalent should derive. +/// +/// +public abstract class MinimalHostTest : HostTest +{ + /// + /// Initializes a new instance of the class. + /// + /// An implementation of the interface. + /// The of caller that ends up invoking this instance. + protected MinimalHostTest(ITestOutputHelper output = null, Type callerType = null) : base(output, callerType) + { + } + + + /// + /// Provides a way to override the defaults. + /// + /// The that initializes an instance of . + protected virtual void ConfigureHost(IHostApplicationBuilder hb) + { } +} +/// +/// Represents a base class from which all implementations of unit testing, that uses Microsoft Dependency Injection (minimal style), should derive. +/// +/// The type of the object that implements the interface. +/// +/// +/// The class needed to be designed in this rather complex way, as this is the only way that xUnit supports a shared context. The need for shared context is theoretical at best, but it does opt-in for Scoped instances. +public abstract class MinimalHostTest : MinimalHostTest, IClassFixture where T : class, IMinimalHostFixture +{ + /// + /// Initializes a new instance of the class. + /// + /// An implementation of the interface. + /// An implementation of the interface. + /// The of caller that ends up invoking this instance. + /// + /// is null. + /// + protected MinimalHostTest(T hostFixture, ITestOutputHelper output = null, Type callerType = null) : this(false, hostFixture, output, callerType) + { + } - /// - /// Provides a way to override the defaults. - /// - /// The that initializes an instance of . - protected virtual void ConfigureHost(IHostApplicationBuilder hb) - { - } -} - -/// -/// Represents a base class from which all implementations of unit testing, that uses Microsoft Dependency Injection (minimal style), should derive. -/// -/// The type of the object that implements the interface. -/// -/// -/// The class needed to be designed in this rather complex way, as this is the only way that xUnit supports a shared context. The need for shared context is theoretical at best, but it does opt-in for Scoped instances. -public abstract class MinimalHostTest : MinimalHostTest, IClassFixture where T : class, IMinimalHostFixture -{ - /// - /// Initializes a new instance of the class. - /// - /// An implementation of the interface. - /// An implementation of the interface. - /// The of caller that ends up invoking this instance. - /// - /// is null. - /// - protected MinimalHostTest(T hostFixture, ITestOutputHelper output = null, Type callerType = null) : this(false, hostFixture, output, callerType) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// A value indicating whether to skip the host fixture initialization. - /// An implementation of the interface. - /// An implementation of the interface. - /// The of caller that ends up invoking this instance. - /// - /// is null. - /// - protected MinimalHostTest(bool skipHostFixtureInitialization, T hostFixture, ITestOutputHelper output = null, Type callerType = null) : base(output, callerType) + /// + /// Initializes a new instance of the class. + /// + /// A value indicating whether to skip the host fixture initialization. + /// An implementation of the interface. + /// An implementation of the interface. + /// The of caller that ends up invoking this instance. + /// + /// is null. + /// + protected MinimalHostTest(bool skipHostFixtureInitialization, T hostFixture, ITestOutputHelper output = null, Type callerType = null) : base(output, callerType) { -#if NETSTANDARD2_0 - if (hostFixture == null) { throw new ArgumentNullException(nameof(hostFixture)); } -#else +#if NETSTANDARD2_0 + if (hostFixture == null) { throw new ArgumentNullException(nameof(hostFixture)); } +#else ArgumentNullException.ThrowIfNull(hostFixture); -#endif - if (skipHostFixtureInitialization) { return; } - if (!hostFixture.HasValidState()) - { - hostFixture.ConfigureCallback = Configure; - hostFixture.ConfigureHostCallback = ConfigureHost; - hostFixture.ConfigureHost(this); - } - Host = hostFixture.Host; - Configure(hostFixture.Configuration, hostFixture.Environment); - } -} +#endif + if (skipHostFixtureInitialization) { return; } + if (!hostFixture.HasValidState()) + { + hostFixture.ConfigureCallback = Configure; + hostFixture.ConfigureHostCallback = ConfigureHost; + hostFixture.ConfigureHost(this); + } + Host = hostFixture.Host; + Configure(hostFixture.Configuration, hostFixture.Environment); + } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting/MinimalHostTestFactory.cs b/src/Codebelt.Extensions.Xunit.Hosting/MinimalHostTestFactory.cs index b81903a..889095a 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting/MinimalHostTestFactory.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting/MinimalHostTestFactory.cs @@ -1,35 +1,35 @@ -using System; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; - -namespace Codebelt.Extensions.Xunit.Hosting; - -/// -/// Provides a set of static methods for unit testing (minimal style). -/// -public static class MinimalHostTestFactory -{ - /// - /// Creates and returns an implementation. - /// - /// The which may be configured. - /// The which may be configured. - /// An optional implementation to use instead of the default instance. - /// An instance of an implementation. - public static IHostTest Create(Action serviceSetup = null, Action hostSetup = null, IMinimalHostFixture hostFixture = null) - { - return new Internal.MinimalHostTest(serviceSetup, hostSetup, hostFixture ?? new ManagedMinimalHostFixture()); - } - - /// - /// Creates and returns an implementation. - /// - /// The which may be configured. - /// The which may be configured. - /// An optional implementation to use instead of the default instance. - /// An instance of an implementation. - public static IHostTest CreateWithHostBuilderContext(Action serviceSetup = null, Action hostSetup = null, IMinimalHostFixture hostFixture = null) - { - return new Internal.MinimalHostTest(serviceSetup, hostSetup, hostFixture ?? new ManagedMinimalHostFixture()); - } -} +using System; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace Codebelt.Extensions.Xunit.Hosting; + +/// +/// Provides a set of static methods for unit testing (minimal style). +/// +public static class MinimalHostTestFactory +{ + /// + /// Creates and returns an implementation. + /// + /// The which may be configured. + /// The which may be configured. + /// An optional implementation to use instead of the default instance. + /// An instance of an implementation. + public static IHostTest Create(Action serviceSetup = null, Action hostSetup = null, IMinimalHostFixture hostFixture = null) + { + return new Internal.MinimalHostTest(serviceSetup, hostSetup, hostFixture ?? new ManagedMinimalHostFixture()); + } + + /// + /// Creates and returns an implementation. + /// + /// The which may be configured. + /// The which may be configured. + /// An optional implementation to use instead of the default instance. + /// An instance of an implementation. + public static IHostTest CreateWithHostBuilderContext(Action serviceSetup = null, Action hostSetup = null, IMinimalHostFixture hostFixture = null) + { + return new Internal.MinimalHostTest(serviceSetup, hostSetup, hostFixture ?? new ManagedMinimalHostFixture()); + } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting/Properties/AssemblyInfo.cs b/src/Codebelt.Extensions.Xunit.Hosting/Properties/AssemblyInfo.cs index 4ecdb1c..440866e 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting/Properties/AssemblyInfo.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting/Properties/AssemblyInfo.cs @@ -1,4 +1,4 @@ -using System.Runtime.InteropServices; - -[assembly: ComVisible(false)] -[assembly: Guid("413e5c92-990d-481d-99e9-e6214ae0d0d2")] \ No newline at end of file +using System.Runtime.InteropServices; + +[assembly: ComVisible(false)] +[assembly: Guid("413e5c92-990d-481d-99e9-e6214ae0d0d2")] diff --git a/src/Codebelt.Extensions.Xunit.Hosting/SelfManagedHostFixture.cs b/src/Codebelt.Extensions.Xunit.Hosting/SelfManagedHostFixture.cs index 17e31bd..7bbf344 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting/SelfManagedHostFixture.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting/SelfManagedHostFixture.cs @@ -1,20 +1,20 @@ -using System.Threading.Tasks; - -namespace Codebelt.Extensions.Xunit.Hosting; - -/// -/// Represents a self-managed implementation of the class. -/// -public sealed class SelfManagedHostFixture : ManagedHostFixture -{ - /// - /// Initializes a new instance of the class. - /// - /// - /// This constructor sets the to a no-op asynchronous delegate. - /// - public SelfManagedHostFixture() - { - AsyncHostRunnerCallback = (_, __) => Task.CompletedTask; - } -} +using System.Threading.Tasks; + +namespace Codebelt.Extensions.Xunit.Hosting; + +/// +/// Represents a self-managed implementation of the class. +/// +public sealed class SelfManagedHostFixture : ManagedHostFixture +{ + /// + /// Initializes a new instance of the class. + /// + /// + /// This constructor sets the to a no-op asynchronous delegate. + /// + public SelfManagedHostFixture() + { + AsyncHostRunnerCallback = (_, __) => Task.CompletedTask; + } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting/SelfManagedMinimalHostFixture.cs b/src/Codebelt.Extensions.Xunit.Hosting/SelfManagedMinimalHostFixture.cs index ebe8a30..9024092 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting/SelfManagedMinimalHostFixture.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting/SelfManagedMinimalHostFixture.cs @@ -1,20 +1,20 @@ -using System.Threading.Tasks; - -namespace Codebelt.Extensions.Xunit.Hosting; - -/// -/// Represents a self-managed implementation of the class. -/// -public sealed class SelfManagedMinimalHostFixture : ManagedMinimalHostFixture -{ - /// - /// Initializes a new instance of the class. - /// - /// - /// This constructor sets the to a no-op asynchronous delegate. - /// - public SelfManagedMinimalHostFixture() - { - AsyncHostRunnerCallback = (_, __) => Task.CompletedTask; - } -} +using System.Threading.Tasks; + +namespace Codebelt.Extensions.Xunit.Hosting; + +/// +/// Represents a self-managed implementation of the class. +/// +public sealed class SelfManagedMinimalHostFixture : ManagedMinimalHostFixture +{ + /// + /// Initializes a new instance of the class. + /// + /// + /// This constructor sets the to a no-op asynchronous delegate. + /// + public SelfManagedMinimalHostFixture() + { + AsyncHostRunnerCallback = (_, __) => Task.CompletedTask; + } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting/ServiceCollectionExtensions.cs b/src/Codebelt.Extensions.Xunit.Hosting/ServiceCollectionExtensions.cs index 8967546..26caaeb 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting/ServiceCollectionExtensions.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting/ServiceCollectionExtensions.cs @@ -1,115 +1,115 @@ -using System; -using System.Linq; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Xunit; - -namespace Codebelt.Extensions.Xunit.Hosting; - -/// -/// Extension methods for the interface. -/// -public static class ServiceCollectionExtensions -{ - /// - /// Adds a unit test optimized implementation of logging to the collection. - /// - /// The to extend. - /// The that specifies the minimum level to include for the logging. - /// A reference to so that additional configuration calls can be chained. - /// - /// cannot be null. - /// - public static IServiceCollection AddXunitTestLogging(this IServiceCollection services, LogLevel minimumLevel = LogLevel.Trace) - { - if (services == null) { throw new ArgumentNullException(nameof(services)); } - if (services.Any(sd => sd.ServiceType == typeof(ITestOutputHelperAccessor))) - { - AddTestOutputHelperAccessor(services, minimumLevel); - } - else - { - services.AddLogging(builder => - { - builder.SetMinimumLevel(minimumLevel); - builder.AddProvider(new XunitTestLoggerProvider()); - }); - } - return services; - } - - /// - /// Adds a unit test optimized implementation of output logging to the collection. - /// - /// The to extend. - /// The that provides the output for the logging. - /// The that specifies the minimum level to include for the logging. - /// A reference to so that additional configuration calls can be chained. - /// - /// cannot be null -or- - /// cannot be null. - /// - public static IServiceCollection AddXunitTestLogging(this IServiceCollection services, ITestOutputHelper output, LogLevel minimumLevel = LogLevel.Trace) +using System; +using System.Linq; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Xunit; + +namespace Codebelt.Extensions.Xunit.Hosting; + +/// +/// Extension methods for the interface. +/// +public static class ServiceCollectionExtensions +{ + /// + /// Adds a unit test optimized implementation of logging to the collection. + /// + /// The to extend. + /// The that specifies the minimum level to include for the logging. + /// A reference to so that additional configuration calls can be chained. + /// + /// cannot be null. + /// + public static IServiceCollection AddXunitTestLogging(this IServiceCollection services, LogLevel minimumLevel = LogLevel.Trace) { -#if NETSTANDARD2_0 - if (services == null) { throw new ArgumentNullException(nameof(services)); } - if (output == null) { throw new ArgumentNullException(nameof(output)); } -#else - ArgumentNullException.ThrowIfNull(services); - ArgumentNullException.ThrowIfNull(output); -#endif - if (services.Any(sd => sd.ServiceType == typeof(ITestOutputHelperAccessor))) - { - AddTestOutputHelperAccessor(services, minimumLevel); - } - else - { - services.AddLogging(builder => - { - builder.SetMinimumLevel(minimumLevel); - builder.AddProvider(new XunitTestLoggerProvider(output)); - }); - } - return services; - } - - private static void AddTestOutputHelperAccessor(IServiceCollection services, LogLevel minimumLevel) - { - services.AddLogging(builder => - { - builder.SetMinimumLevel(minimumLevel); - builder.Services.AddSingleton(provider => - { - var accessor = provider.GetRequiredService(); - return new XunitTestLoggerProvider(accessor); - }); - }); - } - - /// - /// Adds a default implementation of to the collection. - /// - /// The to extend. - /// A reference to so that additional configuration calls can be chained. - public static IServiceCollection AddXunitTestLoggingOutputHelperAccessor(this IServiceCollection services) - { - services.AddXunitTestLoggingOutputHelperAccessor(); - return services; - } - - /// - /// Adds a specified implementation of to the collection. - /// - /// The type of the implementation of . - /// The to extend. - /// A reference to so that additional configuration calls can be chained. - /// - /// cannot be null. - /// - public static IServiceCollection AddXunitTestLoggingOutputHelperAccessor(this IServiceCollection services) where T : class, ITestOutputHelperAccessor - { - if (services == null) { throw new ArgumentNullException(nameof(services)); } - services.AddSingleton(); - return services; - } -} + if (services == null) { throw new ArgumentNullException(nameof(services)); } + if (services.Any(sd => sd.ServiceType == typeof(ITestOutputHelperAccessor))) + { + AddTestOutputHelperAccessor(services, minimumLevel); + } + else + { + services.AddLogging(builder => + { + builder.SetMinimumLevel(minimumLevel); + builder.AddProvider(new XunitTestLoggerProvider()); + }); + } + return services; + } + + /// + /// Adds a unit test optimized implementation of output logging to the collection. + /// + /// The to extend. + /// The that provides the output for the logging. + /// The that specifies the minimum level to include for the logging. + /// A reference to so that additional configuration calls can be chained. + /// + /// cannot be null -or- + /// cannot be null. + /// + public static IServiceCollection AddXunitTestLogging(this IServiceCollection services, ITestOutputHelper output, LogLevel minimumLevel = LogLevel.Trace) + { +#if NETSTANDARD2_0 + if (services == null) { throw new ArgumentNullException(nameof(services)); } + if (output == null) { throw new ArgumentNullException(nameof(output)); } +#else + ArgumentNullException.ThrowIfNull(services); + ArgumentNullException.ThrowIfNull(output); +#endif + if (services.Any(sd => sd.ServiceType == typeof(ITestOutputHelperAccessor))) + { + AddTestOutputHelperAccessor(services, minimumLevel); + } + else + { + services.AddLogging(builder => + { + builder.SetMinimumLevel(minimumLevel); + builder.AddProvider(new XunitTestLoggerProvider(output)); + }); + } + return services; + } + + private static void AddTestOutputHelperAccessor(IServiceCollection services, LogLevel minimumLevel) + { + services.AddLogging(builder => + { + builder.SetMinimumLevel(minimumLevel); + builder.Services.AddSingleton(provider => + { + var accessor = provider.GetRequiredService(); + return new XunitTestLoggerProvider(accessor); + }); + }); + } + + /// + /// Adds a default implementation of to the collection. + /// + /// The to extend. + /// A reference to so that additional configuration calls can be chained. + public static IServiceCollection AddXunitTestLoggingOutputHelperAccessor(this IServiceCollection services) + { + services.AddXunitTestLoggingOutputHelperAccessor(); + return services; + } + + /// + /// Adds a specified implementation of to the collection. + /// + /// The type of the implementation of . + /// The to extend. + /// A reference to so that additional configuration calls can be chained. + /// + /// cannot be null. + /// + public static IServiceCollection AddXunitTestLoggingOutputHelperAccessor(this IServiceCollection services) where T : class, ITestOutputHelperAccessor + { + if (services == null) { throw new ArgumentNullException(nameof(services)); } + services.AddSingleton(); + return services; + } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting/ServiceProviderExtensions.cs b/src/Codebelt.Extensions.Xunit.Hosting/ServiceProviderExtensions.cs index e18b2a9..8aadf5c 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting/ServiceProviderExtensions.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting/ServiceProviderExtensions.cs @@ -1,23 +1,23 @@ -using System; -using Microsoft.Extensions.DependencyInjection; - -namespace Codebelt.Extensions.Xunit.Hosting; - -/// -/// Provides extension methods for the interface. -/// -public static class ServiceProviderExtensions -{ - /// - /// Gets a required scoped service of type from the . - /// - /// The type of the service to retrieve. - /// The to retrieve the service from. - /// The required service of type . - /// There is no service of type . - public static T GetRequiredScopedService(this IServiceProvider provider) - { - using var scope = provider.CreateScope(); - return scope.ServiceProvider.GetRequiredService(); - } -} +using System; +using Microsoft.Extensions.DependencyInjection; + +namespace Codebelt.Extensions.Xunit.Hosting; + +/// +/// Provides extension methods for the interface. +/// +public static class ServiceProviderExtensions +{ + /// + /// Gets a required scoped service of type from the . + /// + /// The type of the service to retrieve. + /// The to retrieve the service from. + /// The required service of type . + /// There is no service of type . + public static T GetRequiredScopedService(this IServiceProvider provider) + { + using var scope = provider.CreateScope(); + return scope.ServiceProvider.GetRequiredService(); + } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting/Tweaker.cs b/src/Codebelt.Extensions.Xunit.Hosting/Tweaker.cs index f2f741a..8a7f70a 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting/Tweaker.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting/Tweaker.cs @@ -1,11 +1,11 @@ -using System; - -namespace Codebelt.Extensions.Xunit.Hosting; - -internal static class Tweaker -{ - internal static T Adjust(T value, Func converter) - { - return converter == null ? value : converter.Invoke(value); - } -} +using System; + +namespace Codebelt.Extensions.Xunit.Hosting; + +internal static class Tweaker +{ + internal static T Adjust(T value, Func converter) + { + return converter == null ? value : converter.Invoke(value); + } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting/XunitTestLogger.cs b/src/Codebelt.Extensions.Xunit.Hosting/XunitTestLogger.cs index 837771e..40a7802 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting/XunitTestLogger.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting/XunitTestLogger.cs @@ -1,76 +1,76 @@ -using System; -using System.Text; -using Microsoft.Extensions.Logging; -using Xunit; - -namespace Codebelt.Extensions.Xunit.Hosting; - -internal sealed class XunitTestLogger : InMemoryTestStore, ILogger, IDisposable -{ - private readonly ITestOutputHelperAccessor _accessor; - private readonly ITestOutputHelper _output; - private readonly XunitTestLoggerProvider _provider; - - public XunitTestLogger(XunitTestLoggerProvider provider, ITestOutputHelper output) - { - _provider = provider; - _output = output; - } - - public XunitTestLogger(XunitTestLoggerProvider provider, ITestOutputHelperAccessor accessor) - { - _provider = provider; - _accessor = accessor; - } - - public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) - { - var builder = new StringBuilder($"{logLevel}: {formatter(state, exception)}"); - if (exception != null) { builder.AppendLine().Append(exception).AppendLine(); } - - var message = builder.ToString(); - - _provider.WriteLoggerEntry(logLevel, eventId, message); - - Add(new XunitTestLoggerEntry(logLevel, eventId, message)); - - if (_accessor != null) - { - try - { - _accessor.TestOutput?.WriteLine(message); - } - catch (InvalidOperationException) - { - // can happen when there is no currently active test - } - } - else - { - try - { - _output?.WriteLine(message); - } - catch (InvalidOperationException) - { - // can happen when there is no currently active test (e.g., you should use the ITestOutputHelperAccessor capability) - } - } - } - - public XunitTestLoggerProvider Provider => _provider; - - public bool IsEnabled(LogLevel logLevel) - { - return logLevel != LogLevel.None; - } - - public IDisposable BeginScope(TState state) where TState : notnull - { - return this; - } - - public void Dispose() - { - } -} +using System; +using System.Text; +using Microsoft.Extensions.Logging; +using Xunit; + +namespace Codebelt.Extensions.Xunit.Hosting; + +internal sealed class XunitTestLogger : InMemoryTestStore, ILogger, IDisposable +{ + private readonly ITestOutputHelperAccessor _accessor; + private readonly ITestOutputHelper _output; + private readonly XunitTestLoggerProvider _provider; + + public XunitTestLogger(XunitTestLoggerProvider provider, ITestOutputHelper output) + { + _provider = provider; + _output = output; + } + + public XunitTestLogger(XunitTestLoggerProvider provider, ITestOutputHelperAccessor accessor) + { + _provider = provider; + _accessor = accessor; + } + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + { + var builder = new StringBuilder($"{logLevel}: {formatter(state, exception)}"); + if (exception != null) { builder.AppendLine().Append(exception).AppendLine(); } + + var message = builder.ToString(); + + _provider.WriteLoggerEntry(logLevel, eventId, message); + + Add(new XunitTestLoggerEntry(logLevel, eventId, message)); + + if (_accessor != null) + { + try + { + _accessor.TestOutput?.WriteLine(message); + } + catch (InvalidOperationException) + { + // can happen when there is no currently active test + } + } + else + { + try + { + _output?.WriteLine(message); + } + catch (InvalidOperationException) + { + // can happen when there is no currently active test (e.g., you should use the ITestOutputHelperAccessor capability) + } + } + } + + public XunitTestLoggerProvider Provider => _provider; + + public bool IsEnabled(LogLevel logLevel) + { + return logLevel != LogLevel.None; + } + + public IDisposable BeginScope(TState state) where TState : notnull + { + return this; + } + + public void Dispose() + { + } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting/XunitTestLoggerEntry.cs b/src/Codebelt.Extensions.Xunit.Hosting/XunitTestLoggerEntry.cs index ca7c73a..d6f65b8 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting/XunitTestLoggerEntry.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting/XunitTestLoggerEntry.cs @@ -1,49 +1,49 @@ -using Microsoft.Extensions.Logging; - -namespace Codebelt.Extensions.Xunit.Hosting; - -/// -/// Represents a captured log-entry for testing purposes. This record encapsulates the , and message. -/// -public record XunitTestLoggerEntry -{ - /// - /// Initializes a new instance of the class. - /// - /// The for this entry. - /// The of this entry. - /// The message of this entry. - public XunitTestLoggerEntry(LogLevel severity, EventId id, string message) - { - Severity = severity; - Id = id; - Message = message; - } - - /// - /// Gets the event identifier. - /// - /// The event identifier. - public EventId Id { get; } - - /// - /// Gets the log level. - /// - /// The log level. - public LogLevel Severity { get; } - - /// - /// Gets the value of the message. - /// - /// The value of the message. - public string Message { get; } - - /// - /// Returns a that represents this instance. - /// - /// A that represents this instance. - public override string ToString() - { - return Message; - } -} +using Microsoft.Extensions.Logging; + +namespace Codebelt.Extensions.Xunit.Hosting; + +/// +/// Represents a captured log-entry for testing purposes. This record encapsulates the , and message. +/// +public record XunitTestLoggerEntry +{ + /// + /// Initializes a new instance of the class. + /// + /// The for this entry. + /// The of this entry. + /// The message of this entry. + public XunitTestLoggerEntry(LogLevel severity, EventId id, string message) + { + Severity = severity; + Id = id; + Message = message; + } + + /// + /// Gets the event identifier. + /// + /// The event identifier. + public EventId Id { get; } + + /// + /// Gets the log level. + /// + /// The log level. + public LogLevel Severity { get; } + + /// + /// Gets the value of the message. + /// + /// The value of the message. + public string Message { get; } + + /// + /// Returns a that represents this instance. + /// + /// A that represents this instance. + public override string ToString() + { + return Message; + } +} diff --git a/src/Codebelt.Extensions.Xunit.Hosting/XunitTestLoggerProvider.cs b/src/Codebelt.Extensions.Xunit.Hosting/XunitTestLoggerProvider.cs index e1bbb0f..33b5881 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting/XunitTestLoggerProvider.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting/XunitTestLoggerProvider.cs @@ -1,56 +1,56 @@ -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using Xunit; - -namespace Codebelt.Extensions.Xunit.Hosting; - -internal sealed class XunitTestLoggerProvider : InMemoryTestStore, ILoggerProvider -{ - private readonly ConcurrentDictionary _loggers = new(StringComparer.OrdinalIgnoreCase); - private readonly ITestOutputHelperAccessor _accessor; - private readonly ITestOutputHelper _output; - - public XunitTestLoggerProvider() - { - } - - public XunitTestLoggerProvider(ITestOutputHelper output) - { - _output = output; - } - - public XunitTestLoggerProvider(ITestOutputHelperAccessor accessor) - { - _accessor = accessor; - } - - public ILogger CreateLogger(string categoryName) - { - return _loggers.GetOrAdd(categoryName, _ => _accessor != null - ? new XunitTestLogger(this, _accessor) - : new XunitTestLogger(this, _output)); - } - - public void WriteLoggerEntry(LogLevel logLevel, EventId eventId, string message) - { - Add(new XunitTestLoggerEntry(logLevel, eventId, message)); - } - - public ITestStore this[string categoryName] - { - get - { - if (_loggers.TryGetValue(categoryName, out var logger)) - { - return logger; - } - throw new KeyNotFoundException($"Logger for category '{categoryName}' not found."); - } - } - - public void Dispose() - { - } -} +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using Xunit; + +namespace Codebelt.Extensions.Xunit.Hosting; + +internal sealed class XunitTestLoggerProvider : InMemoryTestStore, ILoggerProvider +{ + private readonly ConcurrentDictionary _loggers = new(StringComparer.OrdinalIgnoreCase); + private readonly ITestOutputHelperAccessor _accessor; + private readonly ITestOutputHelper _output; + + public XunitTestLoggerProvider() + { + } + + public XunitTestLoggerProvider(ITestOutputHelper output) + { + _output = output; + } + + public XunitTestLoggerProvider(ITestOutputHelperAccessor accessor) + { + _accessor = accessor; + } + + public ILogger CreateLogger(string categoryName) + { + return _loggers.GetOrAdd(categoryName, _ => _accessor != null + ? new XunitTestLogger(this, _accessor) + : new XunitTestLogger(this, _output)); + } + + public void WriteLoggerEntry(LogLevel logLevel, EventId eventId, string message) + { + Add(new XunitTestLoggerEntry(logLevel, eventId, message)); + } + + public ITestStore this[string categoryName] + { + get + { + if (_loggers.TryGetValue(categoryName, out var logger)) + { + return logger; + } + throw new KeyNotFoundException($"Logger for category '{categoryName}' not found."); + } + } + + public void Dispose() + { + } +} diff --git a/src/Codebelt.Extensions.Xunit/DelimitedString.cs b/src/Codebelt.Extensions.Xunit/DelimitedString.cs index 3925e69..b8eb16a 100644 --- a/src/Codebelt.Extensions.Xunit/DelimitedString.cs +++ b/src/Codebelt.Extensions.Xunit/DelimitedString.cs @@ -1,29 +1,29 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Codebelt.Extensions.Xunit; - -internal static class DelimitedString -{ - internal static string Create(IEnumerable source, string delimiter) +using System; +using System.Collections.Generic; +using System.Text; + +namespace Codebelt.Extensions.Xunit; + +internal static class DelimitedString +{ + internal static string Create(IEnumerable source, string delimiter) { -#if NETSTANDARD2_0 - if (source == null) { throw new ArgumentNullException(nameof(source)); } - if (delimiter == null) { throw new ArgumentNullException(nameof(delimiter)); } -#else - ArgumentNullException.ThrowIfNull(source); - ArgumentNullException.ThrowIfNull(delimiter); -#endif - - var sb = new StringBuilder(); - var first = true; - foreach (var item in source) - { - if (!first) { sb.Append(delimiter); } - first = false; - sb.Append(item); - } - return sb.ToString(); - } -} +#if NETSTANDARD2_0 + if (source == null) { throw new ArgumentNullException(nameof(source)); } + if (delimiter == null) { throw new ArgumentNullException(nameof(delimiter)); } +#else + ArgumentNullException.ThrowIfNull(source); + ArgumentNullException.ThrowIfNull(delimiter); +#endif + + var sb = new StringBuilder(); + var first = true; + foreach (var item in source) + { + if (!first) { sb.Append(delimiter); } + first = false; + sb.Append(item); + } + return sb.ToString(); + } +} diff --git a/src/Codebelt.Extensions.Xunit/GlobalSuppressions.cs b/src/Codebelt.Extensions.Xunit/GlobalSuppressions.cs index 1a58ddf..be48b4b 100644 --- a/src/Codebelt.Extensions.Xunit/GlobalSuppressions.cs +++ b/src/Codebelt.Extensions.Xunit/GlobalSuppressions.cs @@ -1,11 +1,11 @@ -// This file is used by Code Analysis to maintain SuppressMessage -// attributes that are applied to this project. -// Project-level suppressions either have no target or are given -// a specific target and scoped to a namespace, type, member, etc. - -using System.Diagnostics.CodeAnalysis; - -[assembly: SuppressMessage("Major Code Smell", "S3881:\"IDisposable\" should be implemented correctly", Justification = "This is a base class implementation of the IDisposable interface tailored to avoid wrong implementations.", Scope = "type", Target = "~T:Codebelt.Extensions.Xunit.Test")] -[assembly: SuppressMessage("Major Code Smell", "S3971:\"GC.SuppressFinalize\" should not be called", Justification = "False-Positive due to IAsyncDisposable living side-by-side with IDisposable.", Scope = "member", Target = "~M:Codebelt.Extensions.Xunit.Test.DisposeAsync~System.Threading.Tasks.ValueTask")] -[assembly: SuppressMessage("Maintainability", "CA1510:Use ArgumentNullException throw helper", Justification = "Wont fix. Multi-TFMs. Only supported with >= .NET 7.", Scope = "member", Target = "~M:Codebelt.Extensions.Xunit.TestOutputHelperExtensions.WriteLines(Xunit.ITestOutputHelper,System.Object[])")] -[assembly: SuppressMessage("Maintainability", "CA1510:Use ArgumentNullException throw helper", Justification = "Wont fix. Multi-TFMs. Only supported with >= .NET 7.", Scope = "member", Target = "~M:Codebelt.Extensions.Xunit.TestOutputHelperExtensions.WriteLines``1(Xunit.ITestOutputHelper,System.Collections.Generic.IEnumerable{``0})")] +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Major Code Smell", "S3881:\"IDisposable\" should be implemented correctly", Justification = "This is a base class implementation of the IDisposable interface tailored to avoid wrong implementations.", Scope = "type", Target = "~T:Codebelt.Extensions.Xunit.Test")] +[assembly: SuppressMessage("Major Code Smell", "S3971:\"GC.SuppressFinalize\" should not be called", Justification = "False-Positive due to IAsyncDisposable living side-by-side with IDisposable.", Scope = "member", Target = "~M:Codebelt.Extensions.Xunit.Test.DisposeAsync~System.Threading.Tasks.ValueTask")] +[assembly: SuppressMessage("Maintainability", "CA1510:Use ArgumentNullException throw helper", Justification = "Wont fix. Multi-TFMs. Only supported with >= .NET 7.", Scope = "member", Target = "~M:Codebelt.Extensions.Xunit.TestOutputHelperExtensions.WriteLines(Xunit.ITestOutputHelper,System.Object[])")] +[assembly: SuppressMessage("Maintainability", "CA1510:Use ArgumentNullException throw helper", Justification = "Wont fix. Multi-TFMs. Only supported with >= .NET 7.", Scope = "member", Target = "~M:Codebelt.Extensions.Xunit.TestOutputHelperExtensions.WriteLines``1(Xunit.ITestOutputHelper,System.Collections.Generic.IEnumerable{``0})")] diff --git a/src/Codebelt.Extensions.Xunit/ITest.cs b/src/Codebelt.Extensions.Xunit/ITest.cs index 042e389..52cd349 100644 --- a/src/Codebelt.Extensions.Xunit/ITest.cs +++ b/src/Codebelt.Extensions.Xunit/ITest.cs @@ -1,16 +1,16 @@ -using System; - -namespace Codebelt.Extensions.Xunit; - -/// -/// Represents the members needed for vanilla testing. -/// -/// -public interface ITest : IDisposable, IAsyncDisposable -{ - /// - /// Gets the type of caller for this instance. Default is . - /// - /// The type of caller for this instance. - Type CallerType { get; } -} +using System; + +namespace Codebelt.Extensions.Xunit; + +/// +/// Represents the members needed for vanilla testing. +/// +/// +public interface ITest : IDisposable, IAsyncDisposable +{ + /// + /// Gets the type of caller for this instance. Default is . + /// + /// The type of caller for this instance. + Type CallerType { get; } +} diff --git a/src/Codebelt.Extensions.Xunit/ITestOutputHelperAccessor.cs b/src/Codebelt.Extensions.Xunit/ITestOutputHelperAccessor.cs index a41cc05..be2c3f5 100644 --- a/src/Codebelt.Extensions.Xunit/ITestOutputHelperAccessor.cs +++ b/src/Codebelt.Extensions.Xunit/ITestOutputHelperAccessor.cs @@ -1,15 +1,15 @@ -using Xunit; - -namespace Codebelt.Extensions.Xunit; - -/// -/// Provides an interface for accessing the instance. -/// -public interface ITestOutputHelperAccessor -{ - /// - /// Gets or sets the instance used for outputting test results. - /// - /// The instance. - ITestOutputHelper TestOutput { get; set; } -} +using Xunit; + +namespace Codebelt.Extensions.Xunit; + +/// +/// Provides an interface for accessing the instance. +/// +public interface ITestOutputHelperAccessor +{ + /// + /// Gets or sets the instance used for outputting test results. + /// + /// The instance. + ITestOutputHelper TestOutput { get; set; } +} diff --git a/src/Codebelt.Extensions.Xunit/ITestStore.cs b/src/Codebelt.Extensions.Xunit/ITestStore.cs index 88fe9fd..c394a86 100644 --- a/src/Codebelt.Extensions.Xunit/ITestStore.cs +++ b/src/Codebelt.Extensions.Xunit/ITestStore.cs @@ -1,37 +1,37 @@ -using System; -using System.Collections.Generic; - -namespace Codebelt.Extensions.Xunit; - -/// -/// Represents the members needed for adding and querying a store tailored for unit testing. -/// -/// The type of the object to reference in the store. -public interface ITestStore -{ - /// - /// Gets the number of elements contained in the . - /// - /// The number of elements contained in the . - int Count { get; } - - /// - /// Adds an item to the . - /// - /// The object to add to the . - void Add(T item); - - /// - /// Filters the elements contained in the based on an optional . - /// - /// The function delegate to test each element for a condition. - /// An that satisfies the condition. - IEnumerable Query(Func predicate = null); - - /// - /// Filters the elements contained in the based on the type . - /// - /// The concrete type that implements . - /// An that satisfies the condition. - IEnumerable QueryFor() where TResult : T; -} +using System; +using System.Collections.Generic; + +namespace Codebelt.Extensions.Xunit; + +/// +/// Represents the members needed for adding and querying a store tailored for unit testing. +/// +/// The type of the object to reference in the store. +public interface ITestStore +{ + /// + /// Gets the number of elements contained in the . + /// + /// The number of elements contained in the . + int Count { get; } + + /// + /// Adds an item to the . + /// + /// The object to add to the . + void Add(T item); + + /// + /// Filters the elements contained in the based on an optional . + /// + /// The function delegate to test each element for a condition. + /// An that satisfies the condition. + IEnumerable Query(Func predicate = null); + + /// + /// Filters the elements contained in the based on the type . + /// + /// The concrete type that implements . + /// An that satisfies the condition. + IEnumerable QueryFor() where TResult : T; +} diff --git a/src/Codebelt.Extensions.Xunit/InMemoryTestStore.cs b/src/Codebelt.Extensions.Xunit/InMemoryTestStore.cs index 5820fbd..57a1a10 100644 --- a/src/Codebelt.Extensions.Xunit/InMemoryTestStore.cs +++ b/src/Codebelt.Extensions.Xunit/InMemoryTestStore.cs @@ -1,65 +1,65 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Codebelt.Extensions.Xunit; - -/// -/// Provides a default base implementation of the interface. -/// -/// The type of the object to reference in the memory store. -/// -public class InMemoryTestStore : ITestStore -{ - private readonly List _store = new(); - - /// - /// Initializes a new instance of the class. - /// - public InMemoryTestStore() - { - } - - /// - /// Gets the reference to the encapsulated store. - /// - /// The reference to the encapsulated store. - protected IList InnerStore => _store; - - /// - /// Gets the number of elements contained in the . - /// - /// The number of elements contained in the . - public int Count => _store.Count; - - /// - /// Adds an item to the . - /// - /// The object to add to the . - public void Add(T item) - { - _store.Add(item); - } - - /// - /// Filters the elements contained in the based on an optional . - /// - /// The function delegate to test each element for a condition. - /// An that satisfies the condition. - public virtual IEnumerable Query(Func predicate = null) - { - return predicate == null - ? _store - : _store.Where(predicate!); - } - - /// - /// Filters the elements contained in the based on the type . - /// - /// The concrete type that implements . - /// An that satisfies the condition. - public IEnumerable QueryFor() where TResult : T - { - return Query(item => item.GetType() == typeof(TResult)).Cast(); - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Codebelt.Extensions.Xunit; + +/// +/// Provides a default base implementation of the interface. +/// +/// The type of the object to reference in the memory store. +/// +public class InMemoryTestStore : ITestStore +{ + private readonly List _store = new(); + + /// + /// Initializes a new instance of the class. + /// + public InMemoryTestStore() + { + } + + /// + /// Gets the reference to the encapsulated store. + /// + /// The reference to the encapsulated store. + protected IList InnerStore => _store; + + /// + /// Gets the number of elements contained in the . + /// + /// The number of elements contained in the . + public int Count => _store.Count; + + /// + /// Adds an item to the . + /// + /// The object to add to the . + public void Add(T item) + { + _store.Add(item); + } + + /// + /// Filters the elements contained in the based on an optional . + /// + /// The function delegate to test each element for a condition. + /// An that satisfies the condition. + public virtual IEnumerable Query(Func predicate = null) + { + return predicate == null + ? _store + : _store.Where(predicate!); + } + + /// + /// Filters the elements contained in the based on the type . + /// + /// The concrete type that implements . + /// An that satisfies the condition. + public IEnumerable QueryFor() where TResult : T + { + return Query(item => item.GetType() == typeof(TResult)).Cast(); + } +} diff --git a/src/Codebelt.Extensions.Xunit/Properties/AssemblyInfo.cs b/src/Codebelt.Extensions.Xunit/Properties/AssemblyInfo.cs index 7f71d36..1631b6b 100644 --- a/src/Codebelt.Extensions.Xunit/Properties/AssemblyInfo.cs +++ b/src/Codebelt.Extensions.Xunit/Properties/AssemblyInfo.cs @@ -1,4 +1,4 @@ -using System.Runtime.InteropServices; - -[assembly: ComVisible(false)] -[assembly: Guid("45986a3b-0eed-4d59-9e94-ea478d0d61ce")] +using System.Runtime.InteropServices; + +[assembly: ComVisible(false)] +[assembly: Guid("45986a3b-0eed-4d59-9e94-ea478d0d61ce")] diff --git a/src/Codebelt.Extensions.Xunit/StringExtensions.cs b/src/Codebelt.Extensions.Xunit/StringExtensions.cs index d9569c1..93bfe82 100644 --- a/src/Codebelt.Extensions.Xunit/StringExtensions.cs +++ b/src/Codebelt.Extensions.Xunit/StringExtensions.cs @@ -1,43 +1,43 @@ -#if NETSTANDARD2_0_OR_GREATER -using System; -using System.Text.RegularExpressions; - -namespace Codebelt.Extensions.Xunit; - -/// -/// Extension methods for the class. -/// -public static class StringExtensions -{ - private static readonly Regex NewLineRegex = new(@"\r\n|\r|\n", RegexOptions.Compiled); - - /// - /// Replaces all newline sequences in the current string with . - /// - /// The to extend. - /// A string whose contents match the current string, but with all newline sequences replaced with . - /// Shamefully stolen from https://github.com/WebFormsCore/WebFormsCore/blob/main/src/WebFormsCore/Util/StringExtensions.cs to support .NET Standard 2.0. - public static string ReplaceLineEndings(this string input) - { - return ReplaceLineEndings(input, Environment.NewLine); - } - - /// - /// Replaces all newline sequences in the current string with . - /// - /// The to extend. - /// The text to use as replacement. - /// A string whose contents match the current string, but with all newline sequences replaced with . - /// Shamefully stolen from https://github.com/WebFormsCore/WebFormsCore/blob/main/src/WebFormsCore/Util/StringExtensions.cs to support .NET Standard 2.0. - /// - /// cannot be null -or- - /// cannot be null. - /// - public static string ReplaceLineEndings(this string input, string replacementText) - { - if (input == null) { throw new ArgumentNullException(nameof(input)); } - if (replacementText == null) { throw new ArgumentNullException(nameof(replacementText)); } - return NewLineRegex.Replace(input, replacementText); - } -} -#endif +#if NETSTANDARD2_0_OR_GREATER +using System; +using System.Text.RegularExpressions; + +namespace Codebelt.Extensions.Xunit; + +/// +/// Extension methods for the class. +/// +public static class StringExtensions +{ + private static readonly Regex NewLineRegex = new(@"\r\n|\r|\n", RegexOptions.Compiled); + + /// + /// Replaces all newline sequences in the current string with . + /// + /// The to extend. + /// A string whose contents match the current string, but with all newline sequences replaced with . + /// Shamefully stolen from https://github.com/WebFormsCore/WebFormsCore/blob/main/src/WebFormsCore/Util/StringExtensions.cs to support .NET Standard 2.0. + public static string ReplaceLineEndings(this string input) + { + return ReplaceLineEndings(input, Environment.NewLine); + } + + /// + /// Replaces all newline sequences in the current string with . + /// + /// The to extend. + /// The text to use as replacement. + /// A string whose contents match the current string, but with all newline sequences replaced with . + /// Shamefully stolen from https://github.com/WebFormsCore/WebFormsCore/blob/main/src/WebFormsCore/Util/StringExtensions.cs to support .NET Standard 2.0. + /// + /// cannot be null -or- + /// cannot be null. + /// + public static string ReplaceLineEndings(this string input, string replacementText) + { + if (input == null) { throw new ArgumentNullException(nameof(input)); } + if (replacementText == null) { throw new ArgumentNullException(nameof(replacementText)); } + return NewLineRegex.Replace(input, replacementText); + } +} +#endif diff --git a/src/Codebelt.Extensions.Xunit/Test.cs b/src/Codebelt.Extensions.Xunit/Test.cs index 318a8c9..8379f97 100644 --- a/src/Codebelt.Extensions.Xunit/Test.cs +++ b/src/Codebelt.Extensions.Xunit/Test.cs @@ -1,207 +1,207 @@ -using System; -using System.Linq; -using System.Text.RegularExpressions; -using System.Threading; -using System.Threading.Tasks; -using Xunit; - -namespace Codebelt.Extensions.Xunit; - -/// -/// Represents the base class from which all implementations of unit testing should derive. -/// -/// -public abstract class Test : ITest, IAsyncLifetime -{ -#if NET10_0_OR_GREATER - private readonly Lock _lock = new(); -#else - private readonly object _lock = new(); -#endif - - /// - /// Provides a way, with wildcard support, to determine if matches . - /// - /// The expected string value. - /// The actual string value. - /// The which may be configured. - /// true if matches , false otherwise. - /// Credits for inspiration goes to this SO answer: https://stackoverflow.com/questions/30299671/matching-strings-with-wildcard/30300521#30300521 - /// - /// cannot be matched with . Includes the non-matched string(s) in . - /// - /// - /// is null -or- - /// is null. - /// - public static bool Match(string expected, string actual, Action setup = null) - { - #if NETSTANDARD2_0 - if (expected == null) { throw new ArgumentNullException(nameof(expected)); } - if (actual == null) { throw new ArgumentNullException(nameof(actual)); } -#else - ArgumentNullException.ThrowIfNull(expected); - ArgumentNullException.ThrowIfNull(actual); -#endif - - var options = new WildcardOptions(); - setup?.Invoke(options); - - var escapedExpected = Regex.Escape(expected); - - var hasWildcards = escapedExpected.IndexOf(options.SingleCharacter, StringComparison.Ordinal) >= 0 || escapedExpected.IndexOf(options.GroupOfCharacters, StringComparison.Ordinal) >= 0; - - if (!hasWildcards) - { - if (string.Equals(expected, actual, StringComparison.Ordinal)) - { - return true; - } - - if (options.ThrowOnNoMatch) - { - var diff = ComputeNonMatchingLines(expected, actual, options); - throw new ArgumentOutOfRangeException(nameof(expected), $"'{diff}'", "The expected value does not match the actual value."); - } - return false; - } - - var pattern = $"^{escapedExpected.Replace(options.SingleCharacter, ".").Replace(options.GroupOfCharacters, ".*")}$"; - - if (Regex.IsMatch(actual, pattern, RegexOptions.None, TimeSpan.FromSeconds(2))) - { - return true; - } - - if (options.ThrowOnNoMatch) - { - var diff = ComputeNonMatchingLines(expected, actual, options); - throw new ArgumentOutOfRangeException(nameof(expected), $"'{diff}'", "The expected value does not match the actual value."); - } - - return false; - - static string ComputeNonMatchingLines(string expectedText, string actualText, WildcardOptions o) - { - var e = expectedText.Split(Environment.NewLine.ToCharArray()); - var a = actualText.Split(Environment.NewLine.ToCharArray()); - var d = e.Except(a).Where(s => s.IndexOf(o.GroupOfCharacters, StringComparison.InvariantCulture) < 0 && s.IndexOf(o.SingleCharacter, StringComparison.InvariantCulture) < 0); - return string.Join(Environment.NewLine, d); - } - } - - /// - /// Initializes a new instance of the class. - /// - /// An implementation of the interface. - /// The of caller that ends up invoking this instance. - /// is initialized automatically in an xUnit project. - protected Test(ITestOutputHelper output = null, Type callerType = null) - { - TestOutput = output; - CallerType = callerType ?? GetType(); - - if (output == null) { return; } - - AppDomain.CurrentDomain.UnhandledException += (_, e) => - { - var exception = e.ExceptionObject as Exception; - output.WriteLine($"Unhandled exception captured: {exception?.Message}"); - }; - } - - /// - /// Gets the type of caller for this instance. Default is . - /// - /// The type of caller for this instance. - public Type CallerType { get; } - - /// - /// Gets the console substitute to write out unit test information. - /// - /// The console substitute to write out unit test information. - protected ITestOutputHelper TestOutput { get; } - - /// - /// Gets a value indicating whether has a reference to an implementation of . - /// - /// true if this instance has a reference to an implementation of ; otherwise, false. - protected bool HasTestOutput => TestOutput != null; - - /// - /// Gets a value indicating whether this object is disposed. - /// - /// true if this object is disposed; otherwise, false. - public bool Disposed { get; private set; } - - /// - /// Called when this object is being disposed by either or having disposing set to true and is false. - /// - protected virtual void OnDisposeManagedResources() - { - } - - /// - /// Called when this object is being disposed by . - /// - protected virtual ValueTask OnDisposeManagedResourcesAsync() - { - return default; - } - - /// - /// Called when this object is being disposed by either or and is false. - /// - protected virtual void OnDisposeUnmanagedResources() - { - } - - /// - /// Releases all resources used by the object. - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Releases the unmanaged resources used by the object and optionally releases the managed resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected void Dispose(bool disposing) - { - if (Disposed) { return; } - lock (_lock) - { - if (Disposed) { return; } - if (disposing) - { - OnDisposeManagedResources(); - } - OnDisposeUnmanagedResources(); - Disposed = true; - } - } - - /// - /// Asynchronously releases the resources used by the . - /// - /// A that represents the asynchronous dispose operation. - /// https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-disposeasync#the-disposeasync-method - public async ValueTask DisposeAsync() - { - await OnDisposeManagedResourcesAsync().ConfigureAwait(false); - Dispose(false); - GC.SuppressFinalize(this); - } - - /// - /// Called immediately after the class has been created, before it is used. - /// - /// A that represents the asynchronous operation. - public virtual ValueTask InitializeAsync() - { - return default; - } -} +using System; +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace Codebelt.Extensions.Xunit; + +/// +/// Represents the base class from which all implementations of unit testing should derive. +/// +/// +public abstract class Test : ITest, IAsyncLifetime +{ +#if NET10_0_OR_GREATER + private readonly Lock _lock = new(); +#else + private readonly object _lock = new(); +#endif + + /// + /// Provides a way, with wildcard support, to determine if matches . + /// + /// The expected string value. + /// The actual string value. + /// The which may be configured. + /// true if matches , false otherwise. + /// Credits for inspiration goes to this SO answer: https://stackoverflow.com/questions/30299671/matching-strings-with-wildcard/30300521#30300521 + /// + /// cannot be matched with . Includes the non-matched string(s) in . + /// + /// + /// is null -or- + /// is null. + /// + public static bool Match(string expected, string actual, Action setup = null) + { +#if NETSTANDARD2_0 + if (expected == null) { throw new ArgumentNullException(nameof(expected)); } + if (actual == null) { throw new ArgumentNullException(nameof(actual)); } +#else + ArgumentNullException.ThrowIfNull(expected); + ArgumentNullException.ThrowIfNull(actual); +#endif + + var options = new WildcardOptions(); + setup?.Invoke(options); + + var escapedExpected = Regex.Escape(expected); + + var hasWildcards = escapedExpected.IndexOf(options.SingleCharacter, StringComparison.Ordinal) >= 0 || escapedExpected.IndexOf(options.GroupOfCharacters, StringComparison.Ordinal) >= 0; + + if (!hasWildcards) + { + if (string.Equals(expected, actual, StringComparison.Ordinal)) + { + return true; + } + + if (options.ThrowOnNoMatch) + { + var diff = ComputeNonMatchingLines(expected, actual, options); + throw new ArgumentOutOfRangeException(nameof(expected), $"'{diff}'", "The expected value does not match the actual value."); + } + return false; + } + + var pattern = $"^{escapedExpected.Replace(options.SingleCharacter, ".").Replace(options.GroupOfCharacters, ".*")}$"; + + if (Regex.IsMatch(actual, pattern, RegexOptions.None, TimeSpan.FromSeconds(2))) + { + return true; + } + + if (options.ThrowOnNoMatch) + { + var diff = ComputeNonMatchingLines(expected, actual, options); + throw new ArgumentOutOfRangeException(nameof(expected), $"'{diff}'", "The expected value does not match the actual value."); + } + + return false; + + static string ComputeNonMatchingLines(string expectedText, string actualText, WildcardOptions o) + { + var e = expectedText.Split(Environment.NewLine.ToCharArray()); + var a = actualText.Split(Environment.NewLine.ToCharArray()); + var d = e.Except(a).Where(s => s.IndexOf(o.GroupOfCharacters, StringComparison.InvariantCulture) < 0 && s.IndexOf(o.SingleCharacter, StringComparison.InvariantCulture) < 0); + return string.Join(Environment.NewLine, d); + } + } + + /// + /// Initializes a new instance of the class. + /// + /// An implementation of the interface. + /// The of caller that ends up invoking this instance. + /// is initialized automatically in an xUnit project. + protected Test(ITestOutputHelper output = null, Type callerType = null) + { + TestOutput = output; + CallerType = callerType ?? GetType(); + + if (output == null) { return; } + + AppDomain.CurrentDomain.UnhandledException += (_, e) => + { + var exception = e.ExceptionObject as Exception; + output.WriteLine($"Unhandled exception captured: {exception?.Message}"); + }; + } + + /// + /// Gets the type of caller for this instance. Default is . + /// + /// The type of caller for this instance. + public Type CallerType { get; } + + /// + /// Gets the console substitute to write out unit test information. + /// + /// The console substitute to write out unit test information. + protected ITestOutputHelper TestOutput { get; } + + /// + /// Gets a value indicating whether has a reference to an implementation of . + /// + /// true if this instance has a reference to an implementation of ; otherwise, false. + protected bool HasTestOutput => TestOutput != null; + + /// + /// Gets a value indicating whether this object is disposed. + /// + /// true if this object is disposed; otherwise, false. + public bool Disposed { get; private set; } + + /// + /// Called when this object is being disposed by either or having disposing set to true and is false. + /// + protected virtual void OnDisposeManagedResources() + { + } + + /// + /// Called when this object is being disposed by . + /// + protected virtual ValueTask OnDisposeManagedResourcesAsync() + { + return default; + } + + /// + /// Called when this object is being disposed by either or and is false. + /// + protected virtual void OnDisposeUnmanagedResources() + { + } + + /// + /// Releases all resources used by the object. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Releases the unmanaged resources used by the object and optionally releases the managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected void Dispose(bool disposing) + { + if (Disposed) { return; } + lock (_lock) + { + if (Disposed) { return; } + if (disposing) + { + OnDisposeManagedResources(); + } + OnDisposeUnmanagedResources(); + Disposed = true; + } + } + + /// + /// Asynchronously releases the resources used by the . + /// + /// A that represents the asynchronous dispose operation. + /// https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-disposeasync#the-disposeasync-method + public async ValueTask DisposeAsync() + { + await OnDisposeManagedResourcesAsync().ConfigureAwait(false); + Dispose(false); + GC.SuppressFinalize(this); + } + + /// + /// Called immediately after the class has been created, before it is used. + /// + /// A that represents the asynchronous operation. + public virtual ValueTask InitializeAsync() + { + return default; + } +} diff --git a/src/Codebelt.Extensions.Xunit/TestOutputHelperAccessor.cs b/src/Codebelt.Extensions.Xunit/TestOutputHelperAccessor.cs index a65fc77..b287529 100644 --- a/src/Codebelt.Extensions.Xunit/TestOutputHelperAccessor.cs +++ b/src/Codebelt.Extensions.Xunit/TestOutputHelperAccessor.cs @@ -1,31 +1,31 @@ -using System.Threading; -using Xunit; - -namespace Codebelt.Extensions.Xunit; - -/// -/// Provides a default implementation of the interface. -/// -public class TestOutputHelperAccessor : ITestOutputHelperAccessor -{ - private static readonly AsyncLocal Current = new(); - - /// - /// Initializes a new instance of the class with the specified instance. - /// - /// The instance to be used for outputting test results. - public TestOutputHelperAccessor(ITestOutputHelper output = null) - { - Current.Value = output; - } - - /// - /// Gets or sets the instance used for outputting test results. - /// - /// The instance. - public ITestOutputHelper TestOutput - { - get => Current.Value; - set => Current.Value = value; - } -} +using System.Threading; +using Xunit; + +namespace Codebelt.Extensions.Xunit; + +/// +/// Provides a default implementation of the interface. +/// +public class TestOutputHelperAccessor : ITestOutputHelperAccessor +{ + private static readonly AsyncLocal Current = new(); + + /// + /// Initializes a new instance of the class with the specified instance. + /// + /// The instance to be used for outputting test results. + public TestOutputHelperAccessor(ITestOutputHelper output = null) + { + Current.Value = output; + } + + /// + /// Gets or sets the instance used for outputting test results. + /// + /// The instance. + public ITestOutputHelper TestOutput + { + get => Current.Value; + set => Current.Value = value; + } +} diff --git a/src/Codebelt.Extensions.Xunit/TestOutputHelperExtensions.cs b/src/Codebelt.Extensions.Xunit/TestOutputHelperExtensions.cs index b3c67c7..bb55d33 100644 --- a/src/Codebelt.Extensions.Xunit/TestOutputHelperExtensions.cs +++ b/src/Codebelt.Extensions.Xunit/TestOutputHelperExtensions.cs @@ -1,60 +1,60 @@ -using System; -using System.Collections.Generic; -using Xunit; - -namespace Codebelt.Extensions.Xunit; - -/// -/// Extension methods for the . -/// -public static class TestOutputHelperExtensions -{ - /// - /// Adds a line of text per object in to the output. - /// - /// The to extend. - /// The values to write, per line, to the output. - /// - /// cannot be null. - /// - public static void WriteLines(this ITestOutputHelper helper, params object[] values) - { -#if NETSTANDARD2_0 - if (helper == null) { throw new ArgumentNullException(nameof(helper)); } -#else - ArgumentNullException.ThrowIfNull(helper); -#endif - helper.WriteLine(DelimitedString.Create(values, Environment.NewLine)); - } - - /// - /// Adds a line of text per item in to the output. - /// - /// The to extend. - /// The values to write, per line, to the output. - /// - /// cannot be null. - /// - public static void WriteLines(this ITestOutputHelper helper, T[] values) - { - WriteLines(helper, (IEnumerable)values); - } - - /// - /// Adds a line of text per item in to the output. - /// - /// The to extend. - /// The values to write, per line, to the output. - /// - /// cannot be null. - /// - public static void WriteLines(this ITestOutputHelper helper, IEnumerable values) - { -#if NETSTANDARD2_0 - if (helper == null) { throw new ArgumentNullException(nameof(helper)); } -#else - ArgumentNullException.ThrowIfNull(helper); -#endif - helper.WriteLine(DelimitedString.Create(values, Environment.NewLine)); - } -} +using System; +using System.Collections.Generic; +using Xunit; + +namespace Codebelt.Extensions.Xunit; + +/// +/// Extension methods for the . +/// +public static class TestOutputHelperExtensions +{ + /// + /// Adds a line of text per object in to the output. + /// + /// The to extend. + /// The values to write, per line, to the output. + /// + /// cannot be null. + /// + public static void WriteLines(this ITestOutputHelper helper, params object[] values) + { +#if NETSTANDARD2_0 + if (helper == null) { throw new ArgumentNullException(nameof(helper)); } +#else + ArgumentNullException.ThrowIfNull(helper); +#endif + helper.WriteLine(DelimitedString.Create(values, Environment.NewLine)); + } + + /// + /// Adds a line of text per item in to the output. + /// + /// The to extend. + /// The values to write, per line, to the output. + /// + /// cannot be null. + /// + public static void WriteLines(this ITestOutputHelper helper, T[] values) + { + WriteLines(helper, (IEnumerable)values); + } + + /// + /// Adds a line of text per item in to the output. + /// + /// The to extend. + /// The values to write, per line, to the output. + /// + /// cannot be null. + /// + public static void WriteLines(this ITestOutputHelper helper, IEnumerable values) + { +#if NETSTANDARD2_0 + if (helper == null) { throw new ArgumentNullException(nameof(helper)); } +#else + ArgumentNullException.ThrowIfNull(helper); +#endif + helper.WriteLine(DelimitedString.Create(values, Environment.NewLine)); + } +} diff --git a/src/Codebelt.Extensions.Xunit/WildcardOptions.cs b/src/Codebelt.Extensions.Xunit/WildcardOptions.cs index 68286d0..c1de747 100644 --- a/src/Codebelt.Extensions.Xunit/WildcardOptions.cs +++ b/src/Codebelt.Extensions.Xunit/WildcardOptions.cs @@ -1,54 +1,54 @@ -using System; - -namespace Codebelt.Extensions.Xunit; - -/// -/// Configuration options for . -/// -public class WildcardOptions -{ - /// - /// Initializes a new instance of the class. - /// - /// - /// The following table shows the initial property values for an instance of . - /// - /// - /// Property - /// Initial Value - /// - /// - /// - /// \\* - /// - /// - /// - /// \\? - /// - /// - /// - public WildcardOptions() - { - GroupOfCharacters = "\\*"; - SingleCharacter = "\\?"; - ThrowOnNoMatch = false; - } - - /// - /// Gets or sets the symbol used to represents any group of characters, including no character. The default value is \\*. - /// - /// The symbol used to represents any group of characters, including no character. - public string GroupOfCharacters { get; set; } - - /// - /// Gets or sets the symbol used to represents any single character. The default value is \\?. - /// - /// The symbol used to represents any single character. - public string SingleCharacter { get; set; } - - /// - /// Gets or sets a value indicating whether a is thrown when no match is found. The default value is false. - /// - /// true if a is thrown when no match is found; otherwise, false. - public bool ThrowOnNoMatch { get; set; } -} +using System; + +namespace Codebelt.Extensions.Xunit; + +/// +/// Configuration options for . +/// +public class WildcardOptions +{ + /// + /// Initializes a new instance of the class. + /// + /// + /// The following table shows the initial property values for an instance of . + /// + /// + /// Property + /// Initial Value + /// + /// + /// + /// \\* + /// + /// + /// + /// \\? + /// + /// + /// + public WildcardOptions() + { + GroupOfCharacters = "\\*"; + SingleCharacter = "\\?"; + ThrowOnNoMatch = false; + } + + /// + /// Gets or sets the symbol used to represents any group of characters, including no character. The default value is \\*. + /// + /// The symbol used to represents any group of characters, including no character. + public string GroupOfCharacters { get; set; } + + /// + /// Gets or sets the symbol used to represents any single character. The default value is \\?. + /// + /// The symbol used to represents any single character. + public string SingleCharacter { get; set; } + + /// + /// Gets or sets a value indicating whether a is thrown when no match is found. The default value is false. + /// + /// true if a is thrown when no match is found; otherwise, false. + public bool ThrowOnNoMatch { get; set; } +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.FunctionalTests/BootstrapperMinimalWebApplicationTestTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.FunctionalTests/BootstrapperMinimalWebApplicationTestTest.cs index 32cb2a6..ddbcb84 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.FunctionalTests/BootstrapperMinimalWebApplicationTestTest.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.FunctionalTests/BootstrapperMinimalWebApplicationTestTest.cs @@ -1,27 +1,27 @@ -using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; -using Xunit; -using BootstrapperMinimalWebMarker = Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWeb.App.BootstrapperMinimalWebMarker; -using BootstrapperMinimalWebProgram = Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWeb.App.Program; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; - +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Xunit; +using BootstrapperMinimalWebMarker = Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWeb.App.BootstrapperMinimalWebMarker; +using BootstrapperMinimalWebProgram = Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWeb.App.Program; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; + public class BootstrapperMinimalWebApplicationTestTest : WebApplicationTest> { public BootstrapperMinimalWebApplicationTestTest(ManagedWebApplicationFixture hostFixture, ITestOutputHelper output) : base(hostFixture, output) - { - } - - [Fact] - public async Task ShouldBootstrapMinimalWebProgram() - { - using var client = Server.CreateClient(); - - var response = await client.GetAsync("/").ConfigureAwait(false); - var body = await response.Content.ReadAsStringAsync().ConfigureAwait(false); - - Assert.True(response.IsSuccessStatusCode); - Assert.Equal("Bootstrapper Minimal Web", body); - Assert.Equal("Bootstrapper Minimal Web", Host.Services.GetRequiredService().Value); - } -} + { + } + + [Fact] + public async Task ShouldBootstrapMinimalWebProgram() + { + using var client = Server.CreateClient(); + + var response = await client.GetAsync("/").ConfigureAwait(false); + var body = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + + Assert.True(response.IsSuccessStatusCode); + Assert.Equal("Bootstrapper Minimal Web", body); + Assert.Equal("Bootstrapper Minimal Web", Host.Services.GetRequiredService().Value); + } +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.FunctionalTests/BootstrapperWebApplicationTestTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.FunctionalTests/BootstrapperWebApplicationTestTest.cs index aaf3003..40027e9 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.FunctionalTests/BootstrapperWebApplicationTestTest.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.FunctionalTests/BootstrapperWebApplicationTestTest.cs @@ -1,28 +1,28 @@ -using System.Threading.Tasks; -using Microsoft.AspNetCore.TestHost; -using Microsoft.Extensions.DependencyInjection; -using Xunit; -using BootstrapperWebMarker = Codebelt.Extensions.Xunit.Hosting.BootstrapperWeb.App.BootstrapperWebMarker; -using BootstrapperWebProgram = Codebelt.Extensions.Xunit.Hosting.BootstrapperWeb.App.Program; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; - +using System.Threading.Tasks; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Xunit; +using BootstrapperWebMarker = Codebelt.Extensions.Xunit.Hosting.BootstrapperWeb.App.BootstrapperWebMarker; +using BootstrapperWebProgram = Codebelt.Extensions.Xunit.Hosting.BootstrapperWeb.App.Program; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; + public class BootstrapperWebApplicationTestTest : WebApplicationTest> { public BootstrapperWebApplicationTestTest(ManagedWebApplicationFixture hostFixture, ITestOutputHelper output) : base(hostFixture, output) - { - } - - [Fact] - public async Task ShouldBootstrapLegacyWebProgramAndStartup() - { - using var client = Host.GetTestClient(); - - var response = await client.GetAsync("/").ConfigureAwait(false); - var body = await response.Content.ReadAsStringAsync().ConfigureAwait(false); - - Assert.True(response.IsSuccessStatusCode); - Assert.Equal("Bootstrapper Web|Development", body); - Assert.Equal("Bootstrapper Web", Host.Services.GetRequiredService().Value); - } -} + { + } + + [Fact] + public async Task ShouldBootstrapLegacyWebProgramAndStartup() + { + using var client = Host.GetTestClient(); + + var response = await client.GetAsync("/").ConfigureAwait(false); + var body = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + + Assert.True(response.IsSuccessStatusCode); + Assert.Equal("Bootstrapper Web|Development", body); + Assert.Equal("Bootstrapper Web", Host.Services.GetRequiredService().Value); + } +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.FunctionalTests/DeferredInvalidWebApplicationTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.FunctionalTests/DeferredInvalidWebApplicationTest.cs index 74e2cc4..4e10582 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.FunctionalTests/DeferredInvalidWebApplicationTest.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.FunctionalTests/DeferredInvalidWebApplicationTest.cs @@ -1,14 +1,14 @@ -using Microsoft.AspNetCore.Hosting; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; - +using Microsoft.AspNetCore.Hosting; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; + internal sealed class DeferredInvalidWebApplicationTest : WebApplicationTest> { public DeferredInvalidWebApplicationTest(ManagedWebApplicationFixture hostFixture) : base(true, hostFixture) - { - } - - public void Configure(IWebHostBuilder builder) - { - } -} + { + } + + public void Configure(IWebHostBuilder builder) + { + } +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.FunctionalTests/DeferredModernWebApplicationTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.FunctionalTests/DeferredModernWebApplicationTest.cs index c680e44..40e11ee 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.FunctionalTests/DeferredModernWebApplicationTest.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.FunctionalTests/DeferredModernWebApplicationTest.cs @@ -1,15 +1,15 @@ -using Microsoft.AspNetCore.Hosting; -using ModernProgram = Codebelt.Extensions.Xunit.Hosting.Program.App.Program; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; - +using Microsoft.AspNetCore.Hosting; +using ModernProgram = Codebelt.Extensions.Xunit.Hosting.Program.App.Program; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; + internal sealed class DeferredModernWebApplicationTest : WebApplicationTest> { public DeferredModernWebApplicationTest(ManagedWebApplicationFixture hostFixture) : base(true, hostFixture) - { - } - - public void Configure(IWebHostBuilder builder) - { - } -} + { + } + + public void Configure(IWebHostBuilder builder) + { + } +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/BoolMiddleware.cs b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/BoolMiddleware.cs index f70ead9..818d8a6 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/BoolMiddleware.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/BoolMiddleware.cs @@ -1,39 +1,39 @@ -using System; -using System.Threading.Tasks; -using Cuemon.AspNetCore; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Options; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore.Assets; - -public class BoolMiddleware : ConfigurableMiddleware -{ - /// - /// Initializes a new instance of the class. - /// - /// The delegate of the request pipeline to invoke. - /// The which need to be configured. - public BoolMiddleware(RequestDelegate next, IOptions setup) : base(next, setup) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The delegate of the request pipeline to invoke. - /// The which need to be configured. - public BoolMiddleware(RequestDelegate next, Action setup) : base(next, setup) - { - } - - /// - /// Executes the . - /// - /// The context of the current request. - /// A task that represents the execution of this middleware. - public override async Task InvokeAsync(HttpContext context) - { - await context.Response.WriteAsync($"A:{Options.A}, B:{Options.B}, C:{Options.C}, D:{Options.D}, E:{Options.E}, F:{Options.F}"); - await Next(context); - } -} +using System; +using System.Threading.Tasks; +using Cuemon.AspNetCore; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Options; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore.Assets; + +public class BoolMiddleware : ConfigurableMiddleware +{ + /// + /// Initializes a new instance of the class. + /// + /// The delegate of the request pipeline to invoke. + /// The which need to be configured. + public BoolMiddleware(RequestDelegate next, IOptions setup) : base(next, setup) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The delegate of the request pipeline to invoke. + /// The which need to be configured. + public BoolMiddleware(RequestDelegate next, Action setup) : base(next, setup) + { + } + + /// + /// Executes the . + /// + /// The context of the current request. + /// A task that represents the execution of this middleware. + public override async Task InvokeAsync(HttpContext context) + { + await context.Response.WriteAsync($"A:{Options.A}, B:{Options.B}, C:{Options.C}, D:{Options.D}, E:{Options.E}, F:{Options.F}"); + await Next(context); + } +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/BoolOptions.cs b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/BoolOptions.cs index 10bb47c..499e385 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/BoolOptions.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/BoolOptions.cs @@ -1,18 +1,18 @@ -using Cuemon.Configuration; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore.Assets; - -public class BoolOptions : IParameterObject -{ - public bool A { get; set; } - - public bool B { get; set; } - - public bool C { get; set; } - - public bool D { get; set; } - - public bool E { get; set; } - - public bool F { get; set; } -} +using Cuemon.Configuration; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore.Assets; + +public class BoolOptions : IParameterObject +{ + public bool A { get; set; } + + public bool B { get; set; } + + public bool C { get; set; } + + public bool D { get; set; } + + public bool E { get; set; } + + public bool F { get; set; } +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/FakeController.cs b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/FakeController.cs index 01980c9..fd02e6b 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/FakeController.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/FakeController.cs @@ -1,18 +1,18 @@ -using Microsoft.AspNetCore.Mvc; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore.Assets; - -[ApiController] -[Route("[controller]")] -public class FakeController : ControllerBase -{ - public FakeController() - { - } - - [HttpGet] - public IActionResult Get() - { - return Ok("Unit Test"); - } -} +using Microsoft.AspNetCore.Mvc; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore.Assets; + +[ApiController] +[Route("[controller]")] +public class FakeController : ControllerBase +{ + public FakeController() + { + } + + [HttpGet] + public IActionResult Get() + { + return Ok("Unit Test"); + } +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/InvalidHostTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/InvalidHostTest.cs index 7d1defb..5712c0a 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/InvalidHostTest.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/InvalidHostTest.cs @@ -1,10 +1,10 @@ -using Xunit; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore.Assets; - -public class InvalidHostTest : Test, IClassFixture where T : class, IGenericHostFixture -{ - public InvalidHostTest(T hostFixture) - { - } -} +using Xunit; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore.Assets; + +public class InvalidHostTest : Test, IClassFixture where T : class, IGenericHostFixture +{ + public InvalidHostTest(T hostFixture) + { + } +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/ScopedCorrelation.cs b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/ScopedCorrelation.cs index 8448c64..2cf15d4 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/ScopedCorrelation.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/ScopedCorrelation.cs @@ -1,7 +1,7 @@ -using Cuemon.Messaging; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore.Assets; - -public sealed record ScopedCorrelation : CorrelationToken -{ -} +using Cuemon.Messaging; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore.Assets; + +public sealed record ScopedCorrelation : CorrelationToken +{ +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/SingletonCorrelation.cs b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/SingletonCorrelation.cs index 71243ab..4d4762a 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/SingletonCorrelation.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/SingletonCorrelation.cs @@ -1,7 +1,7 @@ -using Cuemon.Messaging; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore.Assets; - -public sealed record SingletonCorrelation : CorrelationToken -{ -} +using Cuemon.Messaging; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore.Assets; + +public sealed record SingletonCorrelation : CorrelationToken +{ +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/TransientCorrelation.cs b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/TransientCorrelation.cs index 3c4b0fb..4a03d9a 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/TransientCorrelation.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/TransientCorrelation.cs @@ -1,7 +1,7 @@ -using Cuemon.Messaging; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore.Assets; - -public sealed record TransientCorrelation : CorrelationToken -{ -} +using Cuemon.Messaging; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore.Assets; + +public sealed record TransientCorrelation : CorrelationToken +{ +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/ValidHostTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/ValidHostTest.cs index 68018cf..713d734 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/ValidHostTest.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Assets/ValidHostTest.cs @@ -1,21 +1,21 @@ -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore.Assets; - -public class ValidHostTest : WebHostTest -{ - public ValidHostTest(ManagedWebHostFixture hostFixture) : base(hostFixture) - { - } - - public override void ConfigureServices(IServiceCollection services) - { - - } - - public override void ConfigureApplication(IApplicationBuilder app) - { - - } -} +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.DependencyInjection; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore.Assets; + +public class ValidHostTest : WebHostTest +{ + public ValidHostTest(ManagedWebHostFixture hostFixture) : base(hostFixture) + { + } + + public override void ConfigureServices(IServiceCollection services) + { + + } + + public override void ConfigureApplication(IApplicationBuilder app) + { + + } +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/BlockingManagedWebHostFixtureTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/BlockingManagedWebHostFixtureTest.cs index 0b95263..3ebd15b 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/BlockingManagedWebHostFixtureTest.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/BlockingManagedWebHostFixtureTest.cs @@ -1,18 +1,18 @@ -using Xunit; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; - -public class BlockingManagedWebHostFixtureTest : Test -{ - public BlockingManagedWebHostFixtureTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public void Create_ShouldSucceed_WhenBlockingManagedWebHostFixtureIsUsed() - { - using var startup = WebHostTestFactory.Create(hostFixture: new BlockingManagedWebHostFixture()); - - Assert.NotNull(startup.Host); - } -} +using Xunit; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; + +public class BlockingManagedWebHostFixtureTest : Test +{ + public BlockingManagedWebHostFixtureTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void Create_ShouldSucceed_WhenBlockingManagedWebHostFixtureIsUsed() + { + using var startup = WebHostTestFactory.Create(hostFixture: new BlockingManagedWebHostFixture()); + + Assert.NotNull(startup.Host); + } +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/HostBuilderApplicationExtensionsTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/HostBuilderApplicationExtensionsTest.cs index 125a5ba..6154d30 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/HostBuilderApplicationExtensionsTest.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/HostBuilderApplicationExtensionsTest.cs @@ -1,31 +1,31 @@ -using System; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.Hosting; -using Xunit; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; - -public class HostBuilderApplicationExtensionsTest : Test -{ - public HostBuilderApplicationExtensionsTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public void ToHostBuilder_ShouldReturnHostBuilder_WhenWebApplicationBuilderIsProvided() - { - IHostApplicationBuilder builder = WebApplication.CreateBuilder(); - - var hostBuilder = builder.ToHostBuilder(); - - Assert.NotNull(hostBuilder); - } - - [Fact] - public void ToHostBuilder_ShouldThrowArgumentException_WhenNotWebApplicationBuilder() - { - IHostApplicationBuilder builder = Host.CreateApplicationBuilder(); - - Assert.Throws(() => builder.ToHostBuilder()); - } -} +using System; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Hosting; +using Xunit; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; + +public class HostBuilderApplicationExtensionsTest : Test +{ + public HostBuilderApplicationExtensionsTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void ToHostBuilder_ShouldReturnHostBuilder_WhenWebApplicationBuilderIsProvided() + { + IHostApplicationBuilder builder = WebApplication.CreateBuilder(); + + var hostBuilder = builder.ToHostBuilder(); + + Assert.NotNull(hostBuilder); + } + + [Fact] + public void ToHostBuilder_ShouldThrowArgumentException_WhenNotWebApplicationBuilder() + { + IHostApplicationBuilder builder = Host.CreateApplicationBuilder(); + + Assert.Throws(() => builder.ToHostBuilder()); + } +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Http/Features/FakeHttpResponseFeatureTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Http/Features/FakeHttpResponseFeatureTest.cs index 3aed258..7a8f430 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Http/Features/FakeHttpResponseFeatureTest.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Http/Features/FakeHttpResponseFeatureTest.cs @@ -1,48 +1,48 @@ -using System.Threading.Tasks; -using Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http.Features; -using Xunit; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http.Features; - -public class FakeHttpResponseFeatureTest : Test -{ - public FakeHttpResponseFeatureTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public void OnStarting_ShouldInvokeCallback_OnFirstCall() - { - var feature = new FakeHttpResponseFeature(); - var invoked = false; - - feature.OnStarting(_ => { invoked = true; return Task.CompletedTask; }, null); - - Assert.True(invoked); - Assert.True(feature.HasStarted); - } - - [Fact] - public void OnStarting_ShouldNotInvokeCallback_OnSubsequentCalls() - { - var feature = new FakeHttpResponseFeature(); - var count = 0; - - feature.OnStarting(_ => { count++; return Task.CompletedTask; }, null); - feature.OnStarting(_ => { count++; return Task.CompletedTask; }, null); - - Assert.Equal(1, count); - Assert.True(feature.HasStarted); - } - - [Fact] - public void OnStarting_ShouldNotThrow_WhenCallbackIsNull() - { - var feature = new FakeHttpResponseFeature(); - - var ex = Record.Exception(() => feature.OnStarting(null, null)); - - Assert.Null(ex); - Assert.True(feature.HasStarted); - } -} +using System.Threading.Tasks; +using Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http.Features; +using Xunit; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http.Features; + +public class FakeHttpResponseFeatureTest : Test +{ + public FakeHttpResponseFeatureTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void OnStarting_ShouldInvokeCallback_OnFirstCall() + { + var feature = new FakeHttpResponseFeature(); + var invoked = false; + + feature.OnStarting(_ => { invoked = true; return Task.CompletedTask; }, null); + + Assert.True(invoked); + Assert.True(feature.HasStarted); + } + + [Fact] + public void OnStarting_ShouldNotInvokeCallback_OnSubsequentCalls() + { + var feature = new FakeHttpResponseFeature(); + var count = 0; + + feature.OnStarting(_ => { count++; return Task.CompletedTask; }, null); + feature.OnStarting(_ => { count++; return Task.CompletedTask; }, null); + + Assert.Equal(1, count); + Assert.True(feature.HasStarted); + } + + [Fact] + public void OnStarting_ShouldNotThrow_WhenCallbackIsNull() + { + var feature = new FakeHttpResponseFeature(); + + var ex = Record.Exception(() => feature.OnStarting(null, null)); + + Assert.Null(ex); + Assert.True(feature.HasStarted); + } +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/ManagedWebHostFixtureTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/ManagedWebHostFixtureTest.cs index 75129a6..ff5d819 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/ManagedWebHostFixtureTest.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/ManagedWebHostFixtureTest.cs @@ -1,63 +1,63 @@ -using System; -using Codebelt.Extensions.Xunit.Hosting.AspNetCore.Assets; -using Microsoft.AspNetCore.Builder; -using Xunit; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; - -public class ManagedWebHostFixtureTest : Test -{ - public ManagedWebHostFixtureTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public void ConfigureHost_NullHostTest_ThrowsArgumentNullException() - { - // Arrange - var fixture = new ManagedWebHostFixture(); - - // Act & Assert - Assert.Throws(() => fixture.ConfigureHost(null)); - } - - [Fact] - public void ConfigureHost_InvalidHostTestType_ThrowsArgumentOutOfRangeException() - { - // Arrange - var fixture = new ManagedWebHostFixture(); - var invalidHostTest = new InvalidHostTest(fixture); - - // Act & Assert - Assert.Throws(() => fixture.ConfigureHost(invalidHostTest)); - } - - [Fact] - public void ConfigureApplicationCallback_SetAndGet_ReturnsCorrectValue() - { - // Arrange - var fixture = new ManagedWebHostFixture(); - Action callback = app => { }; - - // Act - fixture.ConfigureApplicationCallback = callback; - - // Assert - Assert.Equal(callback, fixture.ConfigureApplicationCallback); - } - - [Fact] - public void ConfigureHost_ValidHostTest_ConfiguresHostCorrectly() - { - // Arrange - var fixture = new ManagedWebHostFixture(); - var hostTest = new ValidHostTest(fixture); - - // Act - fixture.ConfigureHost(hostTest); - - // Assert - Assert.NotNull(fixture.Host); - Assert.NotNull(fixture.Application); - } -} +using System; +using Codebelt.Extensions.Xunit.Hosting.AspNetCore.Assets; +using Microsoft.AspNetCore.Builder; +using Xunit; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; + +public class ManagedWebHostFixtureTest : Test +{ + public ManagedWebHostFixtureTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void ConfigureHost_NullHostTest_ThrowsArgumentNullException() + { + // Arrange + var fixture = new ManagedWebHostFixture(); + + // Act & Assert + Assert.Throws(() => fixture.ConfigureHost(null)); + } + + [Fact] + public void ConfigureHost_InvalidHostTestType_ThrowsArgumentOutOfRangeException() + { + // Arrange + var fixture = new ManagedWebHostFixture(); + var invalidHostTest = new InvalidHostTest(fixture); + + // Act & Assert + Assert.Throws(() => fixture.ConfigureHost(invalidHostTest)); + } + + [Fact] + public void ConfigureApplicationCallback_SetAndGet_ReturnsCorrectValue() + { + // Arrange + var fixture = new ManagedWebHostFixture(); + Action callback = app => { }; + + // Act + fixture.ConfigureApplicationCallback = callback; + + // Assert + Assert.Equal(callback, fixture.ConfigureApplicationCallback); + } + + [Fact] + public void ConfigureHost_ValidHostTest_ConfiguresHostCorrectly() + { + // Arrange + var fixture = new ManagedWebHostFixture(); + var hostTest = new ValidHostTest(fixture); + + // Act + fixture.ConfigureHost(hostTest); + + // Assert + Assert.NotNull(fixture.Host); + Assert.NotNull(fixture.Application); + } +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/MinimalMvcWebHostTestTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/MinimalMvcWebHostTestTest.cs index 8e62b8a..a258e24 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/MinimalMvcWebHostTestTest.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/MinimalMvcWebHostTestTest.cs @@ -1,57 +1,57 @@ -using System; -using System.Net.Http; -using System.Threading.Tasks; -using Codebelt.Extensions.Xunit.Hosting.AspNetCore.Assets; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.TestHost; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Xunit; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; - -public class MinimalMvcWebHostTestTest : MinimalWebHostTest -{ - private readonly ManagedWebMinimalHostFixture _hostFixture; - private readonly HttpClient _client; - - public MinimalMvcWebHostTestTest(ManagedWebMinimalHostFixture hostFixture, ITestOutputHelper output = null, Type callerType = null) : base(hostFixture, output, callerType) - { - hostFixture.Host.Services.GetRequiredService().TestOutput = output; - _hostFixture = hostFixture; - _client = hostFixture.Host.GetTestClient(); - } - - [Fact] - public async Task GetTestAsync() - { - var response = await _client.GetAsync("/Fake"); - response.EnsureSuccessStatusCode(); - var body = await response.Content.ReadAsStringAsync(); - Assert.Equal("Unit Test", body); - } - - [Fact] - public async Task GetTestAsync2() - { - var response = await _client.GetAsync("/Fake"); - response.EnsureSuccessStatusCode(); - var body = await response.Content.ReadAsStringAsync(); - Assert.Equal("Unit Test", body); - } - - protected override void ConfigureHost(IHostApplicationBuilder hb) - { - hb.Services.AddControllers() - .AddApplicationPart(typeof(FakeController).Assembly); - - hb.Services.AddXunitTestLoggingOutputHelperAccessor(); - hb.Services.AddXunitTestLogging(TestOutput); - } - - public override void ConfigureApplication(IApplicationBuilder app) - { - app.UseRouting(); - app.UseEndpoints(routes => { routes.MapControllers(); }); - } -} +using System; +using System.Net.Http; +using System.Threading.Tasks; +using Codebelt.Extensions.Xunit.Hosting.AspNetCore.Assets; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Xunit; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; + +public class MinimalMvcWebHostTestTest : MinimalWebHostTest +{ + private readonly ManagedWebMinimalHostFixture _hostFixture; + private readonly HttpClient _client; + + public MinimalMvcWebHostTestTest(ManagedWebMinimalHostFixture hostFixture, ITestOutputHelper output = null, Type callerType = null) : base(hostFixture, output, callerType) + { + hostFixture.Host.Services.GetRequiredService().TestOutput = output; + _hostFixture = hostFixture; + _client = hostFixture.Host.GetTestClient(); + } + + [Fact] + public async Task GetTestAsync() + { + var response = await _client.GetAsync("/Fake"); + response.EnsureSuccessStatusCode(); + var body = await response.Content.ReadAsStringAsync(); + Assert.Equal("Unit Test", body); + } + + [Fact] + public async Task GetTestAsync2() + { + var response = await _client.GetAsync("/Fake"); + response.EnsureSuccessStatusCode(); + var body = await response.Content.ReadAsStringAsync(); + Assert.Equal("Unit Test", body); + } + + protected override void ConfigureHost(IHostApplicationBuilder hb) + { + hb.Services.AddControllers() + .AddApplicationPart(typeof(FakeController).Assembly); + + hb.Services.AddXunitTestLoggingOutputHelperAccessor(); + hb.Services.AddXunitTestLogging(TestOutput); + } + + public override void ConfigureApplication(IApplicationBuilder app) + { + app.UseRouting(); + app.UseEndpoints(routes => { routes.MapControllers(); }); + } +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/MinimalWebHostTestFactoryTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/MinimalWebHostTestFactoryTest.cs index bca3e60..3537cd6 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/MinimalWebHostTestFactoryTest.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/MinimalWebHostTestFactoryTest.cs @@ -1,135 +1,135 @@ -using System; -using System.Diagnostics; -using System.Linq; -using System.Security; -using System.Threading; -using System.Threading.Tasks; -using Cuemon.AspNetCore.Diagnostics; -using Cuemon.Extensions.AspNetCore.Diagnostics; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; -using Xunit; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; - -public class MinimalWebHostTestFactoryTest : Test -{ - public MinimalWebHostTestFactoryTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public void Create_ShouldThrowSecurityException_DueToBlockingAspNetCoreHostFixture() - { - Assert.Throws(() => MinimalWebHostTestFactory.Create( - services => - { - services.AddXunitTestLogging(TestOutput); - services.AddAuthorization(o => - { - o.AddPolicy("Test", _ => throw new SecurityException()); - }); - }, - app => - { - var policy = app.ApplicationServices.GetRequiredService(); - })); - } - - [Fact] - public void Create_CallerTypeShouldHaveDeclaringTypeOfMiddlewareTestFactoryTest() - { - Type sut1 = GetType(); - string sut2 = null; - var middleware = MinimalWebHostTestFactory.Create(Assert.NotNull, Assert.NotNull, host => - { - var hb = host.ToHostBuilder(); - hb.ConfigureAppConfiguration((context, _) => - { - sut2 = context.HostingEnvironment.ApplicationName; - }); - }); - - Assert.True(sut1 == middleware.CallerType.DeclaringType); - Assert.Equal(GetType().Assembly.GetName().Name, sut2); - } - - [Fact] - public Task RunAsync_ShouldHaveApplicationNameEqualToThisAssembly() - { - return MinimalWebHostTestFactory.RunAsync(Assert.NotNull, Assert.NotNull, host => - { - var hb = host.ToHostBuilder(); - hb.ConfigureAppConfiguration((context, _) => - { - TestOutput.WriteLine(context.HostingEnvironment.ApplicationName); - Assert.Equal(GetType().Assembly.GetName().Name, context.HostingEnvironment.ApplicationName); - }); - }); - } - - [Fact] - public Task RunWithHostBuilderContextAsync_ShouldHaveApplicationNameEqualToThisAssembly_WithHostBuilderContext() - { - return MinimalWebHostTestFactory.RunWithHostBuilderContextAsync((context, app) => - { - Assert.NotNull(context); - Assert.NotNull(context.HostingEnvironment); - Assert.NotNull(context.Configuration); - Assert.NotNull(context.Properties); - Assert.NotNull(app); - }, - (context, services) => - { - Assert.NotNull(context); - Assert.NotNull(context.HostingEnvironment); - Assert.NotNull(context.Configuration); - Assert.NotNull(context.Properties); - Assert.NotNull(services); - }, - host => - { - var hb = host.ToHostBuilder(); - hb.ConfigureAppConfiguration((context, configuration) => - { - TestOutput.WriteLine(context.HostingEnvironment.ApplicationName); - Assert.Equal(GetType().Assembly.GetName().Name, context.HostingEnvironment.ApplicationName); - }); - }); - } - - [Fact] - public async Task RunAsync_ShouldWorkWithXunitTestLogging() - { - using var response = await MinimalWebHostTestFactory.RunAsync( - services => - { - services.AddXunitTestLogging(TestOutput); - services.AddServerTiming(o => o.SuppressHeaderPredicate = _ => false); - }, - app => - { - app.UseServerTiming(); - app.Use(async (context, next) => - { - var sw = Stopwatch.StartNew(); - context.Response.OnStarting(() => - { - sw.Stop(); - context.RequestServices.GetRequiredService().AddServerTiming("use-middleware", sw.Elapsed); - return Task.CompletedTask; - }); - await next(context).ConfigureAwait(false); - }); - app.Run(context => - { - Thread.Sleep(400); - return context.Response.WriteAsync("Hello World!"); - }); - }).ConfigureAwait(false); - - Assert.StartsWith("use-middleware;dur=", response.Headers.Single(kvp => kvp.Key == ServerTiming.HeaderName).Value.FirstOrDefault()); - } -} +using System; +using System.Diagnostics; +using System.Linq; +using System.Security; +using System.Threading; +using System.Threading.Tasks; +using Cuemon.AspNetCore.Diagnostics; +using Cuemon.Extensions.AspNetCore.Diagnostics; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Xunit; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; + +public class MinimalWebHostTestFactoryTest : Test +{ + public MinimalWebHostTestFactoryTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void Create_ShouldThrowSecurityException_DueToBlockingAspNetCoreHostFixture() + { + Assert.Throws(() => MinimalWebHostTestFactory.Create( + services => + { + services.AddXunitTestLogging(TestOutput); + services.AddAuthorization(o => + { + o.AddPolicy("Test", _ => throw new SecurityException()); + }); + }, + app => + { + var policy = app.ApplicationServices.GetRequiredService(); + })); + } + + [Fact] + public void Create_CallerTypeShouldHaveDeclaringTypeOfMiddlewareTestFactoryTest() + { + Type sut1 = GetType(); + string sut2 = null; + var middleware = MinimalWebHostTestFactory.Create(Assert.NotNull, Assert.NotNull, host => + { + var hb = host.ToHostBuilder(); + hb.ConfigureAppConfiguration((context, _) => + { + sut2 = context.HostingEnvironment.ApplicationName; + }); + }); + + Assert.True(sut1 == middleware.CallerType.DeclaringType); + Assert.Equal(GetType().Assembly.GetName().Name, sut2); + } + + [Fact] + public Task RunAsync_ShouldHaveApplicationNameEqualToThisAssembly() + { + return MinimalWebHostTestFactory.RunAsync(Assert.NotNull, Assert.NotNull, host => + { + var hb = host.ToHostBuilder(); + hb.ConfigureAppConfiguration((context, _) => + { + TestOutput.WriteLine(context.HostingEnvironment.ApplicationName); + Assert.Equal(GetType().Assembly.GetName().Name, context.HostingEnvironment.ApplicationName); + }); + }); + } + + [Fact] + public Task RunWithHostBuilderContextAsync_ShouldHaveApplicationNameEqualToThisAssembly_WithHostBuilderContext() + { + return MinimalWebHostTestFactory.RunWithHostBuilderContextAsync((context, app) => + { + Assert.NotNull(context); + Assert.NotNull(context.HostingEnvironment); + Assert.NotNull(context.Configuration); + Assert.NotNull(context.Properties); + Assert.NotNull(app); + }, + (context, services) => + { + Assert.NotNull(context); + Assert.NotNull(context.HostingEnvironment); + Assert.NotNull(context.Configuration); + Assert.NotNull(context.Properties); + Assert.NotNull(services); + }, + host => + { + var hb = host.ToHostBuilder(); + hb.ConfigureAppConfiguration((context, configuration) => + { + TestOutput.WriteLine(context.HostingEnvironment.ApplicationName); + Assert.Equal(GetType().Assembly.GetName().Name, context.HostingEnvironment.ApplicationName); + }); + }); + } + + [Fact] + public async Task RunAsync_ShouldWorkWithXunitTestLogging() + { + using var response = await MinimalWebHostTestFactory.RunAsync( + services => + { + services.AddXunitTestLogging(TestOutput); + services.AddServerTiming(o => o.SuppressHeaderPredicate = _ => false); + }, + app => + { + app.UseServerTiming(); + app.Use(async (context, next) => + { + var sw = Stopwatch.StartNew(); + context.Response.OnStarting(() => + { + sw.Stop(); + context.RequestServices.GetRequiredService().AddServerTiming("use-middleware", sw.Elapsed); + return Task.CompletedTask; + }); + await next(context).ConfigureAwait(false); + }); + app.Run(context => + { + Thread.Sleep(400); + return context.Response.WriteAsync("Hello World!"); + }); + }).ConfigureAwait(false); + + Assert.StartsWith("use-middleware;dur=", response.Headers.Single(kvp => kvp.Key == ServerTiming.HeaderName).Value.FirstOrDefault()); + } +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/MinimalWebHostTestTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/MinimalWebHostTestTest.cs index 937978a..e361ddd 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/MinimalWebHostTestTest.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/MinimalWebHostTestTest.cs @@ -1,164 +1,164 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using Codebelt.Extensions.Xunit.Hosting.AspNetCore.Assets; -using Cuemon.Extensions.IO; -using Cuemon.Messaging; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Xunit; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; - -public class MinimalWebHostTestTest : MinimalWebHostTest -{ - private readonly IServiceProvider _provider; - private readonly IApplicationBuilder _pipeline; - - public MinimalWebHostTestTest(ManagedWebMinimalHostFixture hostFixture, ITestOutputHelper output) : base(hostFixture, output) - { - _pipeline = hostFixture.Application; - _provider = hostFixture.Host.Services; - _provider.GetRequiredService().TestOutput = output; - } - - [Fact] - public async Task ShouldHaveResultOfBoolMiddlewareInBody() - { - var context = _provider.GetRequiredService().HttpContext; - var options = _provider.GetRequiredService>(); - var pipeline = _pipeline.Build(); - - Assert.Equal("Hello awesome developers!", context!.Response.Body.ToEncodedString(o => o.LeaveOpen = true)); - - var logger = _pipeline.ApplicationServices.GetRequiredService>(); - logger.LogInformation("Hello from {0}", nameof(ShouldHaveResultOfBoolMiddlewareInBody)); - - await pipeline(context); - - Assert.Equal("A:True, B:False, C:True, D:False, E:True, F:False", context.Response.Body.ToEncodedString()); - - Assert.True(options.Value.A); - Assert.False(options.Value.B); - Assert.True(options.Value.C); - Assert.False(options.Value.D); - Assert.True(options.Value.E); - Assert.False(options.Value.F); - } - -#if NET10_0_OR_GREATER - [Fact] - public void ShouldThrowInvalidOperationException_BecauseOneOfTheServicesIsScoped() - { - var ex = Assert.Throws(() => _provider.GetServices()); - - TestOutput.WriteLine(ex.Message); - - Assert.Contains("from root provider because it requires scoped service", ex.Message); - } -#endif - - [Fact] - public void ShouldHaveAccessToCorrelationTokens_UsingScopedProvider() - { - using var scope = _provider.CreateScope(); - - var firstRequest = scope.ServiceProvider.GetServices().ToList(); - var secondRequest = scope.ServiceProvider.GetServices().ToList(); - - TestOutput.WriteLine("----"); - TestOutput.WriteLines(firstRequest); - TestOutput.WriteLine("----"); - TestOutput.WriteLines(secondRequest); - - Assert.Equal(3, firstRequest.Count); - Assert.Equal(3, secondRequest.Count); - - Assert.Same(firstRequest[0], secondRequest[0]); - Assert.NotSame(firstRequest[1], secondRequest[1]); - Assert.Same(firstRequest[2], secondRequest[2]); - - Assert.Equal(firstRequest[0].CorrelationId, secondRequest[0].CorrelationId); - Assert.NotEqual(firstRequest[1].CorrelationId, secondRequest[1].CorrelationId); - Assert.Equal(firstRequest[2].CorrelationId, secondRequest[2].CorrelationId); - } - - [Fact] - public void ShouldHaveAccessToCorrelationTokens_UsingRequestServices() // reference: https://github.com/dotnet/aspnetcore/blob/main/src/Http/Http/src/Features/RequestServicesFeature.cs - { - var context = _provider.GetRequiredService().HttpContext!; - - var firstRequest = context.RequestServices.GetServices().ToList(); - var secondRequest = context.RequestServices.GetServices().ToList(); - - TestOutput.WriteLine("----"); - TestOutput.WriteLines(firstRequest); - TestOutput.WriteLine("----"); - TestOutput.WriteLines(secondRequest); - - Assert.Equal(3, firstRequest.Count); - Assert.Equal(3, secondRequest.Count); - - Assert.Same(firstRequest[0], secondRequest[0]); - Assert.NotSame(firstRequest[1], secondRequest[1]); - Assert.Same(firstRequest[2], secondRequest[2]); - - Assert.Equal(firstRequest[0].CorrelationId, secondRequest[0].CorrelationId); - Assert.NotEqual(firstRequest[1].CorrelationId, secondRequest[1].CorrelationId); - Assert.Equal(firstRequest[2].CorrelationId, secondRequest[2].CorrelationId); - } - - [Fact] - public void ShouldLogToXunitTestLogging() - { - var context = _provider.GetRequiredService().HttpContext; - var logger = _pipeline.ApplicationServices.GetRequiredService>(); - logger.LogInformation("Hello stranger {0}", nameof(ShouldLogToXunitTestLogging)); - var store = _pipeline.ApplicationServices.GetRequiredService>().GetTestStore(); - var entry = store.Query(entry => entry.Message.Contains("Hello stranger", StringComparison.OrdinalIgnoreCase)).SingleOrDefault(); - - Assert.NotNull(entry); - Assert.Equal("Information: Hello stranger ShouldLogToXunitTestLogging", entry.Message); - } - - [Fact] - public void Test_VerifyAbstractions() - { - using var hostTest = WebHostTestFactory.Create(); - Assert.IsAssignableFrom(hostTest); - Assert.IsAssignableFrom(hostTest); - Assert.IsAssignableFrom(hostTest); - Assert.IsAssignableFrom(hostTest); - Assert.IsAssignableFrom(hostTest); - Assert.IsAssignableFrom(hostTest); - Assert.IsAssignableFrom(hostTest); - Assert.IsAssignableFrom(hostTest); - } - - protected override void ConfigureHost(IHostApplicationBuilder hb) - { - hb.Services.AddFakeHttpContextAccessor(); - hb.Services.Configure(o => - { - o.A = true; - o.C = true; - o.E = true; - }); - hb.Services.AddXunitTestLoggingOutputHelperAccessor(); - hb.Services.AddXunitTestLogging(TestOutput); - - hb.Services.AddSingleton(); - hb.Services.AddTransient(); - hb.Services.AddScoped(); - } - - public override void ConfigureApplication(IApplicationBuilder app) - { - app.ApplicationServices.GetRequiredService>().LogInformation(nameof(ConfigureApplication)); - app.UseMiddleware(); - } -} +using System; +using System.Linq; +using System.Threading.Tasks; +using Codebelt.Extensions.Xunit.Hosting.AspNetCore.Assets; +using Cuemon.Extensions.IO; +using Cuemon.Messaging; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Xunit; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; + +public class MinimalWebHostTestTest : MinimalWebHostTest +{ + private readonly IServiceProvider _provider; + private readonly IApplicationBuilder _pipeline; + + public MinimalWebHostTestTest(ManagedWebMinimalHostFixture hostFixture, ITestOutputHelper output) : base(hostFixture, output) + { + _pipeline = hostFixture.Application; + _provider = hostFixture.Host.Services; + _provider.GetRequiredService().TestOutput = output; + } + + [Fact] + public async Task ShouldHaveResultOfBoolMiddlewareInBody() + { + var context = _provider.GetRequiredService().HttpContext; + var options = _provider.GetRequiredService>(); + var pipeline = _pipeline.Build(); + + Assert.Equal("Hello awesome developers!", context!.Response.Body.ToEncodedString(o => o.LeaveOpen = true)); + + var logger = _pipeline.ApplicationServices.GetRequiredService>(); + logger.LogInformation("Hello from {0}", nameof(ShouldHaveResultOfBoolMiddlewareInBody)); + + await pipeline(context); + + Assert.Equal("A:True, B:False, C:True, D:False, E:True, F:False", context.Response.Body.ToEncodedString()); + + Assert.True(options.Value.A); + Assert.False(options.Value.B); + Assert.True(options.Value.C); + Assert.False(options.Value.D); + Assert.True(options.Value.E); + Assert.False(options.Value.F); + } + +#if NET10_0_OR_GREATER + [Fact] + public void ShouldThrowInvalidOperationException_BecauseOneOfTheServicesIsScoped() + { + var ex = Assert.Throws(() => _provider.GetServices()); + + TestOutput.WriteLine(ex.Message); + + Assert.Contains("from root provider because it requires scoped service", ex.Message); + } +#endif + + [Fact] + public void ShouldHaveAccessToCorrelationTokens_UsingScopedProvider() + { + using var scope = _provider.CreateScope(); + + var firstRequest = scope.ServiceProvider.GetServices().ToList(); + var secondRequest = scope.ServiceProvider.GetServices().ToList(); + + TestOutput.WriteLine("----"); + TestOutput.WriteLines(firstRequest); + TestOutput.WriteLine("----"); + TestOutput.WriteLines(secondRequest); + + Assert.Equal(3, firstRequest.Count); + Assert.Equal(3, secondRequest.Count); + + Assert.Same(firstRequest[0], secondRequest[0]); + Assert.NotSame(firstRequest[1], secondRequest[1]); + Assert.Same(firstRequest[2], secondRequest[2]); + + Assert.Equal(firstRequest[0].CorrelationId, secondRequest[0].CorrelationId); + Assert.NotEqual(firstRequest[1].CorrelationId, secondRequest[1].CorrelationId); + Assert.Equal(firstRequest[2].CorrelationId, secondRequest[2].CorrelationId); + } + + [Fact] + public void ShouldHaveAccessToCorrelationTokens_UsingRequestServices() // reference: https://github.com/dotnet/aspnetcore/blob/main/src/Http/Http/src/Features/RequestServicesFeature.cs + { + var context = _provider.GetRequiredService().HttpContext!; + + var firstRequest = context.RequestServices.GetServices().ToList(); + var secondRequest = context.RequestServices.GetServices().ToList(); + + TestOutput.WriteLine("----"); + TestOutput.WriteLines(firstRequest); + TestOutput.WriteLine("----"); + TestOutput.WriteLines(secondRequest); + + Assert.Equal(3, firstRequest.Count); + Assert.Equal(3, secondRequest.Count); + + Assert.Same(firstRequest[0], secondRequest[0]); + Assert.NotSame(firstRequest[1], secondRequest[1]); + Assert.Same(firstRequest[2], secondRequest[2]); + + Assert.Equal(firstRequest[0].CorrelationId, secondRequest[0].CorrelationId); + Assert.NotEqual(firstRequest[1].CorrelationId, secondRequest[1].CorrelationId); + Assert.Equal(firstRequest[2].CorrelationId, secondRequest[2].CorrelationId); + } + + [Fact] + public void ShouldLogToXunitTestLogging() + { + var context = _provider.GetRequiredService().HttpContext; + var logger = _pipeline.ApplicationServices.GetRequiredService>(); + logger.LogInformation("Hello stranger {0}", nameof(ShouldLogToXunitTestLogging)); + var store = _pipeline.ApplicationServices.GetRequiredService>().GetTestStore(); + var entry = store.Query(entry => entry.Message.Contains("Hello stranger", StringComparison.OrdinalIgnoreCase)).SingleOrDefault(); + + Assert.NotNull(entry); + Assert.Equal("Information: Hello stranger ShouldLogToXunitTestLogging", entry.Message); + } + + [Fact] + public void Test_VerifyAbstractions() + { + using var hostTest = WebHostTestFactory.Create(); + Assert.IsAssignableFrom(hostTest); + Assert.IsAssignableFrom(hostTest); + Assert.IsAssignableFrom(hostTest); + Assert.IsAssignableFrom(hostTest); + Assert.IsAssignableFrom(hostTest); + Assert.IsAssignableFrom(hostTest); + Assert.IsAssignableFrom(hostTest); + Assert.IsAssignableFrom(hostTest); + } + + protected override void ConfigureHost(IHostApplicationBuilder hb) + { + hb.Services.AddFakeHttpContextAccessor(); + hb.Services.Configure(o => + { + o.A = true; + o.C = true; + o.E = true; + }); + hb.Services.AddXunitTestLoggingOutputHelperAccessor(); + hb.Services.AddXunitTestLogging(TestOutput); + + hb.Services.AddSingleton(); + hb.Services.AddTransient(); + hb.Services.AddScoped(); + } + + public override void ConfigureApplication(IApplicationBuilder app) + { + app.ApplicationServices.GetRequiredService>().LogInformation(nameof(ConfigureApplication)); + app.UseMiddleware(); + } +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/MvcWebHostTestTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/MvcWebHostTestTest.cs index 301512b..a436597 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/MvcWebHostTestTest.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/MvcWebHostTestTest.cs @@ -1,56 +1,56 @@ -using System; -using System.Net.Http; -using System.Threading.Tasks; -using Codebelt.Extensions.Xunit.Hosting.AspNetCore.Assets; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.TestHost; -using Microsoft.Extensions.DependencyInjection; -using Xunit; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; - -public class MvcWebHostTestTest : WebHostTest -{ - private readonly ManagedWebHostFixture _hostFixture; - private readonly HttpClient _client; - - public MvcWebHostTestTest(ManagedWebHostFixture hostFixture, ITestOutputHelper output = null, Type callerType = null) : base(hostFixture, output, callerType) - { - hostFixture.Host.Services.GetRequiredService().TestOutput = output; - _hostFixture = hostFixture; - _client = hostFixture.Host.GetTestClient(); - } - - [Fact] - public async Task GetTestAsync() - { - var response = await _client.GetAsync("/Fake"); - response.EnsureSuccessStatusCode(); - var body = await response.Content.ReadAsStringAsync(); - Assert.Equal("Unit Test", body); - } - - [Fact] - public async Task GetTestAsync2() - { - var response = await _client.GetAsync("/Fake"); - response.EnsureSuccessStatusCode(); - var body = await response.Content.ReadAsStringAsync(); - Assert.Equal("Unit Test", body); - } - - public override void ConfigureServices(IServiceCollection services) - { - services.AddControllers() - .AddApplicationPart(typeof(FakeController).Assembly); - - services.AddXunitTestLoggingOutputHelperAccessor(); - services.AddXunitTestLogging(TestOutput); - } - - public override void ConfigureApplication(IApplicationBuilder app) - { - app.UseRouting(); - app.UseEndpoints(routes => { routes.MapControllers(); }); - } -} +using System; +using System.Net.Http; +using System.Threading.Tasks; +using Codebelt.Extensions.Xunit.Hosting.AspNetCore.Assets; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Xunit; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; + +public class MvcWebHostTestTest : WebHostTest +{ + private readonly ManagedWebHostFixture _hostFixture; + private readonly HttpClient _client; + + public MvcWebHostTestTest(ManagedWebHostFixture hostFixture, ITestOutputHelper output = null, Type callerType = null) : base(hostFixture, output, callerType) + { + hostFixture.Host.Services.GetRequiredService().TestOutput = output; + _hostFixture = hostFixture; + _client = hostFixture.Host.GetTestClient(); + } + + [Fact] + public async Task GetTestAsync() + { + var response = await _client.GetAsync("/Fake"); + response.EnsureSuccessStatusCode(); + var body = await response.Content.ReadAsStringAsync(); + Assert.Equal("Unit Test", body); + } + + [Fact] + public async Task GetTestAsync2() + { + var response = await _client.GetAsync("/Fake"); + response.EnsureSuccessStatusCode(); + var body = await response.Content.ReadAsStringAsync(); + Assert.Equal("Unit Test", body); + } + + public override void ConfigureServices(IServiceCollection services) + { + services.AddControllers() + .AddApplicationPart(typeof(FakeController).Assembly); + + services.AddXunitTestLoggingOutputHelperAccessor(); + services.AddXunitTestLogging(TestOutput); + } + + public override void ConfigureApplication(IApplicationBuilder app) + { + app.UseRouting(); + app.UseEndpoints(routes => { routes.MapControllers(); }); + } +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/SelfManagedWebHostFixtureTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/SelfManagedWebHostFixtureTest.cs index da2e3b7..546f80e 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/SelfManagedWebHostFixtureTest.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/SelfManagedWebHostFixtureTest.cs @@ -1,18 +1,18 @@ -using Xunit; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; - -public class SelfManagedWebHostFixtureTest : Test -{ - public SelfManagedWebHostFixtureTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public void Create_ShouldSucceed_WhenSelfManagedWebHostFixtureIsUsed() - { - using var startup = WebHostTestFactory.Create(hostFixture: new SelfManagedWebHostFixture()); - - Assert.NotNull(startup.Host); - } -} +using Xunit; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; + +public class SelfManagedWebHostFixtureTest : Test +{ + public SelfManagedWebHostFixtureTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void Create_ShouldSucceed_WhenSelfManagedWebHostFixtureIsUsed() + { + using var startup = WebHostTestFactory.Create(hostFixture: new SelfManagedWebHostFixture()); + + Assert.NotNull(startup.Host); + } +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/SelfManagedWebMinimalHostFixtureTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/SelfManagedWebMinimalHostFixtureTest.cs index 9ad6eae..eaa3241 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/SelfManagedWebMinimalHostFixtureTest.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/SelfManagedWebMinimalHostFixtureTest.cs @@ -1,22 +1,22 @@ -using Microsoft.AspNetCore.Builder; -using Xunit; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; - -public class SelfManagedWebMinimalHostFixtureTest : MinimalWebHostTest -{ - public SelfManagedWebMinimalHostFixtureTest(SelfManagedWebMinimalHostFixture hostFixture, ITestOutputHelper output) : base(hostFixture, output) - { - } - - [Fact] - public void Host_ShouldNotBeNull_WhenNoOpRunnerIsUsed() - { - Assert.NotNull(Host); - Assert.NotNull(Application); - } - - public override void ConfigureApplication(IApplicationBuilder app) - { - } -} +using Microsoft.AspNetCore.Builder; +using Xunit; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; + +public class SelfManagedWebMinimalHostFixtureTest : MinimalWebHostTest +{ + public SelfManagedWebMinimalHostFixtureTest(SelfManagedWebMinimalHostFixture hostFixture, ITestOutputHelper output) : base(hostFixture, output) + { + } + + [Fact] + public void Host_ShouldNotBeNull_WhenNoOpRunnerIsUsed() + { + Assert.NotNull(Host); + Assert.NotNull(Application); + } + + public override void ConfigureApplication(IApplicationBuilder app) + { + } +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/ServiceCollectionExtensionsTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/ServiceCollectionExtensionsTest.cs index 2cc6d43..4c0d287 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/ServiceCollectionExtensionsTest.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/ServiceCollectionExtensionsTest.cs @@ -1,44 +1,44 @@ -using System.ComponentModel; -using Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; -using Xunit; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; - -public class ServiceCollectionExtensionsTest : Test -{ - public ServiceCollectionExtensionsTest(ITestOutputHelper output) : base(output) - { - } - - [Theory] - [InlineData(ServiceLifetime.Transient)] - [InlineData(ServiceLifetime.Scoped)] - [InlineData(ServiceLifetime.Singleton)] - public void AddFakeHttpContextAccessor_ShouldAddService(ServiceLifetime lifetime) - { - // Arrange - var services = new ServiceCollection(); - - // Act - services.AddFakeHttpContextAccessor(lifetime); - var serviceProvider = services.BuildServiceProvider(); - var httpContextAccessor = serviceProvider.GetService(); - - // Assert - Assert.NotNull(httpContextAccessor); - Assert.IsType(httpContextAccessor); - } - - [Fact] - public void AddFakeHttpContextAccessor_ShouldThrowInvalidEnumArgumentException_ForInvalidLifetime() - { - // Arrange - var services = new ServiceCollection(); - var invalidLifetime = (ServiceLifetime)999; - - // Act & Assert - Assert.Throws(() => services.AddFakeHttpContextAccessor(invalidLifetime)); - } -} +using System.ComponentModel; +using Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Xunit; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; + +public class ServiceCollectionExtensionsTest : Test +{ + public ServiceCollectionExtensionsTest(ITestOutputHelper output) : base(output) + { + } + + [Theory] + [InlineData(ServiceLifetime.Transient)] + [InlineData(ServiceLifetime.Scoped)] + [InlineData(ServiceLifetime.Singleton)] + public void AddFakeHttpContextAccessor_ShouldAddService(ServiceLifetime lifetime) + { + // Arrange + var services = new ServiceCollection(); + + // Act + services.AddFakeHttpContextAccessor(lifetime); + var serviceProvider = services.BuildServiceProvider(); + var httpContextAccessor = serviceProvider.GetService(); + + // Assert + Assert.NotNull(httpContextAccessor); + Assert.IsType(httpContextAccessor); + } + + [Fact] + public void AddFakeHttpContextAccessor_ShouldThrowInvalidEnumArgumentException_ForInvalidLifetime() + { + // Arrange + var services = new ServiceCollection(); + var invalidLifetime = (ServiceLifetime)999; + + // Act & Assert + Assert.Throws(() => services.AddFakeHttpContextAccessor(invalidLifetime)); + } +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/WebHostTestFactoryTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/WebHostTestFactoryTest.cs index 019f0c3..076a5ec 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/WebHostTestFactoryTest.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/WebHostTestFactoryTest.cs @@ -1,173 +1,173 @@ -using System; -using System.Diagnostics; -using System.Linq; -using System.Security; -using System.Threading; -using System.Threading.Tasks; -using Cuemon.AspNetCore.Diagnostics; -using Cuemon.Extensions.AspNetCore.Diagnostics; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Xunit; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; - -public class WebHostTestFactoryTest : Test -{ - public WebHostTestFactoryTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public void Create_ShouldThrowSecurityException_DueToBlockingAspNetCoreHostFixture() - { - Assert.Throws(() => WebHostTestFactory.Create( - services => - { - services.AddXunitTestLogging(TestOutput); - services.AddAuthorization(o => - { - o.AddPolicy("Test", _ => throw new SecurityException()); - }); - }, - app => - { - var policy = app.ApplicationServices.GetRequiredService(); - }, - host => - { - host.UseDefaultServiceProvider(o => - { - o.ValidateOnBuild = false; - o.ValidateScopes = false; - }); - }, - new BlockingManagedWebHostFixture())); - } - - [Fact] - public void Create_ShouldCaptureSecurityException_DueToNonBlockingAspNetCoreHostFixture() - { - using (var startup = WebHostTestFactory.Create( - services => - { - services.AddXunitTestLogging(TestOutput); - - services.AddAuthorization(o => { o.AddPolicy("Test", _ => throw new SecurityException()); }); - }, - app => - { - var policy = app.ApplicationServices.GetRequiredService(); - }, - host => - { - host.UseDefaultServiceProvider(o => - { - o.ValidateOnBuild = false; - o.ValidateScopes = false; - }); - })) - { - var loggerStore = startup.Host.Services.GetRequiredService>().GetTestStore(null); - var message = loggerStore.Query(entry => entry.Severity == LogLevel.Critical && entry.Message.Contains("SecurityException", StringComparison.OrdinalIgnoreCase)).SingleOrDefault()?.Message; - Assert.NotNull(message); - Assert.Contains("System.Security.SecurityException: Security error.", message); - } - } - - [Fact] - public void Create_CallerTypeShouldHaveDeclaringTypeOfMiddlewareTestFactoryTest() - { - Type sut1 = GetType(); - string sut2 = null; - var middleware = WebHostTestFactory.Create(Assert.NotNull, Assert.NotNull, host => - { - host.ConfigureAppConfiguration((context, _) => - { - sut2 = context.HostingEnvironment.ApplicationName; - }); - }); - - Assert.True(sut1 == middleware.CallerType.DeclaringType); - Assert.Equal(GetType().Assembly.GetName().Name, sut2); - } - - [Fact] - public Task RunAsync_ShouldHaveApplicationNameEqualToThisAssembly() - { - return WebHostTestFactory.RunAsync(Assert.NotNull, Assert.NotNull, host => - { - host.ConfigureAppConfiguration((context, _) => - { - TestOutput.WriteLine(context.HostingEnvironment.ApplicationName); - Assert.Equal(GetType().Assembly.GetName().Name, context.HostingEnvironment.ApplicationName); - }); - }); - } - - [Fact] - public Task RunWithHostBuilderContextAsync_ShouldHaveApplicationNameEqualToThisAssembly_WithHostBuilderContext() - { - return WebHostTestFactory.RunWithHostBuilderContextAsync((context, app) => - { - Assert.NotNull(context); - Assert.NotNull(context.HostingEnvironment); - Assert.NotNull(context.Configuration); - Assert.NotNull(context.Properties); - Assert.NotNull(app); - }, - (context, services) => - { - Assert.NotNull(context); - Assert.NotNull(context.HostingEnvironment); - Assert.NotNull(context.Configuration); - Assert.NotNull(context.Properties); - Assert.NotNull(services); - }, - host => - { - host.ConfigureAppConfiguration((context, configuration) => - { - TestOutput.WriteLine(context.HostingEnvironment.ApplicationName); - Assert.Equal(GetType().Assembly.GetName().Name, context.HostingEnvironment.ApplicationName); - }); - }); - } - - [Fact] - public async Task RunAsync_ShouldWorkWithXunitTestLogging() - { - using var response = await WebHostTestFactory.RunAsync( - services => - { - services.AddXunitTestLogging(TestOutput); - services.AddServerTiming(o => o.SuppressHeaderPredicate = _ => false); - }, - app => - { - app.UseServerTiming(); - app.Use(async (context, next) => - { - var sw = Stopwatch.StartNew(); - context.Response.OnStarting(() => - { - sw.Stop(); - context.RequestServices.GetRequiredService().AddServerTiming("use-middleware", sw.Elapsed); - return Task.CompletedTask; - }); - await next(context).ConfigureAwait(false); - }); - app.Run(context => - { - Thread.Sleep(400); - return context.Response.WriteAsync("Hello World!"); - }); - }).ConfigureAwait(false); - - Assert.StartsWith("use-middleware;dur=", response.Headers.Single(kvp => kvp.Key == ServerTiming.HeaderName).Value.FirstOrDefault()); - } -} +using System; +using System.Diagnostics; +using System.Linq; +using System.Security; +using System.Threading; +using System.Threading.Tasks; +using Cuemon.AspNetCore.Diagnostics; +using Cuemon.Extensions.AspNetCore.Diagnostics; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Xunit; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; + +public class WebHostTestFactoryTest : Test +{ + public WebHostTestFactoryTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void Create_ShouldThrowSecurityException_DueToBlockingAspNetCoreHostFixture() + { + Assert.Throws(() => WebHostTestFactory.Create( + services => + { + services.AddXunitTestLogging(TestOutput); + services.AddAuthorization(o => + { + o.AddPolicy("Test", _ => throw new SecurityException()); + }); + }, + app => + { + var policy = app.ApplicationServices.GetRequiredService(); + }, + host => + { + host.UseDefaultServiceProvider(o => + { + o.ValidateOnBuild = false; + o.ValidateScopes = false; + }); + }, + new BlockingManagedWebHostFixture())); + } + + [Fact] + public void Create_ShouldCaptureSecurityException_DueToNonBlockingAspNetCoreHostFixture() + { + using (var startup = WebHostTestFactory.Create( + services => + { + services.AddXunitTestLogging(TestOutput); + + services.AddAuthorization(o => { o.AddPolicy("Test", _ => throw new SecurityException()); }); + }, + app => + { + var policy = app.ApplicationServices.GetRequiredService(); + }, + host => + { + host.UseDefaultServiceProvider(o => + { + o.ValidateOnBuild = false; + o.ValidateScopes = false; + }); + })) + { + var loggerStore = startup.Host.Services.GetRequiredService>().GetTestStore(null); + var message = loggerStore.Query(entry => entry.Severity == LogLevel.Critical && entry.Message.Contains("SecurityException", StringComparison.OrdinalIgnoreCase)).SingleOrDefault()?.Message; + Assert.NotNull(message); + Assert.Contains("System.Security.SecurityException: Security error.", message); + } + } + + [Fact] + public void Create_CallerTypeShouldHaveDeclaringTypeOfMiddlewareTestFactoryTest() + { + Type sut1 = GetType(); + string sut2 = null; + var middleware = WebHostTestFactory.Create(Assert.NotNull, Assert.NotNull, host => + { + host.ConfigureAppConfiguration((context, _) => + { + sut2 = context.HostingEnvironment.ApplicationName; + }); + }); + + Assert.True(sut1 == middleware.CallerType.DeclaringType); + Assert.Equal(GetType().Assembly.GetName().Name, sut2); + } + + [Fact] + public Task RunAsync_ShouldHaveApplicationNameEqualToThisAssembly() + { + return WebHostTestFactory.RunAsync(Assert.NotNull, Assert.NotNull, host => + { + host.ConfigureAppConfiguration((context, _) => + { + TestOutput.WriteLine(context.HostingEnvironment.ApplicationName); + Assert.Equal(GetType().Assembly.GetName().Name, context.HostingEnvironment.ApplicationName); + }); + }); + } + + [Fact] + public Task RunWithHostBuilderContextAsync_ShouldHaveApplicationNameEqualToThisAssembly_WithHostBuilderContext() + { + return WebHostTestFactory.RunWithHostBuilderContextAsync((context, app) => + { + Assert.NotNull(context); + Assert.NotNull(context.HostingEnvironment); + Assert.NotNull(context.Configuration); + Assert.NotNull(context.Properties); + Assert.NotNull(app); + }, + (context, services) => + { + Assert.NotNull(context); + Assert.NotNull(context.HostingEnvironment); + Assert.NotNull(context.Configuration); + Assert.NotNull(context.Properties); + Assert.NotNull(services); + }, + host => + { + host.ConfigureAppConfiguration((context, configuration) => + { + TestOutput.WriteLine(context.HostingEnvironment.ApplicationName); + Assert.Equal(GetType().Assembly.GetName().Name, context.HostingEnvironment.ApplicationName); + }); + }); + } + + [Fact] + public async Task RunAsync_ShouldWorkWithXunitTestLogging() + { + using var response = await WebHostTestFactory.RunAsync( + services => + { + services.AddXunitTestLogging(TestOutput); + services.AddServerTiming(o => o.SuppressHeaderPredicate = _ => false); + }, + app => + { + app.UseServerTiming(); + app.Use(async (context, next) => + { + var sw = Stopwatch.StartNew(); + context.Response.OnStarting(() => + { + sw.Stop(); + context.RequestServices.GetRequiredService().AddServerTiming("use-middleware", sw.Elapsed); + return Task.CompletedTask; + }); + await next(context).ConfigureAwait(false); + }); + app.Run(context => + { + Thread.Sleep(400); + return context.Response.WriteAsync("Hello World!"); + }); + }).ConfigureAwait(false); + + Assert.StartsWith("use-middleware;dur=", response.Headers.Single(kvp => kvp.Key == ServerTiming.HeaderName).Value.FirstOrDefault()); + } +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/WebHostTestTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/WebHostTestTest.cs index d02d9b5..3e27ca9 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/WebHostTestTest.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/WebHostTestTest.cs @@ -1,163 +1,163 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using Codebelt.Extensions.Xunit.Hosting.AspNetCore.Assets; -using Cuemon.Extensions.IO; -using Cuemon.Messaging; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Xunit; - -namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; - -public class WebHostTestTest : WebHostTest -{ - private readonly IServiceProvider _provider; - private readonly IApplicationBuilder _pipeline; - - public WebHostTestTest(ManagedWebHostFixture hostFixture, ITestOutputHelper output) : base(hostFixture, output) - { - _pipeline = hostFixture.Application; - _provider = hostFixture.Host.Services; - _provider.GetRequiredService().TestOutput = output; - } - - [Fact] - public async Task ShouldHaveResultOfBoolMiddlewareInBody() - { - var context = _provider.GetRequiredService().HttpContext; - var options = _provider.GetRequiredService>(); - var pipeline = _pipeline.Build(); - - Assert.Equal("Hello awesome developers!", context!.Response.Body.ToEncodedString(o => o.LeaveOpen = true)); - - var logger = _pipeline.ApplicationServices.GetRequiredService>(); - logger.LogInformation("Hello from {0}", nameof(ShouldHaveResultOfBoolMiddlewareInBody)); - - await pipeline(context); - - Assert.Equal("A:True, B:False, C:True, D:False, E:True, F:False", context.Response.Body.ToEncodedString()); - - Assert.True(options.Value.A); - Assert.False(options.Value.B); - Assert.True(options.Value.C); - Assert.False(options.Value.D); - Assert.True(options.Value.E); - Assert.False(options.Value.F); - } - -#if NET10_0_OR_GREATER - [Fact] - public void ShouldThrowInvalidOperationException_BecauseOneOfTheServicesIsScoped() - { - var ex = Assert.Throws(() => _provider.GetServices()); - - TestOutput.WriteLine(ex.Message); - - Assert.Contains("from root provider because it requires scoped service", ex.Message); - } -#endif - - [Fact] - public void ShouldHaveAccessToCorrelationTokens_UsingScopedProvider() - { - using var scope = _provider.CreateScope(); - - var firstRequest = scope.ServiceProvider.GetServices().ToList(); - var secondRequest = scope.ServiceProvider.GetServices().ToList(); - - TestOutput.WriteLine("----"); - TestOutput.WriteLines(firstRequest); - TestOutput.WriteLine("----"); - TestOutput.WriteLines(secondRequest); - - Assert.Equal(3, firstRequest.Count); - Assert.Equal(3, secondRequest.Count); - - Assert.Same(firstRequest[0], secondRequest[0]); - Assert.NotSame(firstRequest[1], secondRequest[1]); - Assert.Same(firstRequest[2], secondRequest[2]); - - Assert.Equal(firstRequest[0].CorrelationId, secondRequest[0].CorrelationId); - Assert.NotEqual(firstRequest[1].CorrelationId, secondRequest[1].CorrelationId); - Assert.Equal(firstRequest[2].CorrelationId, secondRequest[2].CorrelationId); - } - - [Fact] - public void ShouldHaveAccessToCorrelationTokens_UsingRequestServices() // reference: https://github.com/dotnet/aspnetcore/blob/main/src/Http/Http/src/Features/RequestServicesFeature.cs - { - var context = _provider.GetRequiredService().HttpContext!; - - var firstRequest = context.RequestServices.GetServices().ToList(); - var secondRequest = context.RequestServices.GetServices().ToList(); - - TestOutput.WriteLine("----"); - TestOutput.WriteLines(firstRequest); - TestOutput.WriteLine("----"); - TestOutput.WriteLines(secondRequest); - - Assert.Equal(3, firstRequest.Count); - Assert.Equal(3, secondRequest.Count); - - Assert.Same(firstRequest[0], secondRequest[0]); - Assert.NotSame(firstRequest[1], secondRequest[1]); - Assert.Same(firstRequest[2], secondRequest[2]); - - Assert.Equal(firstRequest[0].CorrelationId, secondRequest[0].CorrelationId); - Assert.NotEqual(firstRequest[1].CorrelationId, secondRequest[1].CorrelationId); - Assert.Equal(firstRequest[2].CorrelationId, secondRequest[2].CorrelationId); - } - - [Fact] - public void ShouldLogToXunitTestLogging() - { - var context = _provider.GetRequiredService().HttpContext; - var logger = _pipeline.ApplicationServices.GetRequiredService>(); - logger.LogInformation("Hello stranger {0}", nameof(ShouldLogToXunitTestLogging)); - var store = _pipeline.ApplicationServices.GetRequiredService>().GetTestStore(); - var entry = store.Query(entry => entry.Message.Contains("Hello stranger", StringComparison.OrdinalIgnoreCase)).SingleOrDefault(); - - Assert.NotNull(entry); - Assert.Equal("Information: Hello stranger ShouldLogToXunitTestLogging", entry.Message); - } - - [Fact] - public void Test_VerifyAbstractions() - { - using var hostTest = WebHostTestFactory.Create(); - Assert.IsAssignableFrom(hostTest); - Assert.IsAssignableFrom(hostTest); - Assert.IsAssignableFrom(hostTest); - Assert.IsAssignableFrom(hostTest); - Assert.IsAssignableFrom(hostTest); - Assert.IsAssignableFrom(hostTest); - Assert.IsAssignableFrom(hostTest); - Assert.IsAssignableFrom(hostTest); - } - - public override void ConfigureApplication(IApplicationBuilder app) - { - app.ApplicationServices.GetRequiredService>().LogInformation(nameof(ConfigureApplication)); - app.UseMiddleware(); - } - - public override void ConfigureServices(IServiceCollection services) - { - services.AddFakeHttpContextAccessor(); - services.Configure(o => - { - o.A = true; - o.C = true; - o.E = true; - }); - services.AddXunitTestLoggingOutputHelperAccessor(); - services.AddXunitTestLogging(TestOutput); - - services.AddSingleton(); - services.AddTransient(); - services.AddScoped(); - } -} +using System; +using System.Linq; +using System.Threading.Tasks; +using Codebelt.Extensions.Xunit.Hosting.AspNetCore.Assets; +using Cuemon.Extensions.IO; +using Cuemon.Messaging; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Xunit; + +namespace Codebelt.Extensions.Xunit.Hosting.AspNetCore; + +public class WebHostTestTest : WebHostTest +{ + private readonly IServiceProvider _provider; + private readonly IApplicationBuilder _pipeline; + + public WebHostTestTest(ManagedWebHostFixture hostFixture, ITestOutputHelper output) : base(hostFixture, output) + { + _pipeline = hostFixture.Application; + _provider = hostFixture.Host.Services; + _provider.GetRequiredService().TestOutput = output; + } + + [Fact] + public async Task ShouldHaveResultOfBoolMiddlewareInBody() + { + var context = _provider.GetRequiredService().HttpContext; + var options = _provider.GetRequiredService>(); + var pipeline = _pipeline.Build(); + + Assert.Equal("Hello awesome developers!", context!.Response.Body.ToEncodedString(o => o.LeaveOpen = true)); + + var logger = _pipeline.ApplicationServices.GetRequiredService>(); + logger.LogInformation("Hello from {0}", nameof(ShouldHaveResultOfBoolMiddlewareInBody)); + + await pipeline(context); + + Assert.Equal("A:True, B:False, C:True, D:False, E:True, F:False", context.Response.Body.ToEncodedString()); + + Assert.True(options.Value.A); + Assert.False(options.Value.B); + Assert.True(options.Value.C); + Assert.False(options.Value.D); + Assert.True(options.Value.E); + Assert.False(options.Value.F); + } + +#if NET10_0_OR_GREATER + [Fact] + public void ShouldThrowInvalidOperationException_BecauseOneOfTheServicesIsScoped() + { + var ex = Assert.Throws(() => _provider.GetServices()); + + TestOutput.WriteLine(ex.Message); + + Assert.Contains("from root provider because it requires scoped service", ex.Message); + } +#endif + + [Fact] + public void ShouldHaveAccessToCorrelationTokens_UsingScopedProvider() + { + using var scope = _provider.CreateScope(); + + var firstRequest = scope.ServiceProvider.GetServices().ToList(); + var secondRequest = scope.ServiceProvider.GetServices().ToList(); + + TestOutput.WriteLine("----"); + TestOutput.WriteLines(firstRequest); + TestOutput.WriteLine("----"); + TestOutput.WriteLines(secondRequest); + + Assert.Equal(3, firstRequest.Count); + Assert.Equal(3, secondRequest.Count); + + Assert.Same(firstRequest[0], secondRequest[0]); + Assert.NotSame(firstRequest[1], secondRequest[1]); + Assert.Same(firstRequest[2], secondRequest[2]); + + Assert.Equal(firstRequest[0].CorrelationId, secondRequest[0].CorrelationId); + Assert.NotEqual(firstRequest[1].CorrelationId, secondRequest[1].CorrelationId); + Assert.Equal(firstRequest[2].CorrelationId, secondRequest[2].CorrelationId); + } + + [Fact] + public void ShouldHaveAccessToCorrelationTokens_UsingRequestServices() // reference: https://github.com/dotnet/aspnetcore/blob/main/src/Http/Http/src/Features/RequestServicesFeature.cs + { + var context = _provider.GetRequiredService().HttpContext!; + + var firstRequest = context.RequestServices.GetServices().ToList(); + var secondRequest = context.RequestServices.GetServices().ToList(); + + TestOutput.WriteLine("----"); + TestOutput.WriteLines(firstRequest); + TestOutput.WriteLine("----"); + TestOutput.WriteLines(secondRequest); + + Assert.Equal(3, firstRequest.Count); + Assert.Equal(3, secondRequest.Count); + + Assert.Same(firstRequest[0], secondRequest[0]); + Assert.NotSame(firstRequest[1], secondRequest[1]); + Assert.Same(firstRequest[2], secondRequest[2]); + + Assert.Equal(firstRequest[0].CorrelationId, secondRequest[0].CorrelationId); + Assert.NotEqual(firstRequest[1].CorrelationId, secondRequest[1].CorrelationId); + Assert.Equal(firstRequest[2].CorrelationId, secondRequest[2].CorrelationId); + } + + [Fact] + public void ShouldLogToXunitTestLogging() + { + var context = _provider.GetRequiredService().HttpContext; + var logger = _pipeline.ApplicationServices.GetRequiredService>(); + logger.LogInformation("Hello stranger {0}", nameof(ShouldLogToXunitTestLogging)); + var store = _pipeline.ApplicationServices.GetRequiredService>().GetTestStore(); + var entry = store.Query(entry => entry.Message.Contains("Hello stranger", StringComparison.OrdinalIgnoreCase)).SingleOrDefault(); + + Assert.NotNull(entry); + Assert.Equal("Information: Hello stranger ShouldLogToXunitTestLogging", entry.Message); + } + + [Fact] + public void Test_VerifyAbstractions() + { + using var hostTest = WebHostTestFactory.Create(); + Assert.IsAssignableFrom(hostTest); + Assert.IsAssignableFrom(hostTest); + Assert.IsAssignableFrom(hostTest); + Assert.IsAssignableFrom(hostTest); + Assert.IsAssignableFrom(hostTest); + Assert.IsAssignableFrom(hostTest); + Assert.IsAssignableFrom(hostTest); + Assert.IsAssignableFrom(hostTest); + } + + public override void ConfigureApplication(IApplicationBuilder app) + { + app.ApplicationServices.GetRequiredService>().LogInformation(nameof(ConfigureApplication)); + app.UseMiddleware(); + } + + public override void ConfigureServices(IServiceCollection services) + { + services.AddFakeHttpContextAccessor(); + services.Configure(o => + { + o.A = true; + o.C = true; + o.E = true; + }); + services.AddXunitTestLoggingOutputHelperAccessor(); + services.AddXunitTestLogging(TestOutput); + + services.AddSingleton(); + services.AddTransient(); + services.AddScoped(); + } +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.FunctionalTests/BootstrapperConsoleApplicationTestTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.FunctionalTests/BootstrapperConsoleApplicationTestTest.cs index e151ab2..3bebab0 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.FunctionalTests/BootstrapperConsoleApplicationTestTest.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.FunctionalTests/BootstrapperConsoleApplicationTestTest.cs @@ -1,22 +1,22 @@ using Microsoft.Extensions.DependencyInjection; -using Xunit; -using BootstrapperConsoleMarker = Codebelt.Extensions.Xunit.Hosting.BootstrapperConsole.App.BootstrapperConsoleMarker; -using BootstrapperConsoleProgram = Codebelt.Extensions.Xunit.Hosting.BootstrapperConsole.App.Program; - -namespace Codebelt.Extensions.Xunit.Hosting; - +using Xunit; +using BootstrapperConsoleMarker = Codebelt.Extensions.Xunit.Hosting.BootstrapperConsole.App.BootstrapperConsoleMarker; +using BootstrapperConsoleProgram = Codebelt.Extensions.Xunit.Hosting.BootstrapperConsole.App.Program; + +namespace Codebelt.Extensions.Xunit.Hosting; + public class BootstrapperConsoleApplicationTestTest : ApplicationTest> { public BootstrapperConsoleApplicationTestTest(ManagedApplicationFixture hostFixture, ITestOutputHelper output) : base(hostFixture, output) - { - } - - [Fact] - public void ShouldBootstrapLegacyConsoleProgramAndStartup() - { + { + } + + [Fact] + public void ShouldBootstrapLegacyConsoleProgramAndStartup() + { var marker = Host.Services.GetRequiredService(); - Assert.Equal("Bootstrapper Console", marker.Value); - Assert.Equal("Development", Environment.EnvironmentName); - Assert.NotNull(Host); - } -} + Assert.Equal("Bootstrapper Console", marker.Value); + Assert.Equal("Development", Environment.EnvironmentName); + Assert.NotNull(Host); + } +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.FunctionalTests/BootstrapperMinimalWorkerApplicationTestTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.FunctionalTests/BootstrapperMinimalWorkerApplicationTestTest.cs index 37e36fb..83cf5ee 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.FunctionalTests/BootstrapperMinimalWorkerApplicationTestTest.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.FunctionalTests/BootstrapperMinimalWorkerApplicationTestTest.cs @@ -1,24 +1,24 @@ -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Xunit; -using BootstrapperMinimalWorkerMarker = Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWorker.App.BootstrapperMinimalWorkerMarker; -using BootstrapperMinimalWorkerProgram = Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWorker.App.Program; - -namespace Codebelt.Extensions.Xunit.Hosting; - +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Xunit; +using BootstrapperMinimalWorkerMarker = Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWorker.App.BootstrapperMinimalWorkerMarker; +using BootstrapperMinimalWorkerProgram = Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWorker.App.Program; + +namespace Codebelt.Extensions.Xunit.Hosting; + public class BootstrapperMinimalWorkerApplicationTestTest : ApplicationTest> { public BootstrapperMinimalWorkerApplicationTestTest(ManagedApplicationFixture hostFixture, ITestOutputHelper output) : base(hostFixture, output) - { - } - - [Fact] - public void ShouldBootstrapMinimalWorkerProgram() - { - var marker = Host.Services.GetRequiredService(); - - Assert.Equal("Bootstrapper Minimal Worker", marker.Value); - Assert.Equal("Development", Environment.EnvironmentName); - Assert.NotNull(Host.Services.GetRequiredService()); - } -} + { + } + + [Fact] + public void ShouldBootstrapMinimalWorkerProgram() + { + var marker = Host.Services.GetRequiredService(); + + Assert.Equal("Bootstrapper Minimal Worker", marker.Value); + Assert.Equal("Development", Environment.EnvironmentName); + Assert.NotNull(Host.Services.GetRequiredService()); + } +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.FunctionalTests/BootstrapperWorkerApplicationTestTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.FunctionalTests/BootstrapperWorkerApplicationTestTest.cs index 5cd74e2..1895699 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.FunctionalTests/BootstrapperWorkerApplicationTestTest.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.FunctionalTests/BootstrapperWorkerApplicationTestTest.cs @@ -1,24 +1,24 @@ -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Xunit; -using BootstrapperWorkerMarker = Codebelt.Extensions.Xunit.Hosting.BootstrapperWorker.App.BootstrapperWorkerMarker; -using BootstrapperWorkerProgram = Codebelt.Extensions.Xunit.Hosting.BootstrapperWorker.App.Program; - -namespace Codebelt.Extensions.Xunit.Hosting; - +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Xunit; +using BootstrapperWorkerMarker = Codebelt.Extensions.Xunit.Hosting.BootstrapperWorker.App.BootstrapperWorkerMarker; +using BootstrapperWorkerProgram = Codebelt.Extensions.Xunit.Hosting.BootstrapperWorker.App.Program; + +namespace Codebelt.Extensions.Xunit.Hosting; + public class BootstrapperWorkerApplicationTestTest : ApplicationTest> { public BootstrapperWorkerApplicationTestTest(ManagedApplicationFixture hostFixture, ITestOutputHelper output) : base(hostFixture, output) - { - } - - [Fact] - public void ShouldBootstrapLegacyWorkerProgramAndStartup() - { - var marker = Host.Services.GetRequiredService(); - - Assert.Equal("Bootstrapper Worker", marker.Value); - Assert.Equal("Development", Environment.EnvironmentName); - Assert.NotNull(Host.Services.GetRequiredService()); - } -} + { + } + + [Fact] + public void ShouldBootstrapLegacyWorkerProgramAndStartup() + { + var marker = Host.Services.GetRequiredService(); + + Assert.Equal("Bootstrapper Worker", marker.Value); + Assert.Equal("Development", Environment.EnvironmentName); + Assert.NotNull(Host.Services.GetRequiredService()); + } +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.Tests/Assets/InvalidHostTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.Tests/Assets/InvalidHostTest.cs index c531268..4e480cb 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.Tests/Assets/InvalidHostTest.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.Tests/Assets/InvalidHostTest.cs @@ -1,10 +1,10 @@ -using Xunit; - -namespace Codebelt.Extensions.Xunit.Hosting.Assets; - -public class InvalidHostTest : Test, IClassFixture where T : class, IGenericHostFixture -{ - public InvalidHostTest(T hostFixture) - { - } -} +using Xunit; + +namespace Codebelt.Extensions.Xunit.Hosting.Assets; + +public class InvalidHostTest : Test, IClassFixture where T : class, IGenericHostFixture +{ + public InvalidHostTest(T hostFixture) + { + } +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.Tests/Assets/MinimalValidHostTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.Tests/Assets/MinimalValidHostTest.cs index 07e646e..5115e91 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.Tests/Assets/MinimalValidHostTest.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.Tests/Assets/MinimalValidHostTest.cs @@ -1,11 +1,11 @@ -using System; -using Microsoft.Extensions.DependencyInjection; - -namespace Codebelt.Extensions.Xunit.Hosting.Assets; - -public class MinimalValidHostTest : MinimalHostTest -{ - public MinimalValidHostTest(ManagedMinimalHostFixture hostFixture) : base(hostFixture) - { - } -} +using System; +using Microsoft.Extensions.DependencyInjection; + +namespace Codebelt.Extensions.Xunit.Hosting.Assets; + +public class MinimalValidHostTest : MinimalHostTest +{ + public MinimalValidHostTest(ManagedMinimalHostFixture hostFixture) : base(hostFixture) + { + } +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.Tests/Assets/ScopedCorrelation.cs b/test/Codebelt.Extensions.Xunit.Hosting.Tests/Assets/ScopedCorrelation.cs index 252a720..3be4afb 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.Tests/Assets/ScopedCorrelation.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.Tests/Assets/ScopedCorrelation.cs @@ -1,7 +1,7 @@ -using Cuemon.Messaging; - -namespace Codebelt.Extensions.Xunit.Hosting.Assets; - -public sealed record ScopedCorrelation : CorrelationToken -{ -} +using Cuemon.Messaging; + +namespace Codebelt.Extensions.Xunit.Hosting.Assets; + +public sealed record ScopedCorrelation : CorrelationToken +{ +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.Tests/Assets/SingletonCorrelation.cs b/test/Codebelt.Extensions.Xunit.Hosting.Tests/Assets/SingletonCorrelation.cs index 3b4cc09..901029d 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.Tests/Assets/SingletonCorrelation.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.Tests/Assets/SingletonCorrelation.cs @@ -1,7 +1,7 @@ -using Cuemon.Messaging; - -namespace Codebelt.Extensions.Xunit.Hosting.Assets; - -public sealed record SingletonCorrelation : CorrelationToken -{ -} +using Cuemon.Messaging; + +namespace Codebelt.Extensions.Xunit.Hosting.Assets; + +public sealed record SingletonCorrelation : CorrelationToken +{ +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.Tests/Assets/TransientCorrelation.cs b/test/Codebelt.Extensions.Xunit.Hosting.Tests/Assets/TransientCorrelation.cs index 5fb3ca4..94cfe35 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.Tests/Assets/TransientCorrelation.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.Tests/Assets/TransientCorrelation.cs @@ -1,7 +1,7 @@ -using Cuemon.Messaging; - -namespace Codebelt.Extensions.Xunit.Hosting.Assets; - -public sealed record TransientCorrelation : CorrelationToken -{ -} +using Cuemon.Messaging; + +namespace Codebelt.Extensions.Xunit.Hosting.Assets; + +public sealed record TransientCorrelation : CorrelationToken +{ +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.Tests/Assets/ValidHostTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.Tests/Assets/ValidHostTest.cs index 003092b..7b84632 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.Tests/Assets/ValidHostTest.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.Tests/Assets/ValidHostTest.cs @@ -1,17 +1,17 @@ -using Microsoft.Extensions.DependencyInjection; -using System; -using static System.Net.Mime.MediaTypeNames; - -namespace Codebelt.Extensions.Xunit.Hosting.Assets; - -public class ValidHostTest : HostTest -{ - public ValidHostTest(ManagedHostFixture hostFixture) : base(hostFixture) - { - } - - public override void ConfigureServices(IServiceCollection services) - { - - } -} +using Microsoft.Extensions.DependencyInjection; +using System; +using static System.Net.Mime.MediaTypeNames; + +namespace Codebelt.Extensions.Xunit.Hosting.Assets; + +public class ValidHostTest : HostTest +{ + public ValidHostTest(ManagedHostFixture hostFixture) : base(hostFixture) + { + } + + public override void ConfigureServices(IServiceCollection services) + { + + } +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.Tests/HostTestFactoryTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.Tests/HostTestFactoryTest.cs index 4b25ba6..d448a76 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.Tests/HostTestFactoryTest.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.Tests/HostTestFactoryTest.cs @@ -1,50 +1,50 @@ -using System; -using Xunit; - -namespace Codebelt.Extensions.Xunit.Hosting; - -public class HostTestFactoryTest : Test -{ - public HostTestFactoryTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public void Create_CallerTypeShouldHaveDeclaringTypeOfMiddlewareTestFactoryTest() - { - Type sut1 = GetType(); - string sut2 = null; - var middleware = HostTestFactory.Create(Assert.NotNull, host => - { - host.ConfigureAppConfiguration((context, _) => - { - sut2 = context.HostingEnvironment.ApplicationName; - }); - }); - - Assert.True(sut1 == middleware.CallerType.DeclaringType); - Assert.Equal(GetType().Assembly.GetName().Name, sut2); - } - - - [Fact] - public void CreateWithHostBuilderContext_ShouldHaveApplicationNameEqualToThisAssembly_WithHostBuilderContext() - { - HostTestFactory.CreateWithHostBuilderContext((context, services) => - { - Assert.NotNull(context); - Assert.NotNull(context.HostingEnvironment); - Assert.NotNull(context.Configuration); - Assert.NotNull(context.Properties); - Assert.NotNull(services); - }, - host => - { - host.ConfigureAppConfiguration((context, configuration) => - { - TestOutput.WriteLine(context.HostingEnvironment.ApplicationName); - Assert.Equal(GetType().Assembly.GetName().Name, context.HostingEnvironment.ApplicationName); - }); - }); - } -} +using System; +using Xunit; + +namespace Codebelt.Extensions.Xunit.Hosting; + +public class HostTestFactoryTest : Test +{ + public HostTestFactoryTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void Create_CallerTypeShouldHaveDeclaringTypeOfMiddlewareTestFactoryTest() + { + Type sut1 = GetType(); + string sut2 = null; + var middleware = HostTestFactory.Create(Assert.NotNull, host => + { + host.ConfigureAppConfiguration((context, _) => + { + sut2 = context.HostingEnvironment.ApplicationName; + }); + }); + + Assert.True(sut1 == middleware.CallerType.DeclaringType); + Assert.Equal(GetType().Assembly.GetName().Name, sut2); + } + + + [Fact] + public void CreateWithHostBuilderContext_ShouldHaveApplicationNameEqualToThisAssembly_WithHostBuilderContext() + { + HostTestFactory.CreateWithHostBuilderContext((context, services) => + { + Assert.NotNull(context); + Assert.NotNull(context.HostingEnvironment); + Assert.NotNull(context.Configuration); + Assert.NotNull(context.Properties); + Assert.NotNull(services); + }, + host => + { + host.ConfigureAppConfiguration((context, configuration) => + { + TestOutput.WriteLine(context.HostingEnvironment.ApplicationName); + Assert.Equal(GetType().Assembly.GetName().Name, context.HostingEnvironment.ApplicationName); + }); + }); + } +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.Tests/HostTestTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.Tests/HostTestTest.cs index 267a022..a8f59ce 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.Tests/HostTestTest.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.Tests/HostTestTest.cs @@ -1,116 +1,116 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using Codebelt.Extensions.Xunit.Hosting.Assets; -using Cuemon.Messaging; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Xunit; -using Xunit.v3.Priority; - -namespace Codebelt.Extensions.Xunit.Hosting; - -[TestCaseOrderer(typeof(PriorityOrderer))] -public class HostTestTest : HostTest -{ - private bool _isHostRunning = false; - private readonly IServiceScope _scope; - private readonly IHost _constructorHost; - private readonly IServiceProvider _constructorServices; - private readonly Func> _correlationsFactory; - private static readonly ConcurrentBag ScopedCorrelations = new(); - - public HostTestTest(ManagedHostFixture hostFixture, ITestOutputHelper output) : base(hostFixture, output) - { - _constructorHost = hostFixture.Host; - _constructorServices = hostFixture.Host?.Services; - - Assert.NotNull(_constructorHost); - Assert.NotNull(_constructorServices); - Assert.NotNull(Host); - Assert.NotNull(Configuration); - Assert.NotNull(Environment); - - _scope = hostFixture.Host.Services.CreateScope(); - _correlationsFactory = () => _scope.ServiceProvider.GetServices().ToList(); - var lifetime = hostFixture.Host.Services.GetRequiredService(); - lifetime.ApplicationStarted.Register(() => _isHostRunning = true); - } - - [Fact, Priority(1)] - public void Test_SingletonShouldBeSame() // simulate a request - { - ScopedCorrelations.Add(_correlationsFactory().Single(c => c is ScopedCorrelation)); - var c1 = _correlationsFactory().Single(c => c is SingletonCorrelation); - var c2 = _correlationsFactory().Single(c => c is SingletonCorrelation); - Assert.Equal(c1.CorrelationId, c2.CorrelationId); - } - - [Fact, Priority(2)] - public void Test_TransientShouldBeDifferent() // simulate a request - { - ScopedCorrelations.Add(_correlationsFactory().Single(c => c is ScopedCorrelation)); - var c1 = _correlationsFactory().Single(c => c is TransientCorrelation); - var c2 = _correlationsFactory().Single(c => c is TransientCorrelation); - Assert.NotEqual(c1.CorrelationId, c2.CorrelationId); - } - - [Fact, Priority(3)] - public void Test_ScopedShouldBeSame() // simulate a request - { - ScopedCorrelations.Add(_correlationsFactory().Single(c => c is ScopedCorrelation)); - var c1 = _correlationsFactory().Single(c => c is ScopedCorrelation); - var c2 = _correlationsFactory().Single(c => c is ScopedCorrelation); - Assert.Equal(c1.CorrelationId, c2.CorrelationId); - } - - [Fact] - public void Test_ShouldHaveRunningHost() - { - Assert.True(_isHostRunning); - } - - [Fact] - public void Test_ShouldHaveConfigurationEntry() - { - Assert.Equal("xUnit", Configuration.GetSection("unitTestTool").Value); - } - - [Fact] - public void Test_ShouldHaveEnvironmentOfDevelopment() - { - Assert.Equal("Development", Environment.EnvironmentName); - } - - [Fact] - public void Test_ShouldHaveHostAndServicesAvailableInConstructor() - { - Assert.Same(Host, _constructorHost); - Assert.Same(Host.Services, _constructorServices); - } - - [Fact] - public void Test_VerifyAbstractions() - { - using var hostTest = HostTestFactory.Create(); - Assert.IsAssignableFrom(hostTest); - Assert.IsAssignableFrom(hostTest); - Assert.IsAssignableFrom(hostTest); - Assert.IsAssignableFrom(hostTest); - Assert.IsAssignableFrom(hostTest); - Assert.IsAssignableFrom(hostTest); - } - - protected override void OnDisposeManagedResources() - { - _scope?.Dispose(); - } - - public override void ConfigureServices(IServiceCollection services) - { - services.AddSingleton(); - services.AddTransient(); - services.AddScoped(); - } -} +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using Codebelt.Extensions.Xunit.Hosting.Assets; +using Cuemon.Messaging; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Xunit; +using Xunit.v3.Priority; + +namespace Codebelt.Extensions.Xunit.Hosting; + +[TestCaseOrderer(typeof(PriorityOrderer))] +public class HostTestTest : HostTest +{ + private bool _isHostRunning = false; + private readonly IServiceScope _scope; + private readonly IHost _constructorHost; + private readonly IServiceProvider _constructorServices; + private readonly Func> _correlationsFactory; + private static readonly ConcurrentBag ScopedCorrelations = new(); + + public HostTestTest(ManagedHostFixture hostFixture, ITestOutputHelper output) : base(hostFixture, output) + { + _constructorHost = hostFixture.Host; + _constructorServices = hostFixture.Host?.Services; + + Assert.NotNull(_constructorHost); + Assert.NotNull(_constructorServices); + Assert.NotNull(Host); + Assert.NotNull(Configuration); + Assert.NotNull(Environment); + + _scope = hostFixture.Host.Services.CreateScope(); + _correlationsFactory = () => _scope.ServiceProvider.GetServices().ToList(); + var lifetime = hostFixture.Host.Services.GetRequiredService(); + lifetime.ApplicationStarted.Register(() => _isHostRunning = true); + } + + [Fact, Priority(1)] + public void Test_SingletonShouldBeSame() // simulate a request + { + ScopedCorrelations.Add(_correlationsFactory().Single(c => c is ScopedCorrelation)); + var c1 = _correlationsFactory().Single(c => c is SingletonCorrelation); + var c2 = _correlationsFactory().Single(c => c is SingletonCorrelation); + Assert.Equal(c1.CorrelationId, c2.CorrelationId); + } + + [Fact, Priority(2)] + public void Test_TransientShouldBeDifferent() // simulate a request + { + ScopedCorrelations.Add(_correlationsFactory().Single(c => c is ScopedCorrelation)); + var c1 = _correlationsFactory().Single(c => c is TransientCorrelation); + var c2 = _correlationsFactory().Single(c => c is TransientCorrelation); + Assert.NotEqual(c1.CorrelationId, c2.CorrelationId); + } + + [Fact, Priority(3)] + public void Test_ScopedShouldBeSame() // simulate a request + { + ScopedCorrelations.Add(_correlationsFactory().Single(c => c is ScopedCorrelation)); + var c1 = _correlationsFactory().Single(c => c is ScopedCorrelation); + var c2 = _correlationsFactory().Single(c => c is ScopedCorrelation); + Assert.Equal(c1.CorrelationId, c2.CorrelationId); + } + + [Fact] + public void Test_ShouldHaveRunningHost() + { + Assert.True(_isHostRunning); + } + + [Fact] + public void Test_ShouldHaveConfigurationEntry() + { + Assert.Equal("xUnit", Configuration.GetSection("unitTestTool").Value); + } + + [Fact] + public void Test_ShouldHaveEnvironmentOfDevelopment() + { + Assert.Equal("Development", Environment.EnvironmentName); + } + + [Fact] + public void Test_ShouldHaveHostAndServicesAvailableInConstructor() + { + Assert.Same(Host, _constructorHost); + Assert.Same(Host.Services, _constructorServices); + } + + [Fact] + public void Test_VerifyAbstractions() + { + using var hostTest = HostTestFactory.Create(); + Assert.IsAssignableFrom(hostTest); + Assert.IsAssignableFrom(hostTest); + Assert.IsAssignableFrom(hostTest); + Assert.IsAssignableFrom(hostTest); + Assert.IsAssignableFrom(hostTest); + Assert.IsAssignableFrom(hostTest); + } + + protected override void OnDisposeManagedResources() + { + _scope?.Dispose(); + } + + public override void ConfigureServices(IServiceCollection services) + { + services.AddSingleton(); + services.AddTransient(); + services.AddScoped(); + } +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.Tests/ManagedHostFixtureTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.Tests/ManagedHostFixtureTest.cs index 5f69e91..32e2c06 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.Tests/ManagedHostFixtureTest.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.Tests/ManagedHostFixtureTest.cs @@ -1,59 +1,59 @@ -using System; -using System.Threading.Tasks; -using Codebelt.Extensions.Xunit.Hosting.Assets; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Xunit; - -namespace Codebelt.Extensions.Xunit.Hosting; - -public class ManagedHostFixtureTest : Test -{ - private readonly ManagedHostFixture _hostFixture; - - public ManagedHostFixtureTest(ITestOutputHelper output) : base(output) - { - _hostFixture = new ManagedHostFixture(); - } - - [Fact] - public void ConfigureHost_ShouldThrowArgumentNullException_WhenHostTestIsNull() - { - Assert.Throws(() => _hostFixture.ConfigureHost(null)); - } - - [Fact] - public void ConfigureHost_ShouldThrowArgumentOutOfRangeException_WhenHostTestIsNotAssignableFromHostTest() - { - var invalidHostTest = new InvalidHostTest(new ManagedHostFixture()); - Assert.Throws(() => _hostFixture.ConfigureHost(invalidHostTest)); - } - - [Fact] - public void ConfigureHost_ShouldConfigureHostSuccessfully() - { - var validHostTest = new ValidHostTest(_hostFixture); - - _hostFixture.ConfigureHost(validHostTest); - - Assert.NotNull(_hostFixture.Host); - Assert.NotNull(_hostFixture.Host.Services); - Assert.NotNull(_hostFixture.Configuration); - Assert.NotNull(_hostFixture.Environment); - } - - [Fact] - public void Dispose_ShouldDisposeResources() - { - _hostFixture.Dispose(); - Assert.True(_hostFixture.Disposed); - } - - [Fact] - public async Task DisposeAsync_ShouldDisposeResourcesAsync() - { - await _hostFixture.DisposeAsync(); - Assert.True(_hostFixture.Disposed); - } -} +using System; +using System.Threading.Tasks; +using Codebelt.Extensions.Xunit.Hosting.Assets; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Xunit; + +namespace Codebelt.Extensions.Xunit.Hosting; + +public class ManagedHostFixtureTest : Test +{ + private readonly ManagedHostFixture _hostFixture; + + public ManagedHostFixtureTest(ITestOutputHelper output) : base(output) + { + _hostFixture = new ManagedHostFixture(); + } + + [Fact] + public void ConfigureHost_ShouldThrowArgumentNullException_WhenHostTestIsNull() + { + Assert.Throws(() => _hostFixture.ConfigureHost(null)); + } + + [Fact] + public void ConfigureHost_ShouldThrowArgumentOutOfRangeException_WhenHostTestIsNotAssignableFromHostTest() + { + var invalidHostTest = new InvalidHostTest(new ManagedHostFixture()); + Assert.Throws(() => _hostFixture.ConfigureHost(invalidHostTest)); + } + + [Fact] + public void ConfigureHost_ShouldConfigureHostSuccessfully() + { + var validHostTest = new ValidHostTest(_hostFixture); + + _hostFixture.ConfigureHost(validHostTest); + + Assert.NotNull(_hostFixture.Host); + Assert.NotNull(_hostFixture.Host.Services); + Assert.NotNull(_hostFixture.Configuration); + Assert.NotNull(_hostFixture.Environment); + } + + [Fact] + public void Dispose_ShouldDisposeResources() + { + _hostFixture.Dispose(); + Assert.True(_hostFixture.Disposed); + } + + [Fact] + public async Task DisposeAsync_ShouldDisposeResourcesAsync() + { + await _hostFixture.DisposeAsync(); + Assert.True(_hostFixture.Disposed); + } +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.Tests/MinimalHostFixtureTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.Tests/MinimalHostFixtureTest.cs index b2ecd13..5090aac 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.Tests/MinimalHostFixtureTest.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.Tests/MinimalHostFixtureTest.cs @@ -1,56 +1,56 @@ -using System; -using System.Threading.Tasks; -using Codebelt.Extensions.Xunit.Hosting.Assets; -using Xunit; - -namespace Codebelt.Extensions.Xunit.Hosting; - -public class MinimalHostFixtureTest : Test -{ - private readonly ManagedMinimalHostFixture _hostFixture; - - public MinimalHostFixtureTest(ITestOutputHelper output) : base(output) - { - _hostFixture = new ManagedMinimalHostFixture(); - } - - [Fact] - public void ConfigureHost_ShouldThrowArgumentNullException_WhenHostTestIsNull() - { - Assert.Throws(() => _hostFixture.ConfigureHost(null)); - } - - [Fact] - public void ConfigureHost_ShouldThrowArgumentOutOfRangeException_WhenHostTestIsNotAssignableFromHostTest() - { - var invalidHostTest = new InvalidHostTest(new ManagedHostFixture()); - Assert.Throws(() => _hostFixture.ConfigureHost(invalidHostTest)); - } - - [Fact] - public void ConfigureHost_ShouldConfigureHostSuccessfully() - { - var validHostTest = new MinimalValidHostTest(_hostFixture); - - _hostFixture.ConfigureHost(validHostTest); - - Assert.NotNull(_hostFixture.Host); - Assert.NotNull(_hostFixture.Host.Services); - Assert.NotNull(_hostFixture.Configuration); - Assert.NotNull(_hostFixture.Environment); - } - - [Fact] - public void Dispose_ShouldDisposeResources() - { - _hostFixture.Dispose(); - Assert.True(_hostFixture.Disposed); - } - - [Fact] - public async Task DisposeAsync_ShouldDisposeResourcesAsync() - { - await _hostFixture.DisposeAsync(); - Assert.True(_hostFixture.Disposed); - } -} +using System; +using System.Threading.Tasks; +using Codebelt.Extensions.Xunit.Hosting.Assets; +using Xunit; + +namespace Codebelt.Extensions.Xunit.Hosting; + +public class MinimalHostFixtureTest : Test +{ + private readonly ManagedMinimalHostFixture _hostFixture; + + public MinimalHostFixtureTest(ITestOutputHelper output) : base(output) + { + _hostFixture = new ManagedMinimalHostFixture(); + } + + [Fact] + public void ConfigureHost_ShouldThrowArgumentNullException_WhenHostTestIsNull() + { + Assert.Throws(() => _hostFixture.ConfigureHost(null)); + } + + [Fact] + public void ConfigureHost_ShouldThrowArgumentOutOfRangeException_WhenHostTestIsNotAssignableFromHostTest() + { + var invalidHostTest = new InvalidHostTest(new ManagedHostFixture()); + Assert.Throws(() => _hostFixture.ConfigureHost(invalidHostTest)); + } + + [Fact] + public void ConfigureHost_ShouldConfigureHostSuccessfully() + { + var validHostTest = new MinimalValidHostTest(_hostFixture); + + _hostFixture.ConfigureHost(validHostTest); + + Assert.NotNull(_hostFixture.Host); + Assert.NotNull(_hostFixture.Host.Services); + Assert.NotNull(_hostFixture.Configuration); + Assert.NotNull(_hostFixture.Environment); + } + + [Fact] + public void Dispose_ShouldDisposeResources() + { + _hostFixture.Dispose(); + Assert.True(_hostFixture.Disposed); + } + + [Fact] + public async Task DisposeAsync_ShouldDisposeResourcesAsync() + { + await _hostFixture.DisposeAsync(); + Assert.True(_hostFixture.Disposed); + } +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.Tests/MinimalHostTestFactoryTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.Tests/MinimalHostTestFactoryTest.cs index 9ce11ec..2d93f91 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.Tests/MinimalHostTestFactoryTest.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.Tests/MinimalHostTestFactoryTest.cs @@ -1,44 +1,44 @@ -using System; -using Xunit; - -namespace Codebelt.Extensions.Xunit.Hosting; - -public class MinimalHostTestFactoryTest : Test -{ - public MinimalHostTestFactoryTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public void Create_CallerTypeShouldHaveDeclaringTypeOfMiddlewareTestFactoryTest() - { - Type sut1 = GetType(); - string sut2 = null; - var middleware = MinimalHostTestFactory.Create(Assert.NotNull, host => - { - sut2 = host.Environment.ApplicationName; - }); - - Assert.True(sut1 == middleware.CallerType.DeclaringType); - Assert.Equal(GetType().Assembly.GetName().Name, sut2); - } - - - [Fact] - public void CreateWithHostBuilderContext_ShouldHaveApplicationNameEqualToThisAssembly_WithHostBuilderContext() - { - MinimalHostTestFactory.CreateWithHostBuilderContext((context, services) => - { - Assert.NotNull(context); - Assert.NotNull(context.HostingEnvironment); - Assert.NotNull(context.Configuration); - Assert.NotNull(context.Properties); - Assert.NotNull(services); - }, - host => - { - TestOutput.WriteLine(host.Environment.ApplicationName); - Assert.Equal(GetType().Assembly.GetName().Name, host.Environment.ApplicationName); - }); - } -} +using System; +using Xunit; + +namespace Codebelt.Extensions.Xunit.Hosting; + +public class MinimalHostTestFactoryTest : Test +{ + public MinimalHostTestFactoryTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void Create_CallerTypeShouldHaveDeclaringTypeOfMiddlewareTestFactoryTest() + { + Type sut1 = GetType(); + string sut2 = null; + var middleware = MinimalHostTestFactory.Create(Assert.NotNull, host => + { + sut2 = host.Environment.ApplicationName; + }); + + Assert.True(sut1 == middleware.CallerType.DeclaringType); + Assert.Equal(GetType().Assembly.GetName().Name, sut2); + } + + + [Fact] + public void CreateWithHostBuilderContext_ShouldHaveApplicationNameEqualToThisAssembly_WithHostBuilderContext() + { + MinimalHostTestFactory.CreateWithHostBuilderContext((context, services) => + { + Assert.NotNull(context); + Assert.NotNull(context.HostingEnvironment); + Assert.NotNull(context.Configuration); + Assert.NotNull(context.Properties); + Assert.NotNull(services); + }, + host => + { + TestOutput.WriteLine(host.Environment.ApplicationName); + Assert.Equal(GetType().Assembly.GetName().Name, host.Environment.ApplicationName); + }); + } +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.Tests/MinimalHostTestTest.cs b/test/Codebelt.Extensions.Xunit.Hosting.Tests/MinimalHostTestTest.cs index 5778a2f..644b620 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.Tests/MinimalHostTestTest.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.Tests/MinimalHostTestTest.cs @@ -1,98 +1,98 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using Codebelt.Extensions.Xunit.Hosting.Assets; -using Cuemon.Messaging; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Xunit; -using Xunit.v3.Priority; - -namespace Codebelt.Extensions.Xunit.Hosting; - -[TestCaseOrderer(typeof(PriorityOrderer))] -public class MinimalHostTestTest : MinimalHostTest -{ - private bool _isHostRunning = false; - private readonly IServiceScope _scope; - private readonly Func> _correlationsFactory; - private static readonly ConcurrentBag ScopedCorrelations = new(); - - public MinimalHostTestTest(ManagedMinimalHostFixture hostFixture, ITestOutputHelper output) : base(hostFixture, output) - { - _scope = hostFixture.Host.Services.CreateScope(); - _correlationsFactory = () => _scope.ServiceProvider.GetServices().ToList(); - var lifetime = hostFixture.Host.Services.GetRequiredService(); - lifetime.ApplicationStarted.Register(() => _isHostRunning = true); - } - - [Fact, Priority(1)] - public void Test_SingletonShouldBeSame() // simulate a request - { - ScopedCorrelations.Add(_correlationsFactory().Single(c => c is ScopedCorrelation)); - var c1 = _correlationsFactory().Single(c => c is SingletonCorrelation); - var c2 = _correlationsFactory().Single(c => c is SingletonCorrelation); - Assert.Equal(c1.CorrelationId, c2.CorrelationId); - } - - [Fact, Priority(2)] - public void Test_TransientShouldBeDifferent() // simulate a request - { - ScopedCorrelations.Add(_correlationsFactory().Single(c => c is ScopedCorrelation)); - var c1 = _correlationsFactory().Single(c => c is TransientCorrelation); - var c2 = _correlationsFactory().Single(c => c is TransientCorrelation); - Assert.NotEqual(c1.CorrelationId, c2.CorrelationId); - } - - [Fact, Priority(3)] - public void Test_ScopedShouldBeSame() // simulate a request - { - ScopedCorrelations.Add(_correlationsFactory().Single(c => c is ScopedCorrelation)); - var c1 = _correlationsFactory().Single(c => c is ScopedCorrelation); - var c2 = _correlationsFactory().Single(c => c is ScopedCorrelation); - Assert.Equal(c1.CorrelationId, c2.CorrelationId); - } - - [Fact] - public void Test_ShouldHaveRunningHost() - { - Assert.True(_isHostRunning); - } - - [Fact] - public void Test_ShouldHaveConfigurationEntry() - { - Assert.Equal("xUnit", Configuration.GetSection("unitTestTool").Value); - } - - [Fact] - public void Test_ShouldHaveEnvironmentOfDevelopment() - { - Assert.Equal("Development", Environment.EnvironmentName); - } - - [Fact] - public void Test_VerifyAbstractions() - { - using var hostTest = HostTestFactory.Create(); - Assert.IsAssignableFrom(hostTest); - Assert.IsAssignableFrom(hostTest); - Assert.IsAssignableFrom(hostTest); - Assert.IsAssignableFrom(hostTest); - Assert.IsAssignableFrom(hostTest); - Assert.IsAssignableFrom(hostTest); - } - - protected override void OnDisposeManagedResources() - { - _scope?.Dispose(); - } - - protected override void ConfigureHost(IHostApplicationBuilder hb) - { - hb.Services.AddSingleton(); - hb.Services.AddTransient(); - hb.Services.AddScoped(); - } -} +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using Codebelt.Extensions.Xunit.Hosting.Assets; +using Cuemon.Messaging; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Xunit; +using Xunit.v3.Priority; + +namespace Codebelt.Extensions.Xunit.Hosting; + +[TestCaseOrderer(typeof(PriorityOrderer))] +public class MinimalHostTestTest : MinimalHostTest +{ + private bool _isHostRunning = false; + private readonly IServiceScope _scope; + private readonly Func> _correlationsFactory; + private static readonly ConcurrentBag ScopedCorrelations = new(); + + public MinimalHostTestTest(ManagedMinimalHostFixture hostFixture, ITestOutputHelper output) : base(hostFixture, output) + { + _scope = hostFixture.Host.Services.CreateScope(); + _correlationsFactory = () => _scope.ServiceProvider.GetServices().ToList(); + var lifetime = hostFixture.Host.Services.GetRequiredService(); + lifetime.ApplicationStarted.Register(() => _isHostRunning = true); + } + + [Fact, Priority(1)] + public void Test_SingletonShouldBeSame() // simulate a request + { + ScopedCorrelations.Add(_correlationsFactory().Single(c => c is ScopedCorrelation)); + var c1 = _correlationsFactory().Single(c => c is SingletonCorrelation); + var c2 = _correlationsFactory().Single(c => c is SingletonCorrelation); + Assert.Equal(c1.CorrelationId, c2.CorrelationId); + } + + [Fact, Priority(2)] + public void Test_TransientShouldBeDifferent() // simulate a request + { + ScopedCorrelations.Add(_correlationsFactory().Single(c => c is ScopedCorrelation)); + var c1 = _correlationsFactory().Single(c => c is TransientCorrelation); + var c2 = _correlationsFactory().Single(c => c is TransientCorrelation); + Assert.NotEqual(c1.CorrelationId, c2.CorrelationId); + } + + [Fact, Priority(3)] + public void Test_ScopedShouldBeSame() // simulate a request + { + ScopedCorrelations.Add(_correlationsFactory().Single(c => c is ScopedCorrelation)); + var c1 = _correlationsFactory().Single(c => c is ScopedCorrelation); + var c2 = _correlationsFactory().Single(c => c is ScopedCorrelation); + Assert.Equal(c1.CorrelationId, c2.CorrelationId); + } + + [Fact] + public void Test_ShouldHaveRunningHost() + { + Assert.True(_isHostRunning); + } + + [Fact] + public void Test_ShouldHaveConfigurationEntry() + { + Assert.Equal("xUnit", Configuration.GetSection("unitTestTool").Value); + } + + [Fact] + public void Test_ShouldHaveEnvironmentOfDevelopment() + { + Assert.Equal("Development", Environment.EnvironmentName); + } + + [Fact] + public void Test_VerifyAbstractions() + { + using var hostTest = HostTestFactory.Create(); + Assert.IsAssignableFrom(hostTest); + Assert.IsAssignableFrom(hostTest); + Assert.IsAssignableFrom(hostTest); + Assert.IsAssignableFrom(hostTest); + Assert.IsAssignableFrom(hostTest); + Assert.IsAssignableFrom(hostTest); + } + + protected override void OnDisposeManagedResources() + { + _scope?.Dispose(); + } + + protected override void ConfigureHost(IHostApplicationBuilder hb) + { + hb.Services.AddSingleton(); + hb.Services.AddTransient(); + hb.Services.AddScoped(); + } +} diff --git a/test/Codebelt.Extensions.Xunit.Hosting.Tests/ServiceCollectionExtensions.cs b/test/Codebelt.Extensions.Xunit.Hosting.Tests/ServiceCollectionExtensions.cs index 7815cf7..aae6697 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.Tests/ServiceCollectionExtensions.cs +++ b/test/Codebelt.Extensions.Xunit.Hosting.Tests/ServiceCollectionExtensions.cs @@ -1,160 +1,160 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Xunit; - -namespace Codebelt.Extensions.Xunit.Hosting; - -public class ServiceCollectionExtensions : Test -{ - public ServiceCollectionExtensions(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public void AddXunitTestLogging_ShouldAddXunitTestLogging() - { - var services = new ServiceCollection(); - services.AddXunitTestLogging(); - - var provider = services.BuildServiceProvider(); - - var logger1 = provider.GetRequiredService>(); - var loggerStore1 = logger1.GetTestStore(); - var loggerStore1Duplicate = logger1.GetTestStore(typeof(ServiceCollectionExtensions).FullName!.ToLowerInvariant()); - - var logger2 = provider.GetRequiredService>(); - var loggerStore2 = logger2.GetTestStore(null); // all loggers - - logger1.LogCritical("SUT"); - logger1.LogTrace("SUT"); - logger1.LogDebug("SUT"); - logger1.LogError("SUT"); - logger1.LogInformation("SUT"); - logger1.LogWarning("SUT"); - - logger2.LogInformation("Unique message for logger2."); - - Assert.Throws(() => logger2.GetTestStore("InvalidValue")); - - Assert.Equal(loggerStore1, loggerStore1Duplicate); - - Assert.NotNull(logger1); - Assert.NotNull(loggerStore1); - - Assert.NotNull(logger2); - Assert.NotNull(loggerStore2); - - Assert.Equal(6, loggerStore1.Query().Count()); - Assert.Equal(7, loggerStore2.Query().Count()); - - Assert.Collection(loggerStore1.Query(), - entry => Assert.Equal("Critical: SUT", entry.ToString()), - entry => Assert.Equal("Trace: SUT", entry.ToString()), - entry => Assert.Equal("Debug: SUT", entry.ToString()), - entry => Assert.Equal("Error: SUT", entry.ToString()), - entry => Assert.Equal("Information: SUT", entry.ToString()), - entry => Assert.Equal("Warning: SUT", entry.ToString())); - - Assert.Collection(loggerStore2.Query(), - entry => Assert.Equal("Critical: SUT", entry.ToString()), - entry => Assert.Equal("Trace: SUT", entry.ToString()), - entry => Assert.Equal("Debug: SUT", entry.ToString()), - entry => Assert.Equal("Error: SUT", entry.ToString()), - entry => Assert.Equal("Information: SUT", entry.ToString()), - entry => Assert.Equal("Warning: SUT", entry.ToString()), - entry => Assert.Equal("Information: Unique message for logger2.", entry.ToString())); - } - - [Fact] - public void AddXunitTestLogging_ShouldAddXunitTestLoggingWithTestOutput() - { - var services = new ServiceCollection(); - services.AddXunitTestLogging(TestOutput); - - var provider = services.BuildServiceProvider(); - - var logger1 = provider.GetRequiredService>(); - var loggerStore1 = logger1.GetTestStore(); - var loggerStore1Duplicate = logger1.GetTestStore(typeof(ServiceCollectionExtensions).FullName!.ToLowerInvariant()); - - var logger2 = provider.GetRequiredService>(); - var loggerStore2 = logger2.GetTestStore(null); // all loggers - - logger1.LogCritical("SUT"); - logger1.LogTrace("SUT"); - logger1.LogDebug("SUT"); - logger1.LogError("SUT"); - logger1.LogInformation("SUT"); - logger1.LogWarning("SUT"); - - logger2.LogInformation("Unique message for logger2."); - - Assert.Throws(() => logger2.GetTestStore("InvalidValue")); - - Assert.Equal(loggerStore1, loggerStore1Duplicate); - - Assert.NotNull(logger1); - Assert.NotNull(loggerStore1); - - Assert.NotNull(logger2); - Assert.NotNull(loggerStore2); - - Assert.Equal(6, loggerStore1.Query().Count()); - Assert.Equal(7, loggerStore2.Query().Count()); - - Assert.Collection(loggerStore1.Query(), - entry => Assert.Equal("Critical: SUT", entry.ToString()), - entry => Assert.Equal("Trace: SUT", entry.ToString()), - entry => Assert.Equal("Debug: SUT", entry.ToString()), - entry => Assert.Equal("Error: SUT", entry.ToString()), - entry => Assert.Equal("Information: SUT", entry.ToString()), - entry => Assert.Equal("Warning: SUT", entry.ToString())); - - Assert.Collection(loggerStore2.Query(), - entry => Assert.Equal("Critical: SUT", entry.ToString()), - entry => Assert.Equal("Trace: SUT", entry.ToString()), - entry => Assert.Equal("Debug: SUT", entry.ToString()), - entry => Assert.Equal("Error: SUT", entry.ToString()), - entry => Assert.Equal("Information: SUT", entry.ToString()), - entry => Assert.Equal("Warning: SUT", entry.ToString()), - entry => Assert.Equal("Information: Unique message for logger2.", entry.ToString())); - } - - [Fact] - public void AddXunitTestLogging_ShouldAddXunitTestLogging_UsingHostTest() - { - using var test = HostTestFactory.Create(services => - { - services.AddXunitTestLogging(); - }); - - var logger = test.Host.Services.GetRequiredService>(); - logger.LogInformation("Test"); - - var store = logger.GetTestStore(); - - Assert.Equal("Information: Test", store.Query().First().Message); - } - - [Fact] - public void AddXunitTestLogging_ShouldAddXunitTestLogging_UsingHostTest_WithOutputToRunner() - { - using var test = HostTestFactory.Create(services => - { - services.AddXunitTestLoggingOutputHelperAccessor(); - services.AddXunitTestLogging(); - }); - - test.Host.Services.GetRequiredService().TestOutput = TestOutput; - - var logger = test.Host.Services.GetRequiredService>(); - logger.LogInformation("Test"); - - var store = logger.GetTestStore(); - - Assert.Equal("Information: Test", store.Query().First().Message); - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Xunit; + +namespace Codebelt.Extensions.Xunit.Hosting; + +public class ServiceCollectionExtensions : Test +{ + public ServiceCollectionExtensions(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void AddXunitTestLogging_ShouldAddXunitTestLogging() + { + var services = new ServiceCollection(); + services.AddXunitTestLogging(); + + var provider = services.BuildServiceProvider(); + + var logger1 = provider.GetRequiredService>(); + var loggerStore1 = logger1.GetTestStore(); + var loggerStore1Duplicate = logger1.GetTestStore(typeof(ServiceCollectionExtensions).FullName!.ToLowerInvariant()); + + var logger2 = provider.GetRequiredService>(); + var loggerStore2 = logger2.GetTestStore(null); // all loggers + + logger1.LogCritical("SUT"); + logger1.LogTrace("SUT"); + logger1.LogDebug("SUT"); + logger1.LogError("SUT"); + logger1.LogInformation("SUT"); + logger1.LogWarning("SUT"); + + logger2.LogInformation("Unique message for logger2."); + + Assert.Throws(() => logger2.GetTestStore("InvalidValue")); + + Assert.Equal(loggerStore1, loggerStore1Duplicate); + + Assert.NotNull(logger1); + Assert.NotNull(loggerStore1); + + Assert.NotNull(logger2); + Assert.NotNull(loggerStore2); + + Assert.Equal(6, loggerStore1.Query().Count()); + Assert.Equal(7, loggerStore2.Query().Count()); + + Assert.Collection(loggerStore1.Query(), + entry => Assert.Equal("Critical: SUT", entry.ToString()), + entry => Assert.Equal("Trace: SUT", entry.ToString()), + entry => Assert.Equal("Debug: SUT", entry.ToString()), + entry => Assert.Equal("Error: SUT", entry.ToString()), + entry => Assert.Equal("Information: SUT", entry.ToString()), + entry => Assert.Equal("Warning: SUT", entry.ToString())); + + Assert.Collection(loggerStore2.Query(), + entry => Assert.Equal("Critical: SUT", entry.ToString()), + entry => Assert.Equal("Trace: SUT", entry.ToString()), + entry => Assert.Equal("Debug: SUT", entry.ToString()), + entry => Assert.Equal("Error: SUT", entry.ToString()), + entry => Assert.Equal("Information: SUT", entry.ToString()), + entry => Assert.Equal("Warning: SUT", entry.ToString()), + entry => Assert.Equal("Information: Unique message for logger2.", entry.ToString())); + } + + [Fact] + public void AddXunitTestLogging_ShouldAddXunitTestLoggingWithTestOutput() + { + var services = new ServiceCollection(); + services.AddXunitTestLogging(TestOutput); + + var provider = services.BuildServiceProvider(); + + var logger1 = provider.GetRequiredService>(); + var loggerStore1 = logger1.GetTestStore(); + var loggerStore1Duplicate = logger1.GetTestStore(typeof(ServiceCollectionExtensions).FullName!.ToLowerInvariant()); + + var logger2 = provider.GetRequiredService>(); + var loggerStore2 = logger2.GetTestStore(null); // all loggers + + logger1.LogCritical("SUT"); + logger1.LogTrace("SUT"); + logger1.LogDebug("SUT"); + logger1.LogError("SUT"); + logger1.LogInformation("SUT"); + logger1.LogWarning("SUT"); + + logger2.LogInformation("Unique message for logger2."); + + Assert.Throws(() => logger2.GetTestStore("InvalidValue")); + + Assert.Equal(loggerStore1, loggerStore1Duplicate); + + Assert.NotNull(logger1); + Assert.NotNull(loggerStore1); + + Assert.NotNull(logger2); + Assert.NotNull(loggerStore2); + + Assert.Equal(6, loggerStore1.Query().Count()); + Assert.Equal(7, loggerStore2.Query().Count()); + + Assert.Collection(loggerStore1.Query(), + entry => Assert.Equal("Critical: SUT", entry.ToString()), + entry => Assert.Equal("Trace: SUT", entry.ToString()), + entry => Assert.Equal("Debug: SUT", entry.ToString()), + entry => Assert.Equal("Error: SUT", entry.ToString()), + entry => Assert.Equal("Information: SUT", entry.ToString()), + entry => Assert.Equal("Warning: SUT", entry.ToString())); + + Assert.Collection(loggerStore2.Query(), + entry => Assert.Equal("Critical: SUT", entry.ToString()), + entry => Assert.Equal("Trace: SUT", entry.ToString()), + entry => Assert.Equal("Debug: SUT", entry.ToString()), + entry => Assert.Equal("Error: SUT", entry.ToString()), + entry => Assert.Equal("Information: SUT", entry.ToString()), + entry => Assert.Equal("Warning: SUT", entry.ToString()), + entry => Assert.Equal("Information: Unique message for logger2.", entry.ToString())); + } + + [Fact] + public void AddXunitTestLogging_ShouldAddXunitTestLogging_UsingHostTest() + { + using var test = HostTestFactory.Create(services => + { + services.AddXunitTestLogging(); + }); + + var logger = test.Host.Services.GetRequiredService>(); + logger.LogInformation("Test"); + + var store = logger.GetTestStore(); + + Assert.Equal("Information: Test", store.Query().First().Message); + } + + [Fact] + public void AddXunitTestLogging_ShouldAddXunitTestLogging_UsingHostTest_WithOutputToRunner() + { + using var test = HostTestFactory.Create(services => + { + services.AddXunitTestLoggingOutputHelperAccessor(); + services.AddXunitTestLogging(); + }); + + test.Host.Services.GetRequiredService().TestOutput = TestOutput; + + var logger = test.Host.Services.GetRequiredService>(); + logger.LogInformation("Test"); + + var store = logger.GetTestStore(); + + Assert.Equal("Information: Test", store.Query().First().Message); + } +} diff --git a/test/Codebelt.Extensions.Xunit.Tests/Assets/AsyncDisposable.cs b/test/Codebelt.Extensions.Xunit.Tests/Assets/AsyncDisposable.cs index d03741c..574b6f5 100644 --- a/test/Codebelt.Extensions.Xunit.Tests/Assets/AsyncDisposable.cs +++ b/test/Codebelt.Extensions.Xunit.Tests/Assets/AsyncDisposable.cs @@ -1,37 +1,37 @@ -using System; -using System.IO; -using System.Threading.Tasks; - -namespace Codebelt.Extensions.Xunit.Assets; - -public class AsyncDisposable : Test -{ - IDisposable _disposableResource = new MemoryStream(); -#if NET10_0_OR_GREATER - IAsyncDisposable _asyncDisposableResource = new MemoryStream(); -#else - IAsyncDisposable _asyncDisposableResource = new WemoryStream(); -#endif - - protected override void OnDisposeManagedResources() - { - _disposableResource?.Dispose(); - _disposableResource = null; - DisposableResourceCalled = true; - } - - public bool DisposableResourceCalled { get; private set; } - - protected override async ValueTask OnDisposeManagedResourcesAsync() - { - if (_asyncDisposableResource is not null) - { - await _asyncDisposableResource.DisposeAsync().ConfigureAwait(false); - AsyncDisposableResourceCalled = true; - } - _asyncDisposableResource = null; - OnDisposeManagedResources(); - } - - public bool AsyncDisposableResourceCalled { get; private set; } -} +using System; +using System.IO; +using System.Threading.Tasks; + +namespace Codebelt.Extensions.Xunit.Assets; + +public class AsyncDisposable : Test +{ + IDisposable _disposableResource = new MemoryStream(); +#if NET10_0_OR_GREATER + IAsyncDisposable _asyncDisposableResource = new MemoryStream(); +#else + IAsyncDisposable _asyncDisposableResource = new WemoryStream(); +#endif + + protected override void OnDisposeManagedResources() + { + _disposableResource?.Dispose(); + _disposableResource = null; + DisposableResourceCalled = true; + } + + public bool DisposableResourceCalled { get; private set; } + + protected override async ValueTask OnDisposeManagedResourcesAsync() + { + if (_asyncDisposableResource is not null) + { + await _asyncDisposableResource.DisposeAsync().ConfigureAwait(false); + AsyncDisposableResourceCalled = true; + } + _asyncDisposableResource = null; + OnDisposeManagedResources(); + } + + public bool AsyncDisposableResourceCalled { get; private set; } +} diff --git a/test/Codebelt.Extensions.Xunit.Tests/Assets/ManagedDisposable.cs b/test/Codebelt.Extensions.Xunit.Tests/Assets/ManagedDisposable.cs index 37dfd45..a10b724 100644 --- a/test/Codebelt.Extensions.Xunit.Tests/Assets/ManagedDisposable.cs +++ b/test/Codebelt.Extensions.Xunit.Tests/Assets/ManagedDisposable.cs @@ -1,25 +1,25 @@ -using System.IO; - -namespace Codebelt.Extensions.Xunit.Assets; - -public class ManagedDisposable : Test -{ - public ManagedDisposable() - { - Stream = new MemoryStream(); - } - - public MemoryStream Stream { get; private set; } - - protected override void OnDisposeManagedResources() - { - try - { - Stream?.Dispose(); - } - finally - { - Stream = null; - } - } -} +using System.IO; + +namespace Codebelt.Extensions.Xunit.Assets; + +public class ManagedDisposable : Test +{ + public ManagedDisposable() + { + Stream = new MemoryStream(); + } + + public MemoryStream Stream { get; private set; } + + protected override void OnDisposeManagedResources() + { + try + { + Stream?.Dispose(); + } + finally + { + Stream = null; + } + } +} diff --git a/test/Codebelt.Extensions.Xunit.Tests/Assets/UnmanagedDisposable.cs b/test/Codebelt.Extensions.Xunit.Tests/Assets/UnmanagedDisposable.cs index 5936461..8e284e2 100644 --- a/test/Codebelt.Extensions.Xunit.Tests/Assets/UnmanagedDisposable.cs +++ b/test/Codebelt.Extensions.Xunit.Tests/Assets/UnmanagedDisposable.cs @@ -1,147 +1,147 @@ -using System; -using System.Runtime.InteropServices; -#if NET48_OR_GREATER -using NativeLibraryLoader; -#endif -namespace Codebelt.Extensions.Xunit.Assets; - -public class UnmanagedDisposable : Test -{ - internal IntPtr _handle = IntPtr.Zero; - internal IntPtr _libHandle = IntPtr.Zero; - - public delegate bool CloseHandle(IntPtr hObject); - - public delegate IntPtr CreateFileDelegate(string lpFileName, - uint dwDesiredAccess, - uint dwShareMode, - IntPtr lpSecurityAttributes, - uint dwCreationDisposition, - uint dwFlagsAndAttributes, - IntPtr hTemplateFile); - - public delegate IntPtr PtSname(int fd); - -#if NET48_OR_GREATER - internal NativeLibrary _nativeLibrary; -#endif - - public UnmanagedDisposable() - { -#if NET10_0_OR_GREATER - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - if (NativeLibrary.TryLoad("kernel32.dll", GetType().Assembly, DllImportSearchPath.System32, out _libHandle)) - { - if (NativeLibrary.TryGetExport(_libHandle, "CreateFileW", out var functionHandle)) - { - var createFileFunc = Marshal.GetDelegateForFunctionPointer(functionHandle); - _handle = createFileFunc(@"C:\TestFile.txt", - 0x80000000, //access read-only - 1, //share-read - IntPtr.Zero, - 3, //open existing - 0, - IntPtr.Zero); - } - } - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - if (NativeLibrary.TryLoad("libc.so.6", GetType().Assembly, DllImportSearchPath.SafeDirectories, out _libHandle)) - { - _handle = _libHandle; // i don't know of any native methods on unix - } - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - if (NativeLibrary.TryLoad("libSystem.B.dylib", GetType().Assembly, DllImportSearchPath.SafeDirectories, out _libHandle)) - { - _handle = _libHandle; // i don't know of any native methods on unix - } - } -#else - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - _nativeLibrary = new NativeLibrary("kernel32.dll"); - _libHandle = _nativeLibrary.Handle; - var functionHandle = _nativeLibrary.LoadFunction("CreateFileW"); - var createFileFunc = Marshal.GetDelegateForFunctionPointer(functionHandle); - _handle = createFileFunc(@"C:\TestFile.txt", - 0x80000000, //access read-only - 1, //share-read - IntPtr.Zero, - 3, //open existing - 0, - IntPtr.Zero); - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - _nativeLibrary = new NativeLibrary("libc.so.6"); - _libHandle = _nativeLibrary.Handle; - _handle = _libHandle; // i don't know of any native methods on unix - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - _nativeLibrary = new NativeLibrary("libSystem.B.dylib"); - _libHandle = _nativeLibrary.Handle; - _handle = _libHandle; // i don't know of any native methods on unix - } -#endif - } - - ~UnmanagedDisposable() - { - Dispose(false); - } - - protected override void OnDisposeManagedResources() - { - - } - - protected override void OnDisposeUnmanagedResources() - { -#if NET10_0_OR_GREATER - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - if (_handle != IntPtr.Zero) - { - if (NativeLibrary.TryGetExport(_libHandle, "CloseHandle", out var closeHandle)) - { - var closeHandleAction = Marshal.GetDelegateForFunctionPointer(closeHandle); - closeHandleAction(_handle); - } - } - NativeLibrary.Free(_libHandle); - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - NativeLibrary.Free(_libHandle); - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - NativeLibrary.Free(_libHandle); - } -#else - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - if (_handle != IntPtr.Zero) - { - var closeHandle = _nativeLibrary.LoadFunction("CloseHandle"); - var closeHandleAction = Marshal.GetDelegateForFunctionPointer(closeHandle); - closeHandleAction(_handle); - } - _nativeLibrary.Dispose(); - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - _nativeLibrary.Dispose(); - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - _nativeLibrary.Dispose(); - } -#endif - } -} +using System; +using System.Runtime.InteropServices; +#if NET48_OR_GREATER +using NativeLibraryLoader; +#endif +namespace Codebelt.Extensions.Xunit.Assets; + +public class UnmanagedDisposable : Test +{ + internal IntPtr _handle = IntPtr.Zero; + internal IntPtr _libHandle = IntPtr.Zero; + + public delegate bool CloseHandle(IntPtr hObject); + + public delegate IntPtr CreateFileDelegate(string lpFileName, + uint dwDesiredAccess, + uint dwShareMode, + IntPtr lpSecurityAttributes, + uint dwCreationDisposition, + uint dwFlagsAndAttributes, + IntPtr hTemplateFile); + + public delegate IntPtr PtSname(int fd); + +#if NET48_OR_GREATER + internal NativeLibrary _nativeLibrary; +#endif + + public UnmanagedDisposable() + { +#if NET10_0_OR_GREATER + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + if (NativeLibrary.TryLoad("kernel32.dll", GetType().Assembly, DllImportSearchPath.System32, out _libHandle)) + { + if (NativeLibrary.TryGetExport(_libHandle, "CreateFileW", out var functionHandle)) + { + var createFileFunc = Marshal.GetDelegateForFunctionPointer(functionHandle); + _handle = createFileFunc(@"C:\TestFile.txt", + 0x80000000, //access read-only + 1, //share-read + IntPtr.Zero, + 3, //open existing + 0, + IntPtr.Zero); + } + } + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + if (NativeLibrary.TryLoad("libc.so.6", GetType().Assembly, DllImportSearchPath.SafeDirectories, out _libHandle)) + { + _handle = _libHandle; // i don't know of any native methods on unix + } + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + if (NativeLibrary.TryLoad("libSystem.B.dylib", GetType().Assembly, DllImportSearchPath.SafeDirectories, out _libHandle)) + { + _handle = _libHandle; // i don't know of any native methods on unix + } + } +#else + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + _nativeLibrary = new NativeLibrary("kernel32.dll"); + _libHandle = _nativeLibrary.Handle; + var functionHandle = _nativeLibrary.LoadFunction("CreateFileW"); + var createFileFunc = Marshal.GetDelegateForFunctionPointer(functionHandle); + _handle = createFileFunc(@"C:\TestFile.txt", + 0x80000000, //access read-only + 1, //share-read + IntPtr.Zero, + 3, //open existing + 0, + IntPtr.Zero); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + _nativeLibrary = new NativeLibrary("libc.so.6"); + _libHandle = _nativeLibrary.Handle; + _handle = _libHandle; // i don't know of any native methods on unix + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + _nativeLibrary = new NativeLibrary("libSystem.B.dylib"); + _libHandle = _nativeLibrary.Handle; + _handle = _libHandle; // i don't know of any native methods on unix + } +#endif + } + + ~UnmanagedDisposable() + { + Dispose(false); + } + + protected override void OnDisposeManagedResources() + { + + } + + protected override void OnDisposeUnmanagedResources() + { +#if NET10_0_OR_GREATER + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + if (_handle != IntPtr.Zero) + { + if (NativeLibrary.TryGetExport(_libHandle, "CloseHandle", out var closeHandle)) + { + var closeHandleAction = Marshal.GetDelegateForFunctionPointer(closeHandle); + closeHandleAction(_handle); + } + } + NativeLibrary.Free(_libHandle); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + NativeLibrary.Free(_libHandle); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + NativeLibrary.Free(_libHandle); + } +#else + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + if (_handle != IntPtr.Zero) + { + var closeHandle = _nativeLibrary.LoadFunction("CloseHandle"); + var closeHandleAction = Marshal.GetDelegateForFunctionPointer(closeHandle); + closeHandleAction(_handle); + } + _nativeLibrary.Dispose(); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + _nativeLibrary.Dispose(); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + _nativeLibrary.Dispose(); + } +#endif + } +} diff --git a/test/Codebelt.Extensions.Xunit.Tests/Assets/WemoryStream.cs b/test/Codebelt.Extensions.Xunit.Tests/Assets/WemoryStream.cs index c98a203..280f33b 100644 --- a/test/Codebelt.Extensions.Xunit.Tests/Assets/WemoryStream.cs +++ b/test/Codebelt.Extensions.Xunit.Tests/Assets/WemoryStream.cs @@ -1,19 +1,19 @@ -using System; -using System.IO; -using System.Threading.Tasks; - -namespace Codebelt.Extensions.Xunit.Assets; - -public class WemoryStream : MemoryStream, IAsyncDisposable -{ - protected virtual async ValueTask DisposeAsyncCore() - { - Dispose(false); - } - - public async ValueTask DisposeAsync() - { - await DisposeAsyncCore(); - GC.SuppressFinalize(this); - } -} +using System; +using System.IO; +using System.Threading.Tasks; + +namespace Codebelt.Extensions.Xunit.Assets; + +public class WemoryStream : MemoryStream, IAsyncDisposable +{ + protected virtual async ValueTask DisposeAsyncCore() + { + Dispose(false); + } + + public async ValueTask DisposeAsync() + { + await DisposeAsyncCore(); + GC.SuppressFinalize(this); + } +} diff --git a/test/Codebelt.Extensions.Xunit.Tests/DisposableTest.cs b/test/Codebelt.Extensions.Xunit.Tests/DisposableTest.cs index b2f8270..57600f8 100644 --- a/test/Codebelt.Extensions.Xunit.Tests/DisposableTest.cs +++ b/test/Codebelt.Extensions.Xunit.Tests/DisposableTest.cs @@ -1,93 +1,93 @@ -using System; -using System.Threading.Tasks; -using Codebelt.Extensions.Xunit.Assets; -using Xunit; - -namespace Codebelt.Extensions.Xunit; - -public class DisposableTest : Test -{ - public DisposableTest(ITestOutputHelper output) : base(output) - { - } - -#if NET10_0_OR_GREATER - [Fact] - public async Task AsyncDisposable_VerifyThatAssetIsBeingDisposed() - { - AsyncDisposable ad = new AsyncDisposable(); - await using (ad.ConfigureAwait(false)) - { - Assert.NotNull(ad); - Assert.False(ad.DisposableResourceCalled); - Assert.False(ad.AsyncDisposableResourceCalled); - } - - Assert.NotNull(ad); - Assert.True(ad.DisposableResourceCalled); - Assert.True(ad.AsyncDisposableResourceCalled); - } -#else - [Fact] - public async Task AsyncDisposable_VerifyThatAssetIsBeingDisposed() - { - AsyncDisposable ad = new AsyncDisposable(); - - Assert.NotNull(ad); - Assert.False(ad.DisposableResourceCalled); - Assert.False(ad.AsyncDisposableResourceCalled); - - await ad.DisposeAsync().ConfigureAwait(false); - - Assert.NotNull(ad); - Assert.True(ad.DisposableResourceCalled); - Assert.True(ad.AsyncDisposableResourceCalled); - } -#endif - - [Fact] - public void ManagedDisposable_VerifyThatAssetIsBeingDisposed() - { - ManagedDisposable mdRef = null; - using (var md = new ManagedDisposable()) - { - mdRef = md; - Assert.NotNull(md.Stream); - Assert.Equal(0, md.Stream.Length); - Assert.False(mdRef.Disposed); - } - Assert.NotNull(mdRef); - Assert.Null(mdRef.Stream); - Assert.True(mdRef.Disposed); - } - - private WeakReference unmanaged = null; - - [Fact] - public void UnmanagedDisposable_VerifyThatAssetIsBeingDisposedOnFinalize() - { - Action body = () => - { - var o = new UnmanagedDisposable(); - Assert.NotEqual(IntPtr.Zero, o._libHandle); - Assert.NotEqual(IntPtr.Zero, o._handle); - unmanaged = new WeakReference(o, true); - }; - - try - { - body(); - } - finally - { - GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); - GC.WaitForPendingFinalizers(); - Task.Delay(500).Wait(); // Add a small delay - } - - if (unmanaged.TryGetTarget(out var ud2)) - { - Assert.True(ud2.Disposed); - } - } -} +using System; +using System.Threading.Tasks; +using Codebelt.Extensions.Xunit.Assets; +using Xunit; + +namespace Codebelt.Extensions.Xunit; + +public class DisposableTest : Test +{ + public DisposableTest(ITestOutputHelper output) : base(output) + { + } + +#if NET10_0_OR_GREATER + [Fact] + public async Task AsyncDisposable_VerifyThatAssetIsBeingDisposed() + { + AsyncDisposable ad = new AsyncDisposable(); + await using (ad.ConfigureAwait(false)) + { + Assert.NotNull(ad); + Assert.False(ad.DisposableResourceCalled); + Assert.False(ad.AsyncDisposableResourceCalled); + } + + Assert.NotNull(ad); + Assert.True(ad.DisposableResourceCalled); + Assert.True(ad.AsyncDisposableResourceCalled); + } +#else + [Fact] + public async Task AsyncDisposable_VerifyThatAssetIsBeingDisposed() + { + AsyncDisposable ad = new AsyncDisposable(); + + Assert.NotNull(ad); + Assert.False(ad.DisposableResourceCalled); + Assert.False(ad.AsyncDisposableResourceCalled); + + await ad.DisposeAsync().ConfigureAwait(false); + + Assert.NotNull(ad); + Assert.True(ad.DisposableResourceCalled); + Assert.True(ad.AsyncDisposableResourceCalled); + } +#endif + + [Fact] + public void ManagedDisposable_VerifyThatAssetIsBeingDisposed() + { + ManagedDisposable mdRef = null; + using (var md = new ManagedDisposable()) + { + mdRef = md; + Assert.NotNull(md.Stream); + Assert.Equal(0, md.Stream.Length); + Assert.False(mdRef.Disposed); + } + Assert.NotNull(mdRef); + Assert.Null(mdRef.Stream); + Assert.True(mdRef.Disposed); + } + + private WeakReference unmanaged = null; + + [Fact] + public void UnmanagedDisposable_VerifyThatAssetIsBeingDisposedOnFinalize() + { + Action body = () => + { + var o = new UnmanagedDisposable(); + Assert.NotEqual(IntPtr.Zero, o._libHandle); + Assert.NotEqual(IntPtr.Zero, o._handle); + unmanaged = new WeakReference(o, true); + }; + + try + { + body(); + } + finally + { + GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); + GC.WaitForPendingFinalizers(); + Task.Delay(500).Wait(); // Add a small delay + } + + if (unmanaged.TryGetTarget(out var ud2)) + { + Assert.True(ud2.Disposed); + } + } +} diff --git a/test/Codebelt.Extensions.Xunit.Tests/InMemoryTestStoreTest.cs b/test/Codebelt.Extensions.Xunit.Tests/InMemoryTestStoreTest.cs index d45115c..2c37fc7 100644 --- a/test/Codebelt.Extensions.Xunit.Tests/InMemoryTestStoreTest.cs +++ b/test/Codebelt.Extensions.Xunit.Tests/InMemoryTestStoreTest.cs @@ -1,304 +1,304 @@ -using System.Linq; -using Xunit; - -namespace Codebelt.Extensions.Xunit; - -/// -/// Tests for the class. -/// -public class InMemoryTestStoreTest : Test -{ - public InMemoryTestStoreTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public void Constructor_ShouldInitializeEmptyStore() - { - var sut = new InMemoryTestStore(); - - Assert.Equal(0, sut.Count); - } - - [Fact] - public void Add_ShouldAddItemToStore() - { - var sut = new InMemoryTestStore(); - - sut.Add("test"); - - Assert.Equal(1, sut.Count); - } - - [Fact] - public void Add_ShouldAddMultipleItems() - { - var sut = new InMemoryTestStore(); - - sut.Add("first"); - sut.Add("second"); - sut.Add("third"); - - Assert.Equal(3, sut.Count); - } - - [Fact] - public void Add_ShouldAllowDuplicateItems() - { - var sut = new InMemoryTestStore(); - - sut.Add("duplicate"); - sut.Add("duplicate"); - - Assert.Equal(2, sut.Count); - } - - [Fact] - public void Add_ShouldAllowNullValues() - { - var sut = new InMemoryTestStore(); - - sut.Add(null); - - Assert.Equal(1, sut.Count); - } - - [Fact] - public void Count_ShouldReturnZero_WhenStoreIsEmpty() - { - var sut = new InMemoryTestStore(); - - Assert.Equal(0, sut.Count); - } - - [Fact] - public void Count_ShouldReturnCorrectNumber_AfterAddingItems() - { - var sut = new InMemoryTestStore(); - - sut.Add(1); - sut.Add(2); - sut.Add(3); - - Assert.Equal(3, sut.Count); - } - - [Fact] - public void Query_ShouldReturnAllItems_WhenPredicateIsNull() - { - var sut = new InMemoryTestStore(); - sut.Add("alpha"); - sut.Add("beta"); - sut.Add("gamma"); - - var result = sut.Query(null); - - Assert.Equal(3, result.Count()); - Assert.Contains("alpha", result); - Assert.Contains("beta", result); - Assert.Contains("gamma", result); - } - - [Fact] - public void Query_ShouldReturnAllItems_WhenNoPredicateProvided() - { - var sut = new InMemoryTestStore(); - sut.Add("one"); - sut.Add("two"); - - var result = sut.Query(); - - Assert.Equal(2, result.Count()); - } - - [Fact] - public void Query_ShouldReturnFilteredItems_WhenPredicateProvided() - { - var sut = new InMemoryTestStore(); - sut.Add(1); - sut.Add(2); - sut.Add(3); - sut.Add(4); - sut.Add(5); - - var result = sut.Query(x => x > 3); - - Assert.Equal(2, result.Count()); - Assert.Contains(4, result); - Assert.Contains(5, result); - } - - [Fact] - public void Query_ShouldReturnEmptySequence_WhenNoItemsMatchPredicate() - { - var sut = new InMemoryTestStore(); - sut.Add("cat"); - sut.Add("dog"); - - var result = sut.Query(x => x.StartsWith("z")); - - Assert.Empty(result); - } - - [Fact] - public void Query_ShouldReturnEmptySequence_WhenStoreIsEmpty() - { - var sut = new InMemoryTestStore(); - - var result = sut.Query(x => x.Length > 5); - - Assert.Empty(result); - } - - [Fact] - public void Query_ShouldSupportComplexPredicates() - { - var sut = new InMemoryTestStore(); - sut.Add("apple"); - sut.Add("apricot"); - sut.Add("banana"); - sut.Add("avocado"); - - var result = sut.Query(x => x.StartsWith("a") && x.Length > 5); - - Assert.Equal(2, result.Count()); - Assert.Contains("apricot", result); - Assert.Contains("avocado", result); - } - - [Fact] - public void QueryFor_ShouldReturnOnlyMatchingType() - { - var sut = new InMemoryTestStore(); - sut.Add(new Dog { Name = "Buddy" }); - sut.Add(new Cat { Name = "Whiskers" }); - sut.Add(new Dog { Name = "Max" }); - - var dogs = sut.QueryFor(); - - Assert.Equal(2, dogs.Count()); - Assert.All(dogs, dog => Assert.IsType(dog)); - } - - [Fact] - public void QueryFor_ShouldReturnEmptySequence_WhenNoMatchingType() - { - var sut = new InMemoryTestStore(); - sut.Add(new Dog { Name = "Buddy" }); - sut.Add(new Dog { Name = "Max" }); - - var cats = sut.QueryFor(); - - Assert.Empty(cats); - } - - [Fact] - public void QueryFor_ShouldReturnEmptySequence_WhenStoreIsEmpty() - { - var sut = new InMemoryTestStore(); - - var dogs = sut.QueryFor(); - - Assert.Empty(dogs); - } - - [Fact] - public void QueryFor_ShouldNotReturnDerivedTypes() - { - var sut = new InMemoryTestStore(); - sut.Add(new Dog { Name = "Buddy" }); - sut.Add(new ServiceDog { Name = "Guide", Task = "Assistance" }); - - var dogs = sut.QueryFor(); - - Assert.Single(dogs); - Assert.Equal("Buddy", dogs.First().Name); - } - - [Fact] - public void QueryFor_ShouldWorkWithMultipleTypes() - { - var sut = new InMemoryTestStore(); - sut.Add(new Dog { Name = "Buddy" }); - sut.Add(new Cat { Name = "Whiskers" }); - sut.Add(new Dog { Name = "Max" }); - sut.Add(new Cat { Name = "Fluffy" }); - - var dogs = sut.QueryFor(); - var cats = sut.QueryFor(); - - Assert.Equal(2, dogs.Count()); - Assert.Equal(2, cats.Count()); - } - - [Fact] - public void InMemoryTestStore_ShouldWorkWithReferenceTypes() - { - var sut = new InMemoryTestStore(); - var entity1 = new TestEntity { Id = 1, Name = "First" }; - var entity2 = new TestEntity { Id = 2, Name = "Second" }; - - sut.Add(entity1); - sut.Add(entity2); - - var result = sut.Query(e => e.Id == 1); - - Assert.Single(result); - Assert.Equal("First", result.First().Name); - } - - [Fact] - public void InMemoryTestStore_ShouldWorkWithValueTypes() - { - var sut = new InMemoryTestStore(); - - sut.Add(10); - sut.Add(20); - sut.Add(30); - - var result = sut.Query(x => x >= 20); - - Assert.Equal(2, result.Count()); - } - - [Fact] - public void InMemoryTestStore_ShouldMaintainInsertionOrder() - { - var sut = new InMemoryTestStore(); - - sut.Add("first"); - sut.Add("second"); - sut.Add("third"); - - var result = sut.Query().ToList(); - - Assert.Equal("first", result[0]); - Assert.Equal("second", result[1]); - Assert.Equal("third", result[2]); - } - - // Test helper classes - private abstract class Animal - { - public string Name { get; set; } - } - - private class Dog : Animal - { - } - - private class ServiceDog : Dog - { - public string Task { get; set; } - } - - private class Cat : Animal - { - } - - private class TestEntity - { - public int Id { get; set; } - public string Name { get; set; } - } -} +using System.Linq; +using Xunit; + +namespace Codebelt.Extensions.Xunit; + +/// +/// Tests for the class. +/// +public class InMemoryTestStoreTest : Test +{ + public InMemoryTestStoreTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void Constructor_ShouldInitializeEmptyStore() + { + var sut = new InMemoryTestStore(); + + Assert.Equal(0, sut.Count); + } + + [Fact] + public void Add_ShouldAddItemToStore() + { + var sut = new InMemoryTestStore(); + + sut.Add("test"); + + Assert.Equal(1, sut.Count); + } + + [Fact] + public void Add_ShouldAddMultipleItems() + { + var sut = new InMemoryTestStore(); + + sut.Add("first"); + sut.Add("second"); + sut.Add("third"); + + Assert.Equal(3, sut.Count); + } + + [Fact] + public void Add_ShouldAllowDuplicateItems() + { + var sut = new InMemoryTestStore(); + + sut.Add("duplicate"); + sut.Add("duplicate"); + + Assert.Equal(2, sut.Count); + } + + [Fact] + public void Add_ShouldAllowNullValues() + { + var sut = new InMemoryTestStore(); + + sut.Add(null); + + Assert.Equal(1, sut.Count); + } + + [Fact] + public void Count_ShouldReturnZero_WhenStoreIsEmpty() + { + var sut = new InMemoryTestStore(); + + Assert.Equal(0, sut.Count); + } + + [Fact] + public void Count_ShouldReturnCorrectNumber_AfterAddingItems() + { + var sut = new InMemoryTestStore(); + + sut.Add(1); + sut.Add(2); + sut.Add(3); + + Assert.Equal(3, sut.Count); + } + + [Fact] + public void Query_ShouldReturnAllItems_WhenPredicateIsNull() + { + var sut = new InMemoryTestStore(); + sut.Add("alpha"); + sut.Add("beta"); + sut.Add("gamma"); + + var result = sut.Query(null); + + Assert.Equal(3, result.Count()); + Assert.Contains("alpha", result); + Assert.Contains("beta", result); + Assert.Contains("gamma", result); + } + + [Fact] + public void Query_ShouldReturnAllItems_WhenNoPredicateProvided() + { + var sut = new InMemoryTestStore(); + sut.Add("one"); + sut.Add("two"); + + var result = sut.Query(); + + Assert.Equal(2, result.Count()); + } + + [Fact] + public void Query_ShouldReturnFilteredItems_WhenPredicateProvided() + { + var sut = new InMemoryTestStore(); + sut.Add(1); + sut.Add(2); + sut.Add(3); + sut.Add(4); + sut.Add(5); + + var result = sut.Query(x => x > 3); + + Assert.Equal(2, result.Count()); + Assert.Contains(4, result); + Assert.Contains(5, result); + } + + [Fact] + public void Query_ShouldReturnEmptySequence_WhenNoItemsMatchPredicate() + { + var sut = new InMemoryTestStore(); + sut.Add("cat"); + sut.Add("dog"); + + var result = sut.Query(x => x.StartsWith("z")); + + Assert.Empty(result); + } + + [Fact] + public void Query_ShouldReturnEmptySequence_WhenStoreIsEmpty() + { + var sut = new InMemoryTestStore(); + + var result = sut.Query(x => x.Length > 5); + + Assert.Empty(result); + } + + [Fact] + public void Query_ShouldSupportComplexPredicates() + { + var sut = new InMemoryTestStore(); + sut.Add("apple"); + sut.Add("apricot"); + sut.Add("banana"); + sut.Add("avocado"); + + var result = sut.Query(x => x.StartsWith("a") && x.Length > 5); + + Assert.Equal(2, result.Count()); + Assert.Contains("apricot", result); + Assert.Contains("avocado", result); + } + + [Fact] + public void QueryFor_ShouldReturnOnlyMatchingType() + { + var sut = new InMemoryTestStore(); + sut.Add(new Dog { Name = "Buddy" }); + sut.Add(new Cat { Name = "Whiskers" }); + sut.Add(new Dog { Name = "Max" }); + + var dogs = sut.QueryFor(); + + Assert.Equal(2, dogs.Count()); + Assert.All(dogs, dog => Assert.IsType(dog)); + } + + [Fact] + public void QueryFor_ShouldReturnEmptySequence_WhenNoMatchingType() + { + var sut = new InMemoryTestStore(); + sut.Add(new Dog { Name = "Buddy" }); + sut.Add(new Dog { Name = "Max" }); + + var cats = sut.QueryFor(); + + Assert.Empty(cats); + } + + [Fact] + public void QueryFor_ShouldReturnEmptySequence_WhenStoreIsEmpty() + { + var sut = new InMemoryTestStore(); + + var dogs = sut.QueryFor(); + + Assert.Empty(dogs); + } + + [Fact] + public void QueryFor_ShouldNotReturnDerivedTypes() + { + var sut = new InMemoryTestStore(); + sut.Add(new Dog { Name = "Buddy" }); + sut.Add(new ServiceDog { Name = "Guide", Task = "Assistance" }); + + var dogs = sut.QueryFor(); + + Assert.Single(dogs); + Assert.Equal("Buddy", dogs.First().Name); + } + + [Fact] + public void QueryFor_ShouldWorkWithMultipleTypes() + { + var sut = new InMemoryTestStore(); + sut.Add(new Dog { Name = "Buddy" }); + sut.Add(new Cat { Name = "Whiskers" }); + sut.Add(new Dog { Name = "Max" }); + sut.Add(new Cat { Name = "Fluffy" }); + + var dogs = sut.QueryFor(); + var cats = sut.QueryFor(); + + Assert.Equal(2, dogs.Count()); + Assert.Equal(2, cats.Count()); + } + + [Fact] + public void InMemoryTestStore_ShouldWorkWithReferenceTypes() + { + var sut = new InMemoryTestStore(); + var entity1 = new TestEntity { Id = 1, Name = "First" }; + var entity2 = new TestEntity { Id = 2, Name = "Second" }; + + sut.Add(entity1); + sut.Add(entity2); + + var result = sut.Query(e => e.Id == 1); + + Assert.Single(result); + Assert.Equal("First", result.First().Name); + } + + [Fact] + public void InMemoryTestStore_ShouldWorkWithValueTypes() + { + var sut = new InMemoryTestStore(); + + sut.Add(10); + sut.Add(20); + sut.Add(30); + + var result = sut.Query(x => x >= 20); + + Assert.Equal(2, result.Count()); + } + + [Fact] + public void InMemoryTestStore_ShouldMaintainInsertionOrder() + { + var sut = new InMemoryTestStore(); + + sut.Add("first"); + sut.Add("second"); + sut.Add("third"); + + var result = sut.Query().ToList(); + + Assert.Equal("first", result[0]); + Assert.Equal("second", result[1]); + Assert.Equal("third", result[2]); + } + + // Test helper classes + private abstract class Animal + { + public string Name { get; set; } + } + + private class Dog : Animal + { + } + + private class ServiceDog : Dog + { + public string Task { get; set; } + } + + private class Cat : Animal + { + } + + private class TestEntity + { + public int Id { get; set; } + public string Name { get; set; } + } +} diff --git a/test/Codebelt.Extensions.Xunit.Tests/StringExtensionsTest.cs b/test/Codebelt.Extensions.Xunit.Tests/StringExtensionsTest.cs index 96d9f31..2a99ab1 100644 --- a/test/Codebelt.Extensions.Xunit.Tests/StringExtensionsTest.cs +++ b/test/Codebelt.Extensions.Xunit.Tests/StringExtensionsTest.cs @@ -1,34 +1,34 @@ -using System.Runtime.InteropServices; -using Xunit; - -namespace Codebelt.Extensions.Xunit; - -public class StringExtensionsTest : Test -{ - public StringExtensionsTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public void ReplaceLineEndings_ShouldReplaceNewLineOccurrences() - { - var lineEndings = "Windows has \r\n (CRLF) and Linux has \n (LF)"; - - TestOutput.WriteLine($$""" - Before: {{lineEndings}} - After: {{lineEndings.ReplaceLineEndings()}} - """); - - TestOutput.WriteLine(RuntimeInformation.OSDescription); - TestOutput.WriteLine(RuntimeInformation.FrameworkDescription); - - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - Assert.Equal("Windows has \n (CRLF) and Linux has \n (LF)", lineEndings.ReplaceLineEndings()); - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - Assert.Equal("Windows has \r\n (CRLF) and Linux has \r\n (LF)", lineEndings.ReplaceLineEndings()); - } - } -} +using System.Runtime.InteropServices; +using Xunit; + +namespace Codebelt.Extensions.Xunit; + +public class StringExtensionsTest : Test +{ + public StringExtensionsTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void ReplaceLineEndings_ShouldReplaceNewLineOccurrences() + { + var lineEndings = "Windows has \r\n (CRLF) and Linux has \n (LF)"; + + TestOutput.WriteLine($$""" + Before: {{lineEndings}} + After: {{lineEndings.ReplaceLineEndings()}} + """); + + TestOutput.WriteLine(RuntimeInformation.OSDescription); + TestOutput.WriteLine(RuntimeInformation.FrameworkDescription); + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + Assert.Equal("Windows has \n (CRLF) and Linux has \n (LF)", lineEndings.ReplaceLineEndings()); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + Assert.Equal("Windows has \r\n (CRLF) and Linux has \r\n (LF)", lineEndings.ReplaceLineEndings()); + } + } +} diff --git a/test/Codebelt.Extensions.Xunit.Tests/TestOutputHelperExtensionsTest.cs b/test/Codebelt.Extensions.Xunit.Tests/TestOutputHelperExtensionsTest.cs index e7105ca..38da3ad 100644 --- a/test/Codebelt.Extensions.Xunit.Tests/TestOutputHelperExtensionsTest.cs +++ b/test/Codebelt.Extensions.Xunit.Tests/TestOutputHelperExtensionsTest.cs @@ -1,250 +1,250 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Xunit; - -namespace Codebelt.Extensions.Xunit; - -public class TestOutputHelperExtensionsTest : Test -{ - public TestOutputHelperExtensionsTest(ITestOutputHelper output) : base(output) - { - } - - [Fact] - public void WriteLines_WithParamsObjects_ShouldWriteMultipleLines() - { - var helper = new FakeTestOutputHelper(); - var values = new object[] { "Line1", "Line2", "Line3" }; - - helper.WriteLines(values); - - var expected = $"Line1{Environment.NewLine}Line2{Environment.NewLine}Line3"; - Assert.Equal(expected, helper.Output); - } - - [Fact] - public void WriteLines_WithParamsObjects_NullHelper_ShouldThrowArgumentNullException() - { - ITestOutputHelper helper = null; - - var exception = Assert.Throws(() => helper.WriteLines(new object[] { "test" })); - - Assert.Equal("helper", exception.ParamName); - } - - [Fact] - public void WriteLines_WithTypedArray_ShouldWriteMultipleLines() - { - var helper = new FakeTestOutputHelper(); - var values = new[] { 1, 2, 3 }; - - helper.WriteLines(values); - - var expected = $"1{Environment.NewLine}2{Environment.NewLine}3"; - Assert.Equal(expected, helper.Output); - } - - [Fact] - public void WriteLines_WithTypedArray_NullHelper_ShouldThrowArgumentNullException() - { - ITestOutputHelper helper = null; - var values = new[] { 1, 2, 3 }; - - var exception = Assert.Throws(() => helper.WriteLines(values)); - - Assert.Equal("helper", exception.ParamName); - } - - [Fact] - public void WriteLines_WithEnumerable_ShouldWriteMultipleLines() - { - var helper = new FakeTestOutputHelper(); - var values = new List { "First", "Second", "Third" }; - - helper.WriteLines(values); - - var expected = $"First{Environment.NewLine}Second{Environment.NewLine}Third"; - Assert.Equal(expected, helper.Output); - } - - [Fact] - public void WriteLines_WithEnumerable_NullHelper_ShouldThrowArgumentNullException() - { - ITestOutputHelper helper = null; - var values = new List { "test" }; - - var exception = Assert.Throws(() => helper.WriteLines(values)); - - Assert.Equal("helper", exception.ParamName); - } - - [Fact] - public void WriteLines_WithEmptyArray_ShouldWriteEmptyString() - { - var helper = new FakeTestOutputHelper(); - var values = Array.Empty(); - - helper.WriteLines(values); - - Assert.Equal(string.Empty, helper.Output); - } - - [Fact] - public void WriteLines_WithSingleObjectInArray_ShouldWriteSingleLine() - { - var helper = new FakeTestOutputHelper(); - var values = new object[] { "SingleLine" }; - - helper.WriteLines(values); - - Assert.Equal("SingleLine", helper.Output); - } - - [Fact] - public void WriteLines_WithMixedTypes_ShouldConvertToString() - { - var helper = new FakeTestOutputHelper(); - var values = new object[] { 42, true, 3.14, "text" }; - - helper.WriteLines(values); - - var expected = $"42{Environment.NewLine}True{Environment.NewLine}{3.14.ToString()}{Environment.NewLine}text"; - Assert.Equal(expected, helper.Output); - } - - [Fact] - public void WriteLines_WithComplexObjects_ShouldUseToString() - { - var helper = new FakeTestOutputHelper(); - var values = new object[] - { - new TestObject { Name = "Object1" }, - new TestObject { Name = "Object2" } - }; - - helper.WriteLines(values); - - var expected = $"TestObject: Object1{Environment.NewLine}TestObject: Object2"; - Assert.Equal(expected, helper.Output); - } - - [Theory] - [InlineData(1)] - [InlineData(5)] - [InlineData(10)] - public void WriteLines_WithVariousArraySizes_ShouldHandleCorrectly(int count) - { - var helper = new FakeTestOutputHelper(); - var values = new string[count]; - var expectedBuilder = new StringBuilder(); - - for (var i = 0; i < count; i++) - { - values[i] = $"Item{i}"; - if (i > 0) { expectedBuilder.Append(Environment.NewLine); } - expectedBuilder.Append($"Item{i}"); - } - - helper.WriteLines(values); - - Assert.Equal(expectedBuilder.ToString(), helper.Output); - } - - - [Fact] - public void WriteLines_WithEnumerableOfIntegers_ShouldFormatCorrectly() - { - var helper = new FakeTestOutputHelper(); - var values = new List { 100, 200, 300 }; - - helper.WriteLines(values); - - var expected = $"100{Environment.NewLine}200{Environment.NewLine}300"; - Assert.Equal(expected, helper.Output); - } - - [Fact] - public void WriteLines_WithEnumerableOfBooleans_ShouldFormatCorrectly() - { - var helper = new FakeTestOutputHelper(); - var values = new List { true, false, true }; - - helper.WriteLines(values); - - var expected = $"True{Environment.NewLine}False{Environment.NewLine}True"; - Assert.Equal(expected, helper.Output); - } - - [Fact] - public void WriteLines_WithEnumerableOfDecimals_ShouldFormatCorrectly() - { - var helper = new FakeTestOutputHelper(); - var values = new List { 1.5m, 2.75m, 3.99m }; - - helper.WriteLines(values); - - var expected = $"{1.5m.ToString()}{Environment.NewLine}{2.75m.ToString()}{Environment.NewLine}{3.99m.ToString()}"; - Assert.Equal(expected, helper.Output); - } - - [Fact] - public void WriteLines_WithEnumerableOfDoubles_ShouldFormatCorrectly() - { - var helper = new FakeTestOutputHelper(); - var values = new List { 1.1, 2.2, 3.3 }; - - helper.WriteLines(values); - - var expected = $"{1.1.ToString()}{Environment.NewLine}{2.2.ToString()}{Environment.NewLine}{3.3.ToString()}"; - Assert.Equal(expected, helper.Output); - } - - [Fact] - public void WriteLines_WithEmptyEnumerable_ShouldWriteEmptyString() - { - var helper = new FakeTestOutputHelper(); - var values = new List(); - - helper.WriteLines(values); - - Assert.Equal(string.Empty, helper.Output); - } - - private class FakeTestOutputHelper : ITestOutputHelper - { - private readonly StringBuilder _output = new(); - - public string Output => _output.ToString(); - - public void Write(string message) - { - _output.Append(message); - } - - public void Write(string format, params object[] args) - { - _output.Append(string.Format(format, args)); - } - - public void WriteLine(string message) - { - _output.Append(message); - } - - public void WriteLine(string format, params object[] args) - { - _output.Append(string.Format(format, args)); - } - } - - private class TestObject - { - public string Name { get; set; } - - public override string ToString() - { - return $"TestObject: {Name}"; - } - } -} +using System; +using System.Collections.Generic; +using System.Text; +using Xunit; + +namespace Codebelt.Extensions.Xunit; + +public class TestOutputHelperExtensionsTest : Test +{ + public TestOutputHelperExtensionsTest(ITestOutputHelper output) : base(output) + { + } + + [Fact] + public void WriteLines_WithParamsObjects_ShouldWriteMultipleLines() + { + var helper = new FakeTestOutputHelper(); + var values = new object[] { "Line1", "Line2", "Line3" }; + + helper.WriteLines(values); + + var expected = $"Line1{Environment.NewLine}Line2{Environment.NewLine}Line3"; + Assert.Equal(expected, helper.Output); + } + + [Fact] + public void WriteLines_WithParamsObjects_NullHelper_ShouldThrowArgumentNullException() + { + ITestOutputHelper helper = null; + + var exception = Assert.Throws(() => helper.WriteLines(new object[] { "test" })); + + Assert.Equal("helper", exception.ParamName); + } + + [Fact] + public void WriteLines_WithTypedArray_ShouldWriteMultipleLines() + { + var helper = new FakeTestOutputHelper(); + var values = new[] { 1, 2, 3 }; + + helper.WriteLines(values); + + var expected = $"1{Environment.NewLine}2{Environment.NewLine}3"; + Assert.Equal(expected, helper.Output); + } + + [Fact] + public void WriteLines_WithTypedArray_NullHelper_ShouldThrowArgumentNullException() + { + ITestOutputHelper helper = null; + var values = new[] { 1, 2, 3 }; + + var exception = Assert.Throws(() => helper.WriteLines(values)); + + Assert.Equal("helper", exception.ParamName); + } + + [Fact] + public void WriteLines_WithEnumerable_ShouldWriteMultipleLines() + { + var helper = new FakeTestOutputHelper(); + var values = new List { "First", "Second", "Third" }; + + helper.WriteLines(values); + + var expected = $"First{Environment.NewLine}Second{Environment.NewLine}Third"; + Assert.Equal(expected, helper.Output); + } + + [Fact] + public void WriteLines_WithEnumerable_NullHelper_ShouldThrowArgumentNullException() + { + ITestOutputHelper helper = null; + var values = new List { "test" }; + + var exception = Assert.Throws(() => helper.WriteLines(values)); + + Assert.Equal("helper", exception.ParamName); + } + + [Fact] + public void WriteLines_WithEmptyArray_ShouldWriteEmptyString() + { + var helper = new FakeTestOutputHelper(); + var values = Array.Empty(); + + helper.WriteLines(values); + + Assert.Equal(string.Empty, helper.Output); + } + + [Fact] + public void WriteLines_WithSingleObjectInArray_ShouldWriteSingleLine() + { + var helper = new FakeTestOutputHelper(); + var values = new object[] { "SingleLine" }; + + helper.WriteLines(values); + + Assert.Equal("SingleLine", helper.Output); + } + + [Fact] + public void WriteLines_WithMixedTypes_ShouldConvertToString() + { + var helper = new FakeTestOutputHelper(); + var values = new object[] { 42, true, 3.14, "text" }; + + helper.WriteLines(values); + + var expected = $"42{Environment.NewLine}True{Environment.NewLine}{3.14.ToString()}{Environment.NewLine}text"; + Assert.Equal(expected, helper.Output); + } + + [Fact] + public void WriteLines_WithComplexObjects_ShouldUseToString() + { + var helper = new FakeTestOutputHelper(); + var values = new object[] + { + new TestObject { Name = "Object1" }, + new TestObject { Name = "Object2" } + }; + + helper.WriteLines(values); + + var expected = $"TestObject: Object1{Environment.NewLine}TestObject: Object2"; + Assert.Equal(expected, helper.Output); + } + + [Theory] + [InlineData(1)] + [InlineData(5)] + [InlineData(10)] + public void WriteLines_WithVariousArraySizes_ShouldHandleCorrectly(int count) + { + var helper = new FakeTestOutputHelper(); + var values = new string[count]; + var expectedBuilder = new StringBuilder(); + + for (var i = 0; i < count; i++) + { + values[i] = $"Item{i}"; + if (i > 0) { expectedBuilder.Append(Environment.NewLine); } + expectedBuilder.Append($"Item{i}"); + } + + helper.WriteLines(values); + + Assert.Equal(expectedBuilder.ToString(), helper.Output); + } + + + [Fact] + public void WriteLines_WithEnumerableOfIntegers_ShouldFormatCorrectly() + { + var helper = new FakeTestOutputHelper(); + var values = new List { 100, 200, 300 }; + + helper.WriteLines(values); + + var expected = $"100{Environment.NewLine}200{Environment.NewLine}300"; + Assert.Equal(expected, helper.Output); + } + + [Fact] + public void WriteLines_WithEnumerableOfBooleans_ShouldFormatCorrectly() + { + var helper = new FakeTestOutputHelper(); + var values = new List { true, false, true }; + + helper.WriteLines(values); + + var expected = $"True{Environment.NewLine}False{Environment.NewLine}True"; + Assert.Equal(expected, helper.Output); + } + + [Fact] + public void WriteLines_WithEnumerableOfDecimals_ShouldFormatCorrectly() + { + var helper = new FakeTestOutputHelper(); + var values = new List { 1.5m, 2.75m, 3.99m }; + + helper.WriteLines(values); + + var expected = $"{1.5m.ToString()}{Environment.NewLine}{2.75m.ToString()}{Environment.NewLine}{3.99m.ToString()}"; + Assert.Equal(expected, helper.Output); + } + + [Fact] + public void WriteLines_WithEnumerableOfDoubles_ShouldFormatCorrectly() + { + var helper = new FakeTestOutputHelper(); + var values = new List { 1.1, 2.2, 3.3 }; + + helper.WriteLines(values); + + var expected = $"{1.1.ToString()}{Environment.NewLine}{2.2.ToString()}{Environment.NewLine}{3.3.ToString()}"; + Assert.Equal(expected, helper.Output); + } + + [Fact] + public void WriteLines_WithEmptyEnumerable_ShouldWriteEmptyString() + { + var helper = new FakeTestOutputHelper(); + var values = new List(); + + helper.WriteLines(values); + + Assert.Equal(string.Empty, helper.Output); + } + + private class FakeTestOutputHelper : ITestOutputHelper + { + private readonly StringBuilder _output = new(); + + public string Output => _output.ToString(); + + public void Write(string message) + { + _output.Append(message); + } + + public void Write(string format, params object[] args) + { + _output.Append(string.Format(format, args)); + } + + public void WriteLine(string message) + { + _output.Append(message); + } + + public void WriteLine(string format, params object[] args) + { + _output.Append(string.Format(format, args)); + } + } + + private class TestObject + { + public string Name { get; set; } + + public override string ToString() + { + return $"TestObject: {Name}"; + } + } +} diff --git a/test/Codebelt.Extensions.Xunit.Tests/TestTest.cs b/test/Codebelt.Extensions.Xunit.Tests/TestTest.cs index ca5e7e1..24cc7f9 100644 --- a/test/Codebelt.Extensions.Xunit.Tests/TestTest.cs +++ b/test/Codebelt.Extensions.Xunit.Tests/TestTest.cs @@ -1,322 +1,322 @@ -using System; -using System.Threading.Tasks; -using Xunit; -using Xunit.v3; - -namespace Codebelt.Extensions.Xunit; - -public class TestTest : Test -{ - private const string ExpectedStringValue = "AllIsGood"; - private bool _onDisposeManagedResourcesCalled; - private bool _initializeAsyncCalled; - - public TestTest(ITestOutputHelper output) : base(output) - { - } - - public override ValueTask InitializeAsync() - { - _initializeAsyncCalled = true; - return default; - } - - protected override ValueTask OnDisposeManagedResourcesAsync() - { - TestOutput.WriteLine($"{nameof(IAsyncLifetime.DisposeAsync)} was called."); - return default; - } - - [Fact] - public void Test_InitializeAsyncCalled_ShouldBeTrue() - { - Assert.True(_initializeAsyncCalled); - } - - [Fact] - public void Test_ShouldHaveTestOutput() - { - Assert.True(HasTestOutput); - Assert.IsAssignableFrom(TestOutput); - Assert.IsType(TestOutput); - } - - [Fact] - public void Test_ShouldInvokeDispose() - { - Assert.Equal(ExpectedStringValue, DisposeSensitiveMethod()); - Assert.False(_onDisposeManagedResourcesCalled); - Dispose(); - Assert.True(_onDisposeManagedResourcesCalled); - Assert.True(Disposed); - Assert.Throws(DisposeSensitiveMethod); - } - - [Fact] - public void Test_ShouldHaveCallerTypeOfTestTest() - { - Assert.True(GetType() == typeof(TestTest), "GetType() == typeof(TestTest)"); - } - - [Fact] - public void Match_ShouldReturnTrue_WhenExactMatch() - { - var expected = "Hello World"; - var actual = "Hello World"; - - var result = Match(expected, actual); - - Assert.True(result); - } - - [Fact] - public void Match_ShouldReturnFalse_WhenNoMatch() - { - var expected = "Hello World"; - var actual = "Goodbye World"; - - var result = Match(expected, actual); - - Assert.False(result); - } - - [Fact] - public void Match_ShouldReturnTrue_WhenUsingGroupOfCharactersWildcard() - { - var expected = "Hello * World"; - var actual = "Hello Beautiful World"; - - var result = Match(expected, actual); - - Assert.True(result); - } - - [Fact] - public void Match_ShouldReturnTrue_WhenUsingSingleCharacterWildcard() - { - var expected = "Hello ?orld"; - var actual = "Hello World"; - - var result = Match(expected, actual); - - Assert.True(result); - } - - [Fact] - public void Match_ShouldReturnTrue_WhenUsingMultipleWildcards() - { - var expected = "* test ? result *"; - var actual = "This is a test 1 result with more text"; - - var result = Match(expected, actual); - - Assert.True(result); - } - - [Fact] - public void Match_ShouldReturnTrue_WhenUsingGroupOfCharactersWildcardAtStart() - { - var expected = "*World"; - var actual = "Hello World"; - - var result = Match(expected, actual); - - Assert.True(result); - } - - [Fact] - public void Match_ShouldReturnTrue_WhenUsingGroupOfCharactersWildcardAtEnd() - { - var expected = "Hello*"; - var actual = "Hello World"; - - var result = Match(expected, actual); - - Assert.True(result); - } - - [Fact] - public void Match_ShouldReturnFalse_WhenWildcardPatternDoesNotMatch() - { - var expected = "Hello ? World"; - var actual = "Hello Beautiful World"; - - var result = Match(expected, actual); - - Assert.False(result); - } - - [Fact] - public void Match_ShouldThrowArgumentOutOfRangeException_WhenThrowOnNoMatchIsTrue() - { - var expected = "Hello World"; - var actual = "Goodbye World"; - - var exception = Assert.Throws(() => - Match(expected, actual, options => options.ThrowOnNoMatch = true)); - - TestOutput.WriteLine(exception.Message); - TestOutput.WriteLine($"ParamName: {exception.ParamName}"); - TestOutput.WriteLine($"ActualValue: {exception.ActualValue}"); - TestOutput.WriteLine($"Expected: {expected}"); - - Assert.Equal("expected", exception.ParamName); - Assert.Contains("The expected value does not match the actual value", exception.Message); - } - - [Fact] - public void Match_ShouldReturnTrue_WithCustomGroupOfCharacters() - { - var expected = "Hello ## World"; - var actual = "Hello Beautiful World"; - - var result = Match(expected, actual, options => options.GroupOfCharacters = "\\#\\#"); - - Assert.True(result); - } - - [Fact] - public void Match_ShouldReturnTrue_WithCustomSingleCharacter() - { - var expected = "Hello #orld"; - var actual = "Hello World"; - - var result = Match(expected, actual, options => options.SingleCharacter = "\\#"); - - Assert.True(result); - } - - [Fact] - public void Match_ShouldReturnTrue_WhenMatchingMultilineStrings() - { - var expected = $"Line 1{Environment.NewLine}Line 2{Environment.NewLine}Line 3"; - var actual = $"Line 1{Environment.NewLine}Line 2{Environment.NewLine}Line 3"; - - var result = Match(expected, actual); - - Assert.True(result); - } - - [Fact] - public void Match_ShouldReturnTrue_WhenMatchingMultilineStringsWithWildcards() - { - var expected = $"Line 1{Environment.NewLine}Line ?{Environment.NewLine}Line *"; - var actual = $"Line 1{Environment.NewLine}Line 2{Environment.NewLine}Line 3"; - - var result = Match(expected, actual); - - Assert.True(result); - } - - [Fact] - public void Match_ShouldReturnFalse_WhenMultilineStringsDoNotMatch() - { - var expected = $"Line 1{Environment.NewLine}Line 2{Environment.NewLine}Line 3"; - var actual = $"Line 1{Environment.NewLine}Line 4{Environment.NewLine}Line 3"; - - var result = Match(expected, actual); - - Assert.False(result); - } - - [Fact] - public void Match_ShouldReturnTrue_WhenMatchingEmptyStrings() - { - var expected = string.Empty; - var actual = string.Empty; - - var result = Match(expected, actual); - - Assert.True(result); - } - - [Fact] - public void Match_ShouldReturnTrue_WhenMatchingSpecialRegexCharacters() - { - var expected = "Hello.World"; - var actual = "Hello.World"; - - var result = Match(expected, actual); - - Assert.True(result); - } - - [Fact] - public void Match_ShouldReturnTrue_WhenMatchingWithParentheses() - { - var expected = "Test (value)"; - var actual = "Test (value)"; - - var result = Match(expected, actual); - - Assert.True(result); - } - - [Fact] - public void Match_ShouldReturnTrue_WhenMatchingWithSquareBrackets() - { - var expected = "Test [value]"; - var actual = "Test [value]"; - - var result = Match(expected, actual); - - Assert.True(result); - } - - [Fact] - public void Match_ShouldReturnTrue_WhenMatchingWithPlusSign() - { - var expected = "Test+Value"; - var actual = "Test+Value"; - - var result = Match(expected, actual); - - Assert.True(result); - } - - [Fact] - public void Match_ShouldReturnFalse_WhenSingleCharacterWildcardMatchesMultipleCharacters() - { - var expected = "Test?Value"; - var actual = "TestMultipleValue"; - - var result = Match(expected, actual); - - Assert.False(result); - } - - [Fact] - public void Match_ShouldReturnTrue_WhenGroupOfCharactersWildcardMatchesZeroCharacters() - { - var expected = "Test*Value"; - var actual = "TestValue"; - - var result = Match(expected, actual); - - Assert.True(result); - } - - [Fact] - public void Match_ShouldThrowArgumentOutOfRangeException_WithNonMatchingLines() - { - var expected = $"Line 1{Environment.NewLine}Expected Line{Environment.NewLine}Line 3"; - var actual = $"Line 1{Environment.NewLine}Actual Line{Environment.NewLine}Line 3"; - - var exception = Assert.Throws(() => - Match(expected, actual, options => options.ThrowOnNoMatch = true)); - - Assert.Contains("Expected Line", exception.ActualValue.ToString()); - } - - public string DisposeSensitiveMethod() - { - if (Disposed) { throw new ObjectDisposedException(GetType().FullName); } - return ExpectedStringValue; - } - - protected override void OnDisposeManagedResources() - { - _onDisposeManagedResourcesCalled = true; - base.OnDisposeManagedResources(); - } -} +using System; +using System.Threading.Tasks; +using Xunit; +using Xunit.v3; + +namespace Codebelt.Extensions.Xunit; + +public class TestTest : Test +{ + private const string ExpectedStringValue = "AllIsGood"; + private bool _onDisposeManagedResourcesCalled; + private bool _initializeAsyncCalled; + + public TestTest(ITestOutputHelper output) : base(output) + { + } + + public override ValueTask InitializeAsync() + { + _initializeAsyncCalled = true; + return default; + } + + protected override ValueTask OnDisposeManagedResourcesAsync() + { + TestOutput.WriteLine($"{nameof(IAsyncLifetime.DisposeAsync)} was called."); + return default; + } + + [Fact] + public void Test_InitializeAsyncCalled_ShouldBeTrue() + { + Assert.True(_initializeAsyncCalled); + } + + [Fact] + public void Test_ShouldHaveTestOutput() + { + Assert.True(HasTestOutput); + Assert.IsAssignableFrom(TestOutput); + Assert.IsType(TestOutput); + } + + [Fact] + public void Test_ShouldInvokeDispose() + { + Assert.Equal(ExpectedStringValue, DisposeSensitiveMethod()); + Assert.False(_onDisposeManagedResourcesCalled); + Dispose(); + Assert.True(_onDisposeManagedResourcesCalled); + Assert.True(Disposed); + Assert.Throws(DisposeSensitiveMethod); + } + + [Fact] + public void Test_ShouldHaveCallerTypeOfTestTest() + { + Assert.True(GetType() == typeof(TestTest), "GetType() == typeof(TestTest)"); + } + + [Fact] + public void Match_ShouldReturnTrue_WhenExactMatch() + { + var expected = "Hello World"; + var actual = "Hello World"; + + var result = Match(expected, actual); + + Assert.True(result); + } + + [Fact] + public void Match_ShouldReturnFalse_WhenNoMatch() + { + var expected = "Hello World"; + var actual = "Goodbye World"; + + var result = Match(expected, actual); + + Assert.False(result); + } + + [Fact] + public void Match_ShouldReturnTrue_WhenUsingGroupOfCharactersWildcard() + { + var expected = "Hello * World"; + var actual = "Hello Beautiful World"; + + var result = Match(expected, actual); + + Assert.True(result); + } + + [Fact] + public void Match_ShouldReturnTrue_WhenUsingSingleCharacterWildcard() + { + var expected = "Hello ?orld"; + var actual = "Hello World"; + + var result = Match(expected, actual); + + Assert.True(result); + } + + [Fact] + public void Match_ShouldReturnTrue_WhenUsingMultipleWildcards() + { + var expected = "* test ? result *"; + var actual = "This is a test 1 result with more text"; + + var result = Match(expected, actual); + + Assert.True(result); + } + + [Fact] + public void Match_ShouldReturnTrue_WhenUsingGroupOfCharactersWildcardAtStart() + { + var expected = "*World"; + var actual = "Hello World"; + + var result = Match(expected, actual); + + Assert.True(result); + } + + [Fact] + public void Match_ShouldReturnTrue_WhenUsingGroupOfCharactersWildcardAtEnd() + { + var expected = "Hello*"; + var actual = "Hello World"; + + var result = Match(expected, actual); + + Assert.True(result); + } + + [Fact] + public void Match_ShouldReturnFalse_WhenWildcardPatternDoesNotMatch() + { + var expected = "Hello ? World"; + var actual = "Hello Beautiful World"; + + var result = Match(expected, actual); + + Assert.False(result); + } + + [Fact] + public void Match_ShouldThrowArgumentOutOfRangeException_WhenThrowOnNoMatchIsTrue() + { + var expected = "Hello World"; + var actual = "Goodbye World"; + + var exception = Assert.Throws(() => + Match(expected, actual, options => options.ThrowOnNoMatch = true)); + + TestOutput.WriteLine(exception.Message); + TestOutput.WriteLine($"ParamName: {exception.ParamName}"); + TestOutput.WriteLine($"ActualValue: {exception.ActualValue}"); + TestOutput.WriteLine($"Expected: {expected}"); + + Assert.Equal("expected", exception.ParamName); + Assert.Contains("The expected value does not match the actual value", exception.Message); + } + + [Fact] + public void Match_ShouldReturnTrue_WithCustomGroupOfCharacters() + { + var expected = "Hello ## World"; + var actual = "Hello Beautiful World"; + + var result = Match(expected, actual, options => options.GroupOfCharacters = "\\#\\#"); + + Assert.True(result); + } + + [Fact] + public void Match_ShouldReturnTrue_WithCustomSingleCharacter() + { + var expected = "Hello #orld"; + var actual = "Hello World"; + + var result = Match(expected, actual, options => options.SingleCharacter = "\\#"); + + Assert.True(result); + } + + [Fact] + public void Match_ShouldReturnTrue_WhenMatchingMultilineStrings() + { + var expected = $"Line 1{Environment.NewLine}Line 2{Environment.NewLine}Line 3"; + var actual = $"Line 1{Environment.NewLine}Line 2{Environment.NewLine}Line 3"; + + var result = Match(expected, actual); + + Assert.True(result); + } + + [Fact] + public void Match_ShouldReturnTrue_WhenMatchingMultilineStringsWithWildcards() + { + var expected = $"Line 1{Environment.NewLine}Line ?{Environment.NewLine}Line *"; + var actual = $"Line 1{Environment.NewLine}Line 2{Environment.NewLine}Line 3"; + + var result = Match(expected, actual); + + Assert.True(result); + } + + [Fact] + public void Match_ShouldReturnFalse_WhenMultilineStringsDoNotMatch() + { + var expected = $"Line 1{Environment.NewLine}Line 2{Environment.NewLine}Line 3"; + var actual = $"Line 1{Environment.NewLine}Line 4{Environment.NewLine}Line 3"; + + var result = Match(expected, actual); + + Assert.False(result); + } + + [Fact] + public void Match_ShouldReturnTrue_WhenMatchingEmptyStrings() + { + var expected = string.Empty; + var actual = string.Empty; + + var result = Match(expected, actual); + + Assert.True(result); + } + + [Fact] + public void Match_ShouldReturnTrue_WhenMatchingSpecialRegexCharacters() + { + var expected = "Hello.World"; + var actual = "Hello.World"; + + var result = Match(expected, actual); + + Assert.True(result); + } + + [Fact] + public void Match_ShouldReturnTrue_WhenMatchingWithParentheses() + { + var expected = "Test (value)"; + var actual = "Test (value)"; + + var result = Match(expected, actual); + + Assert.True(result); + } + + [Fact] + public void Match_ShouldReturnTrue_WhenMatchingWithSquareBrackets() + { + var expected = "Test [value]"; + var actual = "Test [value]"; + + var result = Match(expected, actual); + + Assert.True(result); + } + + [Fact] + public void Match_ShouldReturnTrue_WhenMatchingWithPlusSign() + { + var expected = "Test+Value"; + var actual = "Test+Value"; + + var result = Match(expected, actual); + + Assert.True(result); + } + + [Fact] + public void Match_ShouldReturnFalse_WhenSingleCharacterWildcardMatchesMultipleCharacters() + { + var expected = "Test?Value"; + var actual = "TestMultipleValue"; + + var result = Match(expected, actual); + + Assert.False(result); + } + + [Fact] + public void Match_ShouldReturnTrue_WhenGroupOfCharactersWildcardMatchesZeroCharacters() + { + var expected = "Test*Value"; + var actual = "TestValue"; + + var result = Match(expected, actual); + + Assert.True(result); + } + + [Fact] + public void Match_ShouldThrowArgumentOutOfRangeException_WithNonMatchingLines() + { + var expected = $"Line 1{Environment.NewLine}Expected Line{Environment.NewLine}Line 3"; + var actual = $"Line 1{Environment.NewLine}Actual Line{Environment.NewLine}Line 3"; + + var exception = Assert.Throws(() => + Match(expected, actual, options => options.ThrowOnNoMatch = true)); + + Assert.Contains("Expected Line", exception.ActualValue.ToString()); + } + + public string DisposeSensitiveMethod() + { + if (Disposed) { throw new ObjectDisposedException(GetType().FullName); } + return ExpectedStringValue; + } + + protected override void OnDisposeManagedResources() + { + _onDisposeManagedResourcesCalled = true; + base.OnDisposeManagedResources(); + } +} diff --git a/tooling/benchmark-runner/Program.cs b/tooling/benchmark-runner/Program.cs index 9f8e908..ae68fa3 100644 --- a/tooling/benchmark-runner/Program.cs +++ b/tooling/benchmark-runner/Program.cs @@ -1,27 +1,27 @@ -using Codebelt.Extensions.BenchmarkDotNet; -using Codebelt.Extensions.BenchmarkDotNet.Console; -using BenchmarkDotNet.Configs; -using BenchmarkDotNet.Environments; -using BenchmarkDotNet.Jobs; - -namespace benchmark_runner; - -public static class Program -{ - public static void Main(string[] args) - { - BenchmarkProgram.Run(args, o => - { - o.AllowDebugBuild = BenchmarkProgram.IsDebugBuild; - o.SkipBenchmarksWithReports = true; - o.ConfigureBenchmarkDotNet(c => - { - var slimJob = BenchmarkWorkspaceOptions.Slim; - return c - .AddJob(slimJob.WithRuntime(ClrRuntime.Net48)) - .AddJob(slimJob.WithRuntime(CoreRuntime.Core90)) - .AddJob(slimJob.WithRuntime(CoreRuntime.Core10_0)); - }); - }); - } -} +using Codebelt.Extensions.BenchmarkDotNet; +using Codebelt.Extensions.BenchmarkDotNet.Console; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Environments; +using BenchmarkDotNet.Jobs; + +namespace benchmark_runner; + +public static class Program +{ + public static void Main(string[] args) + { + BenchmarkProgram.Run(args, o => + { + o.AllowDebugBuild = BenchmarkProgram.IsDebugBuild; + o.SkipBenchmarksWithReports = true; + o.ConfigureBenchmarkDotNet(c => + { + var slimJob = BenchmarkWorkspaceOptions.Slim; + return c + .AddJob(slimJob.WithRuntime(ClrRuntime.Net48)) + .AddJob(slimJob.WithRuntime(CoreRuntime.Core90)) + .AddJob(slimJob.WithRuntime(CoreRuntime.Core10_0)); + }); + }); + } +} diff --git a/tuning/Codebelt.Extensions.Xunit.Benchmarks/DelimitedStringBenchmark.cs b/tuning/Codebelt.Extensions.Xunit.Benchmarks/DelimitedStringBenchmark.cs index 49dbd51..0c0baf8 100644 --- a/tuning/Codebelt.Extensions.Xunit.Benchmarks/DelimitedStringBenchmark.cs +++ b/tuning/Codebelt.Extensions.Xunit.Benchmarks/DelimitedStringBenchmark.cs @@ -1,63 +1,63 @@ -using System.Linq; -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Configs; -using Xunit; - -namespace Codebelt.Extensions.Xunit; - -[MemoryDiagnoser] -[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)] -public class DelimitedStringBenchmark -{ - [Params(8, 256, 4096)] - public int Count { get; set; } - - private string[] _strings; - private int[] _ints; - private ITestOutputHelper _helper; - - [GlobalSetup] - public void Setup() - { - _strings = Enumerable.Range(0, Count).Select(i => $"item-{i}").ToArray(); - _ints = Enumerable.Range(0, Count).ToArray(); - _helper = new DummyTestOutputHelper(); - } - - [Benchmark(Baseline = true, Description = "Create from string sequence via WriteLines")] - public void Create_String_WriteLines() - { - TestOutputHelperExtensions.WriteLines(_helper, _strings); - } - - [Benchmark(Description = "Create from int sequence via WriteLines")] - public void Create_Int_WriteLines() - { - TestOutputHelperExtensions.WriteLines(_helper, _ints); - } - - private sealed class DummyTestOutputHelper : ITestOutputHelper - { - public void WriteLine(string message) - { - // no-op; avoid I/O during benchmark - } - - public void WriteLine(string format, params object[] args) - { - // no-op; avoid I/O during benchmark - } - - public void Write(string message) - { - // no-op; avoid I/O during benchmark - } - - public void Write(string format, params object[] args) - { - // no-op; avoid I/O during benchmark - } - - public string Output => string.Empty; - } -} +using System.Linq; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Configs; +using Xunit; + +namespace Codebelt.Extensions.Xunit; + +[MemoryDiagnoser] +[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)] +public class DelimitedStringBenchmark +{ + [Params(8, 256, 4096)] + public int Count { get; set; } + + private string[] _strings; + private int[] _ints; + private ITestOutputHelper _helper; + + [GlobalSetup] + public void Setup() + { + _strings = Enumerable.Range(0, Count).Select(i => $"item-{i}").ToArray(); + _ints = Enumerable.Range(0, Count).ToArray(); + _helper = new DummyTestOutputHelper(); + } + + [Benchmark(Baseline = true, Description = "Create from string sequence via WriteLines")] + public void Create_String_WriteLines() + { + TestOutputHelperExtensions.WriteLines(_helper, _strings); + } + + [Benchmark(Description = "Create from int sequence via WriteLines")] + public void Create_Int_WriteLines() + { + TestOutputHelperExtensions.WriteLines(_helper, _ints); + } + + private sealed class DummyTestOutputHelper : ITestOutputHelper + { + public void WriteLine(string message) + { + // no-op; avoid I/O during benchmark + } + + public void WriteLine(string format, params object[] args) + { + // no-op; avoid I/O during benchmark + } + + public void Write(string message) + { + // no-op; avoid I/O during benchmark + } + + public void Write(string format, params object[] args) + { + // no-op; avoid I/O during benchmark + } + + public string Output => string.Empty; + } +} diff --git a/tuning/Codebelt.Extensions.Xunit.Benchmarks/InMemoryTestStoreBenchmark.cs b/tuning/Codebelt.Extensions.Xunit.Benchmarks/InMemoryTestStoreBenchmark.cs index 3b8f36f..ccc9c37 100644 --- a/tuning/Codebelt.Extensions.Xunit.Benchmarks/InMemoryTestStoreBenchmark.cs +++ b/tuning/Codebelt.Extensions.Xunit.Benchmarks/InMemoryTestStoreBenchmark.cs @@ -133,4 +133,4 @@ private sealed class Derived1 : ItemBase private sealed class Derived2 : ItemBase { } -} \ No newline at end of file +} diff --git a/tuning/Codebelt.Extensions.Xunit.Benchmarks/TestBenchmark.cs b/tuning/Codebelt.Extensions.Xunit.Benchmarks/TestBenchmark.cs index aeec4a1..a75e109 100644 --- a/tuning/Codebelt.Extensions.Xunit.Benchmarks/TestBenchmark.cs +++ b/tuning/Codebelt.Extensions.Xunit.Benchmarks/TestBenchmark.cs @@ -1,74 +1,74 @@ -using System; -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Configs; - -namespace Codebelt.Extensions.Xunit; - -/// -/// Benchmarks for the method. -/// -[MemoryDiagnoser] -[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)] -public class TestBenchmark -{ - [Params(8, 256, 4096)] - public int Length { get; set; } - - private string _shortPattern; - private string _shortActual; - private string _wildcardPattern; - private string _wildcardActual; - private string _multilinePattern; - private string _multilineActual; - private string _complexWildcardPattern; - private string _complexWildcardActual; - - [GlobalSetup] - public void Setup() - { - // Simple exact match - baseline - _shortPattern = "test-string"; - _shortActual = "test-string"; - - // Simple wildcard match - _wildcardPattern = "test-*"; - _wildcardActual = "test-string"; - - // Multiline pattern matching - var lines = new string[Length / 10]; - for (int i = 0; i < lines.Length; i++) - { - lines[i] = $"Line {i}: Some content here"; - } - _multilinePattern = string.Join(Environment.NewLine, lines); - _multilineActual = string.Join(Environment.NewLine, lines); - - // Complex wildcard with multiple wildcards - _complexWildcardPattern = "prefix-*-middle-?-suffix"; - _complexWildcardActual = "prefix-some-long-text-middle-X-suffix"; - } - - [Benchmark(Baseline = true, Description = "Match - exact string")] - public bool Match_Exact() - { - return Test.Match(_shortPattern, _shortActual); - } - - [Benchmark(Description = "Match - simple wildcard")] - public bool Match_SimpleWildcard() - { - return Test.Match(_wildcardPattern, _wildcardActual); - } - - [Benchmark(Description = "Match - multiline")] - public bool Match_Multiline() - { - return Test.Match(_multilinePattern, _multilineActual); - } - - [Benchmark(Description = "Match - complex wildcard")] - public bool Match_ComplexWildcard() - { - return Test.Match(_complexWildcardPattern, _complexWildcardActual); - } -} +using System; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Configs; + +namespace Codebelt.Extensions.Xunit; + +/// +/// Benchmarks for the method. +/// +[MemoryDiagnoser] +[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)] +public class TestBenchmark +{ + [Params(8, 256, 4096)] + public int Length { get; set; } + + private string _shortPattern; + private string _shortActual; + private string _wildcardPattern; + private string _wildcardActual; + private string _multilinePattern; + private string _multilineActual; + private string _complexWildcardPattern; + private string _complexWildcardActual; + + [GlobalSetup] + public void Setup() + { + // Simple exact match - baseline + _shortPattern = "test-string"; + _shortActual = "test-string"; + + // Simple wildcard match + _wildcardPattern = "test-*"; + _wildcardActual = "test-string"; + + // Multiline pattern matching + var lines = new string[Length / 10]; + for (int i = 0; i < lines.Length; i++) + { + lines[i] = $"Line {i}: Some content here"; + } + _multilinePattern = string.Join(Environment.NewLine, lines); + _multilineActual = string.Join(Environment.NewLine, lines); + + // Complex wildcard with multiple wildcards + _complexWildcardPattern = "prefix-*-middle-?-suffix"; + _complexWildcardActual = "prefix-some-long-text-middle-X-suffix"; + } + + [Benchmark(Baseline = true, Description = "Match - exact string")] + public bool Match_Exact() + { + return Test.Match(_shortPattern, _shortActual); + } + + [Benchmark(Description = "Match - simple wildcard")] + public bool Match_SimpleWildcard() + { + return Test.Match(_wildcardPattern, _wildcardActual); + } + + [Benchmark(Description = "Match - multiline")] + public bool Match_Multiline() + { + return Test.Match(_multilinePattern, _multilineActual); + } + + [Benchmark(Description = "Match - complex wildcard")] + public bool Match_ComplexWildcard() + { + return Test.Match(_complexWildcardPattern, _complexWildcardActual); + } +} From 0b97551023472f3949773f0186ca0baed298c65c Mon Sep 17 00:00:00 2001 From: gimlichael Date: Tue, 27 Oct 2026 22:39:22 +0100 Subject: [PATCH 27/32] fix MinVerTagPrefix --- Directory.Build.props | 1 + 1 file changed, 1 insertion(+) diff --git a/Directory.Build.props b/Directory.Build.props index 16732d1..10453ba 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -43,6 +43,7 @@ latest Recommended 7035,CA2260 + v true From e9ed95c3b0b8bba70f164e876954fb2a96ef6ce5 Mon Sep 17 00:00:00 2001 From: aicia-bot Date: Wed, 28 Oct 2026 00:16:15 +0100 Subject: [PATCH 28/32] =?UTF-8?q?=F0=9F=8E=A8=20normalize=20line=20endings?= =?UTF-8?q?=20and=20remove=20byte=20order=20marks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Converts files to LF line endings and strips the UTF-8 byte order mark so they match the eol=lf rule in .gitattributes. No content changes. --- .docfx/BuildDocfxImage.ps1 | 8 +- .docfx/Dockerfile.docfx | 32 +- .docfx/PublishDocfxImage.ps1 | 6 +- ....Xunit.Hosting.AspNetCore.Http.Features.md | 24 +- ...xtensions.Xunit.Hosting.AspNetCore.Http.md | 20 +- ...elt.Extensions.Xunit.Hosting.AspNetCore.md | 102 +-- .../Codebelt.Extensions.Xunit.Hosting.md | 82 +- .../namespaces/Codebelt.Extensions.Xunit.md | 32 +- .docfx/docfx.json | 192 ++--- .docfx/filterConfig.yml | 8 +- .docfx/index.md | 34 +- .docfx/templates/modern/public/main.js | 24 +- .docfx/toc.yml | 8 +- .github/CODE_OF_CONDUCT.md | 270 +++---- .github/codecov.yml | 4 +- .github/dependabot.yml | 34 +- .github/workflows/ci-pipeline.yml | 452 +++++------ .gitignore | 756 +++++++++--------- .../PackageReleaseNotes.txt | 484 +++++------ .../Codebelt.Extensions.Xunit.App/README.md | 58 +- .../README.md | 132 +-- .../README.md | 98 +-- .nuget/Codebelt.Extensions.Xunit/README.md | 90 +-- Codebelt.Extensions.Xunit.slnx | 62 +- Directory.Build.props | 248 +++--- Directory.Build.targets | 28 +- Directory.Packages.props | 2 +- .../Properties/launchSettings.json | 24 +- .../Properties/launchSettings.json | 24 +- .../Properties/launchSettings.json | 24 +- .../Properties/launchSettings.json | 24 +- .../Codebelt.Extensions.Xunit.App.csproj | 42 +- ...Extensions.Xunit.Hosting.AspNetCore.csproj | 52 +- .../Codebelt.Extensions.Xunit.Hosting.csproj | 50 +- .../Codebelt.Extensions.Xunit.csproj | 46 +- ...ions.Xunit.Hosting.AspNetCore.Tests.csproj | 34 +- ...sions.Xunit.Hosting.FunctionalTests.csproj | 2 +- ...belt.Extensions.Xunit.Hosting.Tests.csproj | 60 +- .../appsettings.json | 6 +- .../Codebelt.Extensions.Xunit.Tests.csproj | 22 +- .../benchmark-runner/benchmark-runner.csproj | 32 +- 41 files changed, 1866 insertions(+), 1866 deletions(-) diff --git a/.docfx/BuildDocfxImage.ps1 b/.docfx/BuildDocfxImage.ps1 index 185fe06..6628a96 100644 --- a/.docfx/BuildDocfxImage.ps1 +++ b/.docfx/BuildDocfxImage.ps1 @@ -1,4 +1,4 @@ -$version = minver -i -t v -v w -docfx metadata docfx.json -docker buildx build -t xunit-docfx:$version --platform linux/arm64,linux/amd64 --load -f Dockerfile.docfx . -get-childItem -recurse -path api -include *.yml, .manifest | remove-item +$version = minver -i -t v -v w +docfx metadata docfx.json +docker buildx build -t xunit-docfx:$version --platform linux/arm64,linux/amd64 --load -f Dockerfile.docfx . +get-childItem -recurse -path api -include *.yml, .manifest | remove-item diff --git a/.docfx/Dockerfile.docfx b/.docfx/Dockerfile.docfx index 57a6e0f..e29eda1 100644 --- a/.docfx/Dockerfile.docfx +++ b/.docfx/Dockerfile.docfx @@ -1,16 +1,16 @@ -ARG NGINX_VERSION=1.31-alpine - -FROM --platform=$BUILDPLATFORM dhi.io/nginx:${NGINX_VERSION} AS base -RUN rm -rf /usr/share/nginx/html/* - -FROM --platform=$BUILDPLATFORM codebeltnet/docfx:2 AS build - -ADD [".", "docfx"] - -RUN cd docfx; \ - docfx build - -FROM dhi.io/nginx:${NGINX_VERSION} AS final -WORKDIR /usr/share/nginx/html -COPY --from=build /build/docfx/wwwroot /usr/share/nginx/html -COPY nginx.conf /etc/nginx/conf.d/default.conf +ARG NGINX_VERSION=1.31-alpine + +FROM --platform=$BUILDPLATFORM dhi.io/nginx:${NGINX_VERSION} AS base +RUN rm -rf /usr/share/nginx/html/* + +FROM --platform=$BUILDPLATFORM codebeltnet/docfx:2 AS build + +ADD [".", "docfx"] + +RUN cd docfx; \ + docfx build + +FROM dhi.io/nginx:${NGINX_VERSION} AS final +WORKDIR /usr/share/nginx/html +COPY --from=build /build/docfx/wwwroot /usr/share/nginx/html +COPY nginx.conf /etc/nginx/conf.d/default.conf diff --git a/.docfx/PublishDocfxImage.ps1 b/.docfx/PublishDocfxImage.ps1 index fb7993c..75be5e0 100644 --- a/.docfx/PublishDocfxImage.ps1 +++ b/.docfx/PublishDocfxImage.ps1 @@ -1,3 +1,3 @@ -$version = minver -i -t v -v w -docker tag xunit-docfx:$version jcr.codebelt.net/geekle/xunit-docfx:$version -docker push jcr.codebelt.net/geekle/xunit-docfx:$version +$version = minver -i -t v -v w +docker tag xunit-docfx:$version jcr.codebelt.net/geekle/xunit-docfx:$version +docker push jcr.codebelt.net/geekle/xunit-docfx:$version diff --git a/.docfx/api/namespaces/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http.Features.md b/.docfx/api/namespaces/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http.Features.md index 1dab0af..59f755e 100644 --- a/.docfx/api/namespaces/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http.Features.md +++ b/.docfx/api/namespaces/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http.Features.md @@ -1,12 +1,12 @@ ---- -uid: Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http.Features -summary: *content ---- - -Simulate the ASP.NET Core HTTP request and response feature surfaces in unit tests without running a real server. The `Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http.Features` namespace provides `FakeHttpRequestFeature` and `FakeHttpResponseFeature`, which implement `IHttpRequestFeature` and `IHttpResponseFeature` with settable properties backed by default values. Use these fakes when your test code reads request headers, the request body, the response status code, or response headers from the `IFeatureCollection` on `HttpContext.Features`. - -Start with `FakeHttpRequestFeature` when you need to control the HTTP method, path, query string, or request body in isolation. Use `FakeHttpResponseFeature` when the code under test writes status codes, response headers, or the response body. - -[!INCLUDE [availability-modern](../../includes/availability-modern.md)] - -Complements: [Microsoft.AspNetCore.Http.Features namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http.features) 🔗 \ No newline at end of file +--- +uid: Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http.Features +summary: *content +--- + +Simulate the ASP.NET Core HTTP request and response feature surfaces in unit tests without running a real server. The `Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http.Features` namespace provides `FakeHttpRequestFeature` and `FakeHttpResponseFeature`, which implement `IHttpRequestFeature` and `IHttpResponseFeature` with settable properties backed by default values. Use these fakes when your test code reads request headers, the request body, the response status code, or response headers from the `IFeatureCollection` on `HttpContext.Features`. + +Start with `FakeHttpRequestFeature` when you need to control the HTTP method, path, query string, or request body in isolation. Use `FakeHttpResponseFeature` when the code under test writes status codes, response headers, or the response body. + +[!INCLUDE [availability-modern](../../includes/availability-modern.md)] + +Complements: [Microsoft.AspNetCore.Http.Features namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http.features) 🔗 diff --git a/.docfx/api/namespaces/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http.md b/.docfx/api/namespaces/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http.md index b31d29a..5dfee0e 100644 --- a/.docfx/api/namespaces/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http.md +++ b/.docfx/api/namespaces/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http.md @@ -1,10 +1,10 @@ ---- -uid: Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http -summary: *content ---- - -Isolate unit tests from the real `IHttpContextAccessor` and `HttpContext` pipeline. The `Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http` namespace provides `FakeHttpContextAccessor`, a test double that implements `IHttpContextAccessor` with settable `HttpContext` and `HttpContextFactory` properties, letting you simulate request context without hosting a full server. Use this type when your tested code depends on `IHttpContextAccessor` and you need deterministic, fast test setup. - -[!INCLUDE [availability-modern](../../includes/availability-modern.md)] - -Complements: [Microsoft.AspNetCore.Http namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http) 🔗 \ No newline at end of file +--- +uid: Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http +summary: *content +--- + +Isolate unit tests from the real `IHttpContextAccessor` and `HttpContext` pipeline. The `Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http` namespace provides `FakeHttpContextAccessor`, a test double that implements `IHttpContextAccessor` with settable `HttpContext` and `HttpContextFactory` properties, letting you simulate request context without hosting a full server. Use this type when your tested code depends on `IHttpContextAccessor` and you need deterministic, fast test setup. + +[!INCLUDE [availability-modern](../../includes/availability-modern.md)] + +Complements: [Microsoft.AspNetCore.Http namespace](https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http) 🔗 diff --git a/.docfx/api/namespaces/Codebelt.Extensions.Xunit.Hosting.AspNetCore.md b/.docfx/api/namespaces/Codebelt.Extensions.Xunit.Hosting.AspNetCore.md index 66ee465..0b18dda 100644 --- a/.docfx/api/namespaces/Codebelt.Extensions.Xunit.Hosting.AspNetCore.md +++ b/.docfx/api/namespaces/Codebelt.Extensions.Xunit.Hosting.AspNetCore.md @@ -1,58 +1,58 @@ ---- -uid: Codebelt.Extensions.Xunit.Hosting.AspNetCore -summary: *content ---- - -Exercise an ASP.NET Core application's real entry point, dependency-injection graph, middleware pipeline, and endpoints through an in-memory `TestServer`. The `Codebelt.Extensions.Xunit.Hosting.AspNetCore` namespace can bootstrap modern minimal hosting and conventional `Startup` applications, apply test-only web-host configuration, and return either an owned test context or a reusable xUnit fixture. - +--- +uid: Codebelt.Extensions.Xunit.Hosting.AspNetCore +summary: *content +--- + +Exercise an ASP.NET Core application's real entry point, dependency-injection graph, middleware pipeline, and endpoints through an in-memory `TestServer`. The `Codebelt.Extensions.Xunit.Hosting.AspNetCore` namespace can bootstrap modern minimal hosting and conventional `Startup` applications, apply test-only web-host configuration, and return either an owned test context or a reusable xUnit fixture. + For a focused endpoint or service-override test, start with `WebApplicationTestFactory.Create` or its one-request `RunAsync` convenience. Use `WebApplicationTest` with `ManagedWebApplicationFixture` when several tests should share the bootstrapped application. Reach for `WebHostTestFactory` or `MinimalWebHostTestFactory` when the test defines its own pipeline instead of loading an existing application. - -[!INCLUDE [availability-modern](../../includes/availability-modern.md)] - -Complements: [ASP.NET Core integration tests](https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-10.0) · [WebApplicationFactory](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.testing.webapplicationfactory-1?view=aspnetcore-10.0) · [Microsoft.AspNetCore.TestHost namespace](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.testhost) 🔗 - -### Choose an ASP.NET Core Testing Path - -|When you need to|Start with|Why| -|---|---|---| -|Bootstrap an existing ASP.NET Core application for one focused test|`WebApplicationTestFactory.Create`|Returns an owned `IHostTest` whose host exposes the application's `TestServer`, services, configuration, and environment.| -|Send one request to an existing application|`WebApplicationTestFactory.RunAsync`|Combines application startup, `HttpClient` creation, request execution, and cleanup in one call.| + +[!INCLUDE [availability-modern](../../includes/availability-modern.md)] + +Complements: [ASP.NET Core integration tests](https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-10.0) · [WebApplicationFactory](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.testing.webapplicationfactory-1?view=aspnetcore-10.0) · [Microsoft.AspNetCore.TestHost namespace](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.testhost) 🔗 + +### Choose an ASP.NET Core Testing Path + +|When you need to|Start with|Why| +|---|---|---| +|Bootstrap an existing ASP.NET Core application for one focused test|`WebApplicationTestFactory.Create`|Returns an owned `IHostTest` whose host exposes the application's `TestServer`, services, configuration, and environment.| +|Send one request to an existing application|`WebApplicationTestFactory.RunAsync`|Combines application startup, `HttpClient` creation, request execution, and cleanup in one call.| |Share an existing application across an xUnit test class|`WebApplicationTest` with `ManagedWebApplicationFixture`|Uses entrypoint-owned startup while the fixture exposes `TestServer`.| -|Define services and middleware entirely inside the test|`WebHostTestFactory` or `MinimalWebHostTestFactory`|Builds a purpose-specific in-memory pipeline without loading an application project.| -|Attach observers or change state before startup|A `SelfManaged` web fixture|Builds the host and pipeline but leaves startup to the test.| - -### Compared with WebApplicationFactory - -`WebApplicationTestFactory` is an alternative integration-test entry point, not a drop-in replacement for `Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`. - -|Concern|`WebApplicationTestFactory`|Microsoft `WebApplicationFactory`| -|---|---|---| -|Acquisition|Static `Create` or `RunAsync` returns a Codebelt test context.|Instantiate or inject a factory, then call `CreateClient`.| -|Customization|Pass an `IWebHostBuilder` callback at the call site.|Subclass and override `ConfigureWebHost`, or compose with `WithWebHostBuilder`.| -|Sharing|Use Codebelt's `WebApplicationTest` and fixture types when tests should share a context.|Commonly shared directly through xUnit `IClassFixture>`.| -|Lifecycle|The caller disposes the returned context, or delegates ownership to a Codebelt fixture.|The factory owns its `TestServer` and clients and is disposed by the caller or xUnit fixture lifecycle.| -|Scope|Matches Codebelt's equivalent entry-point pattern for console, worker, and Generic Host applications.|Purpose-built for ASP.NET Core applications and includes MVC-testing conventions such as client options and content-root discovery.| - -### Fixture Naming Convention - -ASP.NET Core host fixtures follow the same lifecycle naming convention as the hosting package: - -|Prefix|Convention| -|---|---| +|Define services and middleware entirely inside the test|`WebHostTestFactory` or `MinimalWebHostTestFactory`|Builds a purpose-specific in-memory pipeline without loading an application project.| +|Attach observers or change state before startup|A `SelfManaged` web fixture|Builds the host and pipeline but leaves startup to the test.| + +### Compared with WebApplicationFactory + +`WebApplicationTestFactory` is an alternative integration-test entry point, not a drop-in replacement for `Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory`. + +|Concern|`WebApplicationTestFactory`|Microsoft `WebApplicationFactory`| +|---|---|---| +|Acquisition|Static `Create` or `RunAsync` returns a Codebelt test context.|Instantiate or inject a factory, then call `CreateClient`.| +|Customization|Pass an `IWebHostBuilder` callback at the call site.|Subclass and override `ConfigureWebHost`, or compose with `WithWebHostBuilder`.| +|Sharing|Use Codebelt's `WebApplicationTest` and fixture types when tests should share a context.|Commonly shared directly through xUnit `IClassFixture>`.| +|Lifecycle|The caller disposes the returned context, or delegates ownership to a Codebelt fixture.|The factory owns its `TestServer` and clients and is disposed by the caller or xUnit fixture lifecycle.| +|Scope|Matches Codebelt's equivalent entry-point pattern for console, worker, and Generic Host applications.|Purpose-built for ASP.NET Core applications and includes MVC-testing conventions such as client options and content-root discovery.| + +### Fixture Naming Convention + +ASP.NET Core host fixtures follow the same lifecycle naming convention as the hosting package: + +|Prefix|Convention| +|---|---| |`Managed`|The fixture owns host creation, configuration and disposal while the application entry point owns startup; test-host consumption starts the deferred host when needed.| -|`SelfManaged`|The fixture owns host creation and configuration, but leaves host startup to the test.| +|`SelfManaged`|The fixture owns host creation and configuration, but leaves host startup to the test.| `WebApplicationTestFactory` uses `ManagedWebApplicationFixture` by default, so the real application entry point owns startup and fixture setup remains lazy. Use `ManagedWebApplicationFixture` explicitly with `WebApplicationTest` when tests share the application context; use a `SelfManaged` fixture when the test must control startup itself. `BlockingManagedWebHostFixture` remains the opt-in blocking variant for the lower-level web host fixture family. It is separate from the application-entry-point fixture, which uses `ManagedWebApplicationFixture` for entrypoint-owned startup. - -### Extension Members - -|Type|Ext|Methods| -|--:|:-:|---| -|HttpClient|⬇️|`ToHttpResponseMessageAsync`| -|IHostApplicationBuilder|⬇️|`ToHostBuilder`| -|IServiceCollection|⬇️|`AddFakeHttpContextAccessor`| -|IWebApplicationFixture<TEntryPoint>|⬇️|`HasValidState`| -|IWebHostFixture|⬇️|`HasValidState`| -|IWebMinimalHostFixture|⬇️|`HasValidState`| + +### Extension Members + +|Type|Ext|Methods| +|--:|:-:|---| +|HttpClient|⬇️|`ToHttpResponseMessageAsync`| +|IHostApplicationBuilder|⬇️|`ToHostBuilder`| +|IServiceCollection|⬇️|`AddFakeHttpContextAccessor`| +|IWebApplicationFixture<TEntryPoint>|⬇️|`HasValidState`| +|IWebHostFixture|⬇️|`HasValidState`| +|IWebMinimalHostFixture|⬇️|`HasValidState`| diff --git a/.docfx/api/namespaces/Codebelt.Extensions.Xunit.Hosting.md b/.docfx/api/namespaces/Codebelt.Extensions.Xunit.Hosting.md index 5f22c93..477609d 100644 --- a/.docfx/api/namespaces/Codebelt.Extensions.Xunit.Hosting.md +++ b/.docfx/api/namespaces/Codebelt.Extensions.Xunit.Hosting.md @@ -1,45 +1,45 @@ ---- -uid: Codebelt.Extensions.Xunit.Hosting -summary: *content ---- - -Exercise a real .NET application entry point, hosted service, or dependency-injection graph without rebuilding its host setup inside the test project. The `Codebelt.Extensions.Xunit.Hosting` namespace applies the same test model to console apps, workers, and Generic Host applications: bootstrap the application's `Program` assembly, customize the host for the scenario, inspect configuration and services, and dispose the test host through one `IHostTest` abstraction. - -For a focused test, start with `ApplicationTestFactory.Create`. It is the non-web counterpart to the entry-point pattern commonly associated with ASP.NET Core's `WebApplicationFactory`: the generic argument identifies the application assembly, while the returned test context exposes the resulting host, configuration, and environment. Use the fixture/base-class form when several xUnit tests should share that application context. - -[!INCLUDE [availability-default](../../includes/availability-default.md)] - -Complements: [xUnit: Shared Context between Tests](https://xunit.net/docs/shared-context) 🔗 - -### Choose a Hosting Path - -|When you need to|Start with|Why| -|---|---|---| -|Bootstrap an existing console, worker, or Generic Host application for one test|`ApplicationTestFactory.Create`|Runs the application's entry-point setup and returns an owned `IHostTest` context that the caller disposes.| +--- +uid: Codebelt.Extensions.Xunit.Hosting +summary: *content +--- + +Exercise a real .NET application entry point, hosted service, or dependency-injection graph without rebuilding its host setup inside the test project. The `Codebelt.Extensions.Xunit.Hosting` namespace applies the same test model to console apps, workers, and Generic Host applications: bootstrap the application's `Program` assembly, customize the host for the scenario, inspect configuration and services, and dispose the test host through one `IHostTest` abstraction. + +For a focused test, start with `ApplicationTestFactory.Create`. It is the non-web counterpart to the entry-point pattern commonly associated with ASP.NET Core's `WebApplicationFactory`: the generic argument identifies the application assembly, while the returned test context exposes the resulting host, configuration, and environment. Use the fixture/base-class form when several xUnit tests should share that application context. + +[!INCLUDE [availability-default](../../includes/availability-default.md)] + +Complements: [xUnit: Shared Context between Tests](https://xunit.net/docs/shared-context) 🔗 + +### Choose a Hosting Path + +|When you need to|Start with|Why| +|---|---|---| +|Bootstrap an existing console, worker, or Generic Host application for one test|`ApplicationTestFactory.Create`|Runs the application's entry-point setup and returns an owned `IHostTest` context that the caller disposes.| |Share an existing application across an xUnit test class|`ApplicationTest` with `ManagedApplicationFixture`|Uses entrypoint-owned startup while retaining configuration and service access.| -|Build a conventional Generic Host entirely inside the test|`HostTestFactory`|Configures `IServiceCollection` and `IHostBuilder` directly without requiring an application entry point.| -|Build with the modern `IHostApplicationBuilder` model|`MinimalHostTestFactory`|Keeps minimal-host tests focused on services and application-builder configuration.| -|Configure the host now but decide when it starts|A `SelfManaged` fixture|Leaves startup under test control so observers and pre-start assertions can be attached first.| - -### Fixture Naming Convention - -Host fixtures follow a lifecycle naming convention: - -|Prefix|Convention| -|---|---| +|Build a conventional Generic Host entirely inside the test|`HostTestFactory`|Configures `IServiceCollection` and `IHostBuilder` directly without requiring an application entry point.| +|Build with the modern `IHostApplicationBuilder` model|`MinimalHostTestFactory`|Keeps minimal-host tests focused on services and application-builder configuration.| +|Configure the host now but decide when it starts|A `SelfManaged` fixture|Leaves startup under test control so observers and pre-start assertions can be attached first.| + +### Fixture Naming Convention + +Host fixtures follow a lifecycle naming convention: + +|Prefix|Convention| +|---|---| |`Managed`|The fixture owns host creation, configuration and disposal while the application entry point owns startup; test-host consumption starts the deferred host when needed.| -|`SelfManaged`|The fixture owns host creation and configuration, but leaves host startup to the test.| +|`SelfManaged`|The fixture owns host creation and configuration, but leaves host startup to the test.| `ApplicationTestFactory` uses `ManagedApplicationFixture` by default, so the real application entry point owns startup and fixture setup remains lazy. Use `ManagedApplicationFixture` explicitly with `ApplicationTest` when tests share the application context; use a `SelfManaged` fixture when the test must control startup itself. - -### Extension Members - -|Type|Ext|Methods| -|--:|:-:|---| -|IApplicationFixture<TEntryPoint>|⬇️|`HasValidState`| -|IGenericHostFixture|⬇️|`HasValidState`| -|ILogger|⬇️|`GetTestStore`| -|ILogger<T>|⬇️|`GetTestStore`| -|IMinimalHostFixture|⬇️|`HasValidState`| -|IServiceCollection|⬇️|`AddXunitTestLogging` · `AddXunitTestLoggingOutputHelperAccessor` · `AddXunitTestLoggingOutputHelperAccessor`| -|IServiceProvider|⬇️|`GetRequiredScopedService`| + +### Extension Members + +|Type|Ext|Methods| +|--:|:-:|---| +|IApplicationFixture<TEntryPoint>|⬇️|`HasValidState`| +|IGenericHostFixture|⬇️|`HasValidState`| +|ILogger|⬇️|`GetTestStore`| +|ILogger<T>|⬇️|`GetTestStore`| +|IMinimalHostFixture|⬇️|`HasValidState`| +|IServiceCollection|⬇️|`AddXunitTestLogging` · `AddXunitTestLoggingOutputHelperAccessor` · `AddXunitTestLoggingOutputHelperAccessor`| +|IServiceProvider|⬇️|`GetRequiredScopedService`| diff --git a/.docfx/api/namespaces/Codebelt.Extensions.Xunit.md b/.docfx/api/namespaces/Codebelt.Extensions.Xunit.md index 6ebc893..283df71 100644 --- a/.docfx/api/namespaces/Codebelt.Extensions.Xunit.md +++ b/.docfx/api/namespaces/Codebelt.Extensions.Xunit.md @@ -1,16 +1,16 @@ ---- -uid: Codebelt.Extensions.Xunit -summary: *content ---- -The `Codebelt.Extensions.Xunit` namespace contains types that provides a uniform way of doing unit testing. The namespace relates to the `Xunit.Abstractions` namespace. - -[!INCLUDE [availability-default](../../includes/availability-default.md)] - -Complements: [xUnit: Capturing Output](https://xunit.net/docs/capturing-output) 🔗 - -### Extension Methods - -|Type|Ext|Methods| -|--:|:-:|---| -|ITestOutputHelper|⬇️|`WriteLines`| -|String|⬇️|`ReplaceLineEndings` (TFM netstandard2.0)| +--- +uid: Codebelt.Extensions.Xunit +summary: *content +--- +The `Codebelt.Extensions.Xunit` namespace contains types that provides a uniform way of doing unit testing. The namespace relates to the `Xunit.Abstractions` namespace. + +[!INCLUDE [availability-default](../../includes/availability-default.md)] + +Complements: [xUnit: Capturing Output](https://xunit.net/docs/capturing-output) 🔗 + +### Extension Methods + +|Type|Ext|Methods| +|--:|:-:|---| +|ITestOutputHelper|⬇️|`WriteLines`| +|String|⬇️|`ReplaceLineEndings` (TFM netstandard2.0)| diff --git a/.docfx/docfx.json b/.docfx/docfx.json index 76f9213..6164845 100644 --- a/.docfx/docfx.json +++ b/.docfx/docfx.json @@ -1,96 +1,96 @@ -{ - "metadata": [ - { - "src": [ - { - "files": [ - "Codebelt.Extensions.Xunit/**.csproj", - "Codebelt.Extensions.Xunit.Hosting/**.csproj", - "Codebelt.Extensions.Xunit.Hosting.AspNetCore/**.csproj" - ], - "src": "../src" - } - ], - "dest": "api", - "memberLayout": "separatePages", - "filter": "filterConfig.yml", - "properties": { - "TargetFramework": "net10.0" - } - } - ], - "build": { - "xref": [ - "https://docs.cuemon.net/xrefmap.yml", - "https://github.com/dotnet/docfx/raw/main/.xrefmap.json" - ], - "content": [ - { - "files": [ - "api/**/*.yml", - "packages/**/*.md", - "toc.yml", - "*.md" - ], - "exclude": [ - "api/namespaces/**", - "api/types/**", - "bin/**", - "obj/**" - ] - } - ], - "resource": [ - { - "files": [ - "images/**" - ] - } - ], - "globalMetadata": { - "_appTitle": "Extensions for xUnit by Codebelt", - "_appFooter": "Generated by DocFX. Copyright 2024-2026 Geekle. All rights reserved.", - "_appLogoPath": "images/50x50.png", - "_appFaviconPath": "images/favicon.ico", - "_googleAnalyticsTagId": "G-S3WBFFNKBL", - "_enableSearch": false, - "_disableContribution": false, - "_gitContribute": { - "repo": "https://github.com/codebeltnet/xunit", - "branch": "main" - }, - "_gitUrlPattern": "github" - }, - "dest": "wwwroot", - "globalMetadataFiles": [], - "fileMetadataFiles": [], - "template": [ - "default", - "modern", - "templates/modern", - "templates/ms-style" - ], - "overwrite": [ - { - "files": [ - "api/namespaces/**/*.md", - "api/types/**/*.md" - ], - "exclude": [ - "obj/**", - "wwwroot/**" - ] - } - ], - "postProcessors": [], - "markdownEngineName": "markdig", - "noLangKeyword": false, - "keepFileLink": false, - "cleanupCacheHistory": false, - "disableGitFeatures": false, - "sitemap": { - "baseUrl": "https://xunit.codebelt.net/", - "changefreq": "monthly" - } - } -} +{ + "metadata": [ + { + "src": [ + { + "files": [ + "Codebelt.Extensions.Xunit/**.csproj", + "Codebelt.Extensions.Xunit.Hosting/**.csproj", + "Codebelt.Extensions.Xunit.Hosting.AspNetCore/**.csproj" + ], + "src": "../src" + } + ], + "dest": "api", + "memberLayout": "separatePages", + "filter": "filterConfig.yml", + "properties": { + "TargetFramework": "net10.0" + } + } + ], + "build": { + "xref": [ + "https://docs.cuemon.net/xrefmap.yml", + "https://github.com/dotnet/docfx/raw/main/.xrefmap.json" + ], + "content": [ + { + "files": [ + "api/**/*.yml", + "packages/**/*.md", + "toc.yml", + "*.md" + ], + "exclude": [ + "api/namespaces/**", + "api/types/**", + "bin/**", + "obj/**" + ] + } + ], + "resource": [ + { + "files": [ + "images/**" + ] + } + ], + "globalMetadata": { + "_appTitle": "Extensions for xUnit by Codebelt", + "_appFooter": "Generated by DocFX. Copyright 2024-2026 Geekle. All rights reserved.", + "_appLogoPath": "images/50x50.png", + "_appFaviconPath": "images/favicon.ico", + "_googleAnalyticsTagId": "G-S3WBFFNKBL", + "_enableSearch": false, + "_disableContribution": false, + "_gitContribute": { + "repo": "https://github.com/codebeltnet/xunit", + "branch": "main" + }, + "_gitUrlPattern": "github" + }, + "dest": "wwwroot", + "globalMetadataFiles": [], + "fileMetadataFiles": [], + "template": [ + "default", + "modern", + "templates/modern", + "templates/ms-style" + ], + "overwrite": [ + { + "files": [ + "api/namespaces/**/*.md", + "api/types/**/*.md" + ], + "exclude": [ + "obj/**", + "wwwroot/**" + ] + } + ], + "postProcessors": [], + "markdownEngineName": "markdig", + "noLangKeyword": false, + "keepFileLink": false, + "cleanupCacheHistory": false, + "disableGitFeatures": false, + "sitemap": { + "baseUrl": "https://xunit.codebelt.net/", + "changefreq": "monthly" + } + } +} diff --git a/.docfx/filterConfig.yml b/.docfx/filterConfig.yml index 3343240..bab3762 100644 --- a/.docfx/filterConfig.yml +++ b/.docfx/filterConfig.yml @@ -1,4 +1,4 @@ -apiRules: -- exclude: - uidRegex: ^System\.Object - type: Type \ No newline at end of file +apiRules: +- exclude: + uidRegex: ^System\.Object + type: Type diff --git a/.docfx/index.md b/.docfx/index.md index 7a1d981..6bee70a 100644 --- a/.docfx/index.md +++ b/.docfx/index.md @@ -1,17 +1,17 @@ ---- -uid: frontpage-md -title: Extensions for xUnit by Codebelt ---- -![Extensions for xUnit API by Codebelt](/images/128x128.png) - -# Extensions for xUnit by Codebelt - -Originally part of **Cuemon for .NET**, this project was split into its own project ahead of the .NET 9 release. - -The current target frameworks are .NET 10 and .NET 11, with .NET Standard 2.0 available where applicable. - -This project is now part of [Extensions for xUnit by Codebelt](https://github.com/codebeltnet/xunit). - -[Cuemon for .NET](https://www.cuemon.net/) will pursue its original purpose; providing an addition/complement/sidecar to Microsoft .NET platform. - -Proceed to the [docs](/api/Codebelt.Extensions.Xunit.html) to learn more about the capabilities of this project. +--- +uid: frontpage-md +title: Extensions for xUnit by Codebelt +--- +![Extensions for xUnit API by Codebelt](/images/128x128.png) + +# Extensions for xUnit by Codebelt + +Originally part of **Cuemon for .NET**, this project was split into its own project ahead of the .NET 9 release. + +The current target frameworks are .NET 10 and .NET 11, with .NET Standard 2.0 available where applicable. + +This project is now part of [Extensions for xUnit by Codebelt](https://github.com/codebeltnet/xunit). + +[Cuemon for .NET](https://www.cuemon.net/) will pursue its original purpose; providing an addition/complement/sidecar to Microsoft .NET platform. + +Proceed to the [docs](/api/Codebelt.Extensions.Xunit.html) to learn more about the capabilities of this project. diff --git a/.docfx/templates/modern/public/main.js b/.docfx/templates/modern/public/main.js index 6f678c6..520a512 100644 --- a/.docfx/templates/modern/public/main.js +++ b/.docfx/templates/modern/public/main.js @@ -1,12 +1,12 @@ -export default { - start: () => { - const script = document.createElement('script'); - script.src = 'https://context7.com/widget.js'; - script.setAttribute('data-library', '/codebeltnet/xunit'); - script.setAttribute('data-color', '#059669'); - script.setAttribute('data-position', 'bottom-right'); - script.setAttribute('data-placeholder', 'Ask about Codebelt.Extensions.Xunit...'); - script.async = true; - document.body.appendChild(script); - } -}; +export default { + start: () => { + const script = document.createElement('script'); + script.src = 'https://context7.com/widget.js'; + script.setAttribute('data-library', '/codebeltnet/xunit'); + script.setAttribute('data-color', '#059669'); + script.setAttribute('data-position', 'bottom-right'); + script.setAttribute('data-placeholder', 'Ask about Codebelt.Extensions.Xunit...'); + script.async = true; + document.body.appendChild(script); + } +}; diff --git a/.docfx/toc.yml b/.docfx/toc.yml index f59bd5c..114aec7 100644 --- a/.docfx/toc.yml +++ b/.docfx/toc.yml @@ -1,4 +1,4 @@ -- name: xUnit API - href: api/Codebelt.Extensions.Xunit.html -- name: NuGet - href: packages \ No newline at end of file +- name: xUnit API + href: api/Codebelt.Extensions.Xunit.html +- name: NuGet + href: packages diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md index e5be83d..1fa464e 100644 --- a/.github/CODE_OF_CONDUCT.md +++ b/.github/CODE_OF_CONDUCT.md @@ -1,135 +1,135 @@ -# Contributor Covenant Code of Conduct - -This document is adapted from the Contributor Covenant which is used by many open source projects, -including those under the [.NET Foundation](https://dotnetfoundation.org/code-of-conduct). - -## Our Pledge - -We as members, contributors, and leaders pledge to make participation in our -community a harassment-free experience for everyone, regardless of age, body -size, visible or invisible disability, ethnicity, sex characteristics, gender -identity and expression, level of experience, education, socio-economic status, -nationality, personal appearance, race, caste, color, religion, or sexual identity -and orientation. - -We pledge to act and interact in ways that contribute to an open, welcoming, -diverse, inclusive, and healthy community. - -## Our Standards - -Examples of behavior that contributes to a positive environment for our -community include: - -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, - and learning from the experience -* Focusing on what is best not just for us as individuals, but for the - overall community - -Examples of unacceptable behavior include: - -* The use of sexualized language or imagery, and sexual attention or - advances of any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email - address, without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Enforcement Responsibilities - -Community leaders are responsible for clarifying and enforcing our standards of -acceptable behavior and will take appropriate and fair corrective action in -response to any behavior that they deem inappropriate, threatening, offensive, -or harmful. - -Community leaders 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, and will communicate reasons for moderation -decisions when appropriate. - -## Scope - -This Code of Conduct applies within all community spaces, and also applies when -an individual is officially representing the community in public spaces. -Examples of representing our community include using an official e-mail address, -posting via an official social media account, or acting as an appointed -representative at an online or offline event. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported to the community leaders responsible for enforcement at -[INSERT CONTACT METHOD]. -All complaints will be reviewed and investigated promptly and fairly. - -All community leaders are obligated to respect the privacy and security of the -reporter of any incident. - -## Enforcement Guidelines - -Community leaders will follow these Community Impact Guidelines in determining -the consequences for any action they deem in violation of this Code of Conduct: - -### 1. Correction - -**Community Impact**: Use of inappropriate language or other behavior deemed -unprofessional or unwelcome in the community. - -**Consequence**: A private, written warning from community leaders, providing -clarity around the nature of the violation and an explanation of why the -behavior was inappropriate. A public apology may be requested. - -### 2. Warning - -**Community Impact**: A violation through a single incident or series -of actions. - -**Consequence**: A warning with consequences for continued behavior. No -interaction with the people involved, including unsolicited interaction with -those enforcing the Code of Conduct, for a specified period of time. This -includes avoiding interactions in community spaces as well as external channels -like social media. Violating these terms may lead to a temporary or -permanent ban. - -### 3. Temporary Ban - -**Community Impact**: A serious violation of community standards, including -sustained inappropriate behavior. - -**Consequence**: A temporary ban from any sort of interaction or public -communication with the community for a specified period of time. No public or -private interaction with the people involved, including unsolicited interaction -with those enforcing the Code of Conduct, is allowed during this period. -Violating these terms may lead to a permanent ban. - -### 4. Permanent Ban - -**Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an -individual, or aggression toward or disparagement of classes of individuals. - -**Consequence**: A permanent ban from any sort of public interaction within -the community. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], -version 2.0, available at -[https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0]. - -Community Impact Guidelines were inspired by -[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. - -For answers to common questions about this code of conduct, see the FAQ at -[https://www.contributor-covenant.org/faq][FAQ]. Translations are available -at [https://www.contributor-covenant.org/translations][translations]. - -[homepage]: https://www.contributor-covenant.org -[v2.0]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html -[Mozilla CoC]: https://github.com/mozilla/diversity -[FAQ]: https://www.contributor-covenant.org/faq -[translations]: https://www.contributor-covenant.org/translations \ No newline at end of file +# Contributor Covenant Code of Conduct + +This document is adapted from the Contributor Covenant which is used by many open source projects, +including those under the [.NET Foundation](https://dotnetfoundation.org/code-of-conduct). + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders 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, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +[INSERT CONTACT METHOD]. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +[https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available +at [https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.0]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/.github/codecov.yml b/.github/codecov.yml index 2515756..b52d0c4 100644 --- a/.github/codecov.yml +++ b/.github/codecov.yml @@ -1,2 +1,2 @@ -ignore: - - "test" \ No newline at end of file +ignore: + - "test" diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 1fca42a..7fe3850 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,17 +1,17 @@ -version: 2 -updates: - - package-ecosystem: "nuget" - directory: "/src" - schedule: - interval: "weekly" - open-pull-requests-limit: 0 - - package-ecosystem: "nuget" - directory: "/test" - schedule: - interval: "weekly" - open-pull-requests-limit: 0 - - package-ecosystem: "github-actions" - directory: "/.github/workflows" - schedule: - interval: "weekly" - open-pull-requests-limit: 0 +version: 2 +updates: + - package-ecosystem: "nuget" + directory: "/src" + schedule: + interval: "weekly" + open-pull-requests-limit: 0 + - package-ecosystem: "nuget" + directory: "/test" + schedule: + interval: "weekly" + open-pull-requests-limit: 0 + - package-ecosystem: "github-actions" + directory: "/.github/workflows" + schedule: + interval: "weekly" + open-pull-requests-limit: 0 diff --git a/.github/workflows/ci-pipeline.yml b/.github/workflows/ci-pipeline.yml index 1e2aa22..26d0fcf 100644 --- a/.github/workflows/ci-pipeline.yml +++ b/.github/workflows/ci-pipeline.yml @@ -1,226 +1,226 @@ -name: xUnit Ext. CI Pipeline -on: - pull_request: - branches: [main] - workflow_dispatch: - inputs: - configuration: - type: choice - description: The build configuration to use in the deploy stage. - required: true - default: Release - options: - - Debug - - Release - run_mac_tests: - type: boolean - description: Run the macOS test matrix despite the additional cost and runtime. - default: false - -permissions: - contents: read - -jobs: - init: - name: initialize - runs-on: ubuntu-26.04 - outputs: - run-mac-tests: ${{ steps.vars.outputs.run-mac-tests }} - run-privileged-jobs: ${{ steps.vars.outputs.run-privileged-jobs }} - strong-name-key-filename: ${{ steps.vars.outputs.strong-name-key-filename }} - build-switches: ${{ steps.vars.outputs.build-switches }} - steps: - - id: vars - name: calculate workflow variables - shell: bash - run: | - if [[ "${{ github.event_name }}" == "workflow_dispatch" && "${{ inputs.run_mac_tests }}" == "true" ]]; then - echo "run-mac-tests=true" >> "$GITHUB_OUTPUT" - else - echo "run-mac-tests=false" >> "$GITHUB_OUTPUT" - fi - - if [[ "${{ github.event_name }}" == "pull_request" && "${{ github.event.pull_request.head.repo.full_name }}" != "${{ github.repository }}" ]]; then - echo "run-privileged-jobs=false" >> "$GITHUB_OUTPUT" - echo "strong-name-key-filename=" >> "$GITHUB_OUTPUT" - echo "build-switches=-p:SkipSignAssembly=true" >> "$GITHUB_OUTPUT" - else - echo "run-privileged-jobs=true" >> "$GITHUB_OUTPUT" - echo "strong-name-key-filename=xunit.snk" >> "$GITHUB_OUTPUT" - echo "build-switches=" >> "$GITHUB_OUTPUT" - fi - - build: - name: call-build - needs: [init] - strategy: - matrix: - arch: [X64, ARM64] - configuration: [Debug, Release] - uses: codebeltnet/jobs-dotnet-build/.github/workflows/default.yml@v3 - with: - include-preview: true - configuration: ${{ matrix.configuration }} - strong-name-key-filename: ${{ needs.init.outputs.strong-name-key-filename }} - build-switches: ${{ needs.init.outputs.build-switches }} - runs-on: ${{ matrix.arch == 'ARM64' && 'ubuntu-26.04-arm' || 'ubuntu-26.04' }} - upload-build-artifact-name: build-${{ matrix.configuration }}-${{ matrix.arch }} - secrets: inherit - - pack: - name: call-pack - needs: [build] - strategy: - matrix: - configuration: [Debug, Release] - uses: codebeltnet/jobs-dotnet-pack/.github/workflows/default.yml@v3 - with: - include-preview: true - configuration: ${{ matrix.configuration }} - version: ${{ needs.build.outputs.version }} - download-build-artifact-pattern: build-${{ matrix.configuration }}-X64 - - test_linux: - name: call-test-linux - needs: [build] - strategy: - fail-fast: false - matrix: - configuration: [Debug, Release] - arch: [X64, ARM64] - uses: codebeltnet/jobs-dotnet-test/.github/workflows/default.yml@v3 - with: - include-preview: true - runs-on: ${{ matrix.arch == 'ARM64' && 'ubuntu-26.04-arm' || 'ubuntu-26.04' }} - configuration: ${{ matrix.configuration }} - build-switches: -p:SkipSignAssembly=true - restore: true # we need to restore the packages for the test project - build: true # we need to build for .net48 tests - download-pattern: build-${{ matrix.configuration }}-${{ matrix.arch }} - - test_windows: - name: call-test-windows - needs: [build] - strategy: - fail-fast: false - matrix: - configuration: [Debug, Release] - arch: [X64, ARM64] - uses: codebeltnet/jobs-dotnet-test/.github/workflows/default.yml@v3 - with: - include-preview: true - runs-on: ${{ matrix.arch == 'ARM64' && 'windows-11-arm' || 'windows-2025' }} - configuration: ${{ matrix.configuration }} - build-switches: -p:SkipSignAssembly=true - restore: true # we need to restore the packages for the test project - build: true # we need to build for .net48 tests - download-pattern: build-${{ matrix.configuration }}-${{ matrix.arch }} - - test_mac: - if: ${{ needs.init.outputs.run-mac-tests == 'true' }} - name: call-test-mac - needs: [init, build] - strategy: - fail-fast: false - matrix: - arch: [X64, ARM64] - configuration: [Debug, Release] - uses: codebeltnet/jobs-dotnet-test/.github/workflows/default.yml@v3 - with: - include-preview: true - runs-on: ${{ matrix.arch == 'ARM64' && 'macos-26' || 'macos-26-intel' }} - configuration: ${{ matrix.configuration }} - build-switches: -p:SkipSignAssembly=true - restore: true - build: true # required for xunitv3 - download-pattern: build-${{ matrix.configuration }}-${{ matrix.arch }} - - test_qualitygate: - if: ${{ always() }} - name: test-qualitygate - needs: [init, test_linux, test_windows, test_mac] - runs-on: ubuntu-26.04 - steps: - - name: Evaluate test results - shell: bash - env: - RUN_MAC_TESTS: ${{ needs.init.outputs.run-mac-tests }} - TEST_LINUX_RESULT: ${{ needs.test_linux.result }} - TEST_WINDOWS_RESULT: ${{ needs.test_windows.result }} - TEST_MAC_RESULT: ${{ needs.test_mac.result }} - run: | - require_success() { - local job_name="$1" - local job_result="$2" - - if [[ "$job_result" != "success" ]]; then - echo "::error::$job_name finished with '$job_result'." - exit 1 - fi - } - - require_success_or_skip() { - local job_name="$1" - local job_enabled="$2" - local job_result="$3" - - if [[ "$job_enabled" == "true" ]]; then - require_success "$job_name" "$job_result" - return - fi - - if [[ "$job_result" != "success" && "$job_result" != "skipped" ]]; then - echo "::error::$job_name finished with '$job_result' while disabled." - exit 1 - fi - } - - require_success "test_linux" "$TEST_LINUX_RESULT" - require_success "test_windows" "$TEST_WINDOWS_RESULT" - require_success_or_skip "test_mac" "$RUN_MAC_TESTS" "$TEST_MAC_RESULT" - - sonarcloud: - if: ${{always() && needs.init.outputs.run-privileged-jobs == 'true' && needs.build.result == 'success' && needs.test_qualitygate.result == 'success'}} - name: call-sonarcloud - needs: [init, build, test_qualitygate] - uses: codebeltnet/jobs-sonarcloud/.github/workflows/default.yml@v3 - with: - include-preview: true - organization: geekle - projectKey: xunit - version: ${{ needs.build.outputs.version }} - secrets: inherit - - codecov: - if: ${{always() && needs.init.outputs.run-privileged-jobs == 'true' && needs.build.result == 'success' && needs.test_qualitygate.result == 'success'}} - name: call-codecov - needs: [init, build, test_qualitygate] - uses: codebeltnet/jobs-codecov/.github/workflows/default.yml@v1 - with: - repository: codebeltnet/xunit - secrets: inherit - - codeql: - if: ${{always() && needs.init.outputs.run-privileged-jobs == 'true' && needs.build.result == 'success' && needs.test_qualitygate.result == 'success'}} - name: call-codeql - needs: [init, build, test_qualitygate] - uses: codebeltnet/jobs-codeql/.github/workflows/default.yml@v3 - with: - include-preview: true - permissions: - security-events: write - - deploy: - # Avoid skipped optional jobs (for example disabled macOS matrix runs) from suppressing deployment. - if: ${{ always() && github.event_name != 'pull_request' && needs.build.result == 'success' && needs.pack.result == 'success' && needs.test_qualitygate.result == 'success' && needs.sonarcloud.result == 'success' && needs.codecov.result == 'success' && needs.codeql.result == 'success' }} - name: call-nuget - needs: [build, pack, test_qualitygate, sonarcloud, codecov, codeql] - uses: codebeltnet/jobs-nuget-push/.github/workflows/default.yml@v3 - with: - version: ${{ needs.build.outputs.version }} - environment: Production - configuration: ${{ inputs.configuration == '' && 'Release' || inputs.configuration }} - permissions: - contents: write - packages: write - secrets: inherit +name: xUnit Ext. CI Pipeline +on: + pull_request: + branches: [main] + workflow_dispatch: + inputs: + configuration: + type: choice + description: The build configuration to use in the deploy stage. + required: true + default: Release + options: + - Debug + - Release + run_mac_tests: + type: boolean + description: Run the macOS test matrix despite the additional cost and runtime. + default: false + +permissions: + contents: read + +jobs: + init: + name: initialize + runs-on: ubuntu-26.04 + outputs: + run-mac-tests: ${{ steps.vars.outputs.run-mac-tests }} + run-privileged-jobs: ${{ steps.vars.outputs.run-privileged-jobs }} + strong-name-key-filename: ${{ steps.vars.outputs.strong-name-key-filename }} + build-switches: ${{ steps.vars.outputs.build-switches }} + steps: + - id: vars + name: calculate workflow variables + shell: bash + run: | + if [[ "${{ github.event_name }}" == "workflow_dispatch" && "${{ inputs.run_mac_tests }}" == "true" ]]; then + echo "run-mac-tests=true" >> "$GITHUB_OUTPUT" + else + echo "run-mac-tests=false" >> "$GITHUB_OUTPUT" + fi + + if [[ "${{ github.event_name }}" == "pull_request" && "${{ github.event.pull_request.head.repo.full_name }}" != "${{ github.repository }}" ]]; then + echo "run-privileged-jobs=false" >> "$GITHUB_OUTPUT" + echo "strong-name-key-filename=" >> "$GITHUB_OUTPUT" + echo "build-switches=-p:SkipSignAssembly=true" >> "$GITHUB_OUTPUT" + else + echo "run-privileged-jobs=true" >> "$GITHUB_OUTPUT" + echo "strong-name-key-filename=xunit.snk" >> "$GITHUB_OUTPUT" + echo "build-switches=" >> "$GITHUB_OUTPUT" + fi + + build: + name: call-build + needs: [init] + strategy: + matrix: + arch: [X64, ARM64] + configuration: [Debug, Release] + uses: codebeltnet/jobs-dotnet-build/.github/workflows/default.yml@v3 + with: + include-preview: true + configuration: ${{ matrix.configuration }} + strong-name-key-filename: ${{ needs.init.outputs.strong-name-key-filename }} + build-switches: ${{ needs.init.outputs.build-switches }} + runs-on: ${{ matrix.arch == 'ARM64' && 'ubuntu-26.04-arm' || 'ubuntu-26.04' }} + upload-build-artifact-name: build-${{ matrix.configuration }}-${{ matrix.arch }} + secrets: inherit + + pack: + name: call-pack + needs: [build] + strategy: + matrix: + configuration: [Debug, Release] + uses: codebeltnet/jobs-dotnet-pack/.github/workflows/default.yml@v3 + with: + include-preview: true + configuration: ${{ matrix.configuration }} + version: ${{ needs.build.outputs.version }} + download-build-artifact-pattern: build-${{ matrix.configuration }}-X64 + + test_linux: + name: call-test-linux + needs: [build] + strategy: + fail-fast: false + matrix: + configuration: [Debug, Release] + arch: [X64, ARM64] + uses: codebeltnet/jobs-dotnet-test/.github/workflows/default.yml@v3 + with: + include-preview: true + runs-on: ${{ matrix.arch == 'ARM64' && 'ubuntu-26.04-arm' || 'ubuntu-26.04' }} + configuration: ${{ matrix.configuration }} + build-switches: -p:SkipSignAssembly=true + restore: true # we need to restore the packages for the test project + build: true # we need to build for .net48 tests + download-pattern: build-${{ matrix.configuration }}-${{ matrix.arch }} + + test_windows: + name: call-test-windows + needs: [build] + strategy: + fail-fast: false + matrix: + configuration: [Debug, Release] + arch: [X64, ARM64] + uses: codebeltnet/jobs-dotnet-test/.github/workflows/default.yml@v3 + with: + include-preview: true + runs-on: ${{ matrix.arch == 'ARM64' && 'windows-11-arm' || 'windows-2025' }} + configuration: ${{ matrix.configuration }} + build-switches: -p:SkipSignAssembly=true + restore: true # we need to restore the packages for the test project + build: true # we need to build for .net48 tests + download-pattern: build-${{ matrix.configuration }}-${{ matrix.arch }} + + test_mac: + if: ${{ needs.init.outputs.run-mac-tests == 'true' }} + name: call-test-mac + needs: [init, build] + strategy: + fail-fast: false + matrix: + arch: [X64, ARM64] + configuration: [Debug, Release] + uses: codebeltnet/jobs-dotnet-test/.github/workflows/default.yml@v3 + with: + include-preview: true + runs-on: ${{ matrix.arch == 'ARM64' && 'macos-26' || 'macos-26-intel' }} + configuration: ${{ matrix.configuration }} + build-switches: -p:SkipSignAssembly=true + restore: true + build: true # required for xunitv3 + download-pattern: build-${{ matrix.configuration }}-${{ matrix.arch }} + + test_qualitygate: + if: ${{ always() }} + name: test-qualitygate + needs: [init, test_linux, test_windows, test_mac] + runs-on: ubuntu-26.04 + steps: + - name: Evaluate test results + shell: bash + env: + RUN_MAC_TESTS: ${{ needs.init.outputs.run-mac-tests }} + TEST_LINUX_RESULT: ${{ needs.test_linux.result }} + TEST_WINDOWS_RESULT: ${{ needs.test_windows.result }} + TEST_MAC_RESULT: ${{ needs.test_mac.result }} + run: | + require_success() { + local job_name="$1" + local job_result="$2" + + if [[ "$job_result" != "success" ]]; then + echo "::error::$job_name finished with '$job_result'." + exit 1 + fi + } + + require_success_or_skip() { + local job_name="$1" + local job_enabled="$2" + local job_result="$3" + + if [[ "$job_enabled" == "true" ]]; then + require_success "$job_name" "$job_result" + return + fi + + if [[ "$job_result" != "success" && "$job_result" != "skipped" ]]; then + echo "::error::$job_name finished with '$job_result' while disabled." + exit 1 + fi + } + + require_success "test_linux" "$TEST_LINUX_RESULT" + require_success "test_windows" "$TEST_WINDOWS_RESULT" + require_success_or_skip "test_mac" "$RUN_MAC_TESTS" "$TEST_MAC_RESULT" + + sonarcloud: + if: ${{always() && needs.init.outputs.run-privileged-jobs == 'true' && needs.build.result == 'success' && needs.test_qualitygate.result == 'success'}} + name: call-sonarcloud + needs: [init, build, test_qualitygate] + uses: codebeltnet/jobs-sonarcloud/.github/workflows/default.yml@v3 + with: + include-preview: true + organization: geekle + projectKey: xunit + version: ${{ needs.build.outputs.version }} + secrets: inherit + + codecov: + if: ${{always() && needs.init.outputs.run-privileged-jobs == 'true' && needs.build.result == 'success' && needs.test_qualitygate.result == 'success'}} + name: call-codecov + needs: [init, build, test_qualitygate] + uses: codebeltnet/jobs-codecov/.github/workflows/default.yml@v1 + with: + repository: codebeltnet/xunit + secrets: inherit + + codeql: + if: ${{always() && needs.init.outputs.run-privileged-jobs == 'true' && needs.build.result == 'success' && needs.test_qualitygate.result == 'success'}} + name: call-codeql + needs: [init, build, test_qualitygate] + uses: codebeltnet/jobs-codeql/.github/workflows/default.yml@v3 + with: + include-preview: true + permissions: + security-events: write + + deploy: + # Avoid skipped optional jobs (for example disabled macOS matrix runs) from suppressing deployment. + if: ${{ always() && github.event_name != 'pull_request' && needs.build.result == 'success' && needs.pack.result == 'success' && needs.test_qualitygate.result == 'success' && needs.sonarcloud.result == 'success' && needs.codecov.result == 'success' && needs.codeql.result == 'success' }} + name: call-nuget + needs: [build, pack, test_qualitygate, sonarcloud, codecov, codeql] + uses: codebeltnet/jobs-nuget-push/.github/workflows/default.yml@v3 + with: + version: ${{ needs.build.outputs.version }} + environment: Production + configuration: ${{ inputs.configuration == '' && 'Release' || inputs.configuration }} + permissions: + contents: write + packages: write + secrets: inherit diff --git a/.gitignore b/.gitignore index 82abcd3..3ec01c7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,378 +1,378 @@ -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. -## -## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore - -# User-specific files -*.rsuser -*.suo -*.user -*.userosscache -*.sln.docstates - -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs - -# Mono auto generated files -mono_crash.* - -# Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -x64/ -x86/ -[Ww][Ii][Nn]32/ -[Aa][Rr][Mm]/ -[Aa][Rr][Mm]64/ -bld/ -[Bb]in/ -[Oo]bj/ -[Oo]ut/ -[Ll]og/ -[Ll]ogs/ - -# Visual Studio 2015/2017 cache/options directory -.vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot -#wwwroot/ - -# Visual Studio 2017 auto generated files -Generated\ Files/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -# NUnit -*.VisualState.xml -TestResult.xml -nunit-*.xml - -# Build Results of an ATL Project -[Dd]ebugPS/ -[Rr]eleasePS/ -dlldata.c - -# Benchmark Results -BenchmarkDotNet.Artifacts/ - -# .NET Core -project.lock.json -project.fragment.lock.json -artifacts/ - -# ASP.NET Scaffolding -ScaffoldingReadMe.txt - -# StyleCop -StyleCopReport.xml - -# Files built by Visual Studio -*_i.c -*_p.c -*_h.h -*.ilk -*.meta -*.obj -*.iobj -*.pch -*.pdb -*.ipdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*_wpftmp.csproj -*.log -*.vspscc -*.vssscc -.builds -*.pidb -*.svclog -*.scc - -# Chutzpah Test files -_Chutzpah* - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opendb -*.opensdf -*.sdf -*.cachefile -*.VC.db -*.VC.VC.opendb - -# Visual Studio profiler -*.psess -*.vsp -*.vspx -*.sap - -# Visual Studio Trace Files -*.e2e - -# TFS 2012 Local Workspace -$tf/ - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# AxoCover is a Code Coverage Tool -.axoCover/* -!.axoCover/settings.json - -# Coverlet is a free, cross platform Code Coverage Tool -coverage*.json -coverage*.xml -coverage*.info - -# Visual Studio code coverage results -*.coverage -*.coveragexml - -# NCrunch -_NCrunch_* -.*crunch*.local.xml -nCrunchTemp_* - -# MightyMoose -*.mm.* -AutoTest.Net/ - -# Web workbench (sass) -.sass-cache/ - -# 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 -*.[Pp]ublish.xml -*.azurePubxml -# Note: Comment the next line if you want to checkin your web deploy settings, -# but database connection strings (with potential passwords) will be unencrypted -*.pubxml -*.publishproj - -# Microsoft Azure Web App publish settings. Comment the next line if you want to -# checkin your Azure Web App publish settings, but sensitive information contained -# in these scripts will be unencrypted -PublishScripts/ - -# NuGet Packages -*.nupkg -# NuGet Symbol Packages -*.snupkg -# The packages folder can be ignored because of Package Restore -**/[Pp]ackages/* -# except build/, which is used as an MSBuild target. -!**/[Pp]ackages/build/ -# Uncomment if necessary however generally it will be regenerated when needed -#!**/[Pp]ackages/repositories.config -# NuGet v3's project.json files produces more ignorable files -*.nuget.props -*.nuget.targets - -# Microsoft Azure Build Output -csx/ -*.build.csdef - -# Microsoft Azure Emulator -ecf/ -rcf/ - -# Windows Store app package directories and files -AppPackages/ -BundleArtifacts/ -Package.StoreAssociation.xml -_pkginfo.txt -*.appx -*.appxbundle -*.appxupload - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!?*.[Cc]ache/ - -# Others -ClientBin/ -~$* -*~ -*.dbmdl -*.dbproj.schemaview -*.jfm -*.pfx -*.publishsettings -orleans.codegen.cs - -# Including strong name files can present a security risk -# (https://github.com/github/gitignore/pull/2483#issue-259490424) -#*.snk - -# Since there are multiple workflows, uncomment next line to ignore bower_components -# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) -#bower_components/ - -# 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 -ServiceFabricBackup/ -*.rptproj.bak - -# SQL Server files -*.mdf -*.ldf -*.ndf - -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings -*.rptproj.rsuser -*- [Bb]ackup.rdl -*- [Bb]ackup ([0-9]).rdl -*- [Bb]ackup ([0-9][0-9]).rdl - -# Microsoft Fakes -FakesAssemblies/ - -# GhostDoc plugin setting file -*.GhostDoc.xml - -# Node.js Tools for Visual Studio -.ntvs_analysis.dat -node_modules/ - -# Visual Studio 6 build log -*.plg - -# Visual Studio 6 workspace options file -*.opt - -# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) -*.vbw - -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions - -# Paket dependency manager -.paket/paket.exe -paket-files/ - -# FAKE - F# Make -.fake/ - -# CodeRush personal settings -.cr/personal - -# Python Tools for Visual Studio (PTVS) -__pycache__/ -*.pyc - -# Cake - Uncomment if you are using it -# tools/** -# !tools/packages.config - -# Tabs Studio -*.tss - -# Telerik's JustMock configuration file -*.jmconfig - -# BizTalk build output -*.btp.cs -*.btm.cs -*.odx.cs -*.xsd.cs - -# OpenCover UI analysis results -OpenCover/ - -# Azure Stream Analytics local run output -ASALocalRun/ - -# MSBuild Binary and Structured Log -*.binlog - -# NVidia Nsight GPU debugger configuration file -*.nvuser - -# MFractors (Xamarin productivity tool) working folder -.mfractor/ - -# Local History for Visual Studio -.localhistory/ - -# BeatPulse healthcheck temp database -healthchecksdb - -# Backup folder for Package Reference Convert tool in Visual Studio 2017 -MigrationBackup/ - -# Ionide (cross platform F# VS Code tools) working folder -.ionide/ - -# Fody - auto-generated XML schema -FodyWeavers.xsd - -# DocFx -/.docfx/wwwroot -/.docfx/api/**/*.yml -/.docfx/**/*.manifest -/.docfx/.vscode/docfx-assistant - -### VisualStudioCode ### -.vscode/* -*.code-workspace - -# Strong-Name Key -*.snk - -.bot/ \ No newline at end of file +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Oo]ut/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# 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 +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# 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 +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# DocFx +/.docfx/wwwroot +/.docfx/api/**/*.yml +/.docfx/**/*.manifest +/.docfx/.vscode/docfx-assistant + +### VisualStudioCode ### +.vscode/* +*.code-workspace + +# Strong-Name Key +*.snk + +.bot/ diff --git a/.nuget/Codebelt.Extensions.Xunit.App/PackageReleaseNotes.txt b/.nuget/Codebelt.Extensions.Xunit.App/PackageReleaseNotes.txt index ad94394..a2a08a5 100644 --- a/.nuget/Codebelt.Extensions.Xunit.App/PackageReleaseNotes.txt +++ b/.nuget/Codebelt.Extensions.Xunit.App/PackageReleaseNotes.txt @@ -1,248 +1,248 @@ -Version: 13.0.0 -Availability: .NET 11 and .NET 10 - -# ALM -- CHANGED Target frameworks by adding .NET 11 (net11.0) and removing .NET 9 (net9.0), so the package now targets .NET 11 and .NET 10 - +Version: 13.0.0 +Availability: .NET 11 and .NET 10 + +# ALM +- CHANGED Target frameworks by adding .NET 11 (net11.0) and removing .NET 9 (net9.0), so the package now targets .NET 11 and .NET 10 + Version: 12.0.2 -Availability: .NET 10 and .NET 9 +Availability: .NET 10 and .NET 9 # ALM - CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) Version: 12.0.1 -Availability: .NET 10 and .NET 9 - -# ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) - -Version: 12.0.0 -Availability: .NET 10 and .NET 9 - -# ALM -- CHANGED xunit framework suite from v3.2.2 to v4.0.0 and upgraded supporting dependencies: Codebelt.Bootstrapper packages to v5.2.1, Codebelt.Extensions.BenchmarkDotNet.Console to v1.3.3, and Microsoft.NET.Test.Sdk to v18.9.0 - -Version: 11.2.1 -Availability: .NET 10 and .NET 9 - -# ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) - -Version: 11.2.0 -Availability: .NET 10 and .NET 9 - -# ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) - -Version: 11.1.2 -Availability: .NET 10 and .NET 9 - -# ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) - -Version: 11.1.1 -Availability: .NET 10 and .NET 9 - -# ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) - -Version: 11.1.0 -Availability: .NET 10 and .NET 9 - -# ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) - -Version: 11.0.10 -Availability: .NET 10 and .NET 9 - -# ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) - -Version: 11.0.10 -Availability: .NET 10 and .NET 9 - -# ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) - -Version: 11.0.9 -Availability: .NET 10 and .NET 9 - -# ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) - -Version: 11.0.8 -Availability: .NET 10 and .NET 9 - -# ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) -- CHANGED Release notes generation workflow to improve consistency and formatting of package documentation -- CHANGED PackageReleaseNotes retrieval in Directory.Build.targets to use simplified System.IO.File.ReadAllText approach - -Version: 11.0.7 -Availability: .NET 10 and .NET 9 - -# ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) - -Version: 11.0.6 -Availability: .NET 10 and .NET 9 - -# ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) - -Version: 11.0.5 -Availability: .NET 10 and .NET 9 - -# ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) - -Version: 11.0.4 -Availability: .NET 10 and .NET 9 - -# ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) - -Version: 11.0.3 -Availability: .NET 10 and .NET 9 - -# ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) - -Version: 11.0.2 -Availability: .NET 10 and .NET 9 - -# ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) - -Version: 11.0.1 -Availability: .NET 10 and .NET 9 - -# ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) - -Version: 11.0.0 -Availability: .NET 10 and .NET 9 - -# ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) - -Version: 10.0.7 -Availability: .NET 9 and .NET 8 - -# ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) - -Version: 10.0.6 -Availability: .NET 9 and .NET 8 - -# ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) - -Version: 10.0.5 -Availability: .NET 9 and .NET 8 - -# ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) - -Version: 10.0.4 -Availability: .NET 9 and .NET 8 - -# ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) - -Version: 10.0.3 -Availability: .NET 9 and .NET 8 - -# ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) - -Version: 10.0.2 -Availability: .NET 9 and .NET 8 - -# ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) - -Version: 10.0.1 -Availability: .NET 9 and .NET 8 - -# ALM -- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) - -Version: 10.0.0 -Availability: .NET 9 and .NET 8 - -# ALM -- CHANGED Dependencies to latest and greatest with respect to TFMs - -Version: 9.1.3 -Availability: .NET 9 and .NET 8 - -# ALM -- CHANGED Dependencies to latest and greatest with respect to TFMs - -Version: 9.1.2 -Availability: .NET 9 and .NET 8 - -# ALM -- CHANGED Dependencies to latest and greatest with respect to TFMs - -Version: 9.1.1 -Availability: .NET 9 and .NET 8 - -# ALM -- CHANGED Dependencies to latest and greatest with respect to TFMs - -Version: 9.1.0 -Availability: .NET 9 and .NET 8 - -# ALM -- CHANGED Dependencies to latest and greatest with respect to TFMs - -Version: 9.0.1 -Availability: .NET 9 and .NET 8 - -# ALM -- CHANGED Dependencies to latest and greatest with respect to TFMs - -Version: 9.0.0 -Availability: .NET 9 and .NET 8 - -# ALM -- CHANGED Dependencies to latest and greatest with respect to TFMs -- REMOVED Support for TFM .NET 6 (LTS) - -Version: 8.4.0 -Availability: .NET 8 and .NET 6 - -# ALM -- CHANGED Dependencies to latest and greatest with respect to TFMs - -Version: 8.3.2 -Availability: .NET 8 and .NET 6 - -# ALM -- REMOVED Support for TFM .NET 7 (STS) - -Version: 8.2.0 -Availability: .NET 8, .NET 7 and .NET 6 - -# ALM -- CHANGED Dependencies to latest and greatest with respect to TFMs - -Version: 8.1.0 -Availability: .NET 8, .NET 7 and .NET 6 - -# ALM -- CHANGED Dependencies to latest and greatest with respect to TFMs - -Version: 8.0.1 -Availability: .NET 8, .NET 7 and .NET 6 - -# ALM -- CHANGED Dependencies to latest and greatest with respect to TFMs - -Version: 8.0.0 -Availability: .NET 8, .NET 7 and .NET 6 - -# ALM -- CHANGED Dependencies to latest and greatest with respect to TFMs +Availability: .NET 10 and .NET 9 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + +Version: 12.0.0 +Availability: .NET 10 and .NET 9 + +# ALM +- CHANGED xunit framework suite from v3.2.2 to v4.0.0 and upgraded supporting dependencies: Codebelt.Bootstrapper packages to v5.2.1, Codebelt.Extensions.BenchmarkDotNet.Console to v1.3.3, and Microsoft.NET.Test.Sdk to v18.9.0 + +Version: 11.2.1 +Availability: .NET 10 and .NET 9 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + +Version: 11.2.0 +Availability: .NET 10 and .NET 9 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + +Version: 11.1.2 +Availability: .NET 10 and .NET 9 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + +Version: 11.1.1 +Availability: .NET 10 and .NET 9 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + +Version: 11.1.0 +Availability: .NET 10 and .NET 9 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + +Version: 11.0.10 +Availability: .NET 10 and .NET 9 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + +Version: 11.0.10 +Availability: .NET 10 and .NET 9 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + +Version: 11.0.9 +Availability: .NET 10 and .NET 9 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + +Version: 11.0.8 +Availability: .NET 10 and .NET 9 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) +- CHANGED Release notes generation workflow to improve consistency and formatting of package documentation +- CHANGED PackageReleaseNotes retrieval in Directory.Build.targets to use simplified System.IO.File.ReadAllText approach + +Version: 11.0.7 +Availability: .NET 10 and .NET 9 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + +Version: 11.0.6 +Availability: .NET 10 and .NET 9 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + +Version: 11.0.5 +Availability: .NET 10 and .NET 9 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + +Version: 11.0.4 +Availability: .NET 10 and .NET 9 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + +Version: 11.0.3 +Availability: .NET 10 and .NET 9 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + +Version: 11.0.2 +Availability: .NET 10 and .NET 9 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + +Version: 11.0.1 +Availability: .NET 10 and .NET 9 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + +Version: 11.0.0 +Availability: .NET 10 and .NET 9 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + +Version: 10.0.7 +Availability: .NET 9 and .NET 8 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + +Version: 10.0.6 +Availability: .NET 9 and .NET 8 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + +Version: 10.0.5 +Availability: .NET 9 and .NET 8 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + +Version: 10.0.4 +Availability: .NET 9 and .NET 8 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + +Version: 10.0.3 +Availability: .NET 9 and .NET 8 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + +Version: 10.0.2 +Availability: .NET 9 and .NET 8 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + +Version: 10.0.1 +Availability: .NET 9 and .NET 8 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + +Version: 10.0.0 +Availability: .NET 9 and .NET 8 + +# ALM +- CHANGED Dependencies to latest and greatest with respect to TFMs + +Version: 9.1.3 +Availability: .NET 9 and .NET 8 + +# ALM +- CHANGED Dependencies to latest and greatest with respect to TFMs + +Version: 9.1.2 +Availability: .NET 9 and .NET 8 + +# ALM +- CHANGED Dependencies to latest and greatest with respect to TFMs + +Version: 9.1.1 +Availability: .NET 9 and .NET 8 + +# ALM +- CHANGED Dependencies to latest and greatest with respect to TFMs + +Version: 9.1.0 +Availability: .NET 9 and .NET 8 + +# ALM +- CHANGED Dependencies to latest and greatest with respect to TFMs + +Version: 9.0.1 +Availability: .NET 9 and .NET 8 + +# ALM +- CHANGED Dependencies to latest and greatest with respect to TFMs + +Version: 9.0.0 +Availability: .NET 9 and .NET 8 + +# ALM +- CHANGED Dependencies to latest and greatest with respect to TFMs +- REMOVED Support for TFM .NET 6 (LTS) + +Version: 8.4.0 +Availability: .NET 8 and .NET 6 + +# ALM +- CHANGED Dependencies to latest and greatest with respect to TFMs + +Version: 8.3.2 +Availability: .NET 8 and .NET 6 + +# ALM +- REMOVED Support for TFM .NET 7 (STS) + +Version: 8.2.0 +Availability: .NET 8, .NET 7 and .NET 6 + +# ALM +- CHANGED Dependencies to latest and greatest with respect to TFMs + +Version: 8.1.0 +Availability: .NET 8, .NET 7 and .NET 6 + +# ALM +- CHANGED Dependencies to latest and greatest with respect to TFMs + +Version: 8.0.1 +Availability: .NET 8, .NET 7 and .NET 6 + +# ALM +- CHANGED Dependencies to latest and greatest with respect to TFMs + +Version: 8.0.0 +Availability: .NET 8, .NET 7 and .NET 6 + +# ALM +- CHANGED Dependencies to latest and greatest with respect to TFMs diff --git a/.nuget/Codebelt.Extensions.Xunit.App/README.md b/.nuget/Codebelt.Extensions.Xunit.App/README.md index f42f222..25b033a 100644 --- a/.nuget/Codebelt.Extensions.Xunit.App/README.md +++ b/.nuget/Codebelt.Extensions.Xunit.App/README.md @@ -1,29 +1,29 @@ -## About - -An open-source project (MIT license) that targets and complements the [xUnit.net](https://xunit.net/) test platform. It provides a uniform and convenient way of doing unit test for all project types in .NET. - -Your versatile xUnit companion for: -- Modern development with `.NET 10` and `.NET 11`, -- Cross-platform libraries with `.NET Standard 2` (where applicable), -- Legacy applications on `.NET Framework 4.6.2` and newer. - -It is, by heart, free, flexible and built to extend and boost your agile codebelt. - -## **Codebelt.Extensions.Xunit.App** for .NET - -`Codebelt.Extensions.Xunit.App`, aka `Extensions for xUnit API by Codebelt`, is a toolbelt of assemblies designed to extend upon [xUnit.net](https://xunit.net/) for more advanced unit test scenarios while also being the preferred unit test platform of [Cuemon for .NET](https://github.com/codebeltnet/cuemon). - -More documentation available at our documentation site: - -- [Codebelt.Extensions.Xunit](https://xunit.codebelt.net/api/Codebelt.Extensions.Xunit.html) 🔗 -- [Codebelt.Extensions.Xunit.Hosting](https://xunit.codebelt.net/api/Codebelt.Extensions.Xunit.Hosting.html) 🔗 -- [Codebelt.Extensions.Xunit.Hosting.AspNetCore](https://xunit.codebelt.net/api/Codebelt.Extensions.Xunit.Hosting.AspNetCore.html) 🔗 -- [Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http](https://xunit.codebelt.net/api/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http.html) 🔗 -- [Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http.Features](https://xunit.codebelt.net/api/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http.Features.html) 🔗 - -## Related Packages - -* [Codebelt.Extensions.Xunit](https://www.nuget.org/packages/Codebelt.Extensions.Xunit/) 📦 -* [Codebelt.Extensions.Xunit.App](https://www.nuget.org/packages/Codebelt.Extensions.Xunit.App/) 🏭 -* [Codebelt.Extensions.Xunit.Hosting](https://www.nuget.org/packages/Codebelt.Extensions.Xunit.Hosting/) 📦 -* [Codebelt.Extensions.Xunit.Hosting.AspNetCore](https://www.nuget.org/packages/Codebelt.Extensions.Xunit.Hosting.AspNetCore/) 📦 +## About + +An open-source project (MIT license) that targets and complements the [xUnit.net](https://xunit.net/) test platform. It provides a uniform and convenient way of doing unit test for all project types in .NET. + +Your versatile xUnit companion for: +- Modern development with `.NET 10` and `.NET 11`, +- Cross-platform libraries with `.NET Standard 2` (where applicable), +- Legacy applications on `.NET Framework 4.6.2` and newer. + +It is, by heart, free, flexible and built to extend and boost your agile codebelt. + +## **Codebelt.Extensions.Xunit.App** for .NET + +`Codebelt.Extensions.Xunit.App`, aka `Extensions for xUnit API by Codebelt`, is a toolbelt of assemblies designed to extend upon [xUnit.net](https://xunit.net/) for more advanced unit test scenarios while also being the preferred unit test platform of [Cuemon for .NET](https://github.com/codebeltnet/cuemon). + +More documentation available at our documentation site: + +- [Codebelt.Extensions.Xunit](https://xunit.codebelt.net/api/Codebelt.Extensions.Xunit.html) 🔗 +- [Codebelt.Extensions.Xunit.Hosting](https://xunit.codebelt.net/api/Codebelt.Extensions.Xunit.Hosting.html) 🔗 +- [Codebelt.Extensions.Xunit.Hosting.AspNetCore](https://xunit.codebelt.net/api/Codebelt.Extensions.Xunit.Hosting.AspNetCore.html) 🔗 +- [Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http](https://xunit.codebelt.net/api/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http.html) 🔗 +- [Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http.Features](https://xunit.codebelt.net/api/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http.Features.html) 🔗 + +## Related Packages + +* [Codebelt.Extensions.Xunit](https://www.nuget.org/packages/Codebelt.Extensions.Xunit/) 📦 +* [Codebelt.Extensions.Xunit.App](https://www.nuget.org/packages/Codebelt.Extensions.Xunit.App/) 🏭 +* [Codebelt.Extensions.Xunit.Hosting](https://www.nuget.org/packages/Codebelt.Extensions.Xunit.Hosting/) 📦 +* [Codebelt.Extensions.Xunit.Hosting.AspNetCore](https://www.nuget.org/packages/Codebelt.Extensions.Xunit.Hosting.AspNetCore/) 📦 diff --git a/.nuget/Codebelt.Extensions.Xunit.Hosting.AspNetCore/README.md b/.nuget/Codebelt.Extensions.Xunit.Hosting.AspNetCore/README.md index 1d3506c..6d8edcf 100644 --- a/.nuget/Codebelt.Extensions.Xunit.Hosting.AspNetCore/README.md +++ b/.nuget/Codebelt.Extensions.Xunit.Hosting.AspNetCore/README.md @@ -1,69 +1,69 @@ -## About - -An open-source project (MIT license) that targets and complements the [xUnit.net](https://xunit.net/) test platform. It provides a uniform and convenient way of doing unit test for all project types in .NET. - -Your versatile xUnit companion for: -- Modern development with `.NET 10` and `.NET 11`, -- Cross-platform libraries with `.NET Standard 2` (where applicable), -- Legacy applications on `.NET Framework 4.6.2` and newer. - -It is, by heart, free, flexible and built to extend and boost your agile codebelt. - -## **Codebelt.Extensions.Xunit.Hosting.AspNetCore** for .NET - -The `Codebelt.Extensions.Xunit.Hosting.AspNetCore` namespace contains types that provides a uniform way of doing unit testing that depends on ASP.NET Core and used in conjunction with Microsoft Dependency Injection. The namespace relates to the `Microsoft.AspNetCore.TestHost` namespace. - +## About + +An open-source project (MIT license) that targets and complements the [xUnit.net](https://xunit.net/) test platform. It provides a uniform and convenient way of doing unit test for all project types in .NET. + +Your versatile xUnit companion for: +- Modern development with `.NET 10` and `.NET 11`, +- Cross-platform libraries with `.NET Standard 2` (where applicable), +- Legacy applications on `.NET Framework 4.6.2` and newer. + +It is, by heart, free, flexible and built to extend and boost your agile codebelt. + +## **Codebelt.Extensions.Xunit.Hosting.AspNetCore** for .NET + +The `Codebelt.Extensions.Xunit.Hosting.AspNetCore` namespace contains types that provides a uniform way of doing unit testing that depends on ASP.NET Core and used in conjunction with Microsoft Dependency Injection. The namespace relates to the `Microsoft.AspNetCore.TestHost` namespace. + `WebApplicationTestFactory.Create` is a lightweight alternative for focused integration tests that prefer inline `IWebHostBuilder` customization and Codebelt's common `IHostTest` model. It is not a drop-in replacement for [WebApplicationFactory](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.testing.webapplicationfactory-1): use Microsoft's factory when reusable derived factories, `CreateClient` options, `WithWebHostBuilder`, or MVC content-root conventions are central to the test suite. `WebApplicationTestFactory` uses `ManagedWebApplicationFixture` by default, so the application's `Main` method owns startup and the deferred `TestServer` starts when the test host is consumed. Use `ManagedWebApplicationFixture` explicitly with `WebApplicationTest` when tests share the application context. For lower-level web-host tests that require synchronous startup, use the separate `BlockingManagedWebHostFixture`. - -More documentation available at our documentation site: - -- [Codebelt.Extensions.Xunit.Hosting.AspNetCore](https://xunit.codebelt.net/api/Codebelt.Extensions.Xunit.Hosting.AspNetCore.html) 🔗 -- [Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http](https://xunit.codebelt.net/api/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http.html) 🔗 -- [Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http.Features](https://xunit.codebelt.net/api/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http.Features.html) 🔗 - -## Related Packages - -* [Codebelt.Extensions.Xunit](https://www.nuget.org/packages/Codebelt.Extensions.Xunit/) 📦 -* [Codebelt.Extensions.Xunit.App](https://www.nuget.org/packages/Codebelt.Extensions.Xunit.App/) 🏭 -* [Codebelt.Extensions.Xunit.Hosting](https://www.nuget.org/packages/Codebelt.Extensions.Xunit.Hosting/) 📦 -* [Codebelt.Extensions.Xunit.Hosting.AspNetCore](https://www.nuget.org/packages/Codebelt.Extensions.Xunit.Hosting.AspNetCore/) 📦 - -### CSharp Example - -Source: [ServerTimingMiddlewareTest.cs](https://github.com/codebeltnet/cuemon/blob/main/test/Cuemon.AspNetCore.Tests/Diagnostics/ServerTimingMiddlewareTest.cs) - -```csharp -[Fact] -public async Task InvokeAsync_ShouldMimicSimpleAspNetProject() -{ - using var response = await WebHostTestFactory.RunAsync( - services => - { - services.AddServerTiming(o => o.SuppressHeaderPredicate = _ => false); - } - , app => - { - app.UseServerTiming(); - app.Use(async (context, next) => - { - var sw = Stopwatch.StartNew(); - context.Response.OnStarting(() => - { - sw.Stop(); - context.RequestServices.GetRequiredService().AddServerTiming("use-middleware", sw.Elapsed); - return Task.CompletedTask; - }); - await next(context).ConfigureAwait(false); - }); - app.Run(context => - { - Thread.Sleep(400); - return context.Response.WriteAsync("Hello World!"); - }); - }).ConfigureAwait(false); - - Assert.StartsWith("use-middleware;dur=", response.Headers.Single(kvp => kvp.Key == ServerTiming.HeaderName).Value.FirstOrDefault()); -} -``` + +More documentation available at our documentation site: + +- [Codebelt.Extensions.Xunit.Hosting.AspNetCore](https://xunit.codebelt.net/api/Codebelt.Extensions.Xunit.Hosting.AspNetCore.html) 🔗 +- [Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http](https://xunit.codebelt.net/api/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http.html) 🔗 +- [Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http.Features](https://xunit.codebelt.net/api/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Http.Features.html) 🔗 + +## Related Packages + +* [Codebelt.Extensions.Xunit](https://www.nuget.org/packages/Codebelt.Extensions.Xunit/) 📦 +* [Codebelt.Extensions.Xunit.App](https://www.nuget.org/packages/Codebelt.Extensions.Xunit.App/) 🏭 +* [Codebelt.Extensions.Xunit.Hosting](https://www.nuget.org/packages/Codebelt.Extensions.Xunit.Hosting/) 📦 +* [Codebelt.Extensions.Xunit.Hosting.AspNetCore](https://www.nuget.org/packages/Codebelt.Extensions.Xunit.Hosting.AspNetCore/) 📦 + +### CSharp Example + +Source: [ServerTimingMiddlewareTest.cs](https://github.com/codebeltnet/cuemon/blob/main/test/Cuemon.AspNetCore.Tests/Diagnostics/ServerTimingMiddlewareTest.cs) + +```csharp +[Fact] +public async Task InvokeAsync_ShouldMimicSimpleAspNetProject() +{ + using var response = await WebHostTestFactory.RunAsync( + services => + { + services.AddServerTiming(o => o.SuppressHeaderPredicate = _ => false); + } + , app => + { + app.UseServerTiming(); + app.Use(async (context, next) => + { + var sw = Stopwatch.StartNew(); + context.Response.OnStarting(() => + { + sw.Stop(); + context.RequestServices.GetRequiredService().AddServerTiming("use-middleware", sw.Elapsed); + return Task.CompletedTask; + }); + await next(context).ConfigureAwait(false); + }); + app.Run(context => + { + Thread.Sleep(400); + return context.Response.WriteAsync("Hello World!"); + }); + }).ConfigureAwait(false); + + Assert.StartsWith("use-middleware;dur=", response.Headers.Single(kvp => kvp.Key == ServerTiming.HeaderName).Value.FirstOrDefault()); +} +``` diff --git a/.nuget/Codebelt.Extensions.Xunit.Hosting/README.md b/.nuget/Codebelt.Extensions.Xunit.Hosting/README.md index 43e009f..a41937b 100644 --- a/.nuget/Codebelt.Extensions.Xunit.Hosting/README.md +++ b/.nuget/Codebelt.Extensions.Xunit.Hosting/README.md @@ -1,52 +1,52 @@ -## About - -An open-source project (MIT license) that targets and complements the [xUnit.net](https://xunit.net/) test platform. It provides a uniform and convenient way of doing unit test for all project types in .NET. - -Your versatile xUnit companion for: -- Modern development with `.NET 10` and `.NET 11`, -- Cross-platform libraries with `.NET Standard 2` (where applicable), -- Legacy applications on `.NET Framework 4.6.2` and newer. - -It is, by heart, free, flexible and built to extend and boost your agile codebelt. - -## **Codebelt.Extensions.Xunit.Hosting** for .NET - +## About + +An open-source project (MIT license) that targets and complements the [xUnit.net](https://xunit.net/) test platform. It provides a uniform and convenient way of doing unit test for all project types in .NET. + +Your versatile xUnit companion for: +- Modern development with `.NET 10` and `.NET 11`, +- Cross-platform libraries with `.NET Standard 2` (where applicable), +- Legacy applications on `.NET Framework 4.6.2` and newer. + +It is, by heart, free, flexible and built to extend and boost your agile codebelt. + +## **Codebelt.Extensions.Xunit.Hosting** for .NET + The `Codebelt.Extensions.Xunit.Hosting` namespace contains types that provides a uniform way of doing unit testing that is used in conjunction with Microsoft Dependency Injection. The namespace relates to the `Xunit.Abstractions` namespace. Use `ApplicationTestFactory.Create` for a focused integration test against an existing console, worker or Generic Host application's `Program` assembly. It brings the entry-point testing pattern commonly associated with ASP.NET Core to the rest of the .NET application stack. The factory uses `ManagedApplicationFixture` by default, so the application's `Main` method owns startup and the deferred host starts when the test host is consumed. Use `ManagedApplicationFixture` explicitly with `ApplicationTest` when tests share the application context. - -More documentation available at our documentation site: - -- [Codebelt.Extensions.Xunit.Hosting](https://xunit.codebelt.net/api/Codebelt.Extensions.Xunit.Hosting.html) 🔗 - -## Related Packages - -* [Codebelt.Extensions.Xunit](https://www.nuget.org/packages/Codebelt.Extensions.Xunit/) 📦 -* [Codebelt.Extensions.Xunit.App](https://www.nuget.org/packages/Codebelt.Extensions.Xunit.App/) 🏭 -* [Codebelt.Extensions.Xunit.Hosting](https://www.nuget.org/packages/Codebelt.Extensions.Xunit.Hosting/) 📦 -* [Codebelt.Extensions.Xunit.Hosting.AspNetCore](https://www.nuget.org/packages/Codebelt.Extensions.Xunit.Hosting.AspNetCore/) 📦 - -### CSharp Example - -```csharp -public class HostTestTest : HostTest -{ - private readonly IServiceProvider _provider; - - public HostTestTest(ManagedHostFixture hostFixture, ITestOutputHelper output) : base(hostFixture, output) - { - _provider = hostFixture.Host?.Services; - _provider.GetRequiredService().TestOutput = output; - } - - // intentionally left out for brevity - - public override void ConfigureServices(IServiceCollection services) - { - services.AddXunitTestLoggingOutputHelperAccessor(); - services.AddXunitTestLogging(TestOutput); - } -} -``` - -A similar but real life example can be found here: [WebHostTestTest.cs](https://github.com/codebeltnet/xunit/blob/main/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/WebHostTestTest.cs) + +More documentation available at our documentation site: + +- [Codebelt.Extensions.Xunit.Hosting](https://xunit.codebelt.net/api/Codebelt.Extensions.Xunit.Hosting.html) 🔗 + +## Related Packages + +* [Codebelt.Extensions.Xunit](https://www.nuget.org/packages/Codebelt.Extensions.Xunit/) 📦 +* [Codebelt.Extensions.Xunit.App](https://www.nuget.org/packages/Codebelt.Extensions.Xunit.App/) 🏭 +* [Codebelt.Extensions.Xunit.Hosting](https://www.nuget.org/packages/Codebelt.Extensions.Xunit.Hosting/) 📦 +* [Codebelt.Extensions.Xunit.Hosting.AspNetCore](https://www.nuget.org/packages/Codebelt.Extensions.Xunit.Hosting.AspNetCore/) 📦 + +### CSharp Example + +```csharp +public class HostTestTest : HostTest +{ + private readonly IServiceProvider _provider; + + public HostTestTest(ManagedHostFixture hostFixture, ITestOutputHelper output) : base(hostFixture, output) + { + _provider = hostFixture.Host?.Services; + _provider.GetRequiredService().TestOutput = output; + } + + // intentionally left out for brevity + + public override void ConfigureServices(IServiceCollection services) + { + services.AddXunitTestLoggingOutputHelperAccessor(); + services.AddXunitTestLogging(TestOutput); + } +} +``` + +A similar but real life example can be found here: [WebHostTestTest.cs](https://github.com/codebeltnet/xunit/blob/main/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/WebHostTestTest.cs) diff --git a/.nuget/Codebelt.Extensions.Xunit/README.md b/.nuget/Codebelt.Extensions.Xunit/README.md index a016c47..35a3530 100644 --- a/.nuget/Codebelt.Extensions.Xunit/README.md +++ b/.nuget/Codebelt.Extensions.Xunit/README.md @@ -1,45 +1,45 @@ -## About - -An open-source project (MIT license) that targets and complements the [xUnit.net](https://xunit.net/) test platform. It provides a uniform and convenient way of doing unit test for all project types in .NET. - -Your versatile xUnit companion for: -- Modern development with `.NET 10` and `.NET 11`, -- Cross-platform libraries with `.NET Standard 2` (where applicable), -- Legacy applications on `.NET Framework 4.6.2` and newer. - -It is, by heart, free, flexible and built to extend and boost your agile codebelt. - -## **Codebelt.Extensions.Xunit** for .NET - -The `Codebelt.Extensions.Xunit` namespace contains types that provides a uniform way of doing unit testing. The namespace relates to the `Xunit.Abstractions` namespace. - -More documentation available at our documentation site: - -- [Codebelt.Extensions.Xunit](https://xunit.codebelt.net/api/Codebelt.Extensions.Xunit.html) 🔗 - -## Related Packages - -* [Codebelt.Extensions.Xunit](https://www.nuget.org/packages/Codebelt.Extensions.Xunit/) 📦 -* [Codebelt.Extensions.Xunit.App](https://www.nuget.org/packages/Codebelt.Extensions.Xunit.App/) 🏭 -* [Codebelt.Extensions.Xunit.Hosting](https://www.nuget.org/packages/Codebelt.Extensions.Xunit.Hosting/) 📦 -* [Codebelt.Extensions.Xunit.Hosting.AspNetCore](https://www.nuget.org/packages/Codebelt.Extensions.Xunit.Hosting.AspNetCore/) 📦 - -### CSharp Example - -Source: [TestTest.cs](https://github.com/codebeltnet/xunit/tree/main/test/Codebelt.Extensions.Xunit.Tests/TestTest.cs) - -```csharp -public class TestTest : Test -{ - public TestTest(ITestOutputHelper output) : base(output) - { - } - - // intentionally left out for brevity - - protected override void OnDisposeManagedResources() - { - base.OnDisposeManagedResources(); - } -} -``` +## About + +An open-source project (MIT license) that targets and complements the [xUnit.net](https://xunit.net/) test platform. It provides a uniform and convenient way of doing unit test for all project types in .NET. + +Your versatile xUnit companion for: +- Modern development with `.NET 10` and `.NET 11`, +- Cross-platform libraries with `.NET Standard 2` (where applicable), +- Legacy applications on `.NET Framework 4.6.2` and newer. + +It is, by heart, free, flexible and built to extend and boost your agile codebelt. + +## **Codebelt.Extensions.Xunit** for .NET + +The `Codebelt.Extensions.Xunit` namespace contains types that provides a uniform way of doing unit testing. The namespace relates to the `Xunit.Abstractions` namespace. + +More documentation available at our documentation site: + +- [Codebelt.Extensions.Xunit](https://xunit.codebelt.net/api/Codebelt.Extensions.Xunit.html) 🔗 + +## Related Packages + +* [Codebelt.Extensions.Xunit](https://www.nuget.org/packages/Codebelt.Extensions.Xunit/) 📦 +* [Codebelt.Extensions.Xunit.App](https://www.nuget.org/packages/Codebelt.Extensions.Xunit.App/) 🏭 +* [Codebelt.Extensions.Xunit.Hosting](https://www.nuget.org/packages/Codebelt.Extensions.Xunit.Hosting/) 📦 +* [Codebelt.Extensions.Xunit.Hosting.AspNetCore](https://www.nuget.org/packages/Codebelt.Extensions.Xunit.Hosting.AspNetCore/) 📦 + +### CSharp Example + +Source: [TestTest.cs](https://github.com/codebeltnet/xunit/tree/main/test/Codebelt.Extensions.Xunit.Tests/TestTest.cs) + +```csharp +public class TestTest : Test +{ + public TestTest(ITestOutputHelper output) : base(output) + { + } + + // intentionally left out for brevity + + protected override void OnDisposeManagedResources() + { + base.OnDisposeManagedResources(); + } +} +``` diff --git a/Codebelt.Extensions.Xunit.slnx b/Codebelt.Extensions.Xunit.slnx index d6c2e13..73ae792 100644 --- a/Codebelt.Extensions.Xunit.slnx +++ b/Codebelt.Extensions.Xunit.slnx @@ -1,31 +1,31 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Directory.Build.props b/Directory.Build.props index 10453ba..d1928c6 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,124 +1,124 @@ - - - - $(MSBuildProjectName.EndsWith('Tests')) - $(MSBuildProjectName.EndsWith('Benchmarks')) - $(MSBuildProjectDirectory.ToLower().StartsWith('$(MSBuildThisFileDirectory.ToLower())src')) - $(MSBuildProjectDirectory.ToLower().StartsWith('$(MSBuildThisFileDirectory.ToLower())tooling')) - $([MSBuild]::IsOSPlatform('Linux')) - $([MSBuild]::IsOSPlatform('OSX')) - $([MSBuild]::IsOSPlatform('Windows')) - true - false - ..\..\.nuget\$(MSBuildProjectName)\PackageReleaseNotes.txt - latest - - - - true - true - - - - net11.0;net10.0;netstandard2.0 - Copyright © Geekle 2024-2026. All rights reserved. - gimlichael - Geekle - Extensions for xUnit API by Codebelt - icon.png - README.md - https://xunit.codebelt.net/ - MIT - https://github.com/codebeltnet/xunit - git - en-US - true - true - true - snupkg - true - true - $(MSBuildThisFileDirectory)xunit.snk - true - latest - Recommended - 7035,CA2260 - v - true - - - - - - - - - - - - - - - net11.0;net10.0 - - - - net11.0;net10.0;net48 - - - - false - Exe - false - false - false - true - none - NU1701,NU1902,NU1903 - 0 - false - false - true - - - - false - Exe - true - - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - net11.0;net10.0;netstandard2.0 - false - false - false - false - true - none - NU1701,NU1902,NU1903 - false - false - - - - - - - - + + + + $(MSBuildProjectName.EndsWith('Tests')) + $(MSBuildProjectName.EndsWith('Benchmarks')) + $(MSBuildProjectDirectory.ToLower().StartsWith('$(MSBuildThisFileDirectory.ToLower())src')) + $(MSBuildProjectDirectory.ToLower().StartsWith('$(MSBuildThisFileDirectory.ToLower())tooling')) + $([MSBuild]::IsOSPlatform('Linux')) + $([MSBuild]::IsOSPlatform('OSX')) + $([MSBuild]::IsOSPlatform('Windows')) + true + false + ..\..\.nuget\$(MSBuildProjectName)\PackageReleaseNotes.txt + latest + + + + true + true + + + + net11.0;net10.0;netstandard2.0 + Copyright © Geekle 2024-2026. All rights reserved. + gimlichael + Geekle + Extensions for xUnit API by Codebelt + icon.png + README.md + https://xunit.codebelt.net/ + MIT + https://github.com/codebeltnet/xunit + git + en-US + true + true + true + snupkg + true + true + $(MSBuildThisFileDirectory)xunit.snk + true + latest + Recommended + 7035,CA2260 + v + true + + + + + + + + + + + + + + + net11.0;net10.0 + + + + net11.0;net10.0;net48 + + + + false + Exe + false + false + false + true + none + NU1701,NU1902,NU1903 + 0 + false + false + true + + + + false + Exe + true + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + net11.0;net10.0;netstandard2.0 + false + false + false + false + true + none + NU1701,NU1902,NU1903 + false + false + + + + + + + + diff --git a/Directory.Build.targets b/Directory.Build.targets index 5f9df1e..f6a6a00 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -1,14 +1,14 @@ - - - - $([System.IO.File]::ReadAllText('$(PackageReleaseNotesFile)')) - - - - - - 0 - $(MinVerMajor).$(MinVerMinor).$(MinVerPatch).$(GITHUB_RUN_NUMBER) - - - + + + + $([System.IO.File]::ReadAllText('$(PackageReleaseNotesFile)')) + + + + + + 0 + $(MinVerMajor).$(MinVerMinor).$(MinVerPatch).$(GITHUB_RUN_NUMBER) + + + diff --git a/Directory.Packages.props b/Directory.Packages.props index a6b509f..d7ec1b9 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -47,4 +47,4 @@ - \ No newline at end of file + diff --git a/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWeb.App/Properties/launchSettings.json b/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWeb.App/Properties/launchSettings.json index 0909904..2f438df 100644 --- a/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWeb.App/Properties/launchSettings.json +++ b/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperMinimalWeb.App/Properties/launchSettings.json @@ -1,12 +1,12 @@ -{ - "profiles": { - "Codebelt.Extensions.Xunit.Hosting.AspNetCore.BootstrapperMinimalWebTestSite": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "https://localhost:64266;http://localhost:64268" - } - } -} \ No newline at end of file +{ + "profiles": { + "Codebelt.Extensions.Xunit.Hosting.AspNetCore.BootstrapperMinimalWebTestSite": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:64266;http://localhost:64268" + } + } +} diff --git a/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperWeb.App/Properties/launchSettings.json b/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperWeb.App/Properties/launchSettings.json index 76c3923..43eb982 100644 --- a/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperWeb.App/Properties/launchSettings.json +++ b/app/Codebelt.Extensions.Xunit.Hosting.BootstrapperWeb.App/Properties/launchSettings.json @@ -1,12 +1,12 @@ -{ - "profiles": { - "Codebelt.Extensions.Xunit.Hosting.AspNetCore.BootstrapperWebTestSite": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "https://localhost:64267;http://localhost:64269" - } - } -} \ No newline at end of file +{ + "profiles": { + "Codebelt.Extensions.Xunit.Hosting.AspNetCore.BootstrapperWebTestSite": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:64267;http://localhost:64269" + } + } +} diff --git a/app/Codebelt.Extensions.Xunit.Hosting.ClassicProgram.App/Properties/launchSettings.json b/app/Codebelt.Extensions.Xunit.Hosting.ClassicProgram.App/Properties/launchSettings.json index 45a391b..bf89c1a 100644 --- a/app/Codebelt.Extensions.Xunit.Hosting.ClassicProgram.App/Properties/launchSettings.json +++ b/app/Codebelt.Extensions.Xunit.Hosting.ClassicProgram.App/Properties/launchSettings.json @@ -1,12 +1,12 @@ -{ - "profiles": { - "Codebelt.Extensions.Xunit.Hosting.AspNetCore.ClassicProgramTestSite": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "https://localhost:64340;http://localhost:64341" - } - } -} \ No newline at end of file +{ + "profiles": { + "Codebelt.Extensions.Xunit.Hosting.AspNetCore.ClassicProgramTestSite": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:64340;http://localhost:64341" + } + } +} diff --git a/app/Codebelt.Extensions.Xunit.Hosting.Program.App/Properties/launchSettings.json b/app/Codebelt.Extensions.Xunit.Hosting.Program.App/Properties/launchSettings.json index 3a54a51..10f2e96 100644 --- a/app/Codebelt.Extensions.Xunit.Hosting.Program.App/Properties/launchSettings.json +++ b/app/Codebelt.Extensions.Xunit.Hosting.Program.App/Properties/launchSettings.json @@ -1,12 +1,12 @@ -{ - "profiles": { - "Codebelt.Extensions.Xunit.Hosting.AspNetCore.ProgramTestSite": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "https://localhost:64346;http://localhost:64347" - } - } -} \ No newline at end of file +{ + "profiles": { + "Codebelt.Extensions.Xunit.Hosting.AspNetCore.ProgramTestSite": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:64346;http://localhost:64347" + } + } +} diff --git a/src/Codebelt.Extensions.Xunit.App/Codebelt.Extensions.Xunit.App.csproj b/src/Codebelt.Extensions.Xunit.App/Codebelt.Extensions.Xunit.App.csproj index 301a7cf..e5d4d0d 100644 --- a/src/Codebelt.Extensions.Xunit.App/Codebelt.Extensions.Xunit.App.csproj +++ b/src/Codebelt.Extensions.Xunit.App/Codebelt.Extensions.Xunit.App.csproj @@ -1,21 +1,21 @@ - - - - net11.0;net10.0 - 250bdf91-e7c7-4cb4-a39d-e1a5374c5602 - - - - false - false - Provides a convenient set of default API additions for building .NET test projects based on xUnit. - $(NoWarn);NU5128 - - - - - - - - - + + + + net11.0;net10.0 + 250bdf91-e7c7-4cb4-a39d-e1a5374c5602 + + + + false + false + Provides a convenient set of default API additions for building .NET test projects based on xUnit. + $(NoWarn);NU5128 + + + + + + + + + diff --git a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/Codebelt.Extensions.Xunit.Hosting.AspNetCore.csproj b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/Codebelt.Extensions.Xunit.Hosting.AspNetCore.csproj index 28be837..d9da3bf 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/Codebelt.Extensions.Xunit.Hosting.AspNetCore.csproj +++ b/src/Codebelt.Extensions.Xunit.Hosting.AspNetCore/Codebelt.Extensions.Xunit.Hosting.AspNetCore.csproj @@ -1,26 +1,26 @@ - - - - net11.0;net10.0 - 200bdf91-e7c7-4cb4-a39d-e1a5374c5602 - false - - - - The Codebelt.Extensions.Xunit.Hosting.AspNetCore namespace contains types that provides a uniform way of doing unit testing that depends on ASP.NET Core and used in conjunction with Microsoft Dependency Injection. The namespace relates to the Microsoft.AspNetCore.TestHost namespace. - asp-net-core-host-test class-fixture asp-net-core-host-fixture middleware-test-factory microsoft dependency injection host configuration hosting-environment service-provider configure-services test-server - - - - - - - - - - - - - - - + + + + net11.0;net10.0 + 200bdf91-e7c7-4cb4-a39d-e1a5374c5602 + false + + + + The Codebelt.Extensions.Xunit.Hosting.AspNetCore namespace contains types that provides a uniform way of doing unit testing that depends on ASP.NET Core and used in conjunction with Microsoft Dependency Injection. The namespace relates to the Microsoft.AspNetCore.TestHost namespace. + asp-net-core-host-test class-fixture asp-net-core-host-fixture middleware-test-factory microsoft dependency injection host configuration hosting-environment service-provider configure-services test-server + + + + + + + + + + + + + + + diff --git a/src/Codebelt.Extensions.Xunit.Hosting/Codebelt.Extensions.Xunit.Hosting.csproj b/src/Codebelt.Extensions.Xunit.Hosting/Codebelt.Extensions.Xunit.Hosting.csproj index 7c642f5..607f14d 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting/Codebelt.Extensions.Xunit.Hosting.csproj +++ b/src/Codebelt.Extensions.Xunit.Hosting/Codebelt.Extensions.Xunit.Hosting.csproj @@ -1,25 +1,25 @@ - - - - 1e0bdf91-e7c7-4cb4-a39d-e1a5374c5602 - false - - - - The Codebelt.Extensions.Xunit.Hosting namespace contains types that provides a uniform way of doing unit testing used in conjunction with Microsoft Dependency Injection. The namespace relates to the Xunit.Abstractions namespace. - host-test class-fixture host-fixture microsoft dependency injection host configuration hosting-environment service-provider configure-services - - - - - - - - - - - - - - - + + + + 1e0bdf91-e7c7-4cb4-a39d-e1a5374c5602 + false + + + + The Codebelt.Extensions.Xunit.Hosting namespace contains types that provides a uniform way of doing unit testing used in conjunction with Microsoft Dependency Injection. The namespace relates to the Xunit.Abstractions namespace. + host-test class-fixture host-fixture microsoft dependency injection host configuration hosting-environment service-provider configure-services + + + + + + + + + + + + + + + diff --git a/src/Codebelt.Extensions.Xunit/Codebelt.Extensions.Xunit.csproj b/src/Codebelt.Extensions.Xunit/Codebelt.Extensions.Xunit.csproj index f89e32f..7de6c3c 100644 --- a/src/Codebelt.Extensions.Xunit/Codebelt.Extensions.Xunit.csproj +++ b/src/Codebelt.Extensions.Xunit/Codebelt.Extensions.Xunit.csproj @@ -1,23 +1,23 @@ - - - - 0d0bdf91-e7c7-4cb4-a39d-e1a5374c5602 - true - - - - The Codebelt.Extensions.Xunit namespace contains types that provides a uniform way of doing unit testing. The namespace relates to the Xunit.Abstractions namespace. - test test-output test-disposable test-cleanup - - - - - - - - - - - - - + + + + 0d0bdf91-e7c7-4cb4-a39d-e1a5374c5602 + true + + + + The Codebelt.Extensions.Xunit namespace contains types that provides a uniform way of doing unit testing. The namespace relates to the Xunit.Abstractions namespace. + test test-output test-disposable test-cleanup + + + + + + + + + + + + + diff --git a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests.csproj b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests.csproj index 87084a6..fa4d4ce 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests.csproj +++ b/test/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests/Codebelt.Extensions.Xunit.Hosting.AspNetCore.Tests.csproj @@ -1,17 +1,17 @@ - - - - net11.0;net10.0 - Codebelt.Extensions.Xunit.Hosting.AspNetCore - - - - - - - - - - - - + + + + net11.0;net10.0 + Codebelt.Extensions.Xunit.Hosting.AspNetCore + + + + + + + + + + + + diff --git a/test/Codebelt.Extensions.Xunit.Hosting.FunctionalTests/Codebelt.Extensions.Xunit.Hosting.FunctionalTests.csproj b/test/Codebelt.Extensions.Xunit.Hosting.FunctionalTests/Codebelt.Extensions.Xunit.Hosting.FunctionalTests.csproj index da7f673..562aa20 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.FunctionalTests/Codebelt.Extensions.Xunit.Hosting.FunctionalTests.csproj +++ b/test/Codebelt.Extensions.Xunit.Hosting.FunctionalTests/Codebelt.Extensions.Xunit.Hosting.FunctionalTests.csproj @@ -1,4 +1,4 @@ - + net11.0;net10.0 diff --git a/test/Codebelt.Extensions.Xunit.Hosting.Tests/Codebelt.Extensions.Xunit.Hosting.Tests.csproj b/test/Codebelt.Extensions.Xunit.Hosting.Tests/Codebelt.Extensions.Xunit.Hosting.Tests.csproj index d78b9b5..899c980 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.Tests/Codebelt.Extensions.Xunit.Hosting.Tests.csproj +++ b/test/Codebelt.Extensions.Xunit.Hosting.Tests/Codebelt.Extensions.Xunit.Hosting.Tests.csproj @@ -1,30 +1,30 @@ - - - - Codebelt.Extensions.Xunit.Hosting - - - - - - - - - Always - - - - - - - - - - - - - - - - - + + + + Codebelt.Extensions.Xunit.Hosting + + + + + + + + + Always + + + + + + + + + + + + + + + + + diff --git a/test/Codebelt.Extensions.Xunit.Hosting.Tests/appsettings.json b/test/Codebelt.Extensions.Xunit.Hosting.Tests/appsettings.json index af40053..2728534 100644 --- a/test/Codebelt.Extensions.Xunit.Hosting.Tests/appsettings.json +++ b/test/Codebelt.Extensions.Xunit.Hosting.Tests/appsettings.json @@ -1,3 +1,3 @@ -{ - "unitTestTool": "xUnit" -} \ No newline at end of file +{ + "unitTestTool": "xUnit" +} diff --git a/test/Codebelt.Extensions.Xunit.Tests/Codebelt.Extensions.Xunit.Tests.csproj b/test/Codebelt.Extensions.Xunit.Tests/Codebelt.Extensions.Xunit.Tests.csproj index 723a922..8de9821 100644 --- a/test/Codebelt.Extensions.Xunit.Tests/Codebelt.Extensions.Xunit.Tests.csproj +++ b/test/Codebelt.Extensions.Xunit.Tests/Codebelt.Extensions.Xunit.Tests.csproj @@ -1,11 +1,11 @@ - - - - Codebelt.Extensions.Xunit - - - - - - - + + + + Codebelt.Extensions.Xunit + + + + + + + diff --git a/tooling/benchmark-runner/benchmark-runner.csproj b/tooling/benchmark-runner/benchmark-runner.csproj index 35e8210..7e6e873 100644 --- a/tooling/benchmark-runner/benchmark-runner.csproj +++ b/tooling/benchmark-runner/benchmark-runner.csproj @@ -1,16 +1,16 @@ - - - - net10.0 - benchmark_runner - - - - - - - - - - - + + + + net10.0 + benchmark_runner + + + + + + + + + + + From 74521b3c232e97f9e6ec143e62c5be9e1190cb1e Mon Sep 17 00:00:00 2001 From: aicia-bot Date: Wed, 28 Oct 2026 00:16:25 +0100 Subject: [PATCH 29/32] =?UTF-8?q?=F0=9F=8E=A8=20tidy=20whitespace=20in=20d?= =?UTF-8?q?ocs,=20json,=20and=20source?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removes trailing whitespace and aligns JSON indentation to the two-space style used elsewhere in the repository. No behavioral changes. --- .gitattributes | 6 +- .github/dispatch-targets.json | 10 +- .github/prompts/benchmark.prompt.md | 328 +++++++++--------- AGENTS.md | 8 +- ....DelimitedStringBenchmark-report-github.md | 84 ++--- ...nMemoryTestStoreBenchmark-report-github.md | 156 ++++----- ...sions.Xunit.TestBenchmark-report-github.md | 120 +++---- .../ServiceCollectionExtensions.cs | 36 +- testenvironments.json | 40 +-- ...odebelt.Extensions.Xunit.Benchmarks.csproj | 22 +- 10 files changed, 405 insertions(+), 405 deletions(-) diff --git a/.gitattributes b/.gitattributes index 6f3fcbd..0ea8d1b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -17,7 +17,7 @@ # # Merging from the command prompt will add diff markers to the files if there # are conflicts (Merging from VS is not affected by the settings below, in VS -# the diff markers are never inserted). Diff markers may cause the following +# the diff markers are never inserted). Diff markers may cause the following # file extensions to fail to load in VS. An alternative would be to treat # these files as binary and thus will always conflict and require user # intervention with every merge. To do so, just uncomment the entries below @@ -46,9 +46,9 @@ ############################################################################### # diff behavior for common document formats -# +# # Convert binary document formats to text before diffing them. This feature -# is only available from the command line. Turn it on by uncommenting the +# is only available from the command line. Turn it on by uncommenting the # entries below. ############################################################################### #*.doc diff=astextplain diff --git a/.github/dispatch-targets.json b/.github/dispatch-targets.json index f510e0c..d5e9e52 100644 --- a/.github/dispatch-targets.json +++ b/.github/dispatch-targets.json @@ -1,7 +1,7 @@ [ - "benchmarkdotnet", - "newtonsoft-json", - "aws-signature-v4", - "unitify", - "yamldotnet" + "benchmarkdotnet", + "newtonsoft-json", + "aws-signature-v4", + "unitify", + "yamldotnet" ] diff --git a/.github/prompts/benchmark.prompt.md b/.github/prompts/benchmark.prompt.md index f137b63..531fd49 100644 --- a/.github/prompts/benchmark.prompt.md +++ b/.github/prompts/benchmark.prompt.md @@ -1,164 +1,164 @@ ---- -mode: agent -description: 'Writing Performance Benchmarks' ---- - -# Benchmark Fixture Prompt (Tuning Benchmarks) - -This prompt defines how to generate performance tests (“benchmarks”) for a project/solution using BenchmarkDotNet. -Benchmarks are *not* unit tests — they are micro- or component-level performance measurements that belong under the `tuning/` directory and follow strict conventions. - -Copilot must follow these guidelines when generating benchmark fixtures. - ---- - -## 1. Naming and Placement - -- All benchmark projects live under the `tuning/` folder. - Examples: - - `tuning/.Benchmarks/` - - `tuning/.Console.Benchmarks/` - -- **Namespaces must NOT end with `.Benchmarks`.** - They must mirror the production assembly’s namespace. - - Example: - If benchmarking a type inside `YourProject.Console`, then: - - ```csharp - namespace YourProject.Console - { - public class Sha512256Benchmark { … } - } - ``` - -* **Benchmark class names must end with `Benchmark`.** - Example: `DateSpanBenchmark`, `FowlerNollVoBenchmark`. - -* Benchmark files should be located in the matching benchmark project - (e.g., benchmarks for `YourProject.Console` go in `YourProject.Console.Benchmarks.csproj`). - -* In the `.csproj` for each benchmark project, set the root namespace to the production namespace, for example: - - ```xml - YourProject.Console - ``` - ---- - -## 2. Attributes and Configuration - -Each benchmark class should use: - -```csharp -[MemoryDiagnoser] -[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)] -``` - -Optional but strongly recommended where meaningful: - -* `[Params(...)]` — define small, medium, large input sizes. -* `[GlobalSetup]` — deterministic initialization of benchmark data. -* `[Benchmark(Description = "...")]` — always add descriptions. -* `[Benchmark(Baseline = true)]` — when comparing two implementations. - -Avoid complex global configs; prefer explicit attributes inside the class. - ---- - -## 3. Structure and Best Practices - -A benchmark fixture must: - -* Measure a **single logical operation** per benchmark method. -* Avoid I/O, networking, disk access, logging, or side effects. -* Avoid expensive setup inside `[Benchmark]` methods. -* Use deterministic data (e.g., seeded RNG or predefined constants). -* Use `[GlobalSetup]` to allocate buffers, random payloads, or reusable test data only once. -* Avoid shared mutable state unless reset per iteration. - -Use representative input sizes such as: - -```csharp -[Params(8, 256, 4096)] -public int Count { get; set; } -``` - -BenchmarkDotNet will run each benchmark for each parameter value. - ---- - -## 4. Method Naming Conventions - -Use descriptive names that communicate intent: - -* `Parse_Short` -* `Parse_Long` -* `ComputeHash_Small` -* `ComputeHash_Large` -* `Serialize_Optimized` -* `Serialize_Baseline` - -When comparing approaches, always list them clearly and tag one as the baseline. - ---- - -## 5. Example Benchmark Fixture - -```csharp -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Configs; - -namespace YourProject -{ - [MemoryDiagnoser] - [GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)] - public class SampleOperationBenchmark - { - [Params(8, 256, 4096)] - public int Count { get; set; } - - private byte[] _payload; - - [GlobalSetup] - public void Setup() - { - _payload = new byte[Count]; - // deterministic initialization - } - - [Benchmark(Baseline = true, Description = "Operation - baseline")] - public int Operation_Baseline() => SampleOperation.Process(_payload); - - [Benchmark(Description = "Operation - optimized")] - public int Operation_Optimized() => SampleOperation.ProcessOptimized(_payload); - } -} -``` - ---- - -## 6. Reporting and CI - -* Benchmark projects live exclusively under `tuning/`. They must not affect production builds. -* Heavy BenchmarkDotNet runs should *not* run in CI unless explicitly configured. -* Reports are produced by the benchmark runner and stored under the configured artifacts directory. - ---- - -## 7. Additional Guidelines - -* Keep benchmark fixtures focused and readable. -* Document non-obvious reasoning in short comments. -* Prefer realistic but deterministic data sets. -* When benchmarks reveal regressions or improvements, reference the associated PR or issue in a comment. -* Shared benchmark helpers belong in `tuning/` projects, not in production code. - ---- - -## Final Notes - -* Benchmarks are performance tests, not unit tests. -* Use `[Benchmark]` only for pure performance measurement. -* Avoid `MethodImplOptions.NoInlining` unless absolutely necessary. -* Use small sets of meaningful benchmark scenarios — avoid combinatorial explosion. +--- +mode: agent +description: 'Writing Performance Benchmarks' +--- + +# Benchmark Fixture Prompt (Tuning Benchmarks) + +This prompt defines how to generate performance tests (“benchmarks”) for a project/solution using BenchmarkDotNet.
+Benchmarks are *not* unit tests — they are micro- or component-level performance measurements that belong under the `tuning/` directory and follow strict conventions. + +Copilot must follow these guidelines when generating benchmark fixtures. + +--- + +## 1. Naming and Placement + +- All benchmark projects live under the `tuning/` folder.
+ Examples: + - `tuning/.Benchmarks/` + - `tuning/.Console.Benchmarks/` + +- **Namespaces must NOT end with `.Benchmarks`.**
+ They must mirror the production assembly’s namespace. + + Example:
+ If benchmarking a type inside `YourProject.Console`, then: + + ```csharp + namespace YourProject.Console + { + public class Sha512256Benchmark { … } + } + ``` + +* **Benchmark class names must end with `Benchmark`.** + Example: `DateSpanBenchmark`, `FowlerNollVoBenchmark`. + +* Benchmark files should be located in the matching benchmark project + (e.g., benchmarks for `YourProject.Console` go in `YourProject.Console.Benchmarks.csproj`). + +* In the `.csproj` for each benchmark project, set the root namespace to the production namespace, for example: + + ```xml + YourProject.Console + ``` + +--- + +## 2. Attributes and Configuration + +Each benchmark class should use: + +```csharp +[MemoryDiagnoser] +[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)] +``` + +Optional but strongly recommended where meaningful: + +* `[Params(...)]` — define small, medium, large input sizes. +* `[GlobalSetup]` — deterministic initialization of benchmark data. +* `[Benchmark(Description = "...")]` — always add descriptions. +* `[Benchmark(Baseline = true)]` — when comparing two implementations. + +Avoid complex global configs; prefer explicit attributes inside the class. + +--- + +## 3. Structure and Best Practices + +A benchmark fixture must: + +* Measure a **single logical operation** per benchmark method. +* Avoid I/O, networking, disk access, logging, or side effects. +* Avoid expensive setup inside `[Benchmark]` methods. +* Use deterministic data (e.g., seeded RNG or predefined constants). +* Use `[GlobalSetup]` to allocate buffers, random payloads, or reusable test data only once. +* Avoid shared mutable state unless reset per iteration. + +Use representative input sizes such as: + +```csharp +[Params(8, 256, 4096)] +public int Count { get; set; } +``` + +BenchmarkDotNet will run each benchmark for each parameter value. + +--- + +## 4. Method Naming Conventions + +Use descriptive names that communicate intent: + +* `Parse_Short` +* `Parse_Long` +* `ComputeHash_Small` +* `ComputeHash_Large` +* `Serialize_Optimized` +* `Serialize_Baseline` + +When comparing approaches, always list them clearly and tag one as the baseline. + +--- + +## 5. Example Benchmark Fixture + +```csharp +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Configs; + +namespace YourProject +{ + [MemoryDiagnoser] + [GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)] + public class SampleOperationBenchmark + { + [Params(8, 256, 4096)] + public int Count { get; set; } + + private byte[] _payload; + + [GlobalSetup] + public void Setup() + { + _payload = new byte[Count]; + // deterministic initialization + } + + [Benchmark(Baseline = true, Description = "Operation - baseline")] + public int Operation_Baseline() => SampleOperation.Process(_payload); + + [Benchmark(Description = "Operation - optimized")] + public int Operation_Optimized() => SampleOperation.ProcessOptimized(_payload); + } +} +``` + +--- + +## 6. Reporting and CI + +* Benchmark projects live exclusively under `tuning/`. They must not affect production builds. +* Heavy BenchmarkDotNet runs should *not* run in CI unless explicitly configured. +* Reports are produced by the benchmark runner and stored under the configured artifacts directory. + +--- + +## 7. Additional Guidelines + +* Keep benchmark fixtures focused and readable. +* Document non-obvious reasoning in short comments. +* Prefer realistic but deterministic data sets. +* When benchmarks reveal regressions or improvements, reference the associated PR or issue in a comment. +* Shared benchmark helpers belong in `tuning/` projects, not in production code. + +--- + +## Final Notes + +* Benchmarks are performance tests, not unit tests. +* Use `[Benchmark]` only for pure performance measurement. +* Avoid `MethodImplOptions.NoInlining` unless absolutely necessary. +* Use small sets of meaningful benchmark scenarios — avoid combinatorial explosion. diff --git a/AGENTS.md b/AGENTS.md index 490461f..38aa2d6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -364,7 +364,7 @@ This document provides instructions for writing unit tests for a project/solutio ### 1. Base Class -**Always inherit from the `Test` base class** for all unit test classes. +**Always inherit from the `Test` base class** for all unit test classes.
This ensures consistent setup, teardown, and output handling across all tests. > Important: Do NOT add `using Xunit.Abstractions`. xUnit v3 no longer exposes that namespace; including it is incorrect and will cause compilation errors. Use the `Codebelt.Extensions.Xunit` Test base class and `using Xunit;` as shown in the examples below. If you need access to test output, rely on the Test base class (which accepts the appropriate output helper) rather than importing `Xunit.Abstractions`. @@ -511,13 +511,13 @@ public class DateSpanTest : Test **Pattern name:** Public Facade Testing (also referred to as *Public API Proxy Testing*) -**Description:** +**Description:**
Internal classes and methods must be validated by exercising the public API that consumes them. Tests should assert observable behavior exposed by the public surface rather than targeting internal implementation details directly. #### Example Mapping -- **Internal helper:** `DelimitedString` (internal static class) -- **Public API:** `TestOutputHelperExtensions.WriteLines()` (public extension method) +- **Internal helper:** `DelimitedString` (internal static class)
+- **Public API:** `TestOutputHelperExtensions.WriteLines()` (public extension method)
- **Test strategy:** Write tests for `WriteLines()` and verify its public behavior. The internal call to `DelimitedString.Create()` is exercised implicitly. #### Benefits diff --git a/reports/tuning/Codebelt.Extensions.Xunit.DelimitedStringBenchmark-report-github.md b/reports/tuning/Codebelt.Extensions.Xunit.DelimitedStringBenchmark-report-github.md index 4f2e373..fe51c3f 100644 --- a/reports/tuning/Codebelt.Extensions.Xunit.DelimitedStringBenchmark-report-github.md +++ b/reports/tuning/Codebelt.Extensions.Xunit.DelimitedStringBenchmark-report-github.md @@ -1,42 +1,42 @@ -``` - -BenchmarkDotNet v0.15.8, Windows 11 (10.0.26200.7462/25H2/2025Update/HudsonValley2) -12th Gen Intel Core i9-12900KF 3.20GHz, 1 CPU, 24 logical and 16 physical cores -.NET SDK 10.0.101 - [Host] : .NET 10.0.1 (10.0.1, 10.0.125.57005), X64 RyuJIT x86-64-v3 - Job-LDLMHG : .NET 10.0.1 (10.0.1, 10.0.125.57005), X64 RyuJIT x86-64-v3 - Job-IOAYXE : .NET 9.0.11 (9.0.11, 9.0.1125.51716), X64 RyuJIT x86-64-v3 - Job-GEUVPA : .NET Framework 4.8.1 (4.8.9323.0), X64 RyuJIT VectorSize=256 - -PowerPlanMode=00000000-0000-0000-0000-000000000000 IterationTime=250ms MaxIterationCount=20 -MinIterationCount=15 WarmupCount=1 - -``` -| Method | Runtime | Count | Mean | Error | StdDev | Median | Min | Max | Ratio | RatioSD | Gen0 | Gen1 | Gen2 | Allocated | Alloc Ratio | -|--------------------------------------------- |------------------- |------ |--------------:|--------------:|--------------:|--------------:|--------------:|--------------:|------:|--------:|--------:|--------:|--------:|----------:|------------:| -| **'Create from string sequence via WriteLines'** | **.NET 10.0** | **8** | **96.47 ns** | **10.230 ns** | **11.781 ns** | **102.20 ns** | **78.73 ns** | **113.44 ns** | **1.02** | **0.18** | **0.0336** | **-** | **-** | **528 B** | **1.00** | -| 'Create from int sequence via WriteLines<T>' | .NET 10.0 | 8 | 78.84 ns | 2.944 ns | 3.023 ns | 77.62 ns | 75.04 ns | 84.69 ns | 0.83 | 0.11 | 0.0321 | - | - | 504 B | 0.95 | -| | | | | | | | | | | | | | | | | -| 'Create from string sequence via WriteLines' | .NET 9.0 | 8 | 97.60 ns | 2.546 ns | 2.724 ns | 97.07 ns | 93.33 ns | 103.88 ns | 1.00 | 0.04 | 0.0334 | - | - | 528 B | 1.00 | -| 'Create from int sequence via WriteLines<T>' | .NET 9.0 | 8 | 83.47 ns | 2.513 ns | 2.689 ns | 82.97 ns | 79.37 ns | 88.24 ns | 0.86 | 0.04 | 0.0319 | - | - | 504 B | 0.95 | -| | | | | | | | | | | | | | | | | -| 'Create from string sequence via WriteLines' | .NET Framework 4.8 | 8 | 229.94 ns | 3.558 ns | 3.328 ns | 229.26 ns | 224.79 ns | 236.03 ns | 1.00 | 0.02 | 0.0833 | - | - | 530 B | 1.00 | -| 'Create from int sequence via WriteLines<T>' | .NET Framework 4.8 | 8 | 400.36 ns | 6.012 ns | 5.330 ns | 400.88 ns | 392.75 ns | 409.86 ns | 1.74 | 0.03 | 0.1208 | - | - | 762 B | 1.44 | -| | | | | | | | | | | | | | | | | -| **'Create from string sequence via WriteLines'** | **.NET 10.0** | **256** | **1,237.83 ns** | **43.074 ns** | **44.234 ns** | **1,229.18 ns** | **1,174.89 ns** | **1,343.54 ns** | **1.00** | **0.05** | **0.8793** | **0.0242** | **-** | **13792 B** | **1.00** | -| 'Create from int sequence via WriteLines<T>' | .NET 10.0 | 256 | 1,669.19 ns | 40.290 ns | 44.782 ns | 1,676.20 ns | 1,594.85 ns | 1,754.02 ns | 1.35 | 0.06 | 0.8391 | 0.0131 | - | 13208 B | 0.96 | -| | | | | | | | | | | | | | | | | -| 'Create from string sequence via WriteLines' | .NET 9.0 | 256 | 1,526.52 ns | 61.280 ns | 65.569 ns | 1,510.14 ns | 1,442.55 ns | 1,684.80 ns | 1.00 | 0.06 | 0.8763 | 0.0234 | - | 13792 B | 1.00 | -| 'Create from int sequence via WriteLines<T>' | .NET 9.0 | 256 | 1,739.16 ns | 39.546 ns | 43.955 ns | 1,732.13 ns | 1,675.31 ns | 1,837.88 ns | 1.14 | 0.05 | 0.8371 | 0.0074 | - | 13208 B | 0.96 | -| | | | | | | | | | | | | | | | | -| 'Create from string sequence via WriteLines' | .NET Framework 4.8 | 256 | 6,797.31 ns | 85.372 ns | 79.857 ns | 6,798.25 ns | 6,643.61 ns | 6,940.70 ns | 1.00 | 0.02 | 2.1749 | 0.0544 | - | 13843 B | 1.00 | -| 'Create from int sequence via WriteLines<T>' | .NET Framework 4.8 | 256 | 19,270.68 ns | 2,943.635 ns | 3,389.895 ns | 21,076.97 ns | 12,874.82 ns | 21,802.23 ns | 2.84 | 0.49 | 3.3662 | 0.0502 | - | 21471 B | 1.55 | -| | | | | | | | | | | | | | | | | -| **'Create from string sequence via WriteLines'** | **.NET 10.0** | **4096** | **84,475.85 ns** | **3,893.786 ns** | **4,166.308 ns** | **85,847.66 ns** | **71,001.28 ns** | **86,840.24 ns** | **1.00** | **0.07** | **27.7008** | **27.7008** | **27.7008** | **185417 B** | **1.00** | -| 'Create from int sequence via WriteLines<T>' | .NET 10.0 | 4096 | 68,753.09 ns | 10,908.273 ns | 12,561.983 ns | 73,989.85 ns | 38,408.24 ns | 77,868.69 ns | 0.82 | 0.15 | 20.0777 | 3.2383 | - | 316008 B | 1.70 | -| | | | | | | | | | | | | | | | | -| 'Create from string sequence via WriteLines' | .NET 9.0 | 4096 | 79,696.76 ns | 17,376.810 ns | 20,011.160 ns | 93,059.99 ns | 48,680.45 ns | 95,614.83 ns | 1.08 | 0.43 | 27.6403 | 27.6403 | 27.6403 | 185417 B | 1.00 | -| 'Create from int sequence via WriteLines<T>' | .NET 9.0 | 4096 | 66,911.16 ns | 3,750.424 ns | 4,168.587 ns | 68,161.73 ns | 51,049.20 ns | 69,146.69 ns | 0.91 | 0.28 | 20.0000 | 3.2813 | - | 316008 B | 1.70 | -| | | | | | | | | | | | | | | | | -| 'Create from string sequence via WriteLines' | .NET Framework 4.8 | 4096 | 151,901.64 ns | 31,650.656 ns | 36,448.942 ns | 126,745.90 ns | 120,254.35 ns | 216,739.30 ns | 1.05 | 0.33 | 27.5000 | 27.5000 | 27.5000 | 185727 B | 1.00 | -| 'Create from int sequence via WriteLines<T>' | .NET Framework 4.8 | 4096 | 203,468.65 ns | 4,627.091 ns | 5,328.564 ns | 204,516.18 ns | 193,476.90 ns | 209,626.50 ns | 1.41 | 0.29 | 55.3797 | 7.1203 | - | 351252 B | 1.89 | +``` + +BenchmarkDotNet v0.15.8, Windows 11 (10.0.26200.7462/25H2/2025Update/HudsonValley2) +12th Gen Intel Core i9-12900KF 3.20GHz, 1 CPU, 24 logical and 16 physical cores +.NET SDK 10.0.101 + [Host] : .NET 10.0.1 (10.0.1, 10.0.125.57005), X64 RyuJIT x86-64-v3 + Job-LDLMHG : .NET 10.0.1 (10.0.1, 10.0.125.57005), X64 RyuJIT x86-64-v3 + Job-IOAYXE : .NET 9.0.11 (9.0.11, 9.0.1125.51716), X64 RyuJIT x86-64-v3 + Job-GEUVPA : .NET Framework 4.8.1 (4.8.9323.0), X64 RyuJIT VectorSize=256 + +PowerPlanMode=00000000-0000-0000-0000-000000000000 IterationTime=250ms MaxIterationCount=20 +MinIterationCount=15 WarmupCount=1 + +``` +| Method | Runtime | Count | Mean | Error | StdDev | Median | Min | Max | Ratio | RatioSD | Gen0 | Gen1 | Gen2 | Allocated | Alloc Ratio | +|--------------------------------------------- |------------------- |------ |--------------:|--------------:|--------------:|--------------:|--------------:|--------------:|------:|--------:|--------:|--------:|--------:|----------:|------------:| +| **'Create from string sequence via WriteLines'** | **.NET 10.0** | **8** | **96.47 ns** | **10.230 ns** | **11.781 ns** | **102.20 ns** | **78.73 ns** | **113.44 ns** | **1.02** | **0.18** | **0.0336** | **-** | **-** | **528 B** | **1.00** | +| 'Create from int sequence via WriteLines<T>' | .NET 10.0 | 8 | 78.84 ns | 2.944 ns | 3.023 ns | 77.62 ns | 75.04 ns | 84.69 ns | 0.83 | 0.11 | 0.0321 | - | - | 504 B | 0.95 | +| | | | | | | | | | | | | | | | | +| 'Create from string sequence via WriteLines' | .NET 9.0 | 8 | 97.60 ns | 2.546 ns | 2.724 ns | 97.07 ns | 93.33 ns | 103.88 ns | 1.00 | 0.04 | 0.0334 | - | - | 528 B | 1.00 | +| 'Create from int sequence via WriteLines<T>' | .NET 9.0 | 8 | 83.47 ns | 2.513 ns | 2.689 ns | 82.97 ns | 79.37 ns | 88.24 ns | 0.86 | 0.04 | 0.0319 | - | - | 504 B | 0.95 | +| | | | | | | | | | | | | | | | | +| 'Create from string sequence via WriteLines' | .NET Framework 4.8 | 8 | 229.94 ns | 3.558 ns | 3.328 ns | 229.26 ns | 224.79 ns | 236.03 ns | 1.00 | 0.02 | 0.0833 | - | - | 530 B | 1.00 | +| 'Create from int sequence via WriteLines<T>' | .NET Framework 4.8 | 8 | 400.36 ns | 6.012 ns | 5.330 ns | 400.88 ns | 392.75 ns | 409.86 ns | 1.74 | 0.03 | 0.1208 | - | - | 762 B | 1.44 | +| | | | | | | | | | | | | | | | | +| **'Create from string sequence via WriteLines'** | **.NET 10.0** | **256** | **1,237.83 ns** | **43.074 ns** | **44.234 ns** | **1,229.18 ns** | **1,174.89 ns** | **1,343.54 ns** | **1.00** | **0.05** | **0.8793** | **0.0242** | **-** | **13792 B** | **1.00** | +| 'Create from int sequence via WriteLines<T>' | .NET 10.0 | 256 | 1,669.19 ns | 40.290 ns | 44.782 ns | 1,676.20 ns | 1,594.85 ns | 1,754.02 ns | 1.35 | 0.06 | 0.8391 | 0.0131 | - | 13208 B | 0.96 | +| | | | | | | | | | | | | | | | | +| 'Create from string sequence via WriteLines' | .NET 9.0 | 256 | 1,526.52 ns | 61.280 ns | 65.569 ns | 1,510.14 ns | 1,442.55 ns | 1,684.80 ns | 1.00 | 0.06 | 0.8763 | 0.0234 | - | 13792 B | 1.00 | +| 'Create from int sequence via WriteLines<T>' | .NET 9.0 | 256 | 1,739.16 ns | 39.546 ns | 43.955 ns | 1,732.13 ns | 1,675.31 ns | 1,837.88 ns | 1.14 | 0.05 | 0.8371 | 0.0074 | - | 13208 B | 0.96 | +| | | | | | | | | | | | | | | | | +| 'Create from string sequence via WriteLines' | .NET Framework 4.8 | 256 | 6,797.31 ns | 85.372 ns | 79.857 ns | 6,798.25 ns | 6,643.61 ns | 6,940.70 ns | 1.00 | 0.02 | 2.1749 | 0.0544 | - | 13843 B | 1.00 | +| 'Create from int sequence via WriteLines<T>' | .NET Framework 4.8 | 256 | 19,270.68 ns | 2,943.635 ns | 3,389.895 ns | 21,076.97 ns | 12,874.82 ns | 21,802.23 ns | 2.84 | 0.49 | 3.3662 | 0.0502 | - | 21471 B | 1.55 | +| | | | | | | | | | | | | | | | | +| **'Create from string sequence via WriteLines'** | **.NET 10.0** | **4096** | **84,475.85 ns** | **3,893.786 ns** | **4,166.308 ns** | **85,847.66 ns** | **71,001.28 ns** | **86,840.24 ns** | **1.00** | **0.07** | **27.7008** | **27.7008** | **27.7008** | **185417 B** | **1.00** | +| 'Create from int sequence via WriteLines<T>' | .NET 10.0 | 4096 | 68,753.09 ns | 10,908.273 ns | 12,561.983 ns | 73,989.85 ns | 38,408.24 ns | 77,868.69 ns | 0.82 | 0.15 | 20.0777 | 3.2383 | - | 316008 B | 1.70 | +| | | | | | | | | | | | | | | | | +| 'Create from string sequence via WriteLines' | .NET 9.0 | 4096 | 79,696.76 ns | 17,376.810 ns | 20,011.160 ns | 93,059.99 ns | 48,680.45 ns | 95,614.83 ns | 1.08 | 0.43 | 27.6403 | 27.6403 | 27.6403 | 185417 B | 1.00 | +| 'Create from int sequence via WriteLines<T>' | .NET 9.0 | 4096 | 66,911.16 ns | 3,750.424 ns | 4,168.587 ns | 68,161.73 ns | 51,049.20 ns | 69,146.69 ns | 0.91 | 0.28 | 20.0000 | 3.2813 | - | 316008 B | 1.70 | +| | | | | | | | | | | | | | | | | +| 'Create from string sequence via WriteLines' | .NET Framework 4.8 | 4096 | 151,901.64 ns | 31,650.656 ns | 36,448.942 ns | 126,745.90 ns | 120,254.35 ns | 216,739.30 ns | 1.05 | 0.33 | 27.5000 | 27.5000 | 27.5000 | 185727 B | 1.00 | +| 'Create from int sequence via WriteLines<T>' | .NET Framework 4.8 | 4096 | 203,468.65 ns | 4,627.091 ns | 5,328.564 ns | 204,516.18 ns | 193,476.90 ns | 209,626.50 ns | 1.41 | 0.29 | 55.3797 | 7.1203 | - | 351252 B | 1.89 | diff --git a/reports/tuning/Codebelt.Extensions.Xunit.InMemoryTestStoreBenchmark-report-github.md b/reports/tuning/Codebelt.Extensions.Xunit.InMemoryTestStoreBenchmark-report-github.md index 7f54fc7..85d1116 100644 --- a/reports/tuning/Codebelt.Extensions.Xunit.InMemoryTestStoreBenchmark-report-github.md +++ b/reports/tuning/Codebelt.Extensions.Xunit.InMemoryTestStoreBenchmark-report-github.md @@ -1,78 +1,78 @@ -``` - -BenchmarkDotNet v0.15.8, Windows 11 (10.0.26200.8737/25H2/2025Update/HudsonValley2) -12th Gen Intel Core i9-12900KF 3.20GHz, 1 CPU, 24 logical and 16 physical cores -.NET SDK 11.0.100-preview.4.26230.115 - [Host] : .NET 10.0.10 (10.0.10, 10.0.1026.32716), X64 RyuJIT x86-64-v3 - Job-LDLMHG : .NET 10.0.10 (10.0.10, 10.0.1026.32716), X64 RyuJIT x86-64-v3 - Job-IOAYXE : .NET 9.0.18 (9.0.18, 9.0.1826.31522), X64 RyuJIT x86-64-v3 - Job-GEUVPA : .NET Framework 4.8.1 (4.8.9337.0), X64 RyuJIT VectorSize=256 - -PowerPlanMode=00000000-0000-0000-0000-000000000000 IterationTime=250ms MaxIterationCount=20 -MinIterationCount=15 WarmupCount=1 - -``` -| Method | Runtime | Count | Mean | Error | StdDev | Median | Min | Max | Ratio | RatioSD | Gen0 | Allocated | Alloc Ratio | -|---------------------------------------------- |------------------- |------ |--------------:|--------------:|--------------:|--------------:|--------------:|--------------:|------:|--------:|-------:|----------:|------------:| -| **'Query(predicate).Count() - all items match'** | **.NET 10.0** | **256** | **486.19 ns** | **34.108 ns** | **37.911 ns** | **485.88 ns** | **359.47 ns** | **554.43 ns** | **?** | **?** | **0.0041** | **72 B** | **?** | -| 'Query(predicate).Count() - half items match' | .NET 10.0 | 256 | 788.08 ns | 37.360 ns | 43.023 ns | 789.28 ns | 667.98 ns | 860.36 ns | ? | ? | 0.0025 | 72 B | ? | -| 'Query(predicate).Count() - no items match' | .NET 10.0 | 256 | 535.49 ns | 26.691 ns | 30.737 ns | 538.63 ns | 461.68 ns | 589.39 ns | ? | ? | 0.0031 | 72 B | ? | -| | | | | | | | | | | | | | | -| 'Query(predicate).Count() - all items match' | .NET 9.0 | 256 | 166.94 ns | 13.729 ns | 15.810 ns | 169.24 ns | 102.71 ns | 180.89 ns | ? | ? | 0.0046 | 72 B | ? | -| 'Query(predicate).Count() - half items match' | .NET 9.0 | 256 | 755.63 ns | 67.404 ns | 77.622 ns | 766.26 ns | 560.41 ns | 865.63 ns | ? | ? | 0.0044 | 72 B | ? | -| 'Query(predicate).Count() - no items match' | .NET 9.0 | 256 | 137.16 ns | 18.372 ns | 21.157 ns | 142.14 ns | 76.57 ns | 153.13 ns | ? | ? | 0.0044 | 72 B | ? | -| | | | | | | | | | | | | | | -| 'Query(predicate).Count() - all items match' | .NET Framework 4.8 | 256 | 2,780.37 ns | 303.660 ns | 349.695 ns | 2,827.17 ns | 1,987.71 ns | 3,207.74 ns | ? | ? | 0.0080 | 72 B | ? | -| 'Query(predicate).Count() - half items match' | .NET Framework 4.8 | 256 | 2,737.73 ns | 139.743 ns | 160.928 ns | 2,708.28 ns | 2,510.94 ns | 3,024.14 ns | ? | ? | 0.0068 | 72 B | ? | -| 'Query(predicate).Count() - no items match' | .NET Framework 4.8 | 256 | 1,702.18 ns | 401.519 ns | 462.390 ns | 1,861.91 ns | 1,005.25 ns | 2,310.27 ns | ? | ? | 0.0081 | 72 B | ? | -| | | | | | | | | | | | | | | -| **'Query(predicate).Count() - all items match'** | **.NET 10.0** | **4096** | **7,560.18 ns** | **1,202.244 ns** | **1,384.506 ns** | **8,142.52 ns** | **5,113.88 ns** | **8,937.35 ns** | **?** | **?** | **-** | **72 B** | **?** | -| 'Query(predicate).Count() - half items match' | .NET 10.0 | 4096 | 12,204.71 ns | 1,677.461 ns | 1,931.767 ns | 13,007.85 ns | 9,766.58 ns | 15,321.51 ns | ? | ? | - | 72 B | ? | -| 'Query(predicate).Count() - no items match' | .NET 10.0 | 4096 | 8,452.84 ns | 884.280 ns | 1,018.339 ns | 8,679.65 ns | 5,949.62 ns | 9,637.66 ns | ? | ? | - | 72 B | ? | -| | | | | | | | | | | | | | | -| 'Query(predicate).Count() - all items match' | .NET 9.0 | 4096 | 2,561.76 ns | 602.033 ns | 693.302 ns | 2,891.90 ns | 1,415.56 ns | 3,223.20 ns | ? | ? | - | 72 B | ? | -| 'Query(predicate).Count() - half items match' | .NET 9.0 | 4096 | 9,479.95 ns | 41.398 ns | 38.724 ns | 9,476.20 ns | 9,408.77 ns | 9,545.46 ns | ? | ? | - | 72 B | ? | -| 'Query(predicate).Count() - no items match' | .NET 9.0 | 4096 | 1,223.50 ns | 6.127 ns | 5.431 ns | 1,223.26 ns | 1,215.82 ns | 1,233.80 ns | ? | ? | - | 72 B | ? | -| | | | | | | | | | | | | | | -| 'Query(predicate).Count() - all items match' | .NET Framework 4.8 | 4096 | 31,206.51 ns | 139.450 ns | 130.442 ns | 31,249.54 ns | 30,966.07 ns | 31,347.75 ns | ? | ? | - | 71 B | ? | -| 'Query(predicate).Count() - half items match' | .NET Framework 4.8 | 4096 | 26,401.17 ns | 47.010 ns | 43.973 ns | 26,395.60 ns | 26,337.25 ns | 26,473.50 ns | ? | ? | - | 72 B | ? | -| 'Query(predicate).Count() - no items match' | .NET Framework 4.8 | 4096 | 15,524.36 ns | 73.356 ns | 68.617 ns | 15,532.35 ns | 15,360.02 ns | 15,630.49 ns | ? | ? | - | 72 B | ? | -| | | | | | | | | | | | | | | -| **'Query(predicate).Count() - all items match'** | **.NET 10.0** | **65536** | **95,347.05 ns** | **185.107 ns** | **164.093 ns** | **95,347.39 ns** | **95,071.70 ns** | **95,654.66 ns** | **?** | **?** | **-** | **72 B** | **?** | -| 'Query(predicate).Count() - half items match' | .NET 10.0 | 65536 | 148,640.18 ns | 1,916.770 ns | 1,792.948 ns | 148,486.59 ns | 144,905.11 ns | 151,601.37 ns | ? | ? | - | 72 B | ? | -| 'Query(predicate).Count() - no items match' | .NET 10.0 | 65536 | 108,930.15 ns | 246.646 ns | 192.565 ns | 109,003.67 ns | 108,645.38 ns | 109,125.46 ns | ? | ? | - | 72 B | ? | -| | | | | | | | | | | | | | | -| 'Query(predicate).Count() - all items match' | .NET 9.0 | 65536 | 60,707.35 ns | 2,205.203 ns | 2,539.515 ns | 60,570.73 ns | 55,538.29 ns | 64,007.15 ns | ? | ? | - | 72 B | ? | -| 'Query(predicate).Count() - half items match' | .NET 9.0 | 65536 | 138,382.02 ns | 631.612 ns | 590.810 ns | 138,379.11 ns | 137,269.57 ns | 139,327.25 ns | ? | ? | - | 72 B | ? | -| 'Query(predicate).Count() - no items match' | .NET 9.0 | 65536 | 59,290.61 ns | 3,400.160 ns | 3,915.629 ns | 60,341.58 ns | 50,645.71 ns | 64,947.46 ns | ? | ? | - | 72 B | ? | -| | | | | | | | | | | | | | | -| 'Query(predicate).Count() - all items match' | .NET Framework 4.8 | 65536 | 487,925.14 ns | 3,789.195 ns | 3,544.416 ns | 488,684.38 ns | 481,920.31 ns | 494,299.80 ns | ? | ? | - | 64 B | ? | -| 'Query(predicate).Count() - half items match' | .NET Framework 4.8 | 65536 | 431,345.99 ns | 2,383.974 ns | 2,229.970 ns | 430,600.34 ns | 427,991.22 ns | 435,213.01 ns | ? | ? | - | 69 B | ? | -| 'Query(predicate).Count() - no items match' | .NET Framework 4.8 | 65536 | 249,407.48 ns | 2,384.811 ns | 2,114.073 ns | 249,935.94 ns | 245,151.66 ns | 253,267.58 ns | ? | ? | - | 72 B | ? | -| | | | | | | | | | | | | | | -| **'Query(type-equality predicate).Count()'** | **.NET 10.0** | **256** | **478.94 ns** | **6.868 ns** | **6.745 ns** | **477.84 ns** | **468.76 ns** | **497.91 ns** | **1.00** | **0.02** | **0.0039** | **72 B** | **1.00** | -| QueryFor<Derived1>().Count() | .NET 10.0 | 256 | 810.52 ns | 4.057 ns | 3.596 ns | 810.79 ns | 804.73 ns | 815.75 ns | 1.69 | 0.02 | 0.0065 | 128 B | 1.78 | -| | | | | | | | | | | | | | | -| 'Query(type-equality predicate).Count()' | .NET 9.0 | 256 | 92.89 ns | 1.053 ns | 0.934 ns | 92.50 ns | 91.82 ns | 94.92 ns | 1.00 | 0.01 | 0.0045 | 72 B | 1.00 | -| QueryFor<Derived1>().Count() | .NET 9.0 | 256 | 766.62 ns | 5.193 ns | 4.858 ns | 765.63 ns | 760.25 ns | 778.11 ns | 8.25 | 0.09 | 0.0061 | 128 B | 1.78 | -| | | | | | | | | | | | | | | -| 'Query(type-equality predicate).Count()' | .NET Framework 4.8 | 256 | 1,467.92 ns | 4.804 ns | 4.494 ns | 1,468.57 ns | 1,459.29 ns | 1,477.86 ns | 1.00 | 0.00 | 0.0059 | 72 B | 1.00 | -| QueryFor<Derived1>().Count() | .NET Framework 4.8 | 256 | 3,146.21 ns | 16.584 ns | 14.702 ns | 3,143.60 ns | 3,127.95 ns | 3,173.05 ns | 2.14 | 0.01 | 0.0125 | 128 B | 1.78 | -| | | | | | | | | | | | | | | -| **'Query(type-equality predicate).Count()'** | **.NET 10.0** | **4096** | **7,527.07 ns** | **142.080 ns** | **152.024 ns** | **7,566.75 ns** | **7,254.28 ns** | **7,707.01 ns** | **1.00** | **0.03** | **-** | **72 B** | **1.00** | -| QueryFor<Derived1>().Count() | .NET 10.0 | 4096 | 12,557.97 ns | 139.609 ns | 108.997 ns | 12,545.04 ns | 12,427.51 ns | 12,733.85 ns | 1.67 | 0.04 | - | 128 B | 1.78 | -| | | | | | | | | | | | | | | -| 'Query(type-equality predicate).Count()' | .NET 9.0 | 4096 | 1,146.01 ns | 12.824 ns | 11.996 ns | 1,144.08 ns | 1,128.64 ns | 1,168.73 ns | 1.00 | 0.01 | 0.0045 | 72 B | 1.00 | -| QueryFor<Derived1>().Count() | .NET 9.0 | 4096 | 11,531.14 ns | 57.844 ns | 48.302 ns | 11,514.52 ns | 11,481.89 ns | 11,627.61 ns | 10.06 | 0.11 | - | 128 B | 1.78 | -| | | | | | | | | | | | | | | -| 'Query(type-equality predicate).Count()' | .NET Framework 4.8 | 4096 | 22,921.91 ns | 257.502 ns | 240.867 ns | 22,903.08 ns | 22,595.50 ns | 23,343.91 ns | 1.00 | 0.01 | - | 72 B | 1.00 | -| QueryFor<Derived1>().Count() | .NET Framework 4.8 | 4096 | 48,256.16 ns | 556.949 ns | 493.721 ns | 48,061.74 ns | 47,608.44 ns | 49,229.78 ns | 2.11 | 0.03 | - | 128 B | 1.78 | -| | | | | | | | | | | | | | | -| **'Query(type-equality predicate).Count()'** | **.NET 10.0** | **65536** | **123,428.92 ns** | **2,082.105 ns** | **1,845.732 ns** | **124,128.12 ns** | **118,452.44 ns** | **125,003.91 ns** | **1.00** | **0.02** | **-** | **72 B** | **1.00** | -| QueryFor<Derived1>().Count() | .NET 10.0 | 65536 | 189,344.37 ns | 1,021.614 ns | 955.618 ns | 189,505.28 ns | 187,238.69 ns | 190,478.50 ns | 1.53 | 0.02 | - | 128 B | 1.78 | -| | | | | | | | | | | | | | | -| 'Query(type-equality predicate).Count()' | .NET 9.0 | 65536 | 34,952.85 ns | 690.115 ns | 767.061 ns | 35,169.69 ns | 32,416.76 ns | 35,580.01 ns | 1.00 | 0.03 | - | 72 B | 1.00 | -| QueryFor<Derived1>().Count() | .NET 9.0 | 65536 | 191,255.43 ns | 822.440 ns | 729.071 ns | 191,409.22 ns | 190,129.88 ns | 192,548.32 ns | 5.47 | 0.12 | - | 128 B | 1.78 | -| | | | | | | | | | | | | | | -| 'Query(type-equality predicate).Count()' | .NET Framework 4.8 | 65536 | 365,193.58 ns | 1,548.863 ns | 1,448.807 ns | 365,755.01 ns | 362,013.44 ns | 366,651.24 ns | 1.00 | 0.01 | - | 71 B | 1.00 | -| QueryFor<Derived1>().Count() | .NET Framework 4.8 | 65536 | 794,356.49 ns | 15,709.234 ns | 15,428.568 ns | 791,422.77 ns | 762,669.64 ns | 819,878.87 ns | 2.18 | 0.04 | - | 122 B | 1.72 | +``` + +BenchmarkDotNet v0.15.8, Windows 11 (10.0.26200.8737/25H2/2025Update/HudsonValley2) +12th Gen Intel Core i9-12900KF 3.20GHz, 1 CPU, 24 logical and 16 physical cores +.NET SDK 11.0.100-preview.4.26230.115 + [Host] : .NET 10.0.10 (10.0.10, 10.0.1026.32716), X64 RyuJIT x86-64-v3 + Job-LDLMHG : .NET 10.0.10 (10.0.10, 10.0.1026.32716), X64 RyuJIT x86-64-v3 + Job-IOAYXE : .NET 9.0.18 (9.0.18, 9.0.1826.31522), X64 RyuJIT x86-64-v3 + Job-GEUVPA : .NET Framework 4.8.1 (4.8.9337.0), X64 RyuJIT VectorSize=256 + +PowerPlanMode=00000000-0000-0000-0000-000000000000 IterationTime=250ms MaxIterationCount=20 +MinIterationCount=15 WarmupCount=1 + +``` +| Method | Runtime | Count | Mean | Error | StdDev | Median | Min | Max | Ratio | RatioSD | Gen0 | Allocated | Alloc Ratio | +|---------------------------------------------- |------------------- |------ |--------------:|--------------:|--------------:|--------------:|--------------:|--------------:|------:|--------:|-------:|----------:|------------:| +| **'Query(predicate).Count() - all items match'** | **.NET 10.0** | **256** | **486.19 ns** | **34.108 ns** | **37.911 ns** | **485.88 ns** | **359.47 ns** | **554.43 ns** | **?** | **?** | **0.0041** | **72 B** | **?** | +| 'Query(predicate).Count() - half items match' | .NET 10.0 | 256 | 788.08 ns | 37.360 ns | 43.023 ns | 789.28 ns | 667.98 ns | 860.36 ns | ? | ? | 0.0025 | 72 B | ? | +| 'Query(predicate).Count() - no items match' | .NET 10.0 | 256 | 535.49 ns | 26.691 ns | 30.737 ns | 538.63 ns | 461.68 ns | 589.39 ns | ? | ? | 0.0031 | 72 B | ? | +| | | | | | | | | | | | | | | +| 'Query(predicate).Count() - all items match' | .NET 9.0 | 256 | 166.94 ns | 13.729 ns | 15.810 ns | 169.24 ns | 102.71 ns | 180.89 ns | ? | ? | 0.0046 | 72 B | ? | +| 'Query(predicate).Count() - half items match' | .NET 9.0 | 256 | 755.63 ns | 67.404 ns | 77.622 ns | 766.26 ns | 560.41 ns | 865.63 ns | ? | ? | 0.0044 | 72 B | ? | +| 'Query(predicate).Count() - no items match' | .NET 9.0 | 256 | 137.16 ns | 18.372 ns | 21.157 ns | 142.14 ns | 76.57 ns | 153.13 ns | ? | ? | 0.0044 | 72 B | ? | +| | | | | | | | | | | | | | | +| 'Query(predicate).Count() - all items match' | .NET Framework 4.8 | 256 | 2,780.37 ns | 303.660 ns | 349.695 ns | 2,827.17 ns | 1,987.71 ns | 3,207.74 ns | ? | ? | 0.0080 | 72 B | ? | +| 'Query(predicate).Count() - half items match' | .NET Framework 4.8 | 256 | 2,737.73 ns | 139.743 ns | 160.928 ns | 2,708.28 ns | 2,510.94 ns | 3,024.14 ns | ? | ? | 0.0068 | 72 B | ? | +| 'Query(predicate).Count() - no items match' | .NET Framework 4.8 | 256 | 1,702.18 ns | 401.519 ns | 462.390 ns | 1,861.91 ns | 1,005.25 ns | 2,310.27 ns | ? | ? | 0.0081 | 72 B | ? | +| | | | | | | | | | | | | | | +| **'Query(predicate).Count() - all items match'** | **.NET 10.0** | **4096** | **7,560.18 ns** | **1,202.244 ns** | **1,384.506 ns** | **8,142.52 ns** | **5,113.88 ns** | **8,937.35 ns** | **?** | **?** | **-** | **72 B** | **?** | +| 'Query(predicate).Count() - half items match' | .NET 10.0 | 4096 | 12,204.71 ns | 1,677.461 ns | 1,931.767 ns | 13,007.85 ns | 9,766.58 ns | 15,321.51 ns | ? | ? | - | 72 B | ? | +| 'Query(predicate).Count() - no items match' | .NET 10.0 | 4096 | 8,452.84 ns | 884.280 ns | 1,018.339 ns | 8,679.65 ns | 5,949.62 ns | 9,637.66 ns | ? | ? | - | 72 B | ? | +| | | | | | | | | | | | | | | +| 'Query(predicate).Count() - all items match' | .NET 9.0 | 4096 | 2,561.76 ns | 602.033 ns | 693.302 ns | 2,891.90 ns | 1,415.56 ns | 3,223.20 ns | ? | ? | - | 72 B | ? | +| 'Query(predicate).Count() - half items match' | .NET 9.0 | 4096 | 9,479.95 ns | 41.398 ns | 38.724 ns | 9,476.20 ns | 9,408.77 ns | 9,545.46 ns | ? | ? | - | 72 B | ? | +| 'Query(predicate).Count() - no items match' | .NET 9.0 | 4096 | 1,223.50 ns | 6.127 ns | 5.431 ns | 1,223.26 ns | 1,215.82 ns | 1,233.80 ns | ? | ? | - | 72 B | ? | +| | | | | | | | | | | | | | | +| 'Query(predicate).Count() - all items match' | .NET Framework 4.8 | 4096 | 31,206.51 ns | 139.450 ns | 130.442 ns | 31,249.54 ns | 30,966.07 ns | 31,347.75 ns | ? | ? | - | 71 B | ? | +| 'Query(predicate).Count() - half items match' | .NET Framework 4.8 | 4096 | 26,401.17 ns | 47.010 ns | 43.973 ns | 26,395.60 ns | 26,337.25 ns | 26,473.50 ns | ? | ? | - | 72 B | ? | +| 'Query(predicate).Count() - no items match' | .NET Framework 4.8 | 4096 | 15,524.36 ns | 73.356 ns | 68.617 ns | 15,532.35 ns | 15,360.02 ns | 15,630.49 ns | ? | ? | - | 72 B | ? | +| | | | | | | | | | | | | | | +| **'Query(predicate).Count() - all items match'** | **.NET 10.0** | **65536** | **95,347.05 ns** | **185.107 ns** | **164.093 ns** | **95,347.39 ns** | **95,071.70 ns** | **95,654.66 ns** | **?** | **?** | **-** | **72 B** | **?** | +| 'Query(predicate).Count() - half items match' | .NET 10.0 | 65536 | 148,640.18 ns | 1,916.770 ns | 1,792.948 ns | 148,486.59 ns | 144,905.11 ns | 151,601.37 ns | ? | ? | - | 72 B | ? | +| 'Query(predicate).Count() - no items match' | .NET 10.0 | 65536 | 108,930.15 ns | 246.646 ns | 192.565 ns | 109,003.67 ns | 108,645.38 ns | 109,125.46 ns | ? | ? | - | 72 B | ? | +| | | | | | | | | | | | | | | +| 'Query(predicate).Count() - all items match' | .NET 9.0 | 65536 | 60,707.35 ns | 2,205.203 ns | 2,539.515 ns | 60,570.73 ns | 55,538.29 ns | 64,007.15 ns | ? | ? | - | 72 B | ? | +| 'Query(predicate).Count() - half items match' | .NET 9.0 | 65536 | 138,382.02 ns | 631.612 ns | 590.810 ns | 138,379.11 ns | 137,269.57 ns | 139,327.25 ns | ? | ? | - | 72 B | ? | +| 'Query(predicate).Count() - no items match' | .NET 9.0 | 65536 | 59,290.61 ns | 3,400.160 ns | 3,915.629 ns | 60,341.58 ns | 50,645.71 ns | 64,947.46 ns | ? | ? | - | 72 B | ? | +| | | | | | | | | | | | | | | +| 'Query(predicate).Count() - all items match' | .NET Framework 4.8 | 65536 | 487,925.14 ns | 3,789.195 ns | 3,544.416 ns | 488,684.38 ns | 481,920.31 ns | 494,299.80 ns | ? | ? | - | 64 B | ? | +| 'Query(predicate).Count() - half items match' | .NET Framework 4.8 | 65536 | 431,345.99 ns | 2,383.974 ns | 2,229.970 ns | 430,600.34 ns | 427,991.22 ns | 435,213.01 ns | ? | ? | - | 69 B | ? | +| 'Query(predicate).Count() - no items match' | .NET Framework 4.8 | 65536 | 249,407.48 ns | 2,384.811 ns | 2,114.073 ns | 249,935.94 ns | 245,151.66 ns | 253,267.58 ns | ? | ? | - | 72 B | ? | +| | | | | | | | | | | | | | | +| **'Query(type-equality predicate).Count()'** | **.NET 10.0** | **256** | **478.94 ns** | **6.868 ns** | **6.745 ns** | **477.84 ns** | **468.76 ns** | **497.91 ns** | **1.00** | **0.02** | **0.0039** | **72 B** | **1.00** | +| QueryFor<Derived1>().Count() | .NET 10.0 | 256 | 810.52 ns | 4.057 ns | 3.596 ns | 810.79 ns | 804.73 ns | 815.75 ns | 1.69 | 0.02 | 0.0065 | 128 B | 1.78 | +| | | | | | | | | | | | | | | +| 'Query(type-equality predicate).Count()' | .NET 9.0 | 256 | 92.89 ns | 1.053 ns | 0.934 ns | 92.50 ns | 91.82 ns | 94.92 ns | 1.00 | 0.01 | 0.0045 | 72 B | 1.00 | +| QueryFor<Derived1>().Count() | .NET 9.0 | 256 | 766.62 ns | 5.193 ns | 4.858 ns | 765.63 ns | 760.25 ns | 778.11 ns | 8.25 | 0.09 | 0.0061 | 128 B | 1.78 | +| | | | | | | | | | | | | | | +| 'Query(type-equality predicate).Count()' | .NET Framework 4.8 | 256 | 1,467.92 ns | 4.804 ns | 4.494 ns | 1,468.57 ns | 1,459.29 ns | 1,477.86 ns | 1.00 | 0.00 | 0.0059 | 72 B | 1.00 | +| QueryFor<Derived1>().Count() | .NET Framework 4.8 | 256 | 3,146.21 ns | 16.584 ns | 14.702 ns | 3,143.60 ns | 3,127.95 ns | 3,173.05 ns | 2.14 | 0.01 | 0.0125 | 128 B | 1.78 | +| | | | | | | | | | | | | | | +| **'Query(type-equality predicate).Count()'** | **.NET 10.0** | **4096** | **7,527.07 ns** | **142.080 ns** | **152.024 ns** | **7,566.75 ns** | **7,254.28 ns** | **7,707.01 ns** | **1.00** | **0.03** | **-** | **72 B** | **1.00** | +| QueryFor<Derived1>().Count() | .NET 10.0 | 4096 | 12,557.97 ns | 139.609 ns | 108.997 ns | 12,545.04 ns | 12,427.51 ns | 12,733.85 ns | 1.67 | 0.04 | - | 128 B | 1.78 | +| | | | | | | | | | | | | | | +| 'Query(type-equality predicate).Count()' | .NET 9.0 | 4096 | 1,146.01 ns | 12.824 ns | 11.996 ns | 1,144.08 ns | 1,128.64 ns | 1,168.73 ns | 1.00 | 0.01 | 0.0045 | 72 B | 1.00 | +| QueryFor<Derived1>().Count() | .NET 9.0 | 4096 | 11,531.14 ns | 57.844 ns | 48.302 ns | 11,514.52 ns | 11,481.89 ns | 11,627.61 ns | 10.06 | 0.11 | - | 128 B | 1.78 | +| | | | | | | | | | | | | | | +| 'Query(type-equality predicate).Count()' | .NET Framework 4.8 | 4096 | 22,921.91 ns | 257.502 ns | 240.867 ns | 22,903.08 ns | 22,595.50 ns | 23,343.91 ns | 1.00 | 0.01 | - | 72 B | 1.00 | +| QueryFor<Derived1>().Count() | .NET Framework 4.8 | 4096 | 48,256.16 ns | 556.949 ns | 493.721 ns | 48,061.74 ns | 47,608.44 ns | 49,229.78 ns | 2.11 | 0.03 | - | 128 B | 1.78 | +| | | | | | | | | | | | | | | +| **'Query(type-equality predicate).Count()'** | **.NET 10.0** | **65536** | **123,428.92 ns** | **2,082.105 ns** | **1,845.732 ns** | **124,128.12 ns** | **118,452.44 ns** | **125,003.91 ns** | **1.00** | **0.02** | **-** | **72 B** | **1.00** | +| QueryFor<Derived1>().Count() | .NET 10.0 | 65536 | 189,344.37 ns | 1,021.614 ns | 955.618 ns | 189,505.28 ns | 187,238.69 ns | 190,478.50 ns | 1.53 | 0.02 | - | 128 B | 1.78 | +| | | | | | | | | | | | | | | +| 'Query(type-equality predicate).Count()' | .NET 9.0 | 65536 | 34,952.85 ns | 690.115 ns | 767.061 ns | 35,169.69 ns | 32,416.76 ns | 35,580.01 ns | 1.00 | 0.03 | - | 72 B | 1.00 | +| QueryFor<Derived1>().Count() | .NET 9.0 | 65536 | 191,255.43 ns | 822.440 ns | 729.071 ns | 191,409.22 ns | 190,129.88 ns | 192,548.32 ns | 5.47 | 0.12 | - | 128 B | 1.78 | +| | | | | | | | | | | | | | | +| 'Query(type-equality predicate).Count()' | .NET Framework 4.8 | 65536 | 365,193.58 ns | 1,548.863 ns | 1,448.807 ns | 365,755.01 ns | 362,013.44 ns | 366,651.24 ns | 1.00 | 0.01 | - | 71 B | 1.00 | +| QueryFor<Derived1>().Count() | .NET Framework 4.8 | 65536 | 794,356.49 ns | 15,709.234 ns | 15,428.568 ns | 791,422.77 ns | 762,669.64 ns | 819,878.87 ns | 2.18 | 0.04 | - | 122 B | 1.72 | diff --git a/reports/tuning/Codebelt.Extensions.Xunit.TestBenchmark-report-github.md b/reports/tuning/Codebelt.Extensions.Xunit.TestBenchmark-report-github.md index 5b73138..fbe3d40 100644 --- a/reports/tuning/Codebelt.Extensions.Xunit.TestBenchmark-report-github.md +++ b/reports/tuning/Codebelt.Extensions.Xunit.TestBenchmark-report-github.md @@ -1,60 +1,60 @@ -``` - -BenchmarkDotNet v0.15.8, Windows 11 (10.0.26200.7462/25H2/2025Update/HudsonValley2) -12th Gen Intel Core i9-12900KF 3.20GHz, 1 CPU, 24 logical and 16 physical cores -.NET SDK 10.0.101 - [Host] : .NET 10.0.1 (10.0.1, 10.0.125.57005), X64 RyuJIT x86-64-v3 - Job-LDLMHG : .NET 10.0.1 (10.0.1, 10.0.125.57005), X64 RyuJIT x86-64-v3 - Job-IOAYXE : .NET 9.0.11 (9.0.11, 9.0.1125.51716), X64 RyuJIT x86-64-v3 - Job-GEUVPA : .NET Framework 4.8.1 (4.8.9323.0), X64 RyuJIT VectorSize=256 - -PowerPlanMode=00000000-0000-0000-0000-000000000000 IterationTime=250ms MaxIterationCount=20 -MinIterationCount=15 WarmupCount=1 - -``` -| Method | Runtime | Length | Mean | Error | StdDev | Median | Min | Max | Ratio | RatioSD | Gen0 | Gen1 | Allocated | Alloc Ratio | -|--------------------------- |------------------- |------- |-------------:|-------------:|-------------:|-------------:|-------------:|-------------:|---------:|--------:|-------:|-------:|----------:|------------:| -| **'Match - exact string'** | **.NET 10.0** | **8** | **15.12 ns** | **0.291 ns** | **0.258 ns** | **15.18 ns** | **14.67 ns** | **15.50 ns** | **1.00** | **0.02** | **0.0025** | **-** | **40 B** | **1.00** | -| 'Match - simple wildcard' | .NET 10.0 | 8 | 109.21 ns | 2.173 ns | 2.415 ns | 109.17 ns | 106.04 ns | 113.64 ns | 7.23 | 0.20 | 0.0099 | - | 160 B | 4.00 | -| 'Match - multiline' | .NET 10.0 | 8 | 13.97 ns | 1.594 ns | 1.836 ns | 13.28 ns | 11.69 ns | 17.01 ns | 0.92 | 0.12 | 0.0025 | - | 40 B | 1.00 | -| 'Match - complex wildcard' | .NET 10.0 | 8 | 263.54 ns | 4.400 ns | 3.901 ns | 263.27 ns | 257.85 ns | 269.79 ns | 17.44 | 0.38 | 0.0211 | - | 344 B | 8.60 | -| | | | | | | | | | | | | | | | -| 'Match - exact string' | .NET 9.0 | 8 | 13.73 ns | 0.390 ns | 0.433 ns | 13.64 ns | 13.17 ns | 14.82 ns | 1.00 | 0.04 | 0.0025 | - | 40 B | 1.00 | -| 'Match - simple wildcard' | .NET 9.0 | 8 | 117.91 ns | 2.328 ns | 2.587 ns | 118.37 ns | 114.22 ns | 124.26 ns | 8.60 | 0.32 | 0.0099 | - | 160 B | 4.00 | -| 'Match - multiline' | .NET 9.0 | 8 | 11.29 ns | 0.732 ns | 0.814 ns | 11.32 ns | 10.00 ns | 13.27 ns | 0.82 | 0.06 | 0.0025 | - | 40 B | 1.00 | -| 'Match - complex wildcard' | .NET 9.0 | 8 | 282.81 ns | 6.116 ns | 7.043 ns | 283.45 ns | 269.15 ns | 296.05 ns | 20.62 | 0.80 | 0.0217 | - | 344 B | 8.60 | -| | | | | | | | | | | | | | | | -| 'Match - exact string' | .NET Framework 4.8 | 8 | 89.50 ns | 1.744 ns | 1.631 ns | 88.89 ns | 87.59 ns | 93.49 ns | 1.00 | 0.02 | 0.0062 | - | 40 B | 1.00 | -| 'Match - simple wildcard' | .NET Framework 4.8 | 8 | 432.90 ns | 10.901 ns | 12.553 ns | 430.61 ns | 416.54 ns | 462.62 ns | 4.84 | 0.16 | 0.0954 | - | 602 B | 15.05 | -| 'Match - multiline' | .NET Framework 4.8 | 8 | 21.49 ns | 0.498 ns | 0.553 ns | 21.34 ns | 20.77 ns | 22.61 ns | 0.24 | 0.01 | 0.0063 | - | 40 B | 1.00 | -| 'Match - complex wildcard' | .NET Framework 4.8 | 8 | 770.08 ns | 15.111 ns | 17.402 ns | 769.63 ns | 745.11 ns | 803.13 ns | 8.61 | 0.24 | 0.1474 | - | 931 B | 23.27 | -| | | | | | | | | | | | | | | | -| **'Match - exact string'** | **.NET 10.0** | **256** | **14.93 ns** | **0.376 ns** | **0.433 ns** | **14.82 ns** | **14.29 ns** | **15.81 ns** | **1.00** | **0.04** | **0.0025** | **-** | **40 B** | **1.00** | -| 'Match - simple wildcard' | .NET 10.0 | 256 | 110.95 ns | 2.223 ns | 2.559 ns | 110.70 ns | 107.02 ns | 116.21 ns | 7.44 | 0.27 | 0.0102 | - | 160 B | 4.00 | -| 'Match - multiline' | .NET 10.0 | 256 | 1,447.58 ns | 27.926 ns | 24.755 ns | 1,454.56 ns | 1,402.05 ns | 1,486.62 ns | 97.05 | 3.16 | 0.1069 | - | 1736 B | 43.40 | -| 'Match - complex wildcard' | .NET 10.0 | 256 | 257.42 ns | 7.766 ns | 8.309 ns | 256.00 ns | 247.51 ns | 279.00 ns | 17.26 | 0.73 | 0.0211 | - | 344 B | 8.60 | -| | | | | | | | | | | | | | | | -| 'Match - exact string' | .NET 9.0 | 256 | 14.08 ns | 0.705 ns | 0.784 ns | 14.11 ns | 12.83 ns | 15.68 ns | 1.00 | 0.08 | 0.0025 | - | 40 B | 1.00 | -| 'Match - simple wildcard' | .NET 9.0 | 256 | 118.71 ns | 1.922 ns | 1.605 ns | 118.77 ns | 115.56 ns | 121.17 ns | 8.46 | 0.48 | 0.0100 | - | 160 B | 4.00 | -| 'Match - multiline' | .NET 9.0 | 256 | 1,434.96 ns | 30.533 ns | 35.162 ns | 1,416.97 ns | 1,391.07 ns | 1,512.15 ns | 102.21 | 6.10 | 0.1050 | - | 1736 B | 43.40 | -| 'Match - complex wildcard' | .NET 9.0 | 256 | 288.79 ns | 9.566 ns | 10.236 ns | 285.78 ns | 275.88 ns | 310.98 ns | 20.57 | 1.33 | 0.0217 | - | 344 B | 8.60 | -| | | | | | | | | | | | | | | | -| 'Match - exact string' | .NET Framework 4.8 | 256 | 89.29 ns | 1.399 ns | 1.309 ns | 89.07 ns | 86.80 ns | 91.99 ns | 1.00 | 0.02 | 0.0061 | - | 40 B | 1.00 | -| 'Match - simple wildcard' | .NET Framework 4.8 | 256 | 428.97 ns | 10.686 ns | 12.306 ns | 426.84 ns | 415.34 ns | 461.31 ns | 4.81 | 0.15 | 0.0955 | - | 602 B | 15.05 | -| 'Match - multiline' | .NET Framework 4.8 | 256 | 3,773.89 ns | 89.406 ns | 99.374 ns | 3,773.88 ns | 3,630.37 ns | 3,953.11 ns | 42.28 | 1.24 | 0.6768 | - | 4309 B | 107.72 | -| 'Match - complex wildcard' | .NET Framework 4.8 | 256 | 777.00 ns | 17.789 ns | 19.773 ns | 775.74 ns | 751.58 ns | 812.93 ns | 8.70 | 0.25 | 0.1456 | - | 931 B | 23.27 | -| | | | | | | | | | | | | | | | -| **'Match - exact string'** | **.NET 10.0** | **4096** | **15.52 ns** | **0.865 ns** | **0.996 ns** | **15.50 ns** | **14.30 ns** | **17.50 ns** | **1.00** | **0.09** | **0.0025** | **-** | **40 B** | **1.00** | -| 'Match - simple wildcard' | .NET 10.0 | 4096 | 109.29 ns | 3.125 ns | 3.599 ns | 110.04 ns | 103.08 ns | 114.44 ns | 7.07 | 0.49 | 0.0101 | - | 160 B | 4.00 | -| 'Match - multiline' | .NET 10.0 | 4096 | 23,205.61 ns | 550.277 ns | 633.700 ns | 23,129.00 ns | 22,266.61 ns | 24,565.33 ns | 1,501.35 | 99.61 | 1.7724 | - | 28464 B | 711.60 | -| 'Match - complex wildcard' | .NET 10.0 | 4096 | 265.02 ns | 5.325 ns | 5.468 ns | 263.94 ns | 258.64 ns | 276.42 ns | 17.15 | 1.10 | 0.0216 | - | 344 B | 8.60 | -| | | | | | | | | | | | | | | | -| 'Match - exact string' | .NET 9.0 | 4096 | 13.72 ns | 0.558 ns | 0.642 ns | 13.44 ns | 12.88 ns | 14.89 ns | 1.00 | 0.06 | 0.0025 | - | 40 B | 1.00 | -| 'Match - simple wildcard' | .NET 9.0 | 4096 | 110.52 ns | 2.152 ns | 1.908 ns | 110.23 ns | 107.63 ns | 114.22 ns | 8.07 | 0.39 | 0.0101 | - | 160 B | 4.00 | -| 'Match - multiline' | .NET 9.0 | 4096 | 23,242.24 ns | 482.794 ns | 555.986 ns | 23,324.47 ns | 22,433.46 ns | 24,217.26 ns | 1,697.76 | 85.87 | 1.7619 | - | 28464 B | 711.60 | -| 'Match - complex wildcard' | .NET 9.0 | 4096 | 277.72 ns | 8.913 ns | 9.907 ns | 275.28 ns | 267.04 ns | 302.76 ns | 20.29 | 1.15 | 0.0219 | - | 344 B | 8.60 | -| | | | | | | | | | | | | | | | -| 'Match - exact string' | .NET Framework 4.8 | 4096 | 89.24 ns | 0.583 ns | 0.517 ns | 89.35 ns | 88.34 ns | 89.82 ns | 1.00 | 0.01 | 0.0061 | - | 40 B | 1.00 | -| 'Match - simple wildcard' | .NET Framework 4.8 | 4096 | 445.17 ns | 8.842 ns | 9.080 ns | 445.44 ns | 427.55 ns | 460.91 ns | 4.99 | 0.10 | 0.0952 | - | 602 B | 15.05 | -| 'Match - multiline' | .NET Framework 4.8 | 4096 | 58,890.05 ns | 1,508.951 ns | 1,677.195 ns | 58,832.79 ns | 56,104.01 ns | 62,709.42 ns | 659.92 | 18.70 | 9.6154 | 0.9615 | 61757 B | 1,543.92 | -| 'Match - complex wildcard' | .NET Framework 4.8 | 4096 | 769.62 ns | 14.805 ns | 15.203 ns | 771.12 ns | 745.88 ns | 800.63 ns | 8.62 | 0.17 | 0.1477 | - | 931 B | 23.27 | +``` + +BenchmarkDotNet v0.15.8, Windows 11 (10.0.26200.7462/25H2/2025Update/HudsonValley2) +12th Gen Intel Core i9-12900KF 3.20GHz, 1 CPU, 24 logical and 16 physical cores +.NET SDK 10.0.101 + [Host] : .NET 10.0.1 (10.0.1, 10.0.125.57005), X64 RyuJIT x86-64-v3 + Job-LDLMHG : .NET 10.0.1 (10.0.1, 10.0.125.57005), X64 RyuJIT x86-64-v3 + Job-IOAYXE : .NET 9.0.11 (9.0.11, 9.0.1125.51716), X64 RyuJIT x86-64-v3 + Job-GEUVPA : .NET Framework 4.8.1 (4.8.9323.0), X64 RyuJIT VectorSize=256 + +PowerPlanMode=00000000-0000-0000-0000-000000000000 IterationTime=250ms MaxIterationCount=20 +MinIterationCount=15 WarmupCount=1 + +``` +| Method | Runtime | Length | Mean | Error | StdDev | Median | Min | Max | Ratio | RatioSD | Gen0 | Gen1 | Allocated | Alloc Ratio | +|--------------------------- |------------------- |------- |-------------:|-------------:|-------------:|-------------:|-------------:|-------------:|---------:|--------:|-------:|-------:|----------:|------------:| +| **'Match - exact string'** | **.NET 10.0** | **8** | **15.12 ns** | **0.291 ns** | **0.258 ns** | **15.18 ns** | **14.67 ns** | **15.50 ns** | **1.00** | **0.02** | **0.0025** | **-** | **40 B** | **1.00** | +| 'Match - simple wildcard' | .NET 10.0 | 8 | 109.21 ns | 2.173 ns | 2.415 ns | 109.17 ns | 106.04 ns | 113.64 ns | 7.23 | 0.20 | 0.0099 | - | 160 B | 4.00 | +| 'Match - multiline' | .NET 10.0 | 8 | 13.97 ns | 1.594 ns | 1.836 ns | 13.28 ns | 11.69 ns | 17.01 ns | 0.92 | 0.12 | 0.0025 | - | 40 B | 1.00 | +| 'Match - complex wildcard' | .NET 10.0 | 8 | 263.54 ns | 4.400 ns | 3.901 ns | 263.27 ns | 257.85 ns | 269.79 ns | 17.44 | 0.38 | 0.0211 | - | 344 B | 8.60 | +| | | | | | | | | | | | | | | | +| 'Match - exact string' | .NET 9.0 | 8 | 13.73 ns | 0.390 ns | 0.433 ns | 13.64 ns | 13.17 ns | 14.82 ns | 1.00 | 0.04 | 0.0025 | - | 40 B | 1.00 | +| 'Match - simple wildcard' | .NET 9.0 | 8 | 117.91 ns | 2.328 ns | 2.587 ns | 118.37 ns | 114.22 ns | 124.26 ns | 8.60 | 0.32 | 0.0099 | - | 160 B | 4.00 | +| 'Match - multiline' | .NET 9.0 | 8 | 11.29 ns | 0.732 ns | 0.814 ns | 11.32 ns | 10.00 ns | 13.27 ns | 0.82 | 0.06 | 0.0025 | - | 40 B | 1.00 | +| 'Match - complex wildcard' | .NET 9.0 | 8 | 282.81 ns | 6.116 ns | 7.043 ns | 283.45 ns | 269.15 ns | 296.05 ns | 20.62 | 0.80 | 0.0217 | - | 344 B | 8.60 | +| | | | | | | | | | | | | | | | +| 'Match - exact string' | .NET Framework 4.8 | 8 | 89.50 ns | 1.744 ns | 1.631 ns | 88.89 ns | 87.59 ns | 93.49 ns | 1.00 | 0.02 | 0.0062 | - | 40 B | 1.00 | +| 'Match - simple wildcard' | .NET Framework 4.8 | 8 | 432.90 ns | 10.901 ns | 12.553 ns | 430.61 ns | 416.54 ns | 462.62 ns | 4.84 | 0.16 | 0.0954 | - | 602 B | 15.05 | +| 'Match - multiline' | .NET Framework 4.8 | 8 | 21.49 ns | 0.498 ns | 0.553 ns | 21.34 ns | 20.77 ns | 22.61 ns | 0.24 | 0.01 | 0.0063 | - | 40 B | 1.00 | +| 'Match - complex wildcard' | .NET Framework 4.8 | 8 | 770.08 ns | 15.111 ns | 17.402 ns | 769.63 ns | 745.11 ns | 803.13 ns | 8.61 | 0.24 | 0.1474 | - | 931 B | 23.27 | +| | | | | | | | | | | | | | | | +| **'Match - exact string'** | **.NET 10.0** | **256** | **14.93 ns** | **0.376 ns** | **0.433 ns** | **14.82 ns** | **14.29 ns** | **15.81 ns** | **1.00** | **0.04** | **0.0025** | **-** | **40 B** | **1.00** | +| 'Match - simple wildcard' | .NET 10.0 | 256 | 110.95 ns | 2.223 ns | 2.559 ns | 110.70 ns | 107.02 ns | 116.21 ns | 7.44 | 0.27 | 0.0102 | - | 160 B | 4.00 | +| 'Match - multiline' | .NET 10.0 | 256 | 1,447.58 ns | 27.926 ns | 24.755 ns | 1,454.56 ns | 1,402.05 ns | 1,486.62 ns | 97.05 | 3.16 | 0.1069 | - | 1736 B | 43.40 | +| 'Match - complex wildcard' | .NET 10.0 | 256 | 257.42 ns | 7.766 ns | 8.309 ns | 256.00 ns | 247.51 ns | 279.00 ns | 17.26 | 0.73 | 0.0211 | - | 344 B | 8.60 | +| | | | | | | | | | | | | | | | +| 'Match - exact string' | .NET 9.0 | 256 | 14.08 ns | 0.705 ns | 0.784 ns | 14.11 ns | 12.83 ns | 15.68 ns | 1.00 | 0.08 | 0.0025 | - | 40 B | 1.00 | +| 'Match - simple wildcard' | .NET 9.0 | 256 | 118.71 ns | 1.922 ns | 1.605 ns | 118.77 ns | 115.56 ns | 121.17 ns | 8.46 | 0.48 | 0.0100 | - | 160 B | 4.00 | +| 'Match - multiline' | .NET 9.0 | 256 | 1,434.96 ns | 30.533 ns | 35.162 ns | 1,416.97 ns | 1,391.07 ns | 1,512.15 ns | 102.21 | 6.10 | 0.1050 | - | 1736 B | 43.40 | +| 'Match - complex wildcard' | .NET 9.0 | 256 | 288.79 ns | 9.566 ns | 10.236 ns | 285.78 ns | 275.88 ns | 310.98 ns | 20.57 | 1.33 | 0.0217 | - | 344 B | 8.60 | +| | | | | | | | | | | | | | | | +| 'Match - exact string' | .NET Framework 4.8 | 256 | 89.29 ns | 1.399 ns | 1.309 ns | 89.07 ns | 86.80 ns | 91.99 ns | 1.00 | 0.02 | 0.0061 | - | 40 B | 1.00 | +| 'Match - simple wildcard' | .NET Framework 4.8 | 256 | 428.97 ns | 10.686 ns | 12.306 ns | 426.84 ns | 415.34 ns | 461.31 ns | 4.81 | 0.15 | 0.0955 | - | 602 B | 15.05 | +| 'Match - multiline' | .NET Framework 4.8 | 256 | 3,773.89 ns | 89.406 ns | 99.374 ns | 3,773.88 ns | 3,630.37 ns | 3,953.11 ns | 42.28 | 1.24 | 0.6768 | - | 4309 B | 107.72 | +| 'Match - complex wildcard' | .NET Framework 4.8 | 256 | 777.00 ns | 17.789 ns | 19.773 ns | 775.74 ns | 751.58 ns | 812.93 ns | 8.70 | 0.25 | 0.1456 | - | 931 B | 23.27 | +| | | | | | | | | | | | | | | | +| **'Match - exact string'** | **.NET 10.0** | **4096** | **15.52 ns** | **0.865 ns** | **0.996 ns** | **15.50 ns** | **14.30 ns** | **17.50 ns** | **1.00** | **0.09** | **0.0025** | **-** | **40 B** | **1.00** | +| 'Match - simple wildcard' | .NET 10.0 | 4096 | 109.29 ns | 3.125 ns | 3.599 ns | 110.04 ns | 103.08 ns | 114.44 ns | 7.07 | 0.49 | 0.0101 | - | 160 B | 4.00 | +| 'Match - multiline' | .NET 10.0 | 4096 | 23,205.61 ns | 550.277 ns | 633.700 ns | 23,129.00 ns | 22,266.61 ns | 24,565.33 ns | 1,501.35 | 99.61 | 1.7724 | - | 28464 B | 711.60 | +| 'Match - complex wildcard' | .NET 10.0 | 4096 | 265.02 ns | 5.325 ns | 5.468 ns | 263.94 ns | 258.64 ns | 276.42 ns | 17.15 | 1.10 | 0.0216 | - | 344 B | 8.60 | +| | | | | | | | | | | | | | | | +| 'Match - exact string' | .NET 9.0 | 4096 | 13.72 ns | 0.558 ns | 0.642 ns | 13.44 ns | 12.88 ns | 14.89 ns | 1.00 | 0.06 | 0.0025 | - | 40 B | 1.00 | +| 'Match - simple wildcard' | .NET 9.0 | 4096 | 110.52 ns | 2.152 ns | 1.908 ns | 110.23 ns | 107.63 ns | 114.22 ns | 8.07 | 0.39 | 0.0101 | - | 160 B | 4.00 | +| 'Match - multiline' | .NET 9.0 | 4096 | 23,242.24 ns | 482.794 ns | 555.986 ns | 23,324.47 ns | 22,433.46 ns | 24,217.26 ns | 1,697.76 | 85.87 | 1.7619 | - | 28464 B | 711.60 | +| 'Match - complex wildcard' | .NET 9.0 | 4096 | 277.72 ns | 8.913 ns | 9.907 ns | 275.28 ns | 267.04 ns | 302.76 ns | 20.29 | 1.15 | 0.0219 | - | 344 B | 8.60 | +| | | | | | | | | | | | | | | | +| 'Match - exact string' | .NET Framework 4.8 | 4096 | 89.24 ns | 0.583 ns | 0.517 ns | 89.35 ns | 88.34 ns | 89.82 ns | 1.00 | 0.01 | 0.0061 | - | 40 B | 1.00 | +| 'Match - simple wildcard' | .NET Framework 4.8 | 4096 | 445.17 ns | 8.842 ns | 9.080 ns | 445.44 ns | 427.55 ns | 460.91 ns | 4.99 | 0.10 | 0.0952 | - | 602 B | 15.05 | +| 'Match - multiline' | .NET Framework 4.8 | 4096 | 58,890.05 ns | 1,508.951 ns | 1,677.195 ns | 58,832.79 ns | 56,104.01 ns | 62,709.42 ns | 659.92 | 18.70 | 9.6154 | 0.9615 | 61757 B | 1,543.92 | +| 'Match - complex wildcard' | .NET Framework 4.8 | 4096 | 769.62 ns | 14.805 ns | 15.203 ns | 771.12 ns | 745.88 ns | 800.63 ns | 8.62 | 0.17 | 0.1477 | - | 931 B | 23.27 | diff --git a/src/Codebelt.Extensions.Xunit.Hosting/ServiceCollectionExtensions.cs b/src/Codebelt.Extensions.Xunit.Hosting/ServiceCollectionExtensions.cs index 26caaeb..388dd64 100644 --- a/src/Codebelt.Extensions.Xunit.Hosting/ServiceCollectionExtensions.cs +++ b/src/Codebelt.Extensions.Xunit.Hosting/ServiceCollectionExtensions.cs @@ -11,15 +11,15 @@ namespace Codebelt.Extensions.Xunit.Hosting; /// public static class ServiceCollectionExtensions { - /// + /// /// Adds a unit test optimized implementation of logging to the collection. - /// - /// The to extend. - /// The that specifies the minimum level to include for the logging. - /// A reference to so that additional configuration calls can be chained. - /// + /// + /// The to extend. + /// The that specifies the minimum level to include for the logging. + /// A reference to so that additional configuration calls can be chained. + /// /// cannot be null. - /// + /// public static IServiceCollection AddXunitTestLogging(this IServiceCollection services, LogLevel minimumLevel = LogLevel.Trace) { if (services == null) { throw new ArgumentNullException(nameof(services)); } @@ -38,17 +38,17 @@ public static IServiceCollection AddXunitTestLogging(this IServiceCollection ser return services; } - /// - /// Adds a unit test optimized implementation of output logging to the collection. - /// - /// The to extend. - /// The that provides the output for the logging. - /// The that specifies the minimum level to include for the logging. - /// A reference to so that additional configuration calls can be chained. - /// - /// cannot be null -or- - /// cannot be null. - /// + /// + /// Adds a unit test optimized implementation of output logging to the collection. + /// + /// The to extend. + /// The that provides the output for the logging. + /// The that specifies the minimum level to include for the logging. + /// A reference to so that additional configuration calls can be chained. + /// + /// cannot be null -or- + /// cannot be null. + /// public static IServiceCollection AddXunitTestLogging(this IServiceCollection services, ITestOutputHelper output, LogLevel minimumLevel = LogLevel.Trace) { #if NETSTANDARD2_0 diff --git a/testenvironments.json b/testenvironments.json index 6371963..0651f28 100644 --- a/testenvironments.json +++ b/testenvironments.json @@ -1,20 +1,20 @@ -{ - "version": "1", - "environments": [ - { - "name": "WSL-Ubuntu", - "type": "wsl", - "wslDistribution": "Ubuntu-26.04" - }, - { - "name": "Docker-Ubuntu (net10.0)", - "type": "docker", - "dockerImage": "codebeltnet/ubuntu-testrunner:10" - }, - { - "name": "Docker-Ubuntu (net11.0)", - "type": "docker", - "dockerImage": "codebeltnet/ubuntu-testrunner:11" - } - ] -} +{ + "version": "1", + "environments": [ + { + "name": "WSL-Ubuntu", + "type": "wsl", + "wslDistribution": "Ubuntu-26.04" + }, + { + "name": "Docker-Ubuntu (net10.0)", + "type": "docker", + "dockerImage": "codebeltnet/ubuntu-testrunner:10" + }, + { + "name": "Docker-Ubuntu (net11.0)", + "type": "docker", + "dockerImage": "codebeltnet/ubuntu-testrunner:11" + } + ] +} diff --git a/tuning/Codebelt.Extensions.Xunit.Benchmarks/Codebelt.Extensions.Xunit.Benchmarks.csproj b/tuning/Codebelt.Extensions.Xunit.Benchmarks/Codebelt.Extensions.Xunit.Benchmarks.csproj index f98539a..1b955f3 100644 --- a/tuning/Codebelt.Extensions.Xunit.Benchmarks/Codebelt.Extensions.Xunit.Benchmarks.csproj +++ b/tuning/Codebelt.Extensions.Xunit.Benchmarks/Codebelt.Extensions.Xunit.Benchmarks.csproj @@ -1,11 +1,11 @@ - - - - Codebelt.Extensions.Xunit - - - - - - - + + + + Codebelt.Extensions.Xunit + + + + + + + From 704273ef280a9c7e426453160d8d924138f4358f Mon Sep 17 00:00:00 2001 From: aicia-bot Date: Wed, 28 Oct 2026 00:16:34 +0100 Subject: [PATCH 30/32] =?UTF-8?q?=F0=9F=94=A7=20extend=20editorconfig=20ru?= =?UTF-8?q?les=20to=20vb=20and=20drop=20obsolete=20ones?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extends the existing analyzer severity overrides to VB files, points the S3267 reference at the current Sonar rule page, and removes the IDE0306, CA1016, and S8970 overrides. --- .editorconfig | 368 ++++++++++++++++++++++++-------------------------- 1 file changed, 176 insertions(+), 192 deletions(-) diff --git a/.editorconfig b/.editorconfig index 9bc0265..a083db5 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,192 +1,176 @@ -root = true - -[*] -charset = utf-8 -end_of_line = lf -insert_final_newline = true -trim_trailing_whitespace = true - -# Default: prefer spaces for data/markup -indent_style = space -indent_size = 2 -tab_width = 2 - -# This style rule concern the use of the range operator, which is available in C# 8.0 and later. -# https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0057 -[*.cs] -dotnet_diagnostic.IDE0057.severity = none - -# This style rule concerns the use of switch expressions versus switch statements. -# https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0066 -[*.cs] -dotnet_diagnostic.IDE0066.severity = none - -# Performance rules -# https://docs.microsoft.com/da-dk/dotnet/fundamentals/code-analysis/quality-rules/performance-warnings -[*.cs] -dotnet_analyzer_diagnostic.category-Performance.severity = none # Because many of the suggestions by performance analyzers are not compatible with .NET Standard 2.0 - -# This style rule concerns the use of using statements without curly braces, also known as using declarations. This alternative syntax was introduced in C# 8.0. -# https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0063 -[*.cs] -dotnet_diagnostic.IDE0063.severity = none - -# This style rule concerns with simplification of interpolated strings to improve code readability. It recommends removal of certain explicit method calls, such as ToString(), when the same method would be implicitly invoked by the compiler if the explicit method call is removed. -# https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0071 -[*.cs] -dotnet_diagnostic.IDE0071.severity = none - -# S3267: Loops should be simplified with "LINQ" expressions -# https://sonarcloud.io/organizations/sonarcloud/rules?open=csharpsquid:S3267&rule_key=csharpsquid:S3267 -dotnet_diagnostic.S3267.severity = none - -# CA1859: Use concrete types when possible for improved performance -# This is a violation of Framework Design Guidelines. -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1859 -[*.cs] -dotnet_diagnostic.CA1859.severity = none - -# IDE0008: Use explicit type -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0007-ide0008 -[*.cs] -dotnet_diagnostic.IDE0008.severity = none - -[*.cs] -indent_style = space -indent_size = 4 - -# IDE0161: Namespace declaration preferences -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0161 -# Prefer file-scoped namespaces for new files; existing block-scoped files should not be converted unless explicitly asked -[*.cs] -csharp_style_namespace_declarations = file_scoped:suggestion - -# Top-level statements: DO NOT USE -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0210-ide0211 -# This is enforced via a style preference set to error severity, not a language-level prohibition. -# Always use explicit class declarations with a proper namespace and Main method where applicable. -[*.cs] -csharp_style_prefer_top_level_statements = false:error - -[*.xml] -indent_style = space -indent_size = 2 - -# IDE0078: Use pattern matching -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0078 -[*.cs] -dotnet_diagnostic.IDE0078.severity = none - -# IDE0290: Use primary constructor -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0290 -[*.cs] -dotnet_diagnostic.IDE0290.severity = none - -# IDE0305: Use collection expression for fluent -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0305 -[*.cs] -dotnet_diagnostic.IDE0305.severity = none - -# IDE0011: Add braces -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0011 -[*.cs] -dotnet_diagnostic.IDE0011.severity = none - -# IDE0028: Use collection initializers or expressions -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0028 -[*.cs] -dotnet_diagnostic.IDE0028.severity = none - -# IDE0039: Use collection expression for array -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0300 -[*.cs] -dotnet_diagnostic.IDE0300.severity = none - -# IDE0031: Use collection expression for empty -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0301 -[*.cs] -dotnet_diagnostic.IDE0301.severity = none - -# IDE0046: Use conditional expression for return -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0046 -[*.cs] -dotnet_diagnostic.IDE0046.severity = none - -# IDE0047: Parentheses preferences -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0047-ide0048 -[*.cs] -dotnet_diagnostic.IDE0047.severity = none - -# CA1716: Identifiers should not match keywords -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1716 -[*.cs] -dotnet_diagnostic.CA1716.severity = none - -# CA1720: Identifiers should not contain type names -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1720 -[*.cs] -dotnet_diagnostic.CA1720.severity = none - -# CA1846: Prefer AsSpan over Substring -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1846 -# Excluded while TFMs include netstandard2.0 -[*.cs] -dotnet_diagnostic.CA1846.severity = none - -# CA1847: Use String.Contains(char) instead of String.Contains(string) with single characters -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1847 -# Excluded while TFMs include netstandard2.0 -[*.cs] -dotnet_diagnostic.CA1847.severity = none - -# CA1865-CA1867: Use 'string.Method(char)' instead of 'string.Method(string)' for string with single char -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1865-ca1867 -# Excluded while TFMs include netstandard2.0 -[*.cs] -dotnet_diagnostic.CA1865.severity = none -dotnet_diagnostic.CA1866.severity = none -dotnet_diagnostic.CA1867.severity = none - -# CA2263: Prefer generic overload when type is known -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2263 -# Excluded while TFMs include netstandard2.0 -[*.cs] -dotnet_diagnostic.CA2263.severity = none - -# CA2249: Consider using String.Contains instead of String.IndexOf -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2249 -# Excluded while TFMs include netstandard2.0 -[*.cs] -dotnet_diagnostic.CA2249.severity = none - -# IDE0022: Use expression body for methods -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0022 -[*.cs] -dotnet_diagnostic.IDE0022.severity = none - -# IDE0032: Use auto-property -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0032 -[*.cs] -dotnet_diagnostic.IDE0032.severity = none - -# Order modifiers -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0036 -# Excluded becuase of inconsistency with other analyzers -[*.cs] -dotnet_diagnostic.IDE0036.severity = none - -# Use collection expression for new -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0306 -# Excluded becuase of inconsistency with other analyzers -[*.cs] -dotnet_diagnostic.IDE0306.severity = none - -# CA1016: Mark assemblies with AssemblyVersionAttribute -# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1016 -[*.cs] -dotnet_diagnostic.CA1016.severity = none - -# Sonar rule csharpsquid:S8970 -# https://sonarcloud.io/organizations/sonarcloud/rules?open=csharpsquid:S8970&rule_key=csharpsquid:S8970 -[*.cs] -dotnet_diagnostic.S8970.severity = none +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +# Default: prefer spaces for data/markup +indent_style = space +indent_size = 2 +tab_width = 2 + +# This style rule concern the use of the range operator, which is available in C# 8.0 and later. +# https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0057 +[*.{cs,vb}] +dotnet_diagnostic.IDE0057.severity = none + +# This style rule concerns the use of switch expressions versus switch statements. +# https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0066 +[*.{cs,vb}] +dotnet_diagnostic.IDE0066.severity = none + +# Performance rules +# https://docs.microsoft.com/da-dk/dotnet/fundamentals/code-analysis/quality-rules/performance-warnings +[*.{cs,vb}] +dotnet_analyzer_diagnostic.category-Performance.severity = none # Because many of the suggestions by performance analyzers are not compatible with .NET Standard 2.0 + +# This style rule concerns the use of using statements without curly braces, also known as using declarations. This alternative syntax was introduced in C# 8.0. +# https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0063 +[*.{cs,vb}] +dotnet_diagnostic.IDE0063.severity = none + +# This style rule concerns with simplification of interpolated strings to improve code readability. It recommends removal of certain explicit method calls, such as ToString(), when the same method would be implicitly invoked by the compiler if the explicit method call is removed. +# https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0071 +[*.{cs,vb}] +dotnet_diagnostic.IDE0071.severity = none + +# S3267: Loops should be simplified with "LINQ" expressions +# https://rules.sonarsource.com/csharp/RSPEC-3267 +dotnet_diagnostic.S3267.severity = none + +# CA1859: Use concrete types when possible for improved performance +# This is a violation of Framework Design Guidelines. +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1859 +[*.{cs,vb}] +dotnet_diagnostic.CA1859.severity = none + +# IDE0008: Use explicit type +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0007-ide0008 +[*.{cs,vb}] +dotnet_diagnostic.IDE0008.severity = none + +[*.{cs,vb}] +indent_style = space +indent_size = 4 + +# IDE0161: Namespace declaration preferences +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0161 +# Prefer file-scoped namespaces for new files; existing block-scoped files should not be converted unless explicitly asked +[*.cs] +csharp_style_namespace_declarations = file_scoped:suggestion + +# Top-level statements: DO NOT USE +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0210-ide0211 +# This is enforced via a style preference set to error severity, not a language-level prohibition. +# Always use explicit class declarations with a proper namespace and Main method where applicable. +[*.cs] +csharp_style_prefer_top_level_statements = false:error + +[*.xml] +indent_style = space +indent_size = 2 + +# IDE0078: Use pattern matching +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0078 +[*.{cs,vb}] +dotnet_diagnostic.IDE0078.severity = none + +# IDE0290: Use primary constructor +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0290 +[*.{cs,vb}] +dotnet_diagnostic.IDE0290.severity = none + +# IDE0305: Use collection expression for fluent +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0305 +[*.{cs,vb}] +dotnet_diagnostic.IDE0305.severity = none + +# IDE0011: Add braces +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0011 +[*.{cs,vb}] +dotnet_diagnostic.IDE0011.severity = none + +# IDE0028: Use collection initializers or expressions +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0028 +[*.{cs,vb}] +dotnet_diagnostic.IDE0028.severity = none + +# IDE0039: Use collection expression for array +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0300 +[*.{cs,vb}] +dotnet_diagnostic.IDE0300.severity = none + +# IDE0031: Use collection expression for empty +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0301 +[*.{cs,vb}] +dotnet_diagnostic.IDE0301.severity = none + +# IDE0046: Use conditional expression for return +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0046 +[*.{cs,vb}] +dotnet_diagnostic.IDE0046.severity = none + +# IDE0047: Parentheses preferences +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0047-ide0048 +[*.{cs,vb}] +dotnet_diagnostic.IDE0047.severity = none + +# CA1716: Identifiers should not match keywords +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1716 +[*.{cs,vb}] +dotnet_diagnostic.CA1716.severity = none + +# CA1720: Identifiers should not contain type names +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1720 +[*.{cs,vb}] +dotnet_diagnostic.CA1720.severity = none + +# CA1846: Prefer AsSpan over Substring +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1846 +# Excluded while TFMs include netstandard2.0 +[*.{cs,vb}] +dotnet_diagnostic.CA1846.severity = none + +# CA1847: Use String.Contains(char) instead of String.Contains(string) with single characters +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1847 +# Excluded while TFMs include netstandard2.0 +[*.{cs,vb}] +dotnet_diagnostic.CA1847.severity = none + +# CA1865-CA1867: Use 'string.Method(char)' instead of 'string.Method(string)' for string with single char +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1865-ca1867 +# Excluded while TFMs include netstandard2.0 +[*.{cs,vb}] +dotnet_diagnostic.CA1865.severity = none +dotnet_diagnostic.CA1866.severity = none +dotnet_diagnostic.CA1867.severity = none + +# CA2263: Prefer generic overload when type is known +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2263 +# Excluded while TFMs include netstandard2.0 +[*.{cs,vb}] +dotnet_diagnostic.CA2263.severity = none + +# CA2249: Consider using String.Contains instead of String.IndexOf +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca2249 +# Excluded while TFMs include netstandard2.0 +[*.{cs,vb}] +dotnet_diagnostic.CA2249.severity = none + +# IDE0022: Use expression body for methods +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0022 +[*.{cs,vb}] +dotnet_diagnostic.IDE0022.severity = none + +# IDE0032: Use auto-property +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0032 +[*.{cs,vb}] +dotnet_diagnostic.IDE0032.severity = none + +# Order modifiers +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0036 +# Excluded becuase of inconsistency with other analyzers +[*.{cs,vb}] +dotnet_diagnostic.IDE0036.severity = none From 4051e4039c341fec21d637c36f7ec98aef4fafd3 Mon Sep 17 00:00:00 2001 From: aicia-bot Date: Wed, 28 Oct 2026 00:16:39 +0100 Subject: [PATCH 31/32] =?UTF-8?q?=F0=9F=92=AC=20update=20changelog=20and?= =?UTF-8?q?=20contributor=20guide=20for=20net11?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rewrites the 13.0.0 changelog entry around the .NET 11 baseline and corrects the WSL distribution named in the contributor guide to Ubuntu-26.04. --- .github/CONTRIBUTING.md | 158 ++++++++++++++++++++-------------------- CHANGELOG.md | 33 +++++---- 2 files changed, 97 insertions(+), 94 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 47cc0bb..d3cee0f 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,79 +1,79 @@ -# Contributing to Extensions for xUnit API by Codebelt - -This repository is part of the Codebelt .NET library estate. The instructions below describe the current checkout and its CI contract. Please keep changes focused and preserve the shared Codebelt build skeleton unless a deliberate policy change is being made. - -## Before you start - -- Read the repository `README.md` and open an issue before starting a non-trivial feature or behavioral change. -- Use a .NET 11 SDK to build this repository. The current target frameworks are **net11.0, net10.0, netstandard2.0**, with **net48** additionally used for tests on Windows. -- The solution is `Codebelt.Extensions.Xunit.slnx`. Central package versions are maintained in `Directory.Packages.props`. -- The shared build behavior is in `Directory.Build.props` and `Directory.Build.targets`; repository-specific TFMs, package references and metadata remain local to this library. - -## Repository shape - -- `src/` contains production projects. -- `test/` contains xUnit v3 test projects. -- `Codebelt.Extensions.Xunit.slnx` is the solution used for local development. -- `.github/workflows/ci-pipeline.yml` is the CI workflow and the authority for the test matrix. -- `testenvironments.json` declares the supported `WSL-Ubuntu` and `Docker-Ubuntu` test environments. - -## Build - -Restore and build the solution from the repository root: - -```powershell -dotnet restore "Codebelt.Extensions.Xunit.slnx" -dotnet build "Codebelt.Extensions.Xunit.slnx" --configuration Release --no-restore -``` - -CI builds both Debug and Release configurations. A clean build should complete before opening a pull request. - -## Test - -Run tests one project at a time so a failing or hanging project is attributable. This mirrors the CI matrix; it does not silently turn skipped integration tests into passing tests. - -```powershell -$testProjects = Get-ChildItem test -Filter *.csproj -Recurse -foreach ($project in $testProjects) { - dotnet test --project $project.FullName --configuration Release --no-restore -} -``` - -The CI test plan currently runs **5** project(s) and excludes **0** project(s). The workflow also has an optional macOS test job. - -## Integration and infrastructure - -- `WSL-Ubuntu` — WSL distribution `Ubuntu-24.04`. -- `Docker-Ubuntu` — Docker image `codebeltnet/ubuntu-testrunner:8-9-10-11`. - -This repository has no repository-local `docker-compose.yml` service dependency in the current checkout. -Use the environments declared in `testenvironments.json` when you need the estate test runner. - -## Package and documentation - -Create packages using the same solution and Release configuration: - -```powershell -dotnet pack "Codebelt.Extensions.Xunit.slnx" --configuration Release --no-restore -``` - -Package-specific release notes live under `.nuget//PackageReleaseNotes.txt` and package README files live beside them. `Directory.Build.targets` imports the release notes during packing. Public API changes also require XML documentation updates; DocFX documentation is built by the repository automation. - -## Pull requests - -1. Create or join an issue before substantial work, then fork the repository and create a branch from `main`. -2. Add or update focused tests and public API documentation where applicable. -3. Run restore, build, and the relevant per-project tests locally. -4. Keep the pull request small, explain the behavior change and validation performed, and wait for the CI checks to pass. - -## Issues - -Include the affected project, target framework, operating system, SDK version, exact command, expected result, actual result, and a minimal reproduction. Identify whether the behavior differs between local Windows/WSL, Docker-Ubuntu and GitHub Actions. - -## Coding guidelines - -Follow the existing style, the Framework Design Guidelines, the repository `.editorconfig`, and the shared Codebelt conventions. Do not make unrelated formatting or infrastructure changes in a feature pull request. - -## License - -By contributing to Extensions for xUnit API by Codebelt, you agree that your contributions will be licensed under the MIT license. +# Contributing to Extensions for xUnit API by Codebelt + +This repository is part of the Codebelt .NET library estate. The instructions below describe the current checkout and its CI contract. Please keep changes focused and preserve the shared Codebelt build skeleton unless a deliberate policy change is being made. + +## Before you start + +- Read the repository `README.md` and open an issue before starting a non-trivial feature or behavioral change. +- Use a .NET 11 SDK to build this repository. The current target frameworks are **net11.0, net10.0, netstandard2.0**, with **net48** additionally used for tests on Windows. +- The solution is `Codebelt.Extensions.Xunit.slnx`. Central package versions are maintained in `Directory.Packages.props`. +- The shared build behavior is in `Directory.Build.props` and `Directory.Build.targets`; repository-specific TFMs, package references and metadata remain local to this library. + +## Repository shape + +- `src/` contains production projects. +- `test/` contains xUnit v3 test projects. +- `Codebelt.Extensions.Xunit.slnx` is the solution used for local development. +- `.github/workflows/ci-pipeline.yml` is the CI workflow and the authority for the test matrix. +- `testenvironments.json` declares the supported `WSL-Ubuntu` and `Docker-Ubuntu` test environments. + +## Build + +Restore and build the solution from the repository root: + +```powershell +dotnet restore "Codebelt.Extensions.Xunit.slnx" +dotnet build "Codebelt.Extensions.Xunit.slnx" --configuration Release --no-restore +``` + +CI builds both Debug and Release configurations. A clean build should complete before opening a pull request. + +## Test + +Run tests one project at a time so a failing or hanging project is attributable. This mirrors the CI matrix; it does not silently turn skipped integration tests into passing tests. + +```powershell +$testProjects = Get-ChildItem test -Filter *.csproj -Recurse +foreach ($project in $testProjects) { + dotnet test --project $project.FullName --configuration Release --no-restore +} +``` + +The CI test plan currently runs **5** project(s) and excludes **0** project(s). The workflow also has an optional macOS test job. + +## Integration and infrastructure + +- `WSL-Ubuntu` — WSL distribution `Ubuntu-26.04`. +- `Docker-Ubuntu` — Docker image `codebeltnet/ubuntu-testrunner:8-9-10-11`. + +This repository has no repository-local `docker-compose.yml` service dependency in the current checkout. +Use the environments declared in `testenvironments.json` when you need the estate test runner. + +## Package and documentation + +Create packages using the same solution and Release configuration: + +```powershell +dotnet pack "Codebelt.Extensions.Xunit.slnx" --configuration Release --no-restore +``` + +Package-specific release notes live under `.nuget//PackageReleaseNotes.txt` and package README files live beside them. `Directory.Build.targets` imports the release notes during packing. Public API changes also require XML documentation updates; DocFX documentation is built by the repository automation. + +## Pull requests + +1. Create or join an issue before substantial work, then fork the repository and create a branch from `main`. +2. Add or update focused tests and public API documentation where applicable. +3. Run restore, build, and the relevant per-project tests locally. +4. Keep the pull request small, explain the behavior change and validation performed, and wait for the CI checks to pass. + +## Issues + +Include the affected project, target framework, operating system, SDK version, exact command, expected result, actual result, and a minimal reproduction. Identify whether the behavior differs between local Windows/WSL, Docker-Ubuntu and GitHub Actions. + +## Coding guidelines + +Follow the existing style, the Framework Design Guidelines, the repository `.editorconfig`, and the shared Codebelt conventions. Do not make unrelated formatting or infrastructure changes in a feature pull request. + +## License + +By contributing to Extensions for xUnit API by Codebelt, you agree that your contributions will be licensed under the MIT license. diff --git a/CHANGELOG.md b/CHANGELOG.md index 21b36e9..181d1dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,28 +9,31 @@ For more details, please refer to `PackageReleaseNotes.txt` on a per assembly ba ## [13.0.0] - 2026-11-10 -This is a major release that adds .NET 11 as a target framework and removes .NET 9, so consumers still targeting .NET 9 are no longer covered by this version. The remaining changes refresh package pins, test and benchmark targets, CI, and documentation to the new .NET 11 and .NET 10 baseline. +This is a major release that adds .NET 11 support and removes .NET 9 targets. It also updates dependencies, hosting diagnostics, contributor guidance, and the test environment. ### Added -- .NET 11 (`net11.0`) as a target framework for Codebelt.Extensions.Xunit, Codebelt.Extensions.Xunit.App, Codebelt.Extensions.Xunit.Hosting, and Codebelt.Extensions.Xunit.Hosting.AspNetCore. +- Added `net11.0` targets to Codebelt.Extensions.Xunit, Codebelt.Extensions.Xunit.App, Codebelt.Extensions.Xunit.Hosting, and Codebelt.Extensions.Xunit.Hosting.AspNetCore, +- Added the `SonarAnalyzer.CSharp` 10.35.0.4138 build analyzer to source projects. ### Changed -- Test projects, benchmark projects, and bootstrapper reference applications now target `net11.0` alongside `net10.0`, -- Upgraded Codebelt.Bootstrapper.Console, Codebelt.Bootstrapper.Web, and Codebelt.Bootstrapper.Worker from 5.2.2 to 5.2.3, -- Upgraded Codebelt.Extensions.BenchmarkDotNet.Console from 1.3.4 to 1.3.5, -- Upgraded Codebelt.Coverlet.MTP from 10.1.0 to 10.1.1, -- Pinned Microsoft.Extensions.Configuration, Microsoft.Extensions.Configuration.EnvironmentVariables, Microsoft.Extensions.Configuration.FileExtensions, Microsoft.Extensions.Configuration.Json, Microsoft.Extensions.Hosting, and Microsoft.AspNetCore.TestHost to 11.0.0-rc.2.26504.105 for .NET 11, -- Package READMEs, the root README, the contributing guide, `AGENTS.md`, and the DocFX landing page and availability includes now list .NET 11 and .NET 10 instead of .NET 10 and .NET 9, -- CI pipeline build, pack, test, SonarCloud, and CodeQL jobs enable the preview SDK so they can build against .NET 11, -- Service update workflow derives its baseline version from the latest stable `vMAJOR.MINOR.PATCH` Git tag instead of the first CHANGELOG.md heading, -- Release-note workflow reads the Availability line from the shared DocFX include files instead of a hardcoded fallback string. +- Updated test, benchmark, and reference application targets to .NET 10 and .NET 11, and enabled preview SDKs in the CI build, pack, test, SonarCloud, and CodeQL jobs, +- Updated Codebelt.Bootstrapper.Console, Codebelt.Bootstrapper.Web, and Codebelt.Bootstrapper.Worker from 5.2.2 to 5.2.3, Codebelt.Extensions.BenchmarkDotNet.Console from 1.3.4 to 1.3.5, and Codebelt.Coverlet.MTP from 10.1.0 to 10.1.1, +- Updated Cuemon.Core, Cuemon.Extensions.AspNetCore, and Cuemon.Extensions.IO from 10.8.1 to 10.8.2, and Microsoft.Bcl.AsyncInterfaces from 10.0.13 to 10.0.14 for .NET Standard, +- Updated Microsoft.Extensions.Configuration, Microsoft.Extensions.Configuration.EnvironmentVariables, Microsoft.Extensions.Configuration.FileExtensions, Microsoft.Extensions.Configuration.Json, Microsoft.Extensions.Hosting, and Microsoft.AspNetCore.TestHost from 10.0.13 to 10.0.14 in the .NET 10/.NET Standard version group, and added 11.0.0-rc.3.26522.148 pins for .NET 11, +- Aligned the shared editor settings and combined the per-framework Docker test environments into `codebeltnet/ubuntu-testrunner:8-9-10-11`, while retaining the Ubuntu 26.04 WSL environment, +- Updated the package and repository documentation for .NET 11 and .NET 10, and corrected the contributor guide's WSL distribution, +- Updated the service-update workflow to derive its baseline version from Git tags and package availability from the DocFX include files. + +### Fixed + +- Preserved the original cancellation token when deferred host startup is canceled. ### Removed -- `net9.0` target framework from the Codebelt.Extensions.Xunit, Codebelt.Extensions.Xunit.App, Codebelt.Extensions.Xunit.Hosting, and Codebelt.Extensions.Xunit.Hosting.AspNetCore packages and from the test projects, -- .NET 9 version group of Microsoft.Extensions.Configuration, Microsoft.Extensions.Configuration.EnvironmentVariables, Microsoft.Extensions.Configuration.FileExtensions, Microsoft.Extensions.Configuration.Json, Microsoft.Extensions.Hosting, and Microsoft.AspNetCore.TestHost at 9.0.21 in `Directory.Packages.props`; these packages remain pinned for .NET 10. +- Removed `net9.0` targets from the four Codebelt.Extensions.Xunit packages and their test projects, +- Removed the .NET 9 version declarations for Microsoft.Extensions.Configuration, Microsoft.Extensions.Configuration.EnvironmentVariables, Microsoft.Extensions.Configuration.FileExtensions, Microsoft.Extensions.Configuration.Json, Microsoft.Extensions.Hosting, and Microsoft.AspNetCore.TestHost; the packages remain declared for supported frameworks. ## [12.0.2] - 2026-10-15 @@ -281,9 +284,9 @@ At the same time we also adapted xUnit v3 as this is the one in active developme > [!WARNING] > Design choices made in xUnit v3 are a bit puzzling to me, especially around something as important as versioning and requirement for executable projects. -> +> > All good practices has been put aside, and for the majority of new packages, major version is baked into the package name itself (e.g., `xunit.v3` instead of just `xunit`). -> +> > Semantic Versioning is all about conveying meaning and intent through version numbers, and this approach introduces unnecessary redundancy and makes long-term maintenance less smooth. Its a mess that could lead others down a slippery slope of wrongdoings due to the popularity of the framework. > > Another unfortunate design choice is the requirement of executable projects (i.e., output type `exe` instead of `library`). This has implications when you have optimized your CI to not waste CPU cycles on restoring/building test projects (build once strategy). From 159f95944c425c30d987db8256cd38faa1297c39 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Wed, 28 Oct 2026 21:36:37 +0100 Subject: [PATCH 32/32] changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 181d1dc..0a7128a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,7 +22,7 @@ This is a major release that adds .NET 11 support and removes .NET 9 targets. It - Updated Codebelt.Bootstrapper.Console, Codebelt.Bootstrapper.Web, and Codebelt.Bootstrapper.Worker from 5.2.2 to 5.2.3, Codebelt.Extensions.BenchmarkDotNet.Console from 1.3.4 to 1.3.5, and Codebelt.Coverlet.MTP from 10.1.0 to 10.1.1, - Updated Cuemon.Core, Cuemon.Extensions.AspNetCore, and Cuemon.Extensions.IO from 10.8.1 to 10.8.2, and Microsoft.Bcl.AsyncInterfaces from 10.0.13 to 10.0.14 for .NET Standard, - Updated Microsoft.Extensions.Configuration, Microsoft.Extensions.Configuration.EnvironmentVariables, Microsoft.Extensions.Configuration.FileExtensions, Microsoft.Extensions.Configuration.Json, Microsoft.Extensions.Hosting, and Microsoft.AspNetCore.TestHost from 10.0.13 to 10.0.14 in the .NET 10/.NET Standard version group, and added 11.0.0-rc.3.26522.148 pins for .NET 11, -- Aligned the shared editor settings and combined the per-framework Docker test environments into `codebeltnet/ubuntu-testrunner:8-9-10-11`, while retaining the Ubuntu 26.04 WSL environment, +- Aligned the shared editor settings and split the combined Docker test environment into `codebeltnet/ubuntu-testrunner:10` and `codebeltnet/ubuntu-testrunner:11`, while retaining the Ubuntu 26.04 WSL environment, - Updated the package and repository documentation for .NET 11 and .NET 10, and corrected the contributor guide's WSL distribution, - Updated the service-update workflow to derive its baseline version from Git tags and package availability from the DocFX include files.