From bd5295458936f8372163f869ef4a5c8339b6e853 Mon Sep 17 00:00:00 2001 From: martincostello Date: Thu, 19 Dec 2024 15:37:48 +0000 Subject: [PATCH 01/11] Support xunit v3 - Add support for xunit v3 via the new MartinCostello.Logging.XUnit.v3 NuGet package. - Bump version to 0.5.0. --- Directory.Build.props | 9 +-- Directory.Packages.props | 3 + Logging.XUnit.sln | 14 ++++ build.ps1 | 12 +++- .../MartinCostello.Logging.XUnit.v3.csproj | 68 +++++++++++++++++++ .../AmbientTestOutputHelperAccessor.cs | 2 - .../CompatibilitySuppressions.xml | 11 --- src/Logging.XUnit/IMessageSinkAccessor.cs | 2 - src/Logging.XUnit/IMessageSinkExtensions.cs | 4 ++ .../ITestOutputHelperAccessor.cs | 2 - .../ITestOutputHelperExtensions.cs | 4 ++ .../MartinCostello.Logging.XUnit.csproj | 12 ++-- src/Logging.XUnit/MessageSinkAccessor.cs | 2 - src/Logging.XUnit/StringSyntaxAttribute.cs | 2 +- src/Logging.XUnit/TestOutputHelperAccessor.cs | 2 - src/Logging.XUnit/XUnitLogger.IMessageSink.cs | 1 - .../XUnitLogger.ITestOutputHelper.cs | 1 - src/Logging.XUnit/XUnitLogger.cs | 2 - .../XUnitLoggerExtensions.IMessageSink.cs | 1 - ...XUnitLoggerExtensions.ITestOutputHelper.cs | 1 - src/Logging.XUnit/XUnitLoggerOptions.cs | 2 - .../XUnitLoggerProvider.IMessageSink.cs | 1 - .../XUnitLoggerProvider.ITestOutputHelper.cs | 1 - .../Integration/DatabaseFixture.cs | 24 ++++++- .../Integration/HttpApplicationTests.cs | 20 ++++++ .../Integration/PrintableDiagnosticMessage.cs | 3 +- .../MartinCostello.Logging.XUnit.Tests.csproj | 6 ++ tests/Logging.XUnit.Tests/XUnitLoggerTests.cs | 13 ++-- ...rtinCostello.Logging.XUnit.v3.Tests.csproj | 54 +++++++++++++++ 29 files changed, 224 insertions(+), 55 deletions(-) create mode 100644 src/Logging.XUnit.v3/MartinCostello.Logging.XUnit.v3.csproj delete mode 100644 src/Logging.XUnit/CompatibilitySuppressions.xml create mode 100644 tests/Logging.XUnit.v3.Tests/MartinCostello.Logging.XUnit.v3.Tests.csproj diff --git a/Directory.Build.props b/Directory.Build.props index 786e3912..47ef97e6 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -44,8 +44,8 @@ snupkg true true - 0.4.0.0 - 0.4.1 + 0.5.0.0 + 0.5.0 beta.$(GITHUB_RUN_NUMBER) @@ -73,9 +73,4 @@ - - - - - diff --git a/Directory.Packages.props b/Directory.Packages.props index cf5d0186..bab89e66 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -16,6 +16,9 @@ + + + diff --git a/Logging.XUnit.sln b/Logging.XUnit.sln index 90f8be59..1564adb4 100644 --- a/Logging.XUnit.sln +++ b/Logging.XUnit.sln @@ -67,6 +67,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{ .github\workflows\release.yml = .github\workflows\release.yml EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MartinCostello.Logging.XUnit.v3", "src\Logging.XUnit.v3\MartinCostello.Logging.XUnit.v3.csproj", "{2C161BB9-E6D0-4561-9D98-EDEAA8A49C5E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MartinCostello.Logging.XUnit.v3.Tests", "tests\Logging.XUnit.v3.Tests\MartinCostello.Logging.XUnit.v3.Tests.csproj", "{828C7199-ADF2-456C-8BBB-4A73A329D017}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -85,6 +89,14 @@ Global {B690F271-3B5D-4975-A607-AED1768595B1}.Debug|Any CPU.Build.0 = Debug|Any CPU {B690F271-3B5D-4975-A607-AED1768595B1}.Release|Any CPU.ActiveCfg = Release|Any CPU {B690F271-3B5D-4975-A607-AED1768595B1}.Release|Any CPU.Build.0 = Release|Any CPU + {2C161BB9-E6D0-4561-9D98-EDEAA8A49C5E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2C161BB9-E6D0-4561-9D98-EDEAA8A49C5E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2C161BB9-E6D0-4561-9D98-EDEAA8A49C5E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2C161BB9-E6D0-4561-9D98-EDEAA8A49C5E}.Release|Any CPU.Build.0 = Release|Any CPU + {828C7199-ADF2-456C-8BBB-4A73A329D017}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {828C7199-ADF2-456C-8BBB-4A73A329D017}.Debug|Any CPU.Build.0 = Debug|Any CPU + {828C7199-ADF2-456C-8BBB-4A73A329D017}.Release|Any CPU.ActiveCfg = Release|Any CPU + {828C7199-ADF2-456C-8BBB-4A73A329D017}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -97,6 +109,8 @@ Global {BB443063-F523-474D-83E3-D5FF5B075950} = {2684B19D-7D49-4099-8BD2-4D281455EB29} {B690F271-3B5D-4975-A607-AED1768595B1} = {278BCCB1-39B2-46DB-9395-7F85995A6132} {7764A046-DEE7-4D88-83E2-537DB7767123} = {D0426D09-1FF8-4E1F-A9AF-38DDEE5D7CCA} + {2C161BB9-E6D0-4561-9D98-EDEAA8A49C5E} = {2684B19D-7D49-4099-8BD2-4D281455EB29} + {828C7199-ADF2-456C-8BBB-4A73A329D017} = {278BCCB1-39B2-46DB-9395-7F85995A6132} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3B9E157C-5E92-4357-B233-281B4530EABD} diff --git a/build.ps1 b/build.ps1 index ffb7bca0..c892df9c 100755 --- a/build.ps1 +++ b/build.ps1 @@ -14,10 +14,14 @@ $ProgressPreference = "SilentlyContinue" $solutionPath = $PSScriptRoot $sdkFile = Join-Path $solutionPath "global.json" -$libraryProject = Join-Path $solutionPath "src" "Logging.XUnit" "MartinCostello.Logging.XUnit.csproj" +$libraryProjects = @( + (Join-Path $solutionPath "src" "Logging.XUnit" "MartinCostello.Logging.XUnit.csproj") + (Join-Path $solutionPath "src" "Logging.XUnit.v3" "MartinCostello.Logging.XUnit.v3.csproj") +) $testProjects = @( (Join-Path $solutionPath "tests" "Logging.XUnit.Tests" "MartinCostello.Logging.XUnit.Tests.csproj") + (Join-Path $solutionPath "tests" "Logging.XUnit.v3.Tests" "MartinCostello.Logging.XUnit.v3.Tests.csproj") ) $dotnetVersion = (Get-Content $sdkFile | Out-String | ConvertFrom-Json).sdk.version @@ -102,8 +106,10 @@ function DotNetTest { } } -Write-Information "Packaging library..." -DotNetPack $libraryProject +Write-Information "Packaging libraries..." +ForEach ($libraryProject in $libraryProjects) { + DotNetPack $libraryProject +} if (-Not $SkipTests) { Write-Information "Running tests..." diff --git a/src/Logging.XUnit.v3/MartinCostello.Logging.XUnit.v3.csproj b/src/Logging.XUnit.v3/MartinCostello.Logging.XUnit.v3.csproj new file mode 100644 index 00000000..3938bac7 --- /dev/null +++ b/src/Logging.XUnit.v3/MartinCostello.Logging.XUnit.v3.csproj @@ -0,0 +1,68 @@ + + + Logging Extensions for xunit v3 + $(DefineConstants);XUNIT_V3 + Extensions for Microsoft.Extensions.Logging for xunit v3. + true + true + true + false + Library + MartinCostello.Logging.XUnit.v3 + + MartinCostello.Logging.XUnit + $(Description) + net472;net8.0 + false + xunit v3 Logging Extensions + + + true + + + + false + false + false + false + false + + + + + + + + + <_Parameter1>ed8d1c5e-3ee7-45fe-8d1d-94257a71f02a + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Logging.XUnit/AmbientTestOutputHelperAccessor.cs b/src/Logging.XUnit/AmbientTestOutputHelperAccessor.cs index 86d7fce9..fefe038d 100644 --- a/src/Logging.XUnit/AmbientTestOutputHelperAccessor.cs +++ b/src/Logging.XUnit/AmbientTestOutputHelperAccessor.cs @@ -1,8 +1,6 @@ // Copyright (c) Martin Costello, 2018. All rights reserved. // Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. -using Xunit.Abstractions; - namespace MartinCostello.Logging.XUnit; /// diff --git a/src/Logging.XUnit/CompatibilitySuppressions.xml b/src/Logging.XUnit/CompatibilitySuppressions.xml deleted file mode 100644 index 247ae464..00000000 --- a/src/Logging.XUnit/CompatibilitySuppressions.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - CP0021 - M:MartinCostello.Logging.XUnit.XUnitLogger.BeginScope``1(``0)``0:notnull - lib/netstandard2.0/MartinCostello.Logging.XUnit.dll - lib/netstandard2.0/MartinCostello.Logging.XUnit.dll - true - - \ No newline at end of file diff --git a/src/Logging.XUnit/IMessageSinkAccessor.cs b/src/Logging.XUnit/IMessageSinkAccessor.cs index 3be9a205..8971d230 100644 --- a/src/Logging.XUnit/IMessageSinkAccessor.cs +++ b/src/Logging.XUnit/IMessageSinkAccessor.cs @@ -1,8 +1,6 @@ // Copyright (c) Martin Costello, 2018. All rights reserved. // Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. -using Xunit.Abstractions; - namespace MartinCostello.Logging.XUnit; /// diff --git a/src/Logging.XUnit/IMessageSinkExtensions.cs b/src/Logging.XUnit/IMessageSinkExtensions.cs index 40927cd2..0b3f0d88 100644 --- a/src/Logging.XUnit/IMessageSinkExtensions.cs +++ b/src/Logging.XUnit/IMessageSinkExtensions.cs @@ -4,8 +4,12 @@ using System.ComponentModel; using Microsoft.Extensions.Logging; +#if XUNIT_V3 +namespace Xunit; +#else #pragma warning disable IDE0130 namespace Xunit.Abstractions; +#endif /// /// A class containing extension methods for the interface. This class cannot be inherited. diff --git a/src/Logging.XUnit/ITestOutputHelperAccessor.cs b/src/Logging.XUnit/ITestOutputHelperAccessor.cs index 4cdb62d4..e4007818 100644 --- a/src/Logging.XUnit/ITestOutputHelperAccessor.cs +++ b/src/Logging.XUnit/ITestOutputHelperAccessor.cs @@ -1,8 +1,6 @@ // Copyright (c) Martin Costello, 2018. All rights reserved. // Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. -using Xunit.Abstractions; - namespace MartinCostello.Logging.XUnit; /// diff --git a/src/Logging.XUnit/ITestOutputHelperExtensions.cs b/src/Logging.XUnit/ITestOutputHelperExtensions.cs index f19a5dbf..4b4f22a2 100644 --- a/src/Logging.XUnit/ITestOutputHelperExtensions.cs +++ b/src/Logging.XUnit/ITestOutputHelperExtensions.cs @@ -4,8 +4,12 @@ using System.ComponentModel; using Microsoft.Extensions.Logging; +#if XUNIT_V3 +namespace Xunit; +#else #pragma warning disable IDE0130 namespace Xunit.Abstractions; +#endif /// /// A class containing extension methods for the interface. This class cannot be inherited. diff --git a/src/Logging.XUnit/MartinCostello.Logging.XUnit.csproj b/src/Logging.XUnit/MartinCostello.Logging.XUnit.csproj index 829f413b..7952a092 100644 --- a/src/Logging.XUnit/MartinCostello.Logging.XUnit.csproj +++ b/src/Logging.XUnit/MartinCostello.Logging.XUnit.csproj @@ -4,20 +4,17 @@ Extensions for Microsoft.Extensions.Logging for xunit. true true + true Library MartinCostello.Logging.XUnit - 0.3.0 + 0.4.0 MartinCostello.Logging.XUnit $(Description) netstandard2.0;net8.0 xunit Logging Extensions - true - true - true true - true @@ -33,4 +30,9 @@ + + + + + diff --git a/src/Logging.XUnit/MessageSinkAccessor.cs b/src/Logging.XUnit/MessageSinkAccessor.cs index f44c52a8..65bc2b8f 100644 --- a/src/Logging.XUnit/MessageSinkAccessor.cs +++ b/src/Logging.XUnit/MessageSinkAccessor.cs @@ -1,8 +1,6 @@ // Copyright (c) Martin Costello, 2018. All rights reserved. // Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. -using Xunit.Abstractions; - namespace MartinCostello.Logging.XUnit; /// diff --git a/src/Logging.XUnit/StringSyntaxAttribute.cs b/src/Logging.XUnit/StringSyntaxAttribute.cs index ec5dc7eb..b3dcc7c5 100644 --- a/src/Logging.XUnit/StringSyntaxAttribute.cs +++ b/src/Logging.XUnit/StringSyntaxAttribute.cs @@ -3,7 +3,7 @@ //// Based on https://github.com/dotnet/runtime/blob/65067052e433eda400c5e7cc9f7b21c84640f901/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/StringSyntaxAttribute.cs -#if NETSTANDARD +#if NETSTANDARD || !NETCOREAPP #pragma warning disable IDE0130 #pragma warning disable SA1600 diff --git a/src/Logging.XUnit/TestOutputHelperAccessor.cs b/src/Logging.XUnit/TestOutputHelperAccessor.cs index 5f1c8d63..4bf33910 100644 --- a/src/Logging.XUnit/TestOutputHelperAccessor.cs +++ b/src/Logging.XUnit/TestOutputHelperAccessor.cs @@ -1,8 +1,6 @@ // Copyright (c) Martin Costello, 2018. All rights reserved. // Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. -using Xunit.Abstractions; - namespace MartinCostello.Logging.XUnit; /// diff --git a/src/Logging.XUnit/XUnitLogger.IMessageSink.cs b/src/Logging.XUnit/XUnitLogger.IMessageSink.cs index 5cd9f301..1471b532 100644 --- a/src/Logging.XUnit/XUnitLogger.IMessageSink.cs +++ b/src/Logging.XUnit/XUnitLogger.IMessageSink.cs @@ -2,7 +2,6 @@ // Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. using Microsoft.Extensions.Logging; -using Xunit.Abstractions; namespace MartinCostello.Logging.XUnit; diff --git a/src/Logging.XUnit/XUnitLogger.ITestOutputHelper.cs b/src/Logging.XUnit/XUnitLogger.ITestOutputHelper.cs index 9fdb3257..b11505e8 100644 --- a/src/Logging.XUnit/XUnitLogger.ITestOutputHelper.cs +++ b/src/Logging.XUnit/XUnitLogger.ITestOutputHelper.cs @@ -2,7 +2,6 @@ // Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. using Microsoft.Extensions.Logging; -using Xunit.Abstractions; namespace MartinCostello.Logging.XUnit; diff --git a/src/Logging.XUnit/XUnitLogger.cs b/src/Logging.XUnit/XUnitLogger.cs index bab1c60f..33a1abd3 100644 --- a/src/Logging.XUnit/XUnitLogger.cs +++ b/src/Logging.XUnit/XUnitLogger.cs @@ -3,8 +3,6 @@ using System.Text; using Microsoft.Extensions.Logging; -using Xunit.Abstractions; -using Xunit.Sdk; namespace MartinCostello.Logging.XUnit; diff --git a/src/Logging.XUnit/XUnitLoggerExtensions.IMessageSink.cs b/src/Logging.XUnit/XUnitLoggerExtensions.IMessageSink.cs index 84045bb5..5254210e 100644 --- a/src/Logging.XUnit/XUnitLoggerExtensions.IMessageSink.cs +++ b/src/Logging.XUnit/XUnitLoggerExtensions.IMessageSink.cs @@ -3,7 +3,6 @@ using MartinCostello.Logging.XUnit; using Microsoft.Extensions.DependencyInjection.Extensions; -using Xunit.Abstractions; namespace Microsoft.Extensions.Logging; diff --git a/src/Logging.XUnit/XUnitLoggerExtensions.ITestOutputHelper.cs b/src/Logging.XUnit/XUnitLoggerExtensions.ITestOutputHelper.cs index 43d8ec4b..3bc6dc80 100644 --- a/src/Logging.XUnit/XUnitLoggerExtensions.ITestOutputHelper.cs +++ b/src/Logging.XUnit/XUnitLoggerExtensions.ITestOutputHelper.cs @@ -4,7 +4,6 @@ using System.ComponentModel; using MartinCostello.Logging.XUnit; using Microsoft.Extensions.DependencyInjection.Extensions; -using Xunit.Abstractions; namespace Microsoft.Extensions.Logging; diff --git a/src/Logging.XUnit/XUnitLoggerOptions.cs b/src/Logging.XUnit/XUnitLoggerOptions.cs index 449ccd90..7e4e169f 100644 --- a/src/Logging.XUnit/XUnitLoggerOptions.cs +++ b/src/Logging.XUnit/XUnitLoggerOptions.cs @@ -3,8 +3,6 @@ using System.Diagnostics.CodeAnalysis; using Microsoft.Extensions.Logging; -using Xunit.Abstractions; -using Xunit.Sdk; namespace MartinCostello.Logging.XUnit; diff --git a/src/Logging.XUnit/XUnitLoggerProvider.IMessageSink.cs b/src/Logging.XUnit/XUnitLoggerProvider.IMessageSink.cs index f8cc17e6..3e974504 100644 --- a/src/Logging.XUnit/XUnitLoggerProvider.IMessageSink.cs +++ b/src/Logging.XUnit/XUnitLoggerProvider.IMessageSink.cs @@ -2,7 +2,6 @@ // Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. using Microsoft.Extensions.Logging; -using Xunit.Abstractions; namespace MartinCostello.Logging.XUnit; diff --git a/src/Logging.XUnit/XUnitLoggerProvider.ITestOutputHelper.cs b/src/Logging.XUnit/XUnitLoggerProvider.ITestOutputHelper.cs index 53048f0d..13e2f868 100644 --- a/src/Logging.XUnit/XUnitLoggerProvider.ITestOutputHelper.cs +++ b/src/Logging.XUnit/XUnitLoggerProvider.ITestOutputHelper.cs @@ -2,7 +2,6 @@ // Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. using Microsoft.Extensions.Logging; -using Xunit.Abstractions; namespace MartinCostello.Logging.XUnit; diff --git a/tests/Logging.XUnit.Tests/Integration/DatabaseFixture.cs b/tests/Logging.XUnit.Tests/Integration/DatabaseFixture.cs index 57c61090..80d71f75 100644 --- a/tests/Logging.XUnit.Tests/Integration/DatabaseFixture.cs +++ b/tests/Logging.XUnit.Tests/Integration/DatabaseFixture.cs @@ -14,12 +14,33 @@ public sealed class DatabaseFixture : IAsyncLifetime public DatabaseFixture(IMessageSink messageSink) { using var loggerFactory = new LoggerFactory(); - _initializeLogger = loggerFactory.AddXUnit(messageSink, c => c.MessageSinkMessageFactory = m => new PrintableDiagnosticMessage(m)).CreateLogger(); + + _initializeLogger = loggerFactory.AddXUnit(messageSink, c => c.MessageSinkMessageFactory = CreateMessage).CreateLogger(); _disposeLogger = messageSink.ToLogger(); + +#if XUNIT_V3 + static IMessageSinkMessage CreateMessage(string message) => new DiagnosticMessage() { Message = message }; +#else + static IMessageSinkMessage CreateMessage(string message) => new PrintableDiagnosticMessage(message); +#endif } public string ConnectionString => _connectionString ?? throw new InvalidOperationException("The connection string is only available after InitializeAsync has completed."); +#if XUNIT_V3 + ValueTask IAsyncLifetime.InitializeAsync() + { + _initializeLogger.LogInformation("Initializing database"); + _connectionString = "Server=localhost"; + return ValueTask.CompletedTask; + } + + ValueTask IAsyncDisposable.DisposeAsync() + { + _disposeLogger.LogInformation("Disposing database"); + return ValueTask.CompletedTask; + } +#else Task IAsyncLifetime.InitializeAsync() { _initializeLogger.LogInformation("Initializing database"); @@ -32,4 +53,5 @@ Task IAsyncLifetime.DisposeAsync() _disposeLogger.LogInformation("Disposing database"); return Task.CompletedTask; } +#endif } diff --git a/tests/Logging.XUnit.Tests/Integration/HttpApplicationTests.cs b/tests/Logging.XUnit.Tests/Integration/HttpApplicationTests.cs index d333aa2c..8b01ccfa 100644 --- a/tests/Logging.XUnit.Tests/Integration/HttpApplicationTests.cs +++ b/tests/Logging.XUnit.Tests/Integration/HttpApplicationTests.cs @@ -30,7 +30,11 @@ public async Task Http_Get_Many() using var httpClient = Fixture.CreateClient(); // Act +#if XUNIT_V3 + using var response = await httpClient.GetAsync("api/values", TestContext.Current.CancellationToken); +#else using var response = await httpClient.GetAsync("api/values"); +#endif // Assert response.IsSuccessStatusCode.ShouldBeTrue(); @@ -43,7 +47,11 @@ public async Task Http_Get_Single() using var httpClient = Fixture.CreateClient(); // Act +#if XUNIT_V3 + using var response = await httpClient.GetAsync("api/values/a", TestContext.Current.CancellationToken); +#else using var response = await httpClient.GetAsync("api/values/a"); +#endif // Assert response.IsSuccessStatusCode.ShouldBeTrue(); @@ -56,7 +64,11 @@ public async Task Http_Post() using var httpClient = Fixture.CreateClient(); // Act +#if XUNIT_V3 + using var response = await httpClient.PostAsJsonAsync("api/values", new { }, TestContext.Current.CancellationToken); +#else using var response = await httpClient.PostAsJsonAsync("api/values", new { }); +#endif // Assert response.IsSuccessStatusCode.ShouldBeTrue(); @@ -70,7 +82,11 @@ public async Task Http_Put() // Act using var content = new StringContent(@"""d""", Encoding.UTF8, MediaTypeNames.Application.Json); +#if XUNIT_V3 + using var response = await httpClient.PutAsync("api/values/d", content, TestContext.Current.CancellationToken); +#else using var response = await httpClient.PutAsync("api/values/d", content); +#endif // Assert response.IsSuccessStatusCode.ShouldBeTrue(); @@ -83,7 +99,11 @@ public async Task Http_Delete() using var httpClient = Fixture.CreateClient(); // Act +#if XUNIT_V3 + using var response = await httpClient.DeleteAsync("api/values/d", TestContext.Current.CancellationToken); +#else using var response = await httpClient.DeleteAsync("api/values/d"); +#endif // Assert response.IsSuccessStatusCode.ShouldBeTrue(); diff --git a/tests/Logging.XUnit.Tests/Integration/PrintableDiagnosticMessage.cs b/tests/Logging.XUnit.Tests/Integration/PrintableDiagnosticMessage.cs index 4c5d7831..2a27a42b 100644 --- a/tests/Logging.XUnit.Tests/Integration/PrintableDiagnosticMessage.cs +++ b/tests/Logging.XUnit.Tests/Integration/PrintableDiagnosticMessage.cs @@ -1,7 +1,7 @@ // Copyright (c) Martin Costello, 2018. All rights reserved. // Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. -using Xunit.Sdk; +#if !XUNIT_V3 namespace MartinCostello.Logging.XUnit.Integration; @@ -12,3 +12,4 @@ internal sealed class PrintableDiagnosticMessage(string message) : DiagnosticMes { public override string ToString() => Message; } +#endif diff --git a/tests/Logging.XUnit.Tests/MartinCostello.Logging.XUnit.Tests.csproj b/tests/Logging.XUnit.Tests/MartinCostello.Logging.XUnit.Tests.csproj index 9d7cfe66..23b7597a 100644 --- a/tests/Logging.XUnit.Tests/MartinCostello.Logging.XUnit.Tests.csproj +++ b/tests/Logging.XUnit.Tests/MartinCostello.Logging.XUnit.Tests.csproj @@ -25,4 +25,10 @@ + + + + + + diff --git a/tests/Logging.XUnit.Tests/XUnitLoggerTests.cs b/tests/Logging.XUnit.Tests/XUnitLoggerTests.cs index 1e179d5a..61ef09ee 100644 --- a/tests/Logging.XUnit.Tests/XUnitLoggerTests.cs +++ b/tests/Logging.XUnit.Tests/XUnitLoggerTests.cs @@ -3,7 +3,6 @@ using Microsoft.Extensions.Logging; using NSubstitute; -using Xunit.Sdk; namespace MartinCostello.Logging.XUnit; @@ -86,10 +85,12 @@ XUnitLogger CreateLogger(XUnitLoggerOptions? opts) // Assert actual.Filter.ShouldNotBeNull(); actual.Filter(null, LogLevel.None).ShouldBeTrue(); - actual.MessageSinkMessageFactory.ShouldNotBeNull(); - actual.MessageSinkMessageFactory("message").ShouldBeOfType(); actual.IncludeScopes.ShouldBeFalse(); actual.Name.ShouldBe(name); + actual.MessageSinkMessageFactory.ShouldNotBeNull(); +#pragma warning disable xUnit3002 + actual.MessageSinkMessageFactory("message").ShouldBeOfType(); +#pragma warning restore xUnit3002 } [Fact] @@ -203,7 +204,7 @@ public static void XUnitLogger_Log_Does_Nothing_If_Not_Enabled() logger.Log(LogLevel.Information, new EventId(2), "state", null, Formatter); // Assert - outputHelper.DidNotReceiveWithAnyArgs().WriteLine(default); + outputHelper.DidNotReceiveWithAnyArgs().WriteLine(string.Empty); } [Fact] @@ -224,7 +225,7 @@ public static void XUnitLogger_Log_Does_Nothing_If_Null_Message_And_No_Exception logger.Log(LogLevel.Information, new EventId(2), "state", null, FormatterNull); // Assert - outputHelper.DidNotReceiveWithAnyArgs().WriteLine(default); + outputHelper.DidNotReceiveWithAnyArgs().WriteLine(string.Empty); } [Fact] @@ -245,7 +246,7 @@ public static void XUnitLogger_Log_Does_Nothing_If_Empty_Message_And_No_Exceptio logger.Log(LogLevel.Information, new EventId(2), "state", null, FormatterEmpty); // Assert - outputHelper.DidNotReceiveWithAnyArgs().WriteLine(default); + outputHelper.DidNotReceiveWithAnyArgs().WriteLine(string.Empty); } [Fact] diff --git a/tests/Logging.XUnit.v3.Tests/MartinCostello.Logging.XUnit.v3.Tests.csproj b/tests/Logging.XUnit.v3.Tests/MartinCostello.Logging.XUnit.v3.Tests.csproj new file mode 100644 index 00000000..21252e01 --- /dev/null +++ b/tests/Logging.XUnit.v3.Tests/MartinCostello.Logging.XUnit.v3.Tests.csproj @@ -0,0 +1,54 @@ + + + $(DefineConstants);XUNIT_V3 + Tests for MartinCostello.Logging.XUnit.v3. + false + true + $(NoWarn);CA1062;CA1707;CA1711;CA1861;CA2007;CA2234;SA1600;SA1602 + Exe + true + MartinCostello.Logging.XUnit + $(Description) + net9.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + From b94281a2c885f86a071b0003879af6a536d1fcc1 Mon Sep 17 00:00:00 2001 From: martincostello Date: Thu, 19 Dec 2024 16:00:25 +0000 Subject: [PATCH 02/11] Update dependabot ignores Ignore xunit.v3 updates for libraries used by the library. --- .github/dependabot.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 9a442192..d848d730 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -26,3 +26,5 @@ updates: - dependency-name: Microsoft.Extensions.Logging - dependency-name: xunit.abstractions - dependency-name: xunit.extensibility.execution + - dependency-name: xunit.v3.common + - dependency-name: xunit.v3.core From 598689ca60e94eaf0b343a2ec48a77059f888cae Mon Sep 17 00:00:00 2001 From: martincostello Date: Fri, 20 Dec 2024 07:55:10 +0000 Subject: [PATCH 03/11] Use xunit.v3.extensibility.core Use `xunit.v3.extensibility.core` instead of `xunit.v3.common` and `xunit.v3.core` to resolve issues with the build. --- Directory.Packages.props | 3 +-- .../MartinCostello.Logging.XUnit.v3.csproj | 11 +---------- .../MartinCostello.Logging.XUnit.v3.Tests.csproj | 4 ---- 3 files changed, 2 insertions(+), 16 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index bab89e66..046adcac 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -17,8 +17,7 @@ - - + diff --git a/src/Logging.XUnit.v3/MartinCostello.Logging.XUnit.v3.csproj b/src/Logging.XUnit.v3/MartinCostello.Logging.XUnit.v3.csproj index 3938bac7..12a85dfa 100644 --- a/src/Logging.XUnit.v3/MartinCostello.Logging.XUnit.v3.csproj +++ b/src/Logging.XUnit.v3/MartinCostello.Logging.XUnit.v3.csproj @@ -21,18 +21,9 @@ true - - - false - false - false - false - false - - - + diff --git a/tests/Logging.XUnit.v3.Tests/MartinCostello.Logging.XUnit.v3.Tests.csproj b/tests/Logging.XUnit.v3.Tests/MartinCostello.Logging.XUnit.v3.Tests.csproj index 21252e01..a49e6581 100644 --- a/tests/Logging.XUnit.v3.Tests/MartinCostello.Logging.XUnit.v3.Tests.csproj +++ b/tests/Logging.XUnit.v3.Tests/MartinCostello.Logging.XUnit.v3.Tests.csproj @@ -47,8 +47,4 @@ - - - false - From 5ebe1baea6679a3f6f7770e41c60cdd9dfd03fc1 Mon Sep 17 00:00:00 2001 From: martincostello Date: Fri, 20 Dec 2024 08:02:34 +0000 Subject: [PATCH 04/11] Update dependabot configuration Update the pinned xunit v3 packages. --- .github/dependabot.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index d848d730..10d0181a 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -26,5 +26,4 @@ updates: - dependency-name: Microsoft.Extensions.Logging - dependency-name: xunit.abstractions - dependency-name: xunit.extensibility.execution - - dependency-name: xunit.v3.common - - dependency-name: xunit.v3.core + - dependency-name: xunit.v3.extensibility.core From 64a064749d0ecc49b44d03264ec16ba0232a6f23 Mon Sep 17 00:00:00 2001 From: martincostello Date: Fri, 20 Dec 2024 08:09:18 +0000 Subject: [PATCH 05/11] Move code to Shared Move all of the C# for the libraries into a Shared folder and link to each project from there. --- .../MartinCostello.Logging.XUnit.v3.csproj | 23 +- .../MartinCostello.Logging.XUnit.csproj | 3 + .../AmbientTestOutputHelperAccessor.cs | 50 +- .../IMessageSinkAccessor.cs | 30 +- .../IMessageSinkExtensions.cs | 114 +-- .../ITestOutputHelperAccessor.cs | 0 .../ITestOutputHelperExtensions.cs | 0 .../MessageSinkAccessor.cs | 44 +- .../StringSyntaxAttribute.cs | 0 .../TestOutputHelperAccessor.cs | 0 .../XUnitLogScope.cs | 146 +-- .../XUnitLogger.IMessageSink.cs | 126 +-- .../XUnitLogger.ITestOutputHelper.cs | 92 +- src/{Logging.XUnit => Shared}/XUnitLogger.cs | 628 ++++++------- .../XUnitLoggerExtensions.IMessageSink.cs | 786 ++++++++-------- ...XUnitLoggerExtensions.ITestOutputHelper.cs | 838 +++++++++--------- .../XUnitLoggerOptions.cs | 82 +- .../XUnitLoggerProvider.IMessageSink.cs | 88 +- .../XUnitLoggerProvider.ITestOutputHelper.cs | 88 +- .../XUnitLoggerProvider.cs | 120 +-- 20 files changed, 1622 insertions(+), 1636 deletions(-) rename src/{Logging.XUnit => Shared}/AmbientTestOutputHelperAccessor.cs (97%) rename src/{Logging.XUnit => Shared}/IMessageSinkAccessor.cs (97%) rename src/{Logging.XUnit => Shared}/IMessageSinkExtensions.cs (97%) rename src/{Logging.XUnit => Shared}/ITestOutputHelperAccessor.cs (100%) rename src/{Logging.XUnit => Shared}/ITestOutputHelperExtensions.cs (100%) rename src/{Logging.XUnit => Shared}/MessageSinkAccessor.cs (97%) rename src/{Logging.XUnit => Shared}/StringSyntaxAttribute.cs (100%) rename src/{Logging.XUnit => Shared}/TestOutputHelperAccessor.cs (100%) rename src/{Logging.XUnit => Shared}/XUnitLogScope.cs (96%) rename src/{Logging.XUnit => Shared}/XUnitLogger.IMessageSink.cs (97%) rename src/{Logging.XUnit => Shared}/XUnitLogger.ITestOutputHelper.cs (97%) rename src/{Logging.XUnit => Shared}/XUnitLogger.cs (96%) rename src/{Logging.XUnit => Shared}/XUnitLoggerExtensions.IMessageSink.cs (97%) rename src/{Logging.XUnit => Shared}/XUnitLoggerExtensions.ITestOutputHelper.cs (97%) rename src/{Logging.XUnit => Shared}/XUnitLoggerOptions.cs (97%) rename src/{Logging.XUnit => Shared}/XUnitLoggerProvider.IMessageSink.cs (97%) rename src/{Logging.XUnit => Shared}/XUnitLoggerProvider.ITestOutputHelper.cs (97%) rename src/{Logging.XUnit => Shared}/XUnitLoggerProvider.cs (96%) diff --git a/src/Logging.XUnit.v3/MartinCostello.Logging.XUnit.v3.csproj b/src/Logging.XUnit.v3/MartinCostello.Logging.XUnit.v3.csproj index 12a85dfa..a9f3c2b3 100644 --- a/src/Logging.XUnit.v3/MartinCostello.Logging.XUnit.v3.csproj +++ b/src/Logging.XUnit.v3/MartinCostello.Logging.XUnit.v3.csproj @@ -21,6 +21,9 @@ true + + + @@ -36,24 +39,4 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/src/Logging.XUnit/MartinCostello.Logging.XUnit.csproj b/src/Logging.XUnit/MartinCostello.Logging.XUnit.csproj index 7952a092..b9952979 100644 --- a/src/Logging.XUnit/MartinCostello.Logging.XUnit.csproj +++ b/src/Logging.XUnit/MartinCostello.Logging.XUnit.csproj @@ -16,6 +16,9 @@ true + + + diff --git a/src/Logging.XUnit/AmbientTestOutputHelperAccessor.cs b/src/Shared/AmbientTestOutputHelperAccessor.cs similarity index 97% rename from src/Logging.XUnit/AmbientTestOutputHelperAccessor.cs rename to src/Shared/AmbientTestOutputHelperAccessor.cs index fefe038d..17f1683b 100644 --- a/src/Logging.XUnit/AmbientTestOutputHelperAccessor.cs +++ b/src/Shared/AmbientTestOutputHelperAccessor.cs @@ -1,25 +1,25 @@ -// Copyright (c) Martin Costello, 2018. All rights reserved. -// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. - -namespace MartinCostello.Logging.XUnit; - -/// -/// A class representing an implementation of that -/// stores the as an asynchronous local value. This class cannot be inherited. -/// -internal sealed class AmbientTestOutputHelperAccessor : ITestOutputHelperAccessor -{ - /// - /// A backing field for the for the current thread. - /// - private static readonly AsyncLocal _current = new(); - - /// - /// Gets or sets the current . - /// - public ITestOutputHelper? OutputHelper - { - get { return _current.Value; } - set { _current.Value = value; } - } -} +// Copyright (c) Martin Costello, 2018. All rights reserved. +// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. + +namespace MartinCostello.Logging.XUnit; + +/// +/// A class representing an implementation of that +/// stores the as an asynchronous local value. This class cannot be inherited. +/// +internal sealed class AmbientTestOutputHelperAccessor : ITestOutputHelperAccessor +{ + /// + /// A backing field for the for the current thread. + /// + private static readonly AsyncLocal _current = new(); + + /// + /// Gets or sets the current . + /// + public ITestOutputHelper? OutputHelper + { + get { return _current.Value; } + set { _current.Value = value; } + } +} diff --git a/src/Logging.XUnit/IMessageSinkAccessor.cs b/src/Shared/IMessageSinkAccessor.cs similarity index 97% rename from src/Logging.XUnit/IMessageSinkAccessor.cs rename to src/Shared/IMessageSinkAccessor.cs index 8971d230..93867bac 100644 --- a/src/Logging.XUnit/IMessageSinkAccessor.cs +++ b/src/Shared/IMessageSinkAccessor.cs @@ -1,15 +1,15 @@ -// Copyright (c) Martin Costello, 2018. All rights reserved. -// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. - -namespace MartinCostello.Logging.XUnit; - -/// -/// Defines a property for accessing an . -/// -public interface IMessageSinkAccessor -{ - /// - /// Gets or sets the to use. - /// - IMessageSink? MessageSink { get; set; } -} +// Copyright (c) Martin Costello, 2018. All rights reserved. +// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. + +namespace MartinCostello.Logging.XUnit; + +/// +/// Defines a property for accessing an . +/// +public interface IMessageSinkAccessor +{ + /// + /// Gets or sets the to use. + /// + IMessageSink? MessageSink { get; set; } +} diff --git a/src/Logging.XUnit/IMessageSinkExtensions.cs b/src/Shared/IMessageSinkExtensions.cs similarity index 97% rename from src/Logging.XUnit/IMessageSinkExtensions.cs rename to src/Shared/IMessageSinkExtensions.cs index 0b3f0d88..08217734 100644 --- a/src/Logging.XUnit/IMessageSinkExtensions.cs +++ b/src/Shared/IMessageSinkExtensions.cs @@ -1,57 +1,57 @@ -// Copyright (c) Martin Costello, 2018. All rights reserved. -// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. - -using System.ComponentModel; -using Microsoft.Extensions.Logging; - -#if XUNIT_V3 -namespace Xunit; -#else -#pragma warning disable IDE0130 -namespace Xunit.Abstractions; -#endif - -/// -/// A class containing extension methods for the interface. This class cannot be inherited. -/// -[EditorBrowsable(EditorBrowsableState.Never)] -public static class IMessageSinkExtensions -{ - /// - /// Returns an that logs to the message sink. - /// - /// The to create the logger factory from. - /// - /// An that writes messages to the message sink. - /// - /// - /// is . - /// - public static ILoggerFactory ToLoggerFactory(this IMessageSink messageSink) - { -#if NET - ArgumentNullException.ThrowIfNull(messageSink); -#else - if (messageSink == null) - { - throw new ArgumentNullException(nameof(messageSink)); - } -#endif - - return new LoggerFactory().AddXUnit(messageSink); - } - - /// - /// Returns an that logs to the message sink. - /// - /// The type of the logger to create. - /// The to create the logger from. - /// - /// An that writes messages to the message sink. - /// - /// - /// is . - /// - public static ILogger ToLogger(this IMessageSink messageSink) - => messageSink.ToLoggerFactory().CreateLogger(); -} +// Copyright (c) Martin Costello, 2018. All rights reserved. +// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. + +using System.ComponentModel; +using Microsoft.Extensions.Logging; + +#if XUNIT_V3 +namespace Xunit; +#else +#pragma warning disable IDE0130 +namespace Xunit.Abstractions; +#endif + +/// +/// A class containing extension methods for the interface. This class cannot be inherited. +/// +[EditorBrowsable(EditorBrowsableState.Never)] +public static class IMessageSinkExtensions +{ + /// + /// Returns an that logs to the message sink. + /// + /// The to create the logger factory from. + /// + /// An that writes messages to the message sink. + /// + /// + /// is . + /// + public static ILoggerFactory ToLoggerFactory(this IMessageSink messageSink) + { +#if NET + ArgumentNullException.ThrowIfNull(messageSink); +#else + if (messageSink == null) + { + throw new ArgumentNullException(nameof(messageSink)); + } +#endif + + return new LoggerFactory().AddXUnit(messageSink); + } + + /// + /// Returns an that logs to the message sink. + /// + /// The type of the logger to create. + /// The to create the logger from. + /// + /// An that writes messages to the message sink. + /// + /// + /// is . + /// + public static ILogger ToLogger(this IMessageSink messageSink) + => messageSink.ToLoggerFactory().CreateLogger(); +} diff --git a/src/Logging.XUnit/ITestOutputHelperAccessor.cs b/src/Shared/ITestOutputHelperAccessor.cs similarity index 100% rename from src/Logging.XUnit/ITestOutputHelperAccessor.cs rename to src/Shared/ITestOutputHelperAccessor.cs diff --git a/src/Logging.XUnit/ITestOutputHelperExtensions.cs b/src/Shared/ITestOutputHelperExtensions.cs similarity index 100% rename from src/Logging.XUnit/ITestOutputHelperExtensions.cs rename to src/Shared/ITestOutputHelperExtensions.cs diff --git a/src/Logging.XUnit/MessageSinkAccessor.cs b/src/Shared/MessageSinkAccessor.cs similarity index 97% rename from src/Logging.XUnit/MessageSinkAccessor.cs rename to src/Shared/MessageSinkAccessor.cs index 65bc2b8f..e8755484 100644 --- a/src/Logging.XUnit/MessageSinkAccessor.cs +++ b/src/Shared/MessageSinkAccessor.cs @@ -1,22 +1,22 @@ -// Copyright (c) Martin Costello, 2018. All rights reserved. -// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. - -namespace MartinCostello.Logging.XUnit; - -/// -/// A class representing the default implementation of . This class cannot be inherited. -/// -/// -/// Initializes a new instance of the class. -/// -/// The to use. -/// -/// is . -/// -internal sealed class MessageSinkAccessor(IMessageSink messageSink) : IMessageSinkAccessor -{ - /// - /// Gets or sets the current . - /// - public IMessageSink? MessageSink { get; set; } = messageSink ?? throw new ArgumentNullException(nameof(messageSink)); -} +// Copyright (c) Martin Costello, 2018. All rights reserved. +// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. + +namespace MartinCostello.Logging.XUnit; + +/// +/// A class representing the default implementation of . This class cannot be inherited. +/// +/// +/// Initializes a new instance of the class. +/// +/// The to use. +/// +/// is . +/// +internal sealed class MessageSinkAccessor(IMessageSink messageSink) : IMessageSinkAccessor +{ + /// + /// Gets or sets the current . + /// + public IMessageSink? MessageSink { get; set; } = messageSink ?? throw new ArgumentNullException(nameof(messageSink)); +} diff --git a/src/Logging.XUnit/StringSyntaxAttribute.cs b/src/Shared/StringSyntaxAttribute.cs similarity index 100% rename from src/Logging.XUnit/StringSyntaxAttribute.cs rename to src/Shared/StringSyntaxAttribute.cs diff --git a/src/Logging.XUnit/TestOutputHelperAccessor.cs b/src/Shared/TestOutputHelperAccessor.cs similarity index 100% rename from src/Logging.XUnit/TestOutputHelperAccessor.cs rename to src/Shared/TestOutputHelperAccessor.cs diff --git a/src/Logging.XUnit/XUnitLogScope.cs b/src/Shared/XUnitLogScope.cs similarity index 96% rename from src/Logging.XUnit/XUnitLogScope.cs rename to src/Shared/XUnitLogScope.cs index f7c00f8a..7233ca13 100644 --- a/src/Logging.XUnit/XUnitLogScope.cs +++ b/src/Shared/XUnitLogScope.cs @@ -1,73 +1,73 @@ -// Copyright (c) Martin Costello, 2018. All rights reserved. -// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. - -namespace MartinCostello.Logging.XUnit; - -/// -/// A class representing a scope for logging. This class cannot be inherited. -/// -/// -/// Initializes a new instance of the class. -/// -/// The state object for the scope. -internal sealed class XUnitLogScope(object state) -{ - /// - /// The scope for the current thread. - /// - private static readonly AsyncLocal _value = new(); - - /// - /// Gets the state object for the scope. - /// - public object State { get; } = state; - - /// - /// Gets or sets the current scope. - /// - internal static XUnitLogScope? Current - { - get { return _value.Value; } - set { _value.Value = value; } - } - - /// - /// Gets the parent scope. - /// - internal XUnitLogScope? Parent { get; private set; } - - /// - public override string? ToString() - => State.ToString(); - - /// - /// Pushes a new value into the scope. - /// - /// The state object for the scope. - /// - /// An that pops the scope. - /// - internal static IDisposable Push(object state) - { - var temp = Current; - - Current = new XUnitLogScope(state) - { - Parent = temp, - }; - - return new DisposableScope(); - } - - /// - /// A class the disposes of the current scope. This class cannot be inherited. - /// - private sealed class DisposableScope : IDisposable - { - /// - public void Dispose() - { - Current = Current?.Parent; - } - } -} +// Copyright (c) Martin Costello, 2018. All rights reserved. +// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. + +namespace MartinCostello.Logging.XUnit; + +/// +/// A class representing a scope for logging. This class cannot be inherited. +/// +/// +/// Initializes a new instance of the class. +/// +/// The state object for the scope. +internal sealed class XUnitLogScope(object state) +{ + /// + /// The scope for the current thread. + /// + private static readonly AsyncLocal _value = new(); + + /// + /// Gets the state object for the scope. + /// + public object State { get; } = state; + + /// + /// Gets or sets the current scope. + /// + internal static XUnitLogScope? Current + { + get { return _value.Value; } + set { _value.Value = value; } + } + + /// + /// Gets the parent scope. + /// + internal XUnitLogScope? Parent { get; private set; } + + /// + public override string? ToString() + => State.ToString(); + + /// + /// Pushes a new value into the scope. + /// + /// The state object for the scope. + /// + /// An that pops the scope. + /// + internal static IDisposable Push(object state) + { + var temp = Current; + + Current = new XUnitLogScope(state) + { + Parent = temp, + }; + + return new DisposableScope(); + } + + /// + /// A class the disposes of the current scope. This class cannot be inherited. + /// + private sealed class DisposableScope : IDisposable + { + /// + public void Dispose() + { + Current = Current?.Parent; + } + } +} diff --git a/src/Logging.XUnit/XUnitLogger.IMessageSink.cs b/src/Shared/XUnitLogger.IMessageSink.cs similarity index 97% rename from src/Logging.XUnit/XUnitLogger.IMessageSink.cs rename to src/Shared/XUnitLogger.IMessageSink.cs index 1471b532..4d2efed0 100644 --- a/src/Logging.XUnit/XUnitLogger.IMessageSink.cs +++ b/src/Shared/XUnitLogger.IMessageSink.cs @@ -1,63 +1,63 @@ -// Copyright (c) Martin Costello, 2018. All rights reserved. -// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. - -using Microsoft.Extensions.Logging; - -namespace MartinCostello.Logging.XUnit; - -/// -/// A class representing an to use with xunit. -/// -public partial class XUnitLogger -{ - /// - /// The to use. This field is read-only. - /// - private readonly IMessageSinkAccessor? _messageSinkAccessor; - - /// - /// Gets or sets the message sink message factory to use when writing to an . - /// - private Func _messageSinkMessageFactory; - - /// - /// Initializes a new instance of the class. - /// - /// The name for messages produced by the logger. - /// The to use. - /// The to use. - /// - /// or is . - /// - public XUnitLogger(string name, IMessageSink messageSink, XUnitLoggerOptions? options) - : this(name, new MessageSinkAccessor(messageSink), options) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The name for messages produced by the logger. - /// The to use. - /// The to use. - /// - /// or is . - /// - public XUnitLogger(string name, IMessageSinkAccessor accessor, XUnitLoggerOptions? options) - : this(name, options) - { - _messageSinkAccessor = accessor ?? throw new ArgumentNullException(nameof(accessor)); - } - - /// - /// Gets or sets the message sink message factory to use when writing to an . - /// - /// - /// is . - /// - public Func MessageSinkMessageFactory - { - get { return _messageSinkMessageFactory; } - set { _messageSinkMessageFactory = value ?? throw new ArgumentNullException(nameof(value)); } - } -} +// Copyright (c) Martin Costello, 2018. All rights reserved. +// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. + +using Microsoft.Extensions.Logging; + +namespace MartinCostello.Logging.XUnit; + +/// +/// A class representing an to use with xunit. +/// +public partial class XUnitLogger +{ + /// + /// The to use. This field is read-only. + /// + private readonly IMessageSinkAccessor? _messageSinkAccessor; + + /// + /// Gets or sets the message sink message factory to use when writing to an . + /// + private Func _messageSinkMessageFactory; + + /// + /// Initializes a new instance of the class. + /// + /// The name for messages produced by the logger. + /// The to use. + /// The to use. + /// + /// or is . + /// + public XUnitLogger(string name, IMessageSink messageSink, XUnitLoggerOptions? options) + : this(name, new MessageSinkAccessor(messageSink), options) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The name for messages produced by the logger. + /// The to use. + /// The to use. + /// + /// or is . + /// + public XUnitLogger(string name, IMessageSinkAccessor accessor, XUnitLoggerOptions? options) + : this(name, options) + { + _messageSinkAccessor = accessor ?? throw new ArgumentNullException(nameof(accessor)); + } + + /// + /// Gets or sets the message sink message factory to use when writing to an . + /// + /// + /// is . + /// + public Func MessageSinkMessageFactory + { + get { return _messageSinkMessageFactory; } + set { _messageSinkMessageFactory = value ?? throw new ArgumentNullException(nameof(value)); } + } +} diff --git a/src/Logging.XUnit/XUnitLogger.ITestOutputHelper.cs b/src/Shared/XUnitLogger.ITestOutputHelper.cs similarity index 97% rename from src/Logging.XUnit/XUnitLogger.ITestOutputHelper.cs rename to src/Shared/XUnitLogger.ITestOutputHelper.cs index b11505e8..60675af6 100644 --- a/src/Logging.XUnit/XUnitLogger.ITestOutputHelper.cs +++ b/src/Shared/XUnitLogger.ITestOutputHelper.cs @@ -1,46 +1,46 @@ -// Copyright (c) Martin Costello, 2018. All rights reserved. -// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. - -using Microsoft.Extensions.Logging; - -namespace MartinCostello.Logging.XUnit; - -/// -/// A class representing an to use with xunit. -/// -public partial class XUnitLogger -{ - /// - /// The to use. This field is read-only. - /// - private readonly ITestOutputHelperAccessor? _outputHelperAccessor; - - /// - /// Initializes a new instance of the class. - /// - /// The name for messages produced by the logger. - /// The to use. - /// The to use. - /// - /// or is . - /// - public XUnitLogger(string name, ITestOutputHelper outputHelper, XUnitLoggerOptions? options) - : this(name, new TestOutputHelperAccessor(outputHelper), options) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The name for messages produced by the logger. - /// The to use. - /// The to use. - /// - /// or is . - /// - public XUnitLogger(string name, ITestOutputHelperAccessor accessor, XUnitLoggerOptions? options) - : this(name, options) - { - _outputHelperAccessor = accessor ?? throw new ArgumentNullException(nameof(accessor)); - } -} +// Copyright (c) Martin Costello, 2018. All rights reserved. +// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. + +using Microsoft.Extensions.Logging; + +namespace MartinCostello.Logging.XUnit; + +/// +/// A class representing an to use with xunit. +/// +public partial class XUnitLogger +{ + /// + /// The to use. This field is read-only. + /// + private readonly ITestOutputHelperAccessor? _outputHelperAccessor; + + /// + /// Initializes a new instance of the class. + /// + /// The name for messages produced by the logger. + /// The to use. + /// The to use. + /// + /// or is . + /// + public XUnitLogger(string name, ITestOutputHelper outputHelper, XUnitLoggerOptions? options) + : this(name, new TestOutputHelperAccessor(outputHelper), options) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The name for messages produced by the logger. + /// The to use. + /// The to use. + /// + /// or is . + /// + public XUnitLogger(string name, ITestOutputHelperAccessor accessor, XUnitLoggerOptions? options) + : this(name, options) + { + _outputHelperAccessor = accessor ?? throw new ArgumentNullException(nameof(accessor)); + } +} diff --git a/src/Logging.XUnit/XUnitLogger.cs b/src/Shared/XUnitLogger.cs similarity index 96% rename from src/Logging.XUnit/XUnitLogger.cs rename to src/Shared/XUnitLogger.cs index 33a1abd3..90c6df8b 100644 --- a/src/Logging.XUnit/XUnitLogger.cs +++ b/src/Shared/XUnitLogger.cs @@ -1,314 +1,314 @@ -// Copyright (c) Martin Costello, 2018. All rights reserved. -// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. - -using System.Text; -using Microsoft.Extensions.Logging; - -namespace MartinCostello.Logging.XUnit; - -/// -/// A class representing an to use with xunit. -/// -public partial class XUnitLogger : ILogger -{ - //// Based on https://github.com/dotnet/runtime/blob/65067052e433eda400c5e7cc9f7b21c84640f901/src/libraries/Microsoft.Extensions.Logging.Console/src/ConsoleLogger.cs#L41-L66 - - /// - /// The padding to use for log levels. - /// - private const string LogLevelPadding = ": "; - - /// - /// The padding to use for messages. This field is read-only. - /// - private static readonly string MessagePadding = new(' ', GetLogLevelString(LogLevel.Debug).Length + LogLevelPadding.Length); - - /// - /// The padding to use for new lines. This field is read-only. - /// - private static readonly string NewLineWithMessagePadding = Environment.NewLine + MessagePadding; - - /// - /// The current builder to use to generate log messages. - /// - [ThreadStatic] - private static StringBuilder? _logBuilder; - - /// - /// The format string used to format the timestamp in log messages. - /// - private readonly string _timestampFormat; - - /// - /// Gets or sets the filter to use. - /// - private Func _filter; - - /// - /// Initializes a new instance of the class. - /// - /// The name for messages produced by the logger. - /// The to use. - private XUnitLogger(string name, XUnitLoggerOptions? options) - { - Name = name ?? throw new ArgumentNullException(nameof(name)); - - _filter = options?.Filter ?? (static (_, _) => true); - _messageSinkMessageFactory = options?.MessageSinkMessageFactory ?? (static (message) => new DiagnosticMessage(message)); - _timestampFormat = options?.TimestampFormat ?? "u"; - IncludeScopes = options?.IncludeScopes ?? false; - } - - /// - /// Gets or sets the category filter to apply to logs. - /// - /// - /// is . - /// - public Func Filter - { - get => _filter; - set => _filter = value ?? throw new ArgumentNullException(nameof(value)); - } - - /// - /// Gets or sets a value indicating whether to include scopes. - /// - public bool IncludeScopes { get; set; } - - /// - /// Gets the name of the logger. - /// - public string Name { get; } - - /// - /// Gets or sets a delegate representing the system clock. - /// - internal Func Clock { get; set; } = static () => DateTimeOffset.Now; - - /// - public IDisposable? BeginScope(TState state) - where TState : notnull - { -#if NET - ArgumentNullException.ThrowIfNull(state); -#else - if (state == null) - { - throw new ArgumentNullException(nameof(state)); - } -#endif - - return XUnitLogScope.Push(state); - } - - /// - public bool IsEnabled(LogLevel logLevel) - { - if (logLevel == LogLevel.None) - { - return false; - } - - return Filter(Name, logLevel); - } - - /// - public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) - { - if (!IsEnabled(logLevel)) - { - return; - } - -#if NET - ArgumentNullException.ThrowIfNull(formatter); -#else - if (formatter == null) - { - throw new ArgumentNullException(nameof(formatter)); - } -#endif - - string? message = formatter(state, exception); - - if (!string.IsNullOrEmpty(message) || exception != null) - { - WriteMessage(logLevel, eventId.Id, message, exception); - } - } - - /// - /// Writes a message to the or associated with the instance. - /// - /// The message to write will be written on this level. - /// The Id of the event. - /// The message to write. - /// The exception related to this message. - public virtual void WriteMessage(LogLevel logLevel, int eventId, string? message, Exception? exception) - { - ITestOutputHelper? outputHelper = _outputHelperAccessor?.OutputHelper; - IMessageSink? messageSink = _messageSinkAccessor?.MessageSink; - - if (outputHelper is null && messageSink is null) - { - return; - } - - StringBuilder? logBuilder = _logBuilder; - _logBuilder = null; - - logBuilder ??= new StringBuilder(); - - string logLevelString = GetLogLevelString(logLevel); - - logBuilder.Append(LogLevelPadding); - logBuilder.Append(Name); - logBuilder.Append('['); - logBuilder.Append(eventId); - logBuilder.Append(']'); - logBuilder.AppendLine(); - - if (IncludeScopes) - { - GetScopeInformation(logBuilder); - } - - bool hasMessage = !string.IsNullOrEmpty(message); - - if (hasMessage) - { - logBuilder.Append(MessagePadding); - - int length = logBuilder.Length; - logBuilder.Append(message); - logBuilder.Replace(Environment.NewLine, NewLineWithMessagePadding, length, message!.Length); - } - - if (exception != null) - { - if (hasMessage) - { - logBuilder.AppendLine(); - } - - logBuilder.Append(exception); - } - - // Prefix the formatted message so it renders like this: - // [{timestamp}] {logLevelString}{message} - logBuilder.Insert(0, logLevelString); - logBuilder.Insert(0, "] "); - logBuilder.Insert(0, Clock().ToString(_timestampFormat, CultureInfo.CurrentCulture)); - logBuilder.Insert(0, '['); - - string line = logBuilder.ToString(); - - try - { - outputHelper?.WriteLine(line); - - if (messageSink != null) - { - var sinkMessage = _messageSinkMessageFactory(line); - messageSink.OnMessage(sinkMessage); - } - } - catch (InvalidOperationException) - { - // Ignore exception if the application tries to log after the test ends - // but before the ITestOutputHelper is detached, e.g. "There is no currently active test." - } - - logBuilder.Clear(); - - if (logBuilder.Capacity > 1024) - { - logBuilder.Capacity = 1024; - } - - _logBuilder = logBuilder; - } - - /// - /// Returns the string to use for the specified logging level. - /// - /// The log level to get the representation for. - /// - /// A containing the text representation of . - /// - private static string GetLogLevelString(LogLevel logLevel) - { - return logLevel switch - { - LogLevel.Critical => "crit", - LogLevel.Debug => "dbug", - LogLevel.Error => "fail", - LogLevel.Information => "info", - LogLevel.Trace => "trce", - LogLevel.Warning => "warn", - _ => throw new ArgumentOutOfRangeException(nameof(logLevel)), - }; - } - - /// - /// Gets the scope information for the current operation. - /// - /// The to write the scope to. - private static void GetScopeInformation(StringBuilder builder) - { - var current = XUnitLogScope.Current; - - var stack = new Stack(); - while (current != null) - { - stack.Push(current); - current = current.Parent; - } - - var depth = 0; - static string DepthPadding(int depth) => new(' ', depth * 2); - - while (stack.Count > 0) - { - var elem = stack.Pop(); - foreach (var property in StringifyScope(elem)) - { - builder.Append(MessagePadding) - .Append(DepthPadding(depth)) - .Append("=> ") - .Append(property) - .AppendLine(); - } - - depth++; - } - } - - /// - /// Returns one or more stringified properties from the log scope. - /// - /// The to stringify. - /// An enumeration of scope properties from the current scope. - private static IEnumerable StringifyScope(XUnitLogScope scope) - { - if (scope.State is IEnumerable> pairs) - { - foreach (var pair in pairs) - { - yield return $"{pair.Key}: {pair.Value}"; - } - } - else if (scope.State is IEnumerable entries) - { - foreach (var entry in entries) - { - yield return entry; - } - } - else - { - yield return scope.ToString(); - } - } -} +// Copyright (c) Martin Costello, 2018. All rights reserved. +// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. + +using System.Text; +using Microsoft.Extensions.Logging; + +namespace MartinCostello.Logging.XUnit; + +/// +/// A class representing an to use with xunit. +/// +public partial class XUnitLogger : ILogger +{ + //// Based on https://github.com/dotnet/runtime/blob/65067052e433eda400c5e7cc9f7b21c84640f901/src/libraries/Microsoft.Extensions.Logging.Console/src/ConsoleLogger.cs#L41-L66 + + /// + /// The padding to use for log levels. + /// + private const string LogLevelPadding = ": "; + + /// + /// The padding to use for messages. This field is read-only. + /// + private static readonly string MessagePadding = new(' ', GetLogLevelString(LogLevel.Debug).Length + LogLevelPadding.Length); + + /// + /// The padding to use for new lines. This field is read-only. + /// + private static readonly string NewLineWithMessagePadding = Environment.NewLine + MessagePadding; + + /// + /// The current builder to use to generate log messages. + /// + [ThreadStatic] + private static StringBuilder? _logBuilder; + + /// + /// The format string used to format the timestamp in log messages. + /// + private readonly string _timestampFormat; + + /// + /// Gets or sets the filter to use. + /// + private Func _filter; + + /// + /// Initializes a new instance of the class. + /// + /// The name for messages produced by the logger. + /// The to use. + private XUnitLogger(string name, XUnitLoggerOptions? options) + { + Name = name ?? throw new ArgumentNullException(nameof(name)); + + _filter = options?.Filter ?? (static (_, _) => true); + _messageSinkMessageFactory = options?.MessageSinkMessageFactory ?? (static (message) => new DiagnosticMessage(message)); + _timestampFormat = options?.TimestampFormat ?? "u"; + IncludeScopes = options?.IncludeScopes ?? false; + } + + /// + /// Gets or sets the category filter to apply to logs. + /// + /// + /// is . + /// + public Func Filter + { + get => _filter; + set => _filter = value ?? throw new ArgumentNullException(nameof(value)); + } + + /// + /// Gets or sets a value indicating whether to include scopes. + /// + public bool IncludeScopes { get; set; } + + /// + /// Gets the name of the logger. + /// + public string Name { get; } + + /// + /// Gets or sets a delegate representing the system clock. + /// + internal Func Clock { get; set; } = static () => DateTimeOffset.Now; + + /// + public IDisposable? BeginScope(TState state) + where TState : notnull + { +#if NET + ArgumentNullException.ThrowIfNull(state); +#else + if (state == null) + { + throw new ArgumentNullException(nameof(state)); + } +#endif + + return XUnitLogScope.Push(state); + } + + /// + public bool IsEnabled(LogLevel logLevel) + { + if (logLevel == LogLevel.None) + { + return false; + } + + return Filter(Name, logLevel); + } + + /// + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) + { + if (!IsEnabled(logLevel)) + { + return; + } + +#if NET + ArgumentNullException.ThrowIfNull(formatter); +#else + if (formatter == null) + { + throw new ArgumentNullException(nameof(formatter)); + } +#endif + + string? message = formatter(state, exception); + + if (!string.IsNullOrEmpty(message) || exception != null) + { + WriteMessage(logLevel, eventId.Id, message, exception); + } + } + + /// + /// Writes a message to the or associated with the instance. + /// + /// The message to write will be written on this level. + /// The Id of the event. + /// The message to write. + /// The exception related to this message. + public virtual void WriteMessage(LogLevel logLevel, int eventId, string? message, Exception? exception) + { + ITestOutputHelper? outputHelper = _outputHelperAccessor?.OutputHelper; + IMessageSink? messageSink = _messageSinkAccessor?.MessageSink; + + if (outputHelper is null && messageSink is null) + { + return; + } + + StringBuilder? logBuilder = _logBuilder; + _logBuilder = null; + + logBuilder ??= new StringBuilder(); + + string logLevelString = GetLogLevelString(logLevel); + + logBuilder.Append(LogLevelPadding); + logBuilder.Append(Name); + logBuilder.Append('['); + logBuilder.Append(eventId); + logBuilder.Append(']'); + logBuilder.AppendLine(); + + if (IncludeScopes) + { + GetScopeInformation(logBuilder); + } + + bool hasMessage = !string.IsNullOrEmpty(message); + + if (hasMessage) + { + logBuilder.Append(MessagePadding); + + int length = logBuilder.Length; + logBuilder.Append(message); + logBuilder.Replace(Environment.NewLine, NewLineWithMessagePadding, length, message!.Length); + } + + if (exception != null) + { + if (hasMessage) + { + logBuilder.AppendLine(); + } + + logBuilder.Append(exception); + } + + // Prefix the formatted message so it renders like this: + // [{timestamp}] {logLevelString}{message} + logBuilder.Insert(0, logLevelString); + logBuilder.Insert(0, "] "); + logBuilder.Insert(0, Clock().ToString(_timestampFormat, CultureInfo.CurrentCulture)); + logBuilder.Insert(0, '['); + + string line = logBuilder.ToString(); + + try + { + outputHelper?.WriteLine(line); + + if (messageSink != null) + { + var sinkMessage = _messageSinkMessageFactory(line); + messageSink.OnMessage(sinkMessage); + } + } + catch (InvalidOperationException) + { + // Ignore exception if the application tries to log after the test ends + // but before the ITestOutputHelper is detached, e.g. "There is no currently active test." + } + + logBuilder.Clear(); + + if (logBuilder.Capacity > 1024) + { + logBuilder.Capacity = 1024; + } + + _logBuilder = logBuilder; + } + + /// + /// Returns the string to use for the specified logging level. + /// + /// The log level to get the representation for. + /// + /// A containing the text representation of . + /// + private static string GetLogLevelString(LogLevel logLevel) + { + return logLevel switch + { + LogLevel.Critical => "crit", + LogLevel.Debug => "dbug", + LogLevel.Error => "fail", + LogLevel.Information => "info", + LogLevel.Trace => "trce", + LogLevel.Warning => "warn", + _ => throw new ArgumentOutOfRangeException(nameof(logLevel)), + }; + } + + /// + /// Gets the scope information for the current operation. + /// + /// The to write the scope to. + private static void GetScopeInformation(StringBuilder builder) + { + var current = XUnitLogScope.Current; + + var stack = new Stack(); + while (current != null) + { + stack.Push(current); + current = current.Parent; + } + + var depth = 0; + static string DepthPadding(int depth) => new(' ', depth * 2); + + while (stack.Count > 0) + { + var elem = stack.Pop(); + foreach (var property in StringifyScope(elem)) + { + builder.Append(MessagePadding) + .Append(DepthPadding(depth)) + .Append("=> ") + .Append(property) + .AppendLine(); + } + + depth++; + } + } + + /// + /// Returns one or more stringified properties from the log scope. + /// + /// The to stringify. + /// An enumeration of scope properties from the current scope. + private static IEnumerable StringifyScope(XUnitLogScope scope) + { + if (scope.State is IEnumerable> pairs) + { + foreach (var pair in pairs) + { + yield return $"{pair.Key}: {pair.Value}"; + } + } + else if (scope.State is IEnumerable entries) + { + foreach (var entry in entries) + { + yield return entry; + } + } + else + { + yield return scope.ToString(); + } + } +} diff --git a/src/Logging.XUnit/XUnitLoggerExtensions.IMessageSink.cs b/src/Shared/XUnitLoggerExtensions.IMessageSink.cs similarity index 97% rename from src/Logging.XUnit/XUnitLoggerExtensions.IMessageSink.cs rename to src/Shared/XUnitLoggerExtensions.IMessageSink.cs index 5254210e..fa101e02 100644 --- a/src/Logging.XUnit/XUnitLoggerExtensions.IMessageSink.cs +++ b/src/Shared/XUnitLoggerExtensions.IMessageSink.cs @@ -1,393 +1,393 @@ -// Copyright (c) Martin Costello, 2018. All rights reserved. -// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. - -using MartinCostello.Logging.XUnit; -using Microsoft.Extensions.DependencyInjection.Extensions; - -namespace Microsoft.Extensions.Logging; - -/// -/// A class containing extension methods for configuring logging to xunit. This class cannot be inherited. -/// -public static partial class XUnitLoggerExtensions -{ - /// - /// Adds an xunit logger to the logging builder. - /// - /// The to use. - /// The to use. - /// - /// The instance of specified by . - /// - /// - /// or is . - /// - public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, IMessageSinkAccessor accessor) - { -#if NET - ArgumentNullException.ThrowIfNull(builder); - ArgumentNullException.ThrowIfNull(accessor); -#else - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (accessor == null) - { - throw new ArgumentNullException(nameof(accessor)); - } -#endif - - return builder.AddXUnit(accessor, static (_) => { }); - } - - /// - /// Adds an xunit logger to the logging builder. - /// - /// The to use. - /// The to use. - /// A delegate to a method to use to configure the logging options. - /// - /// The instance of specified by . - /// - /// - /// , or is . - /// - public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, IMessageSinkAccessor accessor, Action configure) - { -#if NET - ArgumentNullException.ThrowIfNull(builder); - ArgumentNullException.ThrowIfNull(accessor); - ArgumentNullException.ThrowIfNull(configure); -#else - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (accessor == null) - { - throw new ArgumentNullException(nameof(accessor)); - } - - if (configure == null) - { - throw new ArgumentNullException(nameof(configure)); - } -#endif - - var options = new XUnitLoggerOptions(); - - configure(options); - -#pragma warning disable CA2000 - builder.AddProvider(new XUnitLoggerProvider(accessor, options)); -#pragma warning restore CA2000 - - builder.Services.TryAddSingleton(accessor); - - return builder; - } - - /// - /// Adds an xunit logger to the logging builder. - /// - /// The to use. - /// The to use. - /// - /// The instance of specified by . - /// - /// - /// or is . - /// - public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, IMessageSink messageSink) - { -#if NET - ArgumentNullException.ThrowIfNull(builder); - ArgumentNullException.ThrowIfNull(messageSink); -#else - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (messageSink == null) - { - throw new ArgumentNullException(nameof(messageSink)); - } -#endif - - return builder.AddXUnit(messageSink, static (_) => { }); - } - - /// - /// Adds an xunit logger to the logging builder. - /// - /// The to use. - /// The to use. - /// A delegate to a method to use to configure the logging options. - /// - /// The instance of specified by . - /// - /// - /// , or is . - /// - public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, IMessageSink messageSink, Action configure) - { -#if NET - ArgumentNullException.ThrowIfNull(builder); - ArgumentNullException.ThrowIfNull(messageSink); - ArgumentNullException.ThrowIfNull(configure); -#else - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (messageSink == null) - { - throw new ArgumentNullException(nameof(messageSink)); - } - - if (configure == null) - { - throw new ArgumentNullException(nameof(configure)); - } -#endif - - var options = new XUnitLoggerOptions(); - - configure(options); - -#pragma warning disable CA2000 - return builder.AddProvider(new XUnitLoggerProvider(messageSink, options)); -#pragma warning restore CA2000 - } - - /// - /// Adds an xunit logger to the factory. - /// - /// The to use. - /// The to use. - /// The minimum to be logged. - /// - /// The instance of specified by . - /// - /// - /// or is . - /// - public static ILoggerFactory AddXUnit(this ILoggerFactory factory, IMessageSink messageSink, LogLevel minLevel) - { -#if NET - ArgumentNullException.ThrowIfNull(factory); - ArgumentNullException.ThrowIfNull(messageSink); -#else - if (factory == null) - { - throw new ArgumentNullException(nameof(factory)); - } - - if (messageSink == null) - { - throw new ArgumentNullException(nameof(messageSink)); - } -#endif - - return factory.AddXUnit(messageSink, (_, level) => level >= minLevel); - } - - /// - /// Adds an xunit logger to the factory. - /// - /// The to use. - /// The to use. - /// The category filter to apply to logs. - /// - /// The instance of specified by . - /// - /// - /// , or is . - /// - public static ILoggerFactory AddXUnit(this ILoggerFactory factory, IMessageSink messageSink, Func filter) - { -#if NET - ArgumentNullException.ThrowIfNull(factory); - ArgumentNullException.ThrowIfNull(messageSink); - ArgumentNullException.ThrowIfNull(filter); -#else - if (factory == null) - { - throw new ArgumentNullException(nameof(factory)); - } - - if (messageSink == null) - { - throw new ArgumentNullException(nameof(messageSink)); - } - - if (filter == null) - { - throw new ArgumentNullException(nameof(filter)); - } -#endif - - return factory.AddXUnit(messageSink, (options) => options.Filter = filter); - } - - /// - /// Adds an xunit logger to the factory. - /// - /// The to use. - /// The to use. - /// - /// The instance of specified by . - /// - /// - /// or is . - /// - public static ILoggerFactory AddXUnit(this ILoggerFactory factory, IMessageSink messageSink) - { -#if NET - ArgumentNullException.ThrowIfNull(factory); - ArgumentNullException.ThrowIfNull(messageSink); -#else - if (factory == null) - { - throw new ArgumentNullException(nameof(factory)); - } - - if (messageSink == null) - { - throw new ArgumentNullException(nameof(messageSink)); - } -#endif - - return factory.AddXUnit(messageSink, static (_) => { }); - } - - /// - /// Adds an xunit logger to the factory. - /// - /// The to use. - /// The to use. - /// The options to use for logging to xunit. - /// - /// The instance of specified by . - /// - /// - /// , OR is . - /// - public static ILoggerFactory AddXUnit(this ILoggerFactory factory, IMessageSink messageSink, XUnitLoggerOptions options) - { -#if NET - ArgumentNullException.ThrowIfNull(factory); - ArgumentNullException.ThrowIfNull(messageSink); - ArgumentNullException.ThrowIfNull(options); -#else - if (factory == null) - { - throw new ArgumentNullException(nameof(factory)); - } - - if (messageSink == null) - { - throw new ArgumentNullException(nameof(messageSink)); - } - - if (options == null) - { - throw new ArgumentNullException(nameof(options)); - } -#endif - - return factory.AddXUnit(messageSink, () => options); - } - - /// - /// Adds an xunit logger to the factory. - /// - /// The to use. - /// The to use. - /// A delegate to a method to use to configure the logging options. - /// - /// The instance of specified by . - /// - /// - /// , or is . - /// - public static ILoggerFactory AddXUnit(this ILoggerFactory factory, IMessageSink messageSink, Action configure) - { -#if NET - ArgumentNullException.ThrowIfNull(factory); - ArgumentNullException.ThrowIfNull(messageSink); - ArgumentNullException.ThrowIfNull(configure); -#else - if (factory == null) - { - throw new ArgumentNullException(nameof(factory)); - } - - if (messageSink == null) - { - throw new ArgumentNullException(nameof(messageSink)); - } - - if (configure == null) - { - throw new ArgumentNullException(nameof(configure)); - } -#endif - - return factory.AddXUnit(messageSink, () => - { - var options = new XUnitLoggerOptions(); - configure(options); - return options; - }); - } - - /// - /// Adds an xunit logger to the factory. - /// - /// The to use. - /// The to use. - /// A delegate to a method that returns a configured to use. - /// - /// The instance of specified by . - /// - /// - /// , or is . - /// - public static ILoggerFactory AddXUnit(this ILoggerFactory factory, IMessageSink messageSink, Func configure) - { -#if NET - ArgumentNullException.ThrowIfNull(factory); - ArgumentNullException.ThrowIfNull(messageSink); - ArgumentNullException.ThrowIfNull(configure); -#else - if (factory == null) - { - throw new ArgumentNullException(nameof(factory)); - } - - if (messageSink == null) - { - throw new ArgumentNullException(nameof(messageSink)); - } - - if (configure == null) - { - throw new ArgumentNullException(nameof(configure)); - } -#endif - - var options = configure(); - -#pragma warning disable CA2000 - factory.AddProvider(new XUnitLoggerProvider(messageSink, options)); -#pragma warning restore CA2000 - - return factory; - } -} +// Copyright (c) Martin Costello, 2018. All rights reserved. +// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. + +using MartinCostello.Logging.XUnit; +using Microsoft.Extensions.DependencyInjection.Extensions; + +namespace Microsoft.Extensions.Logging; + +/// +/// A class containing extension methods for configuring logging to xunit. This class cannot be inherited. +/// +public static partial class XUnitLoggerExtensions +{ + /// + /// Adds an xunit logger to the logging builder. + /// + /// The to use. + /// The to use. + /// + /// The instance of specified by . + /// + /// + /// or is . + /// + public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, IMessageSinkAccessor accessor) + { +#if NET + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(accessor); +#else + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (accessor == null) + { + throw new ArgumentNullException(nameof(accessor)); + } +#endif + + return builder.AddXUnit(accessor, static (_) => { }); + } + + /// + /// Adds an xunit logger to the logging builder. + /// + /// The to use. + /// The to use. + /// A delegate to a method to use to configure the logging options. + /// + /// The instance of specified by . + /// + /// + /// , or is . + /// + public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, IMessageSinkAccessor accessor, Action configure) + { +#if NET + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(accessor); + ArgumentNullException.ThrowIfNull(configure); +#else + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (accessor == null) + { + throw new ArgumentNullException(nameof(accessor)); + } + + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } +#endif + + var options = new XUnitLoggerOptions(); + + configure(options); + +#pragma warning disable CA2000 + builder.AddProvider(new XUnitLoggerProvider(accessor, options)); +#pragma warning restore CA2000 + + builder.Services.TryAddSingleton(accessor); + + return builder; + } + + /// + /// Adds an xunit logger to the logging builder. + /// + /// The to use. + /// The to use. + /// + /// The instance of specified by . + /// + /// + /// or is . + /// + public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, IMessageSink messageSink) + { +#if NET + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(messageSink); +#else + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (messageSink == null) + { + throw new ArgumentNullException(nameof(messageSink)); + } +#endif + + return builder.AddXUnit(messageSink, static (_) => { }); + } + + /// + /// Adds an xunit logger to the logging builder. + /// + /// The to use. + /// The to use. + /// A delegate to a method to use to configure the logging options. + /// + /// The instance of specified by . + /// + /// + /// , or is . + /// + public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, IMessageSink messageSink, Action configure) + { +#if NET + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(messageSink); + ArgumentNullException.ThrowIfNull(configure); +#else + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (messageSink == null) + { + throw new ArgumentNullException(nameof(messageSink)); + } + + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } +#endif + + var options = new XUnitLoggerOptions(); + + configure(options); + +#pragma warning disable CA2000 + return builder.AddProvider(new XUnitLoggerProvider(messageSink, options)); +#pragma warning restore CA2000 + } + + /// + /// Adds an xunit logger to the factory. + /// + /// The to use. + /// The to use. + /// The minimum to be logged. + /// + /// The instance of specified by . + /// + /// + /// or is . + /// + public static ILoggerFactory AddXUnit(this ILoggerFactory factory, IMessageSink messageSink, LogLevel minLevel) + { +#if NET + ArgumentNullException.ThrowIfNull(factory); + ArgumentNullException.ThrowIfNull(messageSink); +#else + if (factory == null) + { + throw new ArgumentNullException(nameof(factory)); + } + + if (messageSink == null) + { + throw new ArgumentNullException(nameof(messageSink)); + } +#endif + + return factory.AddXUnit(messageSink, (_, level) => level >= minLevel); + } + + /// + /// Adds an xunit logger to the factory. + /// + /// The to use. + /// The to use. + /// The category filter to apply to logs. + /// + /// The instance of specified by . + /// + /// + /// , or is . + /// + public static ILoggerFactory AddXUnit(this ILoggerFactory factory, IMessageSink messageSink, Func filter) + { +#if NET + ArgumentNullException.ThrowIfNull(factory); + ArgumentNullException.ThrowIfNull(messageSink); + ArgumentNullException.ThrowIfNull(filter); +#else + if (factory == null) + { + throw new ArgumentNullException(nameof(factory)); + } + + if (messageSink == null) + { + throw new ArgumentNullException(nameof(messageSink)); + } + + if (filter == null) + { + throw new ArgumentNullException(nameof(filter)); + } +#endif + + return factory.AddXUnit(messageSink, (options) => options.Filter = filter); + } + + /// + /// Adds an xunit logger to the factory. + /// + /// The to use. + /// The to use. + /// + /// The instance of specified by . + /// + /// + /// or is . + /// + public static ILoggerFactory AddXUnit(this ILoggerFactory factory, IMessageSink messageSink) + { +#if NET + ArgumentNullException.ThrowIfNull(factory); + ArgumentNullException.ThrowIfNull(messageSink); +#else + if (factory == null) + { + throw new ArgumentNullException(nameof(factory)); + } + + if (messageSink == null) + { + throw new ArgumentNullException(nameof(messageSink)); + } +#endif + + return factory.AddXUnit(messageSink, static (_) => { }); + } + + /// + /// Adds an xunit logger to the factory. + /// + /// The to use. + /// The to use. + /// The options to use for logging to xunit. + /// + /// The instance of specified by . + /// + /// + /// , OR is . + /// + public static ILoggerFactory AddXUnit(this ILoggerFactory factory, IMessageSink messageSink, XUnitLoggerOptions options) + { +#if NET + ArgumentNullException.ThrowIfNull(factory); + ArgumentNullException.ThrowIfNull(messageSink); + ArgumentNullException.ThrowIfNull(options); +#else + if (factory == null) + { + throw new ArgumentNullException(nameof(factory)); + } + + if (messageSink == null) + { + throw new ArgumentNullException(nameof(messageSink)); + } + + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } +#endif + + return factory.AddXUnit(messageSink, () => options); + } + + /// + /// Adds an xunit logger to the factory. + /// + /// The to use. + /// The to use. + /// A delegate to a method to use to configure the logging options. + /// + /// The instance of specified by . + /// + /// + /// , or is . + /// + public static ILoggerFactory AddXUnit(this ILoggerFactory factory, IMessageSink messageSink, Action configure) + { +#if NET + ArgumentNullException.ThrowIfNull(factory); + ArgumentNullException.ThrowIfNull(messageSink); + ArgumentNullException.ThrowIfNull(configure); +#else + if (factory == null) + { + throw new ArgumentNullException(nameof(factory)); + } + + if (messageSink == null) + { + throw new ArgumentNullException(nameof(messageSink)); + } + + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } +#endif + + return factory.AddXUnit(messageSink, () => + { + var options = new XUnitLoggerOptions(); + configure(options); + return options; + }); + } + + /// + /// Adds an xunit logger to the factory. + /// + /// The to use. + /// The to use. + /// A delegate to a method that returns a configured to use. + /// + /// The instance of specified by . + /// + /// + /// , or is . + /// + public static ILoggerFactory AddXUnit(this ILoggerFactory factory, IMessageSink messageSink, Func configure) + { +#if NET + ArgumentNullException.ThrowIfNull(factory); + ArgumentNullException.ThrowIfNull(messageSink); + ArgumentNullException.ThrowIfNull(configure); +#else + if (factory == null) + { + throw new ArgumentNullException(nameof(factory)); + } + + if (messageSink == null) + { + throw new ArgumentNullException(nameof(messageSink)); + } + + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } +#endif + + var options = configure(); + +#pragma warning disable CA2000 + factory.AddProvider(new XUnitLoggerProvider(messageSink, options)); +#pragma warning restore CA2000 + + return factory; + } +} diff --git a/src/Logging.XUnit/XUnitLoggerExtensions.ITestOutputHelper.cs b/src/Shared/XUnitLoggerExtensions.ITestOutputHelper.cs similarity index 97% rename from src/Logging.XUnit/XUnitLoggerExtensions.ITestOutputHelper.cs rename to src/Shared/XUnitLoggerExtensions.ITestOutputHelper.cs index 3bc6dc80..4b92e11a 100644 --- a/src/Logging.XUnit/XUnitLoggerExtensions.ITestOutputHelper.cs +++ b/src/Shared/XUnitLoggerExtensions.ITestOutputHelper.cs @@ -1,419 +1,419 @@ -// Copyright (c) Martin Costello, 2018. All rights reserved. -// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. - -using System.ComponentModel; -using MartinCostello.Logging.XUnit; -using Microsoft.Extensions.DependencyInjection.Extensions; - -namespace Microsoft.Extensions.Logging; - -/// -/// A class containing extension methods for configuring logging to xunit. This class cannot be inherited. -/// -[EditorBrowsable(EditorBrowsableState.Never)] -public static partial class XUnitLoggerExtensions -{ - /// - /// Adds an xunit logger to the logging builder. - /// - /// The to use. - /// - /// The instance of specified by . - /// - /// - /// is . - /// - public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder) - { -#if NET - ArgumentNullException.ThrowIfNull(builder); -#else - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } -#endif - - return builder.AddXUnit(new AmbientTestOutputHelperAccessor(), static (_) => { }); - } - - /// - /// Adds an xunit logger to the logging builder. - /// - /// The to use. - /// The to use. - /// - /// The instance of specified by . - /// - /// - /// or is . - /// - public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, ITestOutputHelperAccessor accessor) - { -#if NET - ArgumentNullException.ThrowIfNull(builder); - ArgumentNullException.ThrowIfNull(accessor); -#else - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (accessor == null) - { - throw new ArgumentNullException(nameof(accessor)); - } -#endif - - return builder.AddXUnit(accessor, static (_) => { }); - } - - /// - /// Adds an xunit logger to the logging builder. - /// - /// The to use. - /// The to use. - /// A delegate to a method to use to configure the logging options. - /// - /// The instance of specified by . - /// - /// - /// , OR is . - /// - public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, ITestOutputHelperAccessor accessor, Action configure) - { -#if NET - ArgumentNullException.ThrowIfNull(builder); - ArgumentNullException.ThrowIfNull(accessor); - ArgumentNullException.ThrowIfNull(configure); -#else - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (accessor == null) - { - throw new ArgumentNullException(nameof(accessor)); - } - - if (configure == null) - { - throw new ArgumentNullException(nameof(configure)); - } -#endif - - var options = new XUnitLoggerOptions(); - - configure(options); - -#pragma warning disable CA2000 - builder.AddProvider(new XUnitLoggerProvider(accessor, options)); -#pragma warning restore CA2000 - - builder.Services.TryAddSingleton(accessor); - - return builder; - } - - /// - /// Adds an xunit logger to the logging builder. - /// - /// The to use. - /// The to use. - /// - /// The instance of specified by . - /// - /// - /// or is . - /// - public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, ITestOutputHelper outputHelper) - { -#if NET - ArgumentNullException.ThrowIfNull(builder); - ArgumentNullException.ThrowIfNull(outputHelper); -#else - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (outputHelper == null) - { - throw new ArgumentNullException(nameof(outputHelper)); - } -#endif - - return builder.AddXUnit(outputHelper, static (_) => { }); - } - - /// - /// Adds an xunit logger to the logging builder. - /// - /// The to use. - /// The to use. - /// A delegate to a method to use to configure the logging options. - /// - /// The instance of specified by . - /// - /// - /// , OR is . - /// - public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, ITestOutputHelper outputHelper, Action configure) - { -#if NET - ArgumentNullException.ThrowIfNull(builder); - ArgumentNullException.ThrowIfNull(outputHelper); - ArgumentNullException.ThrowIfNull(configure); -#else - if (builder == null) - { - throw new ArgumentNullException(nameof(builder)); - } - - if (outputHelper == null) - { - throw new ArgumentNullException(nameof(outputHelper)); - } - - if (configure == null) - { - throw new ArgumentNullException(nameof(configure)); - } -#endif - - var options = new XUnitLoggerOptions(); - - configure(options); - -#pragma warning disable CA2000 - return builder.AddProvider(new XUnitLoggerProvider(outputHelper, options)); -#pragma warning restore CA2000 - } - - /// - /// Adds an xunit logger to the factory. - /// - /// The to use. - /// The to use. - /// The minimum to be logged. - /// - /// The instance of specified by . - /// - /// - /// or is . - /// - public static ILoggerFactory AddXUnit(this ILoggerFactory factory, ITestOutputHelper outputHelper, LogLevel minLevel) - { -#if NET - ArgumentNullException.ThrowIfNull(factory); - ArgumentNullException.ThrowIfNull(outputHelper); -#else - if (factory == null) - { - throw new ArgumentNullException(nameof(factory)); - } - - if (outputHelper == null) - { - throw new ArgumentNullException(nameof(outputHelper)); - } -#endif - - return factory.AddXUnit(outputHelper, (_, level) => level >= minLevel); - } - - /// - /// Adds an xunit logger to the factory. - /// - /// The to use. - /// The to use. - /// The category filter to apply to logs. - /// - /// The instance of specified by . - /// - /// - /// , or is . - /// - public static ILoggerFactory AddXUnit(this ILoggerFactory factory, ITestOutputHelper outputHelper, Func filter) - { -#if NET - ArgumentNullException.ThrowIfNull(factory); - ArgumentNullException.ThrowIfNull(outputHelper); - ArgumentNullException.ThrowIfNull(filter); -#else - if (factory == null) - { - throw new ArgumentNullException(nameof(factory)); - } - - if (outputHelper == null) - { - throw new ArgumentNullException(nameof(outputHelper)); - } - - if (filter == null) - { - throw new ArgumentNullException(nameof(filter)); - } -#endif - - return factory.AddXUnit(outputHelper, (options) => options.Filter = filter); - } - - /// - /// Adds an xunit logger to the factory. - /// - /// The to use. - /// The to use. - /// - /// The instance of specified by . - /// - /// - /// or is . - /// - public static ILoggerFactory AddXUnit(this ILoggerFactory factory, ITestOutputHelper outputHelper) - { -#if NET - ArgumentNullException.ThrowIfNull(factory); - ArgumentNullException.ThrowIfNull(outputHelper); -#else - if (factory == null) - { - throw new ArgumentNullException(nameof(factory)); - } - - if (outputHelper == null) - { - throw new ArgumentNullException(nameof(outputHelper)); - } -#endif - - return factory.AddXUnit(outputHelper, static (_) => { }); - } - - /// - /// Adds an xunit logger to the factory. - /// - /// The to use. - /// The to use. - /// The options to use for logging to xunit. - /// - /// The instance of specified by . - /// - /// - /// , OR is . - /// - public static ILoggerFactory AddXUnit(this ILoggerFactory factory, ITestOutputHelper outputHelper, XUnitLoggerOptions options) - { -#if NET - ArgumentNullException.ThrowIfNull(factory); - ArgumentNullException.ThrowIfNull(outputHelper); - ArgumentNullException.ThrowIfNull(options); -#else - if (factory == null) - { - throw new ArgumentNullException(nameof(factory)); - } - - if (outputHelper == null) - { - throw new ArgumentNullException(nameof(outputHelper)); - } - - if (options == null) - { - throw new ArgumentNullException(nameof(options)); - } -#endif - - return factory.AddXUnit(outputHelper, () => options); - } - - /// - /// Adds an xunit logger to the factory. - /// - /// The to use. - /// The to use. - /// A delegate to a method to use to configure the logging options. - /// - /// The instance of specified by . - /// - /// - /// , OR is . - /// - public static ILoggerFactory AddXUnit(this ILoggerFactory factory, ITestOutputHelper outputHelper, Action configure) - { -#if NET - ArgumentNullException.ThrowIfNull(factory); - ArgumentNullException.ThrowIfNull(outputHelper); - ArgumentNullException.ThrowIfNull(configure); -#else - if (factory == null) - { - throw new ArgumentNullException(nameof(factory)); - } - - if (outputHelper == null) - { - throw new ArgumentNullException(nameof(outputHelper)); - } - - if (configure == null) - { - throw new ArgumentNullException(nameof(configure)); - } -#endif - - return factory.AddXUnit(outputHelper, () => - { - var options = new XUnitLoggerOptions(); - configure(options); - return options; - }); - } - - /// - /// Adds an xunit logger to the factory. - /// - /// The to use. - /// The to use. - /// A delegate to a method that returns a configured to use. - /// - /// The instance of specified by . - /// - /// - /// , or is . - /// - public static ILoggerFactory AddXUnit(this ILoggerFactory factory, ITestOutputHelper outputHelper, Func configure) - { -#if NET - ArgumentNullException.ThrowIfNull(factory); - ArgumentNullException.ThrowIfNull(outputHelper); - ArgumentNullException.ThrowIfNull(configure); -#else - if (factory == null) - { - throw new ArgumentNullException(nameof(factory)); - } - - if (outputHelper == null) - { - throw new ArgumentNullException(nameof(outputHelper)); - } - - if (configure == null) - { - throw new ArgumentNullException(nameof(configure)); - } -#endif - - var options = configure(); - -#pragma warning disable CA2000 - factory.AddProvider(new XUnitLoggerProvider(outputHelper, options)); -#pragma warning restore CA2000 - - return factory; - } -} +// Copyright (c) Martin Costello, 2018. All rights reserved. +// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. + +using System.ComponentModel; +using MartinCostello.Logging.XUnit; +using Microsoft.Extensions.DependencyInjection.Extensions; + +namespace Microsoft.Extensions.Logging; + +/// +/// A class containing extension methods for configuring logging to xunit. This class cannot be inherited. +/// +[EditorBrowsable(EditorBrowsableState.Never)] +public static partial class XUnitLoggerExtensions +{ + /// + /// Adds an xunit logger to the logging builder. + /// + /// The to use. + /// + /// The instance of specified by . + /// + /// + /// is . + /// + public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder) + { +#if NET + ArgumentNullException.ThrowIfNull(builder); +#else + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } +#endif + + return builder.AddXUnit(new AmbientTestOutputHelperAccessor(), static (_) => { }); + } + + /// + /// Adds an xunit logger to the logging builder. + /// + /// The to use. + /// The to use. + /// + /// The instance of specified by . + /// + /// + /// or is . + /// + public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, ITestOutputHelperAccessor accessor) + { +#if NET + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(accessor); +#else + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (accessor == null) + { + throw new ArgumentNullException(nameof(accessor)); + } +#endif + + return builder.AddXUnit(accessor, static (_) => { }); + } + + /// + /// Adds an xunit logger to the logging builder. + /// + /// The to use. + /// The to use. + /// A delegate to a method to use to configure the logging options. + /// + /// The instance of specified by . + /// + /// + /// , OR is . + /// + public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, ITestOutputHelperAccessor accessor, Action configure) + { +#if NET + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(accessor); + ArgumentNullException.ThrowIfNull(configure); +#else + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (accessor == null) + { + throw new ArgumentNullException(nameof(accessor)); + } + + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } +#endif + + var options = new XUnitLoggerOptions(); + + configure(options); + +#pragma warning disable CA2000 + builder.AddProvider(new XUnitLoggerProvider(accessor, options)); +#pragma warning restore CA2000 + + builder.Services.TryAddSingleton(accessor); + + return builder; + } + + /// + /// Adds an xunit logger to the logging builder. + /// + /// The to use. + /// The to use. + /// + /// The instance of specified by . + /// + /// + /// or is . + /// + public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, ITestOutputHelper outputHelper) + { +#if NET + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(outputHelper); +#else + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (outputHelper == null) + { + throw new ArgumentNullException(nameof(outputHelper)); + } +#endif + + return builder.AddXUnit(outputHelper, static (_) => { }); + } + + /// + /// Adds an xunit logger to the logging builder. + /// + /// The to use. + /// The to use. + /// A delegate to a method to use to configure the logging options. + /// + /// The instance of specified by . + /// + /// + /// , OR is . + /// + public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, ITestOutputHelper outputHelper, Action configure) + { +#if NET + ArgumentNullException.ThrowIfNull(builder); + ArgumentNullException.ThrowIfNull(outputHelper); + ArgumentNullException.ThrowIfNull(configure); +#else + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (outputHelper == null) + { + throw new ArgumentNullException(nameof(outputHelper)); + } + + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } +#endif + + var options = new XUnitLoggerOptions(); + + configure(options); + +#pragma warning disable CA2000 + return builder.AddProvider(new XUnitLoggerProvider(outputHelper, options)); +#pragma warning restore CA2000 + } + + /// + /// Adds an xunit logger to the factory. + /// + /// The to use. + /// The to use. + /// The minimum to be logged. + /// + /// The instance of specified by . + /// + /// + /// or is . + /// + public static ILoggerFactory AddXUnit(this ILoggerFactory factory, ITestOutputHelper outputHelper, LogLevel minLevel) + { +#if NET + ArgumentNullException.ThrowIfNull(factory); + ArgumentNullException.ThrowIfNull(outputHelper); +#else + if (factory == null) + { + throw new ArgumentNullException(nameof(factory)); + } + + if (outputHelper == null) + { + throw new ArgumentNullException(nameof(outputHelper)); + } +#endif + + return factory.AddXUnit(outputHelper, (_, level) => level >= minLevel); + } + + /// + /// Adds an xunit logger to the factory. + /// + /// The to use. + /// The to use. + /// The category filter to apply to logs. + /// + /// The instance of specified by . + /// + /// + /// , or is . + /// + public static ILoggerFactory AddXUnit(this ILoggerFactory factory, ITestOutputHelper outputHelper, Func filter) + { +#if NET + ArgumentNullException.ThrowIfNull(factory); + ArgumentNullException.ThrowIfNull(outputHelper); + ArgumentNullException.ThrowIfNull(filter); +#else + if (factory == null) + { + throw new ArgumentNullException(nameof(factory)); + } + + if (outputHelper == null) + { + throw new ArgumentNullException(nameof(outputHelper)); + } + + if (filter == null) + { + throw new ArgumentNullException(nameof(filter)); + } +#endif + + return factory.AddXUnit(outputHelper, (options) => options.Filter = filter); + } + + /// + /// Adds an xunit logger to the factory. + /// + /// The to use. + /// The to use. + /// + /// The instance of specified by . + /// + /// + /// or is . + /// + public static ILoggerFactory AddXUnit(this ILoggerFactory factory, ITestOutputHelper outputHelper) + { +#if NET + ArgumentNullException.ThrowIfNull(factory); + ArgumentNullException.ThrowIfNull(outputHelper); +#else + if (factory == null) + { + throw new ArgumentNullException(nameof(factory)); + } + + if (outputHelper == null) + { + throw new ArgumentNullException(nameof(outputHelper)); + } +#endif + + return factory.AddXUnit(outputHelper, static (_) => { }); + } + + /// + /// Adds an xunit logger to the factory. + /// + /// The to use. + /// The to use. + /// The options to use for logging to xunit. + /// + /// The instance of specified by . + /// + /// + /// , OR is . + /// + public static ILoggerFactory AddXUnit(this ILoggerFactory factory, ITestOutputHelper outputHelper, XUnitLoggerOptions options) + { +#if NET + ArgumentNullException.ThrowIfNull(factory); + ArgumentNullException.ThrowIfNull(outputHelper); + ArgumentNullException.ThrowIfNull(options); +#else + if (factory == null) + { + throw new ArgumentNullException(nameof(factory)); + } + + if (outputHelper == null) + { + throw new ArgumentNullException(nameof(outputHelper)); + } + + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } +#endif + + return factory.AddXUnit(outputHelper, () => options); + } + + /// + /// Adds an xunit logger to the factory. + /// + /// The to use. + /// The to use. + /// A delegate to a method to use to configure the logging options. + /// + /// The instance of specified by . + /// + /// + /// , OR is . + /// + public static ILoggerFactory AddXUnit(this ILoggerFactory factory, ITestOutputHelper outputHelper, Action configure) + { +#if NET + ArgumentNullException.ThrowIfNull(factory); + ArgumentNullException.ThrowIfNull(outputHelper); + ArgumentNullException.ThrowIfNull(configure); +#else + if (factory == null) + { + throw new ArgumentNullException(nameof(factory)); + } + + if (outputHelper == null) + { + throw new ArgumentNullException(nameof(outputHelper)); + } + + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } +#endif + + return factory.AddXUnit(outputHelper, () => + { + var options = new XUnitLoggerOptions(); + configure(options); + return options; + }); + } + + /// + /// Adds an xunit logger to the factory. + /// + /// The to use. + /// The to use. + /// A delegate to a method that returns a configured to use. + /// + /// The instance of specified by . + /// + /// + /// , or is . + /// + public static ILoggerFactory AddXUnit(this ILoggerFactory factory, ITestOutputHelper outputHelper, Func configure) + { +#if NET + ArgumentNullException.ThrowIfNull(factory); + ArgumentNullException.ThrowIfNull(outputHelper); + ArgumentNullException.ThrowIfNull(configure); +#else + if (factory == null) + { + throw new ArgumentNullException(nameof(factory)); + } + + if (outputHelper == null) + { + throw new ArgumentNullException(nameof(outputHelper)); + } + + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } +#endif + + var options = configure(); + +#pragma warning disable CA2000 + factory.AddProvider(new XUnitLoggerProvider(outputHelper, options)); +#pragma warning restore CA2000 + + return factory; + } +} diff --git a/src/Logging.XUnit/XUnitLoggerOptions.cs b/src/Shared/XUnitLoggerOptions.cs similarity index 97% rename from src/Logging.XUnit/XUnitLoggerOptions.cs rename to src/Shared/XUnitLoggerOptions.cs index 7e4e169f..db397713 100644 --- a/src/Logging.XUnit/XUnitLoggerOptions.cs +++ b/src/Shared/XUnitLoggerOptions.cs @@ -1,41 +1,41 @@ -// Copyright (c) Martin Costello, 2018. All rights reserved. -// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. - -using System.Diagnostics.CodeAnalysis; -using Microsoft.Extensions.Logging; - -namespace MartinCostello.Logging.XUnit; - -/// -/// A class representing configuration options for logging to xunit. -/// -public class XUnitLoggerOptions -{ - /// - /// Initializes a new instance of the class. - /// - public XUnitLoggerOptions() - { - } - - /// - /// Gets or sets the category filter to apply to logs. - /// - public Func Filter { get; set; } = static (c, l) => true; // By default log everything - - /// - /// Gets or sets the message sink message factory to use when writing to a . - /// - public Func MessageSinkMessageFactory { get; set; } = static (m) => new DiagnosticMessage(m); - - /// - /// Gets or sets a value indicating whether to include scopes. - /// - public bool IncludeScopes { get; set; } - - /// - /// Gets or sets format string used to format the timestamp in log messages. Defaults to u. - /// - [StringSyntax(StringSyntaxAttribute.DateTimeFormat)] - public string? TimestampFormat { get; set; } -} +// Copyright (c) Martin Costello, 2018. All rights reserved. +// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. + +using System.Diagnostics.CodeAnalysis; +using Microsoft.Extensions.Logging; + +namespace MartinCostello.Logging.XUnit; + +/// +/// A class representing configuration options for logging to xunit. +/// +public class XUnitLoggerOptions +{ + /// + /// Initializes a new instance of the class. + /// + public XUnitLoggerOptions() + { + } + + /// + /// Gets or sets the category filter to apply to logs. + /// + public Func Filter { get; set; } = static (c, l) => true; // By default log everything + + /// + /// Gets or sets the message sink message factory to use when writing to a . + /// + public Func MessageSinkMessageFactory { get; set; } = static (m) => new DiagnosticMessage(m); + + /// + /// Gets or sets a value indicating whether to include scopes. + /// + public bool IncludeScopes { get; set; } + + /// + /// Gets or sets format string used to format the timestamp in log messages. Defaults to u. + /// + [StringSyntax(StringSyntaxAttribute.DateTimeFormat)] + public string? TimestampFormat { get; set; } +} diff --git a/src/Logging.XUnit/XUnitLoggerProvider.IMessageSink.cs b/src/Shared/XUnitLoggerProvider.IMessageSink.cs similarity index 97% rename from src/Logging.XUnit/XUnitLoggerProvider.IMessageSink.cs rename to src/Shared/XUnitLoggerProvider.IMessageSink.cs index 3e974504..e5c82f3e 100644 --- a/src/Logging.XUnit/XUnitLoggerProvider.IMessageSink.cs +++ b/src/Shared/XUnitLoggerProvider.IMessageSink.cs @@ -1,44 +1,44 @@ -// Copyright (c) Martin Costello, 2018. All rights reserved. -// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. - -using Microsoft.Extensions.Logging; - -namespace MartinCostello.Logging.XUnit; - -/// -/// A class representing an to use with xunit. -/// -public partial class XUnitLoggerProvider -{ - /// - /// The to use. This field is readonly. - /// - private readonly IMessageSinkAccessor? _messageSinkAccessor; - - /// - /// Initializes a new instance of the class. - /// - /// The to use. - /// The options to use for logging to xunit. - /// - /// or is . - /// - public XUnitLoggerProvider(IMessageSink messageSink, XUnitLoggerOptions options) - : this(new MessageSinkAccessor(messageSink), options) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The to use. - /// The options to use for logging to xunit. - /// - /// or is . - /// - public XUnitLoggerProvider(IMessageSinkAccessor accessor, XUnitLoggerOptions options) - { - _messageSinkAccessor = accessor ?? throw new ArgumentNullException(nameof(accessor)); - _options = options ?? throw new ArgumentNullException(nameof(options)); - } -} +// Copyright (c) Martin Costello, 2018. All rights reserved. +// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. + +using Microsoft.Extensions.Logging; + +namespace MartinCostello.Logging.XUnit; + +/// +/// A class representing an to use with xunit. +/// +public partial class XUnitLoggerProvider +{ + /// + /// The to use. This field is readonly. + /// + private readonly IMessageSinkAccessor? _messageSinkAccessor; + + /// + /// Initializes a new instance of the class. + /// + /// The to use. + /// The options to use for logging to xunit. + /// + /// or is . + /// + public XUnitLoggerProvider(IMessageSink messageSink, XUnitLoggerOptions options) + : this(new MessageSinkAccessor(messageSink), options) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The to use. + /// The options to use for logging to xunit. + /// + /// or is . + /// + public XUnitLoggerProvider(IMessageSinkAccessor accessor, XUnitLoggerOptions options) + { + _messageSinkAccessor = accessor ?? throw new ArgumentNullException(nameof(accessor)); + _options = options ?? throw new ArgumentNullException(nameof(options)); + } +} diff --git a/src/Logging.XUnit/XUnitLoggerProvider.ITestOutputHelper.cs b/src/Shared/XUnitLoggerProvider.ITestOutputHelper.cs similarity index 97% rename from src/Logging.XUnit/XUnitLoggerProvider.ITestOutputHelper.cs rename to src/Shared/XUnitLoggerProvider.ITestOutputHelper.cs index 13e2f868..c5eccc84 100644 --- a/src/Logging.XUnit/XUnitLoggerProvider.ITestOutputHelper.cs +++ b/src/Shared/XUnitLoggerProvider.ITestOutputHelper.cs @@ -1,44 +1,44 @@ -// Copyright (c) Martin Costello, 2018. All rights reserved. -// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. - -using Microsoft.Extensions.Logging; - -namespace MartinCostello.Logging.XUnit; - -/// -/// A class representing an to use with xunit. -/// -public partial class XUnitLoggerProvider -{ - /// - /// The to use. This field is readonly. - /// - private readonly ITestOutputHelperAccessor? _outputHelperAccessor; - - /// - /// Initializes a new instance of the class. - /// - /// The to use. - /// The options to use for logging to xunit. - /// - /// or is . - /// - public XUnitLoggerProvider(ITestOutputHelper outputHelper, XUnitLoggerOptions options) - : this(new TestOutputHelperAccessor(outputHelper), options) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The to use. - /// The options to use for logging to xunit. - /// - /// or is . - /// - public XUnitLoggerProvider(ITestOutputHelperAccessor accessor, XUnitLoggerOptions options) - { - _outputHelperAccessor = accessor ?? throw new ArgumentNullException(nameof(accessor)); - _options = options ?? throw new ArgumentNullException(nameof(options)); - } -} +// Copyright (c) Martin Costello, 2018. All rights reserved. +// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. + +using Microsoft.Extensions.Logging; + +namespace MartinCostello.Logging.XUnit; + +/// +/// A class representing an to use with xunit. +/// +public partial class XUnitLoggerProvider +{ + /// + /// The to use. This field is readonly. + /// + private readonly ITestOutputHelperAccessor? _outputHelperAccessor; + + /// + /// Initializes a new instance of the class. + /// + /// The to use. + /// The options to use for logging to xunit. + /// + /// or is . + /// + public XUnitLoggerProvider(ITestOutputHelper outputHelper, XUnitLoggerOptions options) + : this(new TestOutputHelperAccessor(outputHelper), options) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The to use. + /// The options to use for logging to xunit. + /// + /// or is . + /// + public XUnitLoggerProvider(ITestOutputHelperAccessor accessor, XUnitLoggerOptions options) + { + _outputHelperAccessor = accessor ?? throw new ArgumentNullException(nameof(accessor)); + _options = options ?? throw new ArgumentNullException(nameof(options)); + } +} diff --git a/src/Logging.XUnit/XUnitLoggerProvider.cs b/src/Shared/XUnitLoggerProvider.cs similarity index 96% rename from src/Logging.XUnit/XUnitLoggerProvider.cs rename to src/Shared/XUnitLoggerProvider.cs index 38a18622..99a44892 100644 --- a/src/Logging.XUnit/XUnitLoggerProvider.cs +++ b/src/Shared/XUnitLoggerProvider.cs @@ -1,60 +1,60 @@ -// Copyright (c) Martin Costello, 2018. All rights reserved. -// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. - -using System.Collections.Concurrent; -using Microsoft.Extensions.Logging; - -namespace MartinCostello.Logging.XUnit; - -/// -/// A class representing an to use with xunit. -/// -[ProviderAlias("XUnit")] -public partial class XUnitLoggerProvider : ILoggerProvider -{ - /// - /// The cached loggers to use for each category. This field is readonly. - /// - private readonly ConcurrentDictionary _loggers = []; - - /// - /// The to use. This field is readonly. - /// - private readonly XUnitLoggerOptions _options; - - /// - /// Finalizes an instance of the class. - /// - ~XUnitLoggerProvider() - { - Dispose(false); - } - - /// - public virtual ILogger CreateLogger(string categoryName) - { - return _loggers.GetOrAdd(categoryName, (name) => - _outputHelperAccessor is not null ? - new(name, _outputHelperAccessor, _options) : - new(name, _messageSinkAccessor!, _options)); - } - - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// - /// - /// to release both managed and unmanaged resources; - /// to release only unmanaged resources. - /// - protected virtual void Dispose(bool disposing) - { - // Nothing to dispose of - } -} +// Copyright (c) Martin Costello, 2018. All rights reserved. +// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. + +using System.Collections.Concurrent; +using Microsoft.Extensions.Logging; + +namespace MartinCostello.Logging.XUnit; + +/// +/// A class representing an to use with xunit. +/// +[ProviderAlias("XUnit")] +public partial class XUnitLoggerProvider : ILoggerProvider +{ + /// + /// The cached loggers to use for each category. This field is readonly. + /// + private readonly ConcurrentDictionary _loggers = []; + + /// + /// The to use. This field is readonly. + /// + private readonly XUnitLoggerOptions _options; + + /// + /// Finalizes an instance of the class. + /// + ~XUnitLoggerProvider() + { + Dispose(false); + } + + /// + public virtual ILogger CreateLogger(string categoryName) + { + return _loggers.GetOrAdd(categoryName, (name) => + _outputHelperAccessor is not null ? + new(name, _outputHelperAccessor, _options) : + new(name, _messageSinkAccessor!, _options)); + } + + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + /// + /// to release both managed and unmanaged resources; + /// to release only unmanaged resources. + /// + protected virtual void Dispose(bool disposing) + { + // Nothing to dispose of + } +} From 43610239dcc5978f7c4eb0920785230179eb5f66 Mon Sep 17 00:00:00 2001 From: martincostello Date: Fri, 20 Dec 2024 08:22:52 +0000 Subject: [PATCH 06/11] Simplify project files Move properties back to Directory.Build.props. --- Directory.Build.props | 11 +++++++++++ .../MartinCostello.Logging.XUnit.v3.csproj | 9 +-------- src/Logging.XUnit/MartinCostello.Logging.XUnit.csproj | 5 ----- .../MartinCostello.Logging.XUnit.Tests.csproj | 6 ------ .../MartinCostello.Logging.XUnit.v3.Tests.csproj | 6 ------ tests/SampleApp/SampleApp.csproj | 5 +++++ 6 files changed, 17 insertions(+), 25 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 47ef97e6..ff9231db 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -72,5 +72,16 @@ + + + + + + + + + + + diff --git a/src/Logging.XUnit.v3/MartinCostello.Logging.XUnit.v3.csproj b/src/Logging.XUnit.v3/MartinCostello.Logging.XUnit.v3.csproj index a9f3c2b3..24a499d8 100644 --- a/src/Logging.XUnit.v3/MartinCostello.Logging.XUnit.v3.csproj +++ b/src/Logging.XUnit.v3/MartinCostello.Logging.XUnit.v3.csproj @@ -6,7 +6,6 @@ true true true - false Library MartinCostello.Logging.XUnit.v3 MartinCostello.Logging.XUnit $(Description) - net472;net8.0 - false + net8.0;net472 xunit v3 Logging Extensions @@ -34,9 +32,4 @@ - - - - - diff --git a/src/Logging.XUnit/MartinCostello.Logging.XUnit.csproj b/src/Logging.XUnit/MartinCostello.Logging.XUnit.csproj index b9952979..73552208 100644 --- a/src/Logging.XUnit/MartinCostello.Logging.XUnit.csproj +++ b/src/Logging.XUnit/MartinCostello.Logging.XUnit.csproj @@ -33,9 +33,4 @@ - - - - - diff --git a/tests/Logging.XUnit.Tests/MartinCostello.Logging.XUnit.Tests.csproj b/tests/Logging.XUnit.Tests/MartinCostello.Logging.XUnit.Tests.csproj index 23b7597a..9d7cfe66 100644 --- a/tests/Logging.XUnit.Tests/MartinCostello.Logging.XUnit.Tests.csproj +++ b/tests/Logging.XUnit.Tests/MartinCostello.Logging.XUnit.Tests.csproj @@ -25,10 +25,4 @@ - - - - - - diff --git a/tests/Logging.XUnit.v3.Tests/MartinCostello.Logging.XUnit.v3.Tests.csproj b/tests/Logging.XUnit.v3.Tests/MartinCostello.Logging.XUnit.v3.Tests.csproj index a49e6581..a24d0a7c 100644 --- a/tests/Logging.XUnit.v3.Tests/MartinCostello.Logging.XUnit.v3.Tests.csproj +++ b/tests/Logging.XUnit.v3.Tests/MartinCostello.Logging.XUnit.v3.Tests.csproj @@ -41,10 +41,4 @@ - - - - - - diff --git a/tests/SampleApp/SampleApp.csproj b/tests/SampleApp/SampleApp.csproj index d1406cd9..9335bc40 100644 --- a/tests/SampleApp/SampleApp.csproj +++ b/tests/SampleApp/SampleApp.csproj @@ -4,4 +4,9 @@ $(NoWarn);CA1801;CA1822;CA1861;SA1600;SA1601 net9.0 + + + + + From 0fa5e53fadd97b6ee31747eb1d71e51f42d59474 Mon Sep 17 00:00:00 2001 From: martincostello Date: Fri, 20 Dec 2024 08:29:16 +0000 Subject: [PATCH 07/11] Move tests to Shared Move all the code for the tests to a Shared folder for re-use. --- .../MartinCostello.Logging.XUnit.Tests.csproj | 3 +- ...rtinCostello.Logging.XUnit.v3.Tests.csproj | 17 +- .../Constructor.cs | 22 +- .../Examples.cs | 108 +- .../Integration/DatabaseFixture.cs | 114 +- .../Integration/DatabaseTests.cs | 30 +- .../Integration/HttpApplicationTests.cs | 0 .../Integration/HttpServerCollection.cs | 0 .../Integration/HttpServerFixture.cs | 0 .../Integration/PrintableDiagnosticMessage.cs | 30 +- .../IntegrationTests.cs | 0 .../XUnitLoggerExtensionsTests.cs | 510 +++--- .../XUnitLoggerProviderTests.cs | 144 +- .../XUnitLoggerTests.cs | 1362 ++++++++--------- .../xunit.runner.json | 0 15 files changed, 1164 insertions(+), 1176 deletions(-) rename tests/{Logging.XUnit.Tests => Shared}/Constructor.cs (96%) rename tests/{Logging.XUnit.Tests => Shared}/Examples.cs (96%) rename tests/{Logging.XUnit.Tests => Shared}/Integration/DatabaseFixture.cs (97%) rename tests/{Logging.XUnit.Tests => Shared}/Integration/DatabaseTests.cs (97%) rename tests/{Logging.XUnit.Tests => Shared}/Integration/HttpApplicationTests.cs (100%) rename tests/{Logging.XUnit.Tests => Shared}/Integration/HttpServerCollection.cs (100%) rename tests/{Logging.XUnit.Tests => Shared}/Integration/HttpServerFixture.cs (100%) rename tests/{Logging.XUnit.Tests => Shared}/Integration/PrintableDiagnosticMessage.cs (97%) rename tests/{Logging.XUnit.Tests => Shared}/IntegrationTests.cs (100%) rename tests/{Logging.XUnit.Tests => Shared}/XUnitLoggerExtensionsTests.cs (98%) rename tests/{Logging.XUnit.Tests => Shared}/XUnitLoggerProviderTests.cs (97%) rename tests/{Logging.XUnit.Tests => Shared}/XUnitLoggerTests.cs (96%) rename tests/{Logging.XUnit.Tests => Shared}/xunit.runner.json (100%) diff --git a/tests/Logging.XUnit.Tests/MartinCostello.Logging.XUnit.Tests.csproj b/tests/Logging.XUnit.Tests/MartinCostello.Logging.XUnit.Tests.csproj index 9d7cfe66..f52f0ea5 100644 --- a/tests/Logging.XUnit.Tests/MartinCostello.Logging.XUnit.Tests.csproj +++ b/tests/Logging.XUnit.Tests/MartinCostello.Logging.XUnit.Tests.csproj @@ -10,7 +10,8 @@ net9.0 - + + diff --git a/tests/Logging.XUnit.v3.Tests/MartinCostello.Logging.XUnit.v3.Tests.csproj b/tests/Logging.XUnit.v3.Tests/MartinCostello.Logging.XUnit.v3.Tests.csproj index a24d0a7c..0253a79e 100644 --- a/tests/Logging.XUnit.v3.Tests/MartinCostello.Logging.XUnit.v3.Tests.csproj +++ b/tests/Logging.XUnit.v3.Tests/MartinCostello.Logging.XUnit.v3.Tests.csproj @@ -12,21 +12,8 @@ net9.0 - - - - - - - - - - - - - - - + + diff --git a/tests/Logging.XUnit.Tests/Constructor.cs b/tests/Shared/Constructor.cs similarity index 96% rename from tests/Logging.XUnit.Tests/Constructor.cs rename to tests/Shared/Constructor.cs index 0e30f684..ec954b8e 100644 --- a/tests/Logging.XUnit.Tests/Constructor.cs +++ b/tests/Shared/Constructor.cs @@ -1,11 +1,11 @@ -// Copyright (c) Martin Costello, 2018. All rights reserved. -// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. - -namespace MartinCostello.Logging.XUnit; - -public enum Constructor -{ - ITestOutputHelper, - - IMessageSink, -} +// Copyright (c) Martin Costello, 2018. All rights reserved. +// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. + +namespace MartinCostello.Logging.XUnit; + +public enum Constructor +{ + ITestOutputHelper, + + IMessageSink, +} diff --git a/tests/Logging.XUnit.Tests/Examples.cs b/tests/Shared/Examples.cs similarity index 96% rename from tests/Logging.XUnit.Tests/Examples.cs rename to tests/Shared/Examples.cs index c8bbf021..f2213ef1 100644 --- a/tests/Logging.XUnit.Tests/Examples.cs +++ b/tests/Shared/Examples.cs @@ -1,54 +1,54 @@ -// Copyright (c) Martin Costello, 2018. All rights reserved. -// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. - -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; - -namespace MartinCostello.Logging.XUnit; - -public class Examples(ITestOutputHelper outputHelper) -{ - [Fact] - public void Calculator_Sums_Two_Equal_Integers() - { - // Arrange using conversion to a logger - var calculator = new Calculator(outputHelper.ToLogger()); - - // Act - int actual = calculator.Sum(2, 2); - - // Assert - actual.ShouldBe(4); - } - - [Fact] - public void Calculator_Sums_Two_Different_Integers() - { - // Arrange using the logging provider - var services = new ServiceCollection() - .AddLogging((builder) => builder.AddXUnit(outputHelper)) - .AddSingleton(); - - IServiceProvider provider = services.BuildServiceProvider(); - - var calculator = provider.GetRequiredService(); - - // Act - int actual = calculator.Sum(1, 2); - - // Assert - actual.ShouldBe(3); - } - - private sealed class Calculator(ILogger logger) - { - public int Sum(int x, int y) - { - int sum = x + y; - - logger.LogInformation("The sum of {X} and {Y} is {Sum}.", x, y, sum); - - return sum; - } - } -} +// Copyright (c) Martin Costello, 2018. All rights reserved. +// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. + +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace MartinCostello.Logging.XUnit; + +public class Examples(ITestOutputHelper outputHelper) +{ + [Fact] + public void Calculator_Sums_Two_Equal_Integers() + { + // Arrange using conversion to a logger + var calculator = new Calculator(outputHelper.ToLogger()); + + // Act + int actual = calculator.Sum(2, 2); + + // Assert + actual.ShouldBe(4); + } + + [Fact] + public void Calculator_Sums_Two_Different_Integers() + { + // Arrange using the logging provider + var services = new ServiceCollection() + .AddLogging((builder) => builder.AddXUnit(outputHelper)) + .AddSingleton(); + + IServiceProvider provider = services.BuildServiceProvider(); + + var calculator = provider.GetRequiredService(); + + // Act + int actual = calculator.Sum(1, 2); + + // Assert + actual.ShouldBe(3); + } + + private sealed class Calculator(ILogger logger) + { + public int Sum(int x, int y) + { + int sum = x + y; + + logger.LogInformation("The sum of {X} and {Y} is {Sum}.", x, y, sum); + + return sum; + } + } +} diff --git a/tests/Logging.XUnit.Tests/Integration/DatabaseFixture.cs b/tests/Shared/Integration/DatabaseFixture.cs similarity index 97% rename from tests/Logging.XUnit.Tests/Integration/DatabaseFixture.cs rename to tests/Shared/Integration/DatabaseFixture.cs index 80d71f75..00323215 100644 --- a/tests/Logging.XUnit.Tests/Integration/DatabaseFixture.cs +++ b/tests/Shared/Integration/DatabaseFixture.cs @@ -1,57 +1,57 @@ -// Copyright (c) Martin Costello, 2018. All rights reserved. -// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. - -using Microsoft.Extensions.Logging; - -namespace MartinCostello.Logging.XUnit.Integration; - -public sealed class DatabaseFixture : IAsyncLifetime -{ - private readonly ILogger _initializeLogger; - private readonly ILogger _disposeLogger; - private string? _connectionString; - - public DatabaseFixture(IMessageSink messageSink) - { - using var loggerFactory = new LoggerFactory(); - - _initializeLogger = loggerFactory.AddXUnit(messageSink, c => c.MessageSinkMessageFactory = CreateMessage).CreateLogger(); - _disposeLogger = messageSink.ToLogger(); - -#if XUNIT_V3 - static IMessageSinkMessage CreateMessage(string message) => new DiagnosticMessage() { Message = message }; -#else - static IMessageSinkMessage CreateMessage(string message) => new PrintableDiagnosticMessage(message); -#endif - } - - public string ConnectionString => _connectionString ?? throw new InvalidOperationException("The connection string is only available after InitializeAsync has completed."); - -#if XUNIT_V3 - ValueTask IAsyncLifetime.InitializeAsync() - { - _initializeLogger.LogInformation("Initializing database"); - _connectionString = "Server=localhost"; - return ValueTask.CompletedTask; - } - - ValueTask IAsyncDisposable.DisposeAsync() - { - _disposeLogger.LogInformation("Disposing database"); - return ValueTask.CompletedTask; - } -#else - Task IAsyncLifetime.InitializeAsync() - { - _initializeLogger.LogInformation("Initializing database"); - _connectionString = "Server=localhost"; - return Task.CompletedTask; - } - - Task IAsyncLifetime.DisposeAsync() - { - _disposeLogger.LogInformation("Disposing database"); - return Task.CompletedTask; - } -#endif -} +// Copyright (c) Martin Costello, 2018. All rights reserved. +// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. + +using Microsoft.Extensions.Logging; + +namespace MartinCostello.Logging.XUnit.Integration; + +public sealed class DatabaseFixture : IAsyncLifetime +{ + private readonly ILogger _initializeLogger; + private readonly ILogger _disposeLogger; + private string? _connectionString; + + public DatabaseFixture(IMessageSink messageSink) + { + using var loggerFactory = new LoggerFactory(); + + _initializeLogger = loggerFactory.AddXUnit(messageSink, c => c.MessageSinkMessageFactory = CreateMessage).CreateLogger(); + _disposeLogger = messageSink.ToLogger(); + +#if XUNIT_V3 + static IMessageSinkMessage CreateMessage(string message) => new DiagnosticMessage() { Message = message }; +#else + static IMessageSinkMessage CreateMessage(string message) => new PrintableDiagnosticMessage(message); +#endif + } + + public string ConnectionString => _connectionString ?? throw new InvalidOperationException("The connection string is only available after InitializeAsync has completed."); + +#if XUNIT_V3 + ValueTask IAsyncLifetime.InitializeAsync() + { + _initializeLogger.LogInformation("Initializing database"); + _connectionString = "Server=localhost"; + return ValueTask.CompletedTask; + } + + ValueTask IAsyncDisposable.DisposeAsync() + { + _disposeLogger.LogInformation("Disposing database"); + return ValueTask.CompletedTask; + } +#else + Task IAsyncLifetime.InitializeAsync() + { + _initializeLogger.LogInformation("Initializing database"); + _connectionString = "Server=localhost"; + return Task.CompletedTask; + } + + Task IAsyncLifetime.DisposeAsync() + { + _disposeLogger.LogInformation("Disposing database"); + return Task.CompletedTask; + } +#endif +} diff --git a/tests/Logging.XUnit.Tests/Integration/DatabaseTests.cs b/tests/Shared/Integration/DatabaseTests.cs similarity index 97% rename from tests/Logging.XUnit.Tests/Integration/DatabaseTests.cs rename to tests/Shared/Integration/DatabaseTests.cs index 9a9bff77..e0f5b9b0 100644 --- a/tests/Logging.XUnit.Tests/Integration/DatabaseTests.cs +++ b/tests/Shared/Integration/DatabaseTests.cs @@ -1,15 +1,15 @@ -// Copyright (c) Martin Costello, 2018. All rights reserved. -// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. - -namespace MartinCostello.Logging.XUnit.Integration; - -public class DatabaseTests(DatabaseFixture databaseFixture) : IClassFixture -{ - public DatabaseFixture DatabaseFixture { get; } = databaseFixture; - - [Fact] - public void Run_Database_Test() - { - DatabaseFixture.ConnectionString.ShouldNotBeEmpty(); - } -} +// Copyright (c) Martin Costello, 2018. All rights reserved. +// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. + +namespace MartinCostello.Logging.XUnit.Integration; + +public class DatabaseTests(DatabaseFixture databaseFixture) : IClassFixture +{ + public DatabaseFixture DatabaseFixture { get; } = databaseFixture; + + [Fact] + public void Run_Database_Test() + { + DatabaseFixture.ConnectionString.ShouldNotBeEmpty(); + } +} diff --git a/tests/Logging.XUnit.Tests/Integration/HttpApplicationTests.cs b/tests/Shared/Integration/HttpApplicationTests.cs similarity index 100% rename from tests/Logging.XUnit.Tests/Integration/HttpApplicationTests.cs rename to tests/Shared/Integration/HttpApplicationTests.cs diff --git a/tests/Logging.XUnit.Tests/Integration/HttpServerCollection.cs b/tests/Shared/Integration/HttpServerCollection.cs similarity index 100% rename from tests/Logging.XUnit.Tests/Integration/HttpServerCollection.cs rename to tests/Shared/Integration/HttpServerCollection.cs diff --git a/tests/Logging.XUnit.Tests/Integration/HttpServerFixture.cs b/tests/Shared/Integration/HttpServerFixture.cs similarity index 100% rename from tests/Logging.XUnit.Tests/Integration/HttpServerFixture.cs rename to tests/Shared/Integration/HttpServerFixture.cs diff --git a/tests/Logging.XUnit.Tests/Integration/PrintableDiagnosticMessage.cs b/tests/Shared/Integration/PrintableDiagnosticMessage.cs similarity index 97% rename from tests/Logging.XUnit.Tests/Integration/PrintableDiagnosticMessage.cs rename to tests/Shared/Integration/PrintableDiagnosticMessage.cs index 2a27a42b..0a51bff4 100644 --- a/tests/Logging.XUnit.Tests/Integration/PrintableDiagnosticMessage.cs +++ b/tests/Shared/Integration/PrintableDiagnosticMessage.cs @@ -1,15 +1,15 @@ -// Copyright (c) Martin Costello, 2018. All rights reserved. -// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. - -#if !XUNIT_V3 - -namespace MartinCostello.Logging.XUnit.Integration; - -/// -/// See https://github.com/xunit/xunit/pull/2148#issuecomment-839838421. -/// -internal sealed class PrintableDiagnosticMessage(string message) : DiagnosticMessage(message) -{ - public override string ToString() => Message; -} -#endif +// Copyright (c) Martin Costello, 2018. All rights reserved. +// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. + +#if !XUNIT_V3 + +namespace MartinCostello.Logging.XUnit.Integration; + +/// +/// See https://github.com/xunit/xunit/pull/2148#issuecomment-839838421. +/// +internal sealed class PrintableDiagnosticMessage(string message) : DiagnosticMessage(message) +{ + public override string ToString() => Message; +} +#endif diff --git a/tests/Logging.XUnit.Tests/IntegrationTests.cs b/tests/Shared/IntegrationTests.cs similarity index 100% rename from tests/Logging.XUnit.Tests/IntegrationTests.cs rename to tests/Shared/IntegrationTests.cs diff --git a/tests/Logging.XUnit.Tests/XUnitLoggerExtensionsTests.cs b/tests/Shared/XUnitLoggerExtensionsTests.cs similarity index 98% rename from tests/Logging.XUnit.Tests/XUnitLoggerExtensionsTests.cs rename to tests/Shared/XUnitLoggerExtensionsTests.cs index 492af3de..7e61a8ad 100644 --- a/tests/Logging.XUnit.Tests/XUnitLoggerExtensionsTests.cs +++ b/tests/Shared/XUnitLoggerExtensionsTests.cs @@ -1,255 +1,255 @@ -// Copyright (c) Martin Costello, 2018. All rights reserved. -// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. - -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; -using NSubstitute; - -namespace MartinCostello.Logging.XUnit; - -public static class XUnitLoggerExtensionsTests -{ - [Fact] - public static void AddXUnit_TestOutputHelper_For_ILoggerBuilder_Validates_Parameters() - { - // Arrange - var builder = Substitute.For(); - var outputHelper = Substitute.For(); - var accessor = Substitute.For(); - - // Act and Assert - Assert.Throws("builder", () => (null as ILoggingBuilder)!.AddXUnit()); - Assert.Throws("builder", () => (null as ILoggingBuilder)!.AddXUnit(outputHelper)); - Assert.Throws("builder", () => (null as ILoggingBuilder)!.AddXUnit(outputHelper, ConfigureAction)); - Assert.Throws("builder", () => (null as ILoggingBuilder)!.AddXUnit(accessor)); - Assert.Throws("builder", () => (null as ILoggingBuilder)!.AddXUnit(accessor, ConfigureAction)); - Assert.Throws("accessor", () => builder.AddXUnit((null as ITestOutputHelperAccessor)!)); - Assert.Throws("accessor", () => builder.AddXUnit((null as ITestOutputHelperAccessor)!, ConfigureAction)); - Assert.Throws("outputHelper", () => builder.AddXUnit((null as ITestOutputHelper)!)); - Assert.Throws("outputHelper", () => builder.AddXUnit((null as ITestOutputHelper)!, ConfigureAction)); - Assert.Throws("configure", () => builder.AddXUnit(outputHelper, null!)); - Assert.Throws("configure", () => builder.AddXUnit(accessor, null!)); - } - - [Fact] - public static void AddXUnit_MessageSink_For_ILoggerBuilder_Validates_Parameters() - { - // Arrange - var builder = Substitute.For(); - var messageSink = Substitute.For(); - var accessor = Substitute.For(); - - // Act and Assert - Assert.Throws("builder", () => (null as ILoggingBuilder)!.AddXUnit(messageSink)); - Assert.Throws("builder", () => (null as ILoggingBuilder)!.AddXUnit(messageSink, ConfigureAction)); - Assert.Throws("builder", () => (null as ILoggingBuilder)!.AddXUnit(accessor)); - Assert.Throws("builder", () => (null as ILoggingBuilder)!.AddXUnit(accessor, ConfigureAction)); - Assert.Throws("accessor", () => builder.AddXUnit((null as IMessageSinkAccessor)!)); - Assert.Throws("accessor", () => builder.AddXUnit((null as IMessageSinkAccessor)!, ConfigureAction)); - Assert.Throws("messageSink", () => builder.AddXUnit((null as IMessageSink)!)); - Assert.Throws("messageSink", () => builder.AddXUnit((null as IMessageSink)!, ConfigureAction)); - Assert.Throws("configure", () => builder.AddXUnit(messageSink, null!)); - Assert.Throws("configure", () => builder.AddXUnit(accessor, null!)); - } - - [Fact] - public static void AddXUnit_TestOutputHelper_For_ILoggerFactory_Validates_Parameters() - { - // Arrange - ILoggerFactory factory = NullLoggerFactory.Instance; - var logLevel = LogLevel.Information; - var outputHelper = Substitute.For(); - var options = new XUnitLoggerOptions(); - - // Act and Assert - Assert.Throws("factory", () => (null as ILoggerFactory)!.AddXUnit(outputHelper)); - Assert.Throws("factory", () => (null as ILoggerFactory)!.AddXUnit(outputHelper, options)); - Assert.Throws("factory", () => (null as ILoggerFactory)!.AddXUnit(outputHelper, ConfigureAction)); - Assert.Throws("factory", () => (null as ILoggerFactory)!.AddXUnit(outputHelper, ConfigureFunction)); - Assert.Throws("factory", () => (null as ILoggerFactory)!.AddXUnit(outputHelper, Filter)); - Assert.Throws("factory", () => (null as ILoggerFactory)!.AddXUnit(outputHelper, logLevel)); - Assert.Throws("outputHelper", () => factory.AddXUnit((null as ITestOutputHelper)!)); - Assert.Throws("outputHelper", () => factory.AddXUnit((null as ITestOutputHelper)!, ConfigureAction)); - Assert.Throws("outputHelper", () => factory.AddXUnit((null as ITestOutputHelper)!, ConfigureFunction)); - Assert.Throws("outputHelper", () => factory.AddXUnit((null as ITestOutputHelper)!, Filter)); - Assert.Throws("outputHelper", () => factory.AddXUnit((null as ITestOutputHelper)!, logLevel)); - Assert.Throws("outputHelper", () => factory.AddXUnit((null as ITestOutputHelper)!, options)); - Assert.Throws("options", () => factory.AddXUnit(outputHelper, (null as XUnitLoggerOptions)!)); - Assert.Throws("configure", () => factory.AddXUnit(outputHelper, (null as Action)!)); - Assert.Throws("configure", () => factory.AddXUnit(outputHelper, (null as Func)!)); - Assert.Throws("filter", () => factory.AddXUnit(outputHelper, (null as Func)!)); - } - - [Fact] - public static void AddXUnit_MessageSink_For_ILoggerFactory_Validates_Parameters() - { - // Arrange - ILoggerFactory factory = NullLoggerFactory.Instance; - var logLevel = LogLevel.Information; - var messageSink = Substitute.For(); - var options = new XUnitLoggerOptions(); - - // Act and Assert - Assert.Throws("factory", () => (null as ILoggerFactory)!.AddXUnit(messageSink)); - Assert.Throws("factory", () => (null as ILoggerFactory)!.AddXUnit(messageSink, options)); - Assert.Throws("factory", () => (null as ILoggerFactory)!.AddXUnit(messageSink, ConfigureAction)); - Assert.Throws("factory", () => (null as ILoggerFactory)!.AddXUnit(messageSink, ConfigureFunction)); - Assert.Throws("factory", () => (null as ILoggerFactory)!.AddXUnit(messageSink, Filter)); - Assert.Throws("factory", () => (null as ILoggerFactory)!.AddXUnit(messageSink, logLevel)); - Assert.Throws("messageSink", () => factory.AddXUnit((null as IMessageSink)!)); - Assert.Throws("messageSink", () => factory.AddXUnit((null as IMessageSink)!, ConfigureAction)); - Assert.Throws("messageSink", () => factory.AddXUnit((null as IMessageSink)!, ConfigureFunction)); - Assert.Throws("messageSink", () => factory.AddXUnit((null as IMessageSink)!, Filter)); - Assert.Throws("messageSink", () => factory.AddXUnit((null as IMessageSink)!, logLevel)); - Assert.Throws("messageSink", () => factory.AddXUnit((null as IMessageSink)!, options)); - Assert.Throws("options", () => factory.AddXUnit(messageSink, (null as XUnitLoggerOptions)!)); - Assert.Throws("configure", () => factory.AddXUnit(messageSink, (null as Action)!)); - Assert.Throws("configure", () => factory.AddXUnit(messageSink, (null as Func)!)); - Assert.Throws("filter", () => factory.AddXUnit(messageSink, (null as Func)!)); - } - - [Fact] - public static void ToLoggerFactory_Validates_Parameters() - { - // Arrange - ITestOutputHelper? outputHelper = null; - IMessageSink? messageSink = null; - - // Act and Assert - Assert.Throws("outputHelper", () => outputHelper!.ToLoggerFactory()); - Assert.Throws("messageSink", () => messageSink!.ToLoggerFactory()); - } - - [Fact] - public static void AddXUnit_Registers_Services() - { - // Arrange - var services = new ServiceCollection(); - - // Act - services.AddLogging(c => c.AddXUnit()); - - // Assert - var serviceProvider = services.BuildServiceProvider(); - serviceProvider.GetService().ShouldBeOfType(); - serviceProvider.GetService().ShouldBeOfType(); - } - - [Fact] - public static void AddXUnit_ITestOutputHelperAccessor_Registers_Services() - { - // Arrange - var services = new ServiceCollection(); - var accessor = Substitute.For(); - - // Act - services.AddLogging(c => c.AddXUnit(accessor)); - - // Assert - var serviceProvider = services.BuildServiceProvider(); - serviceProvider.GetService().ShouldBeOfType(); - serviceProvider.GetService().ShouldBe(accessor); - } - - [Fact] - public static void AddXUnit_IMessageSinkAccessor_Registers_Services() - { - // Arrange - var services = new ServiceCollection(); - var accessor = Substitute.For(); - - // Act - services.AddLogging(c => c.AddXUnit(accessor)); - - // Assert - var serviceProvider = services.BuildServiceProvider(); - serviceProvider.GetService().ShouldBeOfType(); - serviceProvider.GetService().ShouldBe(accessor); - } - - [Fact] - public static void AddXUnit_ITestOutputHelper_Registers_Services() - { - // Arrange - var services = new ServiceCollection(); - var testOutputHelper = Substitute.For(); - - // Act - services.AddLogging(c => c.AddXUnit(testOutputHelper)); - - // Assert - var serviceProvider = services.BuildServiceProvider(); - serviceProvider.GetService().ShouldBeOfType(); - } - - [Fact] - public static void AddXUnit_IMessageSink_Registers_Services() - { - // Arrange - var services = new ServiceCollection(); - var messageSink = Substitute.For(); - - // Act - services.AddLogging(c => c.AddXUnit(messageSink)); - - // Assert - var serviceProvider = services.BuildServiceProvider(); - serviceProvider.GetService().ShouldBeOfType(); - } - - [Fact] - public static void AddXUnit_IMessageSink_With_LogLevel_Works() - { - // Arrange - var factory = NullLoggerFactory.Instance; - var messageSink = Substitute.For(); - var minLevel = LogLevel.Debug; - - // Act - factory.AddXUnit(messageSink, minLevel); - - // Assert - ILogger logger = factory.CreateLogger("SomeLogger"); - logger.LogInformation("Some message"); - } - - [Fact] - public static void AddXUnit_IMessageSink_With_Filter_Works() - { - // Arrange - var factory = NullLoggerFactory.Instance; - var messageSink = Substitute.For(); - - // Act - factory.AddXUnit(messageSink, (_) => { }); - - // Assert - ILogger logger = factory.CreateLogger("SomeLogger"); - logger.LogInformation("Some message"); - } - - [Fact] - public static void AddXUnit_IMessageSink_With_Options_Works() - { - // Arrange - var factory = NullLoggerFactory.Instance; - var messageSink = Substitute.For(); - var options = new XUnitLoggerOptions(); - - // Act - factory.AddXUnit(messageSink, options); - - // Assert - ILogger logger = factory.CreateLogger("SomeLogger"); - logger.LogInformation("Some message"); - } - - private static void ConfigureAction(XUnitLoggerOptions options) - { - } - - private static XUnitLoggerOptions ConfigureFunction() => new(); - - private static bool Filter(string? categoryName, LogLevel level) => true; -} +// Copyright (c) Martin Costello, 2018. All rights reserved. +// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. + +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using NSubstitute; + +namespace MartinCostello.Logging.XUnit; + +public static class XUnitLoggerExtensionsTests +{ + [Fact] + public static void AddXUnit_TestOutputHelper_For_ILoggerBuilder_Validates_Parameters() + { + // Arrange + var builder = Substitute.For(); + var outputHelper = Substitute.For(); + var accessor = Substitute.For(); + + // Act and Assert + Assert.Throws("builder", () => (null as ILoggingBuilder)!.AddXUnit()); + Assert.Throws("builder", () => (null as ILoggingBuilder)!.AddXUnit(outputHelper)); + Assert.Throws("builder", () => (null as ILoggingBuilder)!.AddXUnit(outputHelper, ConfigureAction)); + Assert.Throws("builder", () => (null as ILoggingBuilder)!.AddXUnit(accessor)); + Assert.Throws("builder", () => (null as ILoggingBuilder)!.AddXUnit(accessor, ConfigureAction)); + Assert.Throws("accessor", () => builder.AddXUnit((null as ITestOutputHelperAccessor)!)); + Assert.Throws("accessor", () => builder.AddXUnit((null as ITestOutputHelperAccessor)!, ConfigureAction)); + Assert.Throws("outputHelper", () => builder.AddXUnit((null as ITestOutputHelper)!)); + Assert.Throws("outputHelper", () => builder.AddXUnit((null as ITestOutputHelper)!, ConfigureAction)); + Assert.Throws("configure", () => builder.AddXUnit(outputHelper, null!)); + Assert.Throws("configure", () => builder.AddXUnit(accessor, null!)); + } + + [Fact] + public static void AddXUnit_MessageSink_For_ILoggerBuilder_Validates_Parameters() + { + // Arrange + var builder = Substitute.For(); + var messageSink = Substitute.For(); + var accessor = Substitute.For(); + + // Act and Assert + Assert.Throws("builder", () => (null as ILoggingBuilder)!.AddXUnit(messageSink)); + Assert.Throws("builder", () => (null as ILoggingBuilder)!.AddXUnit(messageSink, ConfigureAction)); + Assert.Throws("builder", () => (null as ILoggingBuilder)!.AddXUnit(accessor)); + Assert.Throws("builder", () => (null as ILoggingBuilder)!.AddXUnit(accessor, ConfigureAction)); + Assert.Throws("accessor", () => builder.AddXUnit((null as IMessageSinkAccessor)!)); + Assert.Throws("accessor", () => builder.AddXUnit((null as IMessageSinkAccessor)!, ConfigureAction)); + Assert.Throws("messageSink", () => builder.AddXUnit((null as IMessageSink)!)); + Assert.Throws("messageSink", () => builder.AddXUnit((null as IMessageSink)!, ConfigureAction)); + Assert.Throws("configure", () => builder.AddXUnit(messageSink, null!)); + Assert.Throws("configure", () => builder.AddXUnit(accessor, null!)); + } + + [Fact] + public static void AddXUnit_TestOutputHelper_For_ILoggerFactory_Validates_Parameters() + { + // Arrange + ILoggerFactory factory = NullLoggerFactory.Instance; + var logLevel = LogLevel.Information; + var outputHelper = Substitute.For(); + var options = new XUnitLoggerOptions(); + + // Act and Assert + Assert.Throws("factory", () => (null as ILoggerFactory)!.AddXUnit(outputHelper)); + Assert.Throws("factory", () => (null as ILoggerFactory)!.AddXUnit(outputHelper, options)); + Assert.Throws("factory", () => (null as ILoggerFactory)!.AddXUnit(outputHelper, ConfigureAction)); + Assert.Throws("factory", () => (null as ILoggerFactory)!.AddXUnit(outputHelper, ConfigureFunction)); + Assert.Throws("factory", () => (null as ILoggerFactory)!.AddXUnit(outputHelper, Filter)); + Assert.Throws("factory", () => (null as ILoggerFactory)!.AddXUnit(outputHelper, logLevel)); + Assert.Throws("outputHelper", () => factory.AddXUnit((null as ITestOutputHelper)!)); + Assert.Throws("outputHelper", () => factory.AddXUnit((null as ITestOutputHelper)!, ConfigureAction)); + Assert.Throws("outputHelper", () => factory.AddXUnit((null as ITestOutputHelper)!, ConfigureFunction)); + Assert.Throws("outputHelper", () => factory.AddXUnit((null as ITestOutputHelper)!, Filter)); + Assert.Throws("outputHelper", () => factory.AddXUnit((null as ITestOutputHelper)!, logLevel)); + Assert.Throws("outputHelper", () => factory.AddXUnit((null as ITestOutputHelper)!, options)); + Assert.Throws("options", () => factory.AddXUnit(outputHelper, (null as XUnitLoggerOptions)!)); + Assert.Throws("configure", () => factory.AddXUnit(outputHelper, (null as Action)!)); + Assert.Throws("configure", () => factory.AddXUnit(outputHelper, (null as Func)!)); + Assert.Throws("filter", () => factory.AddXUnit(outputHelper, (null as Func)!)); + } + + [Fact] + public static void AddXUnit_MessageSink_For_ILoggerFactory_Validates_Parameters() + { + // Arrange + ILoggerFactory factory = NullLoggerFactory.Instance; + var logLevel = LogLevel.Information; + var messageSink = Substitute.For(); + var options = new XUnitLoggerOptions(); + + // Act and Assert + Assert.Throws("factory", () => (null as ILoggerFactory)!.AddXUnit(messageSink)); + Assert.Throws("factory", () => (null as ILoggerFactory)!.AddXUnit(messageSink, options)); + Assert.Throws("factory", () => (null as ILoggerFactory)!.AddXUnit(messageSink, ConfigureAction)); + Assert.Throws("factory", () => (null as ILoggerFactory)!.AddXUnit(messageSink, ConfigureFunction)); + Assert.Throws("factory", () => (null as ILoggerFactory)!.AddXUnit(messageSink, Filter)); + Assert.Throws("factory", () => (null as ILoggerFactory)!.AddXUnit(messageSink, logLevel)); + Assert.Throws("messageSink", () => factory.AddXUnit((null as IMessageSink)!)); + Assert.Throws("messageSink", () => factory.AddXUnit((null as IMessageSink)!, ConfigureAction)); + Assert.Throws("messageSink", () => factory.AddXUnit((null as IMessageSink)!, ConfigureFunction)); + Assert.Throws("messageSink", () => factory.AddXUnit((null as IMessageSink)!, Filter)); + Assert.Throws("messageSink", () => factory.AddXUnit((null as IMessageSink)!, logLevel)); + Assert.Throws("messageSink", () => factory.AddXUnit((null as IMessageSink)!, options)); + Assert.Throws("options", () => factory.AddXUnit(messageSink, (null as XUnitLoggerOptions)!)); + Assert.Throws("configure", () => factory.AddXUnit(messageSink, (null as Action)!)); + Assert.Throws("configure", () => factory.AddXUnit(messageSink, (null as Func)!)); + Assert.Throws("filter", () => factory.AddXUnit(messageSink, (null as Func)!)); + } + + [Fact] + public static void ToLoggerFactory_Validates_Parameters() + { + // Arrange + ITestOutputHelper? outputHelper = null; + IMessageSink? messageSink = null; + + // Act and Assert + Assert.Throws("outputHelper", () => outputHelper!.ToLoggerFactory()); + Assert.Throws("messageSink", () => messageSink!.ToLoggerFactory()); + } + + [Fact] + public static void AddXUnit_Registers_Services() + { + // Arrange + var services = new ServiceCollection(); + + // Act + services.AddLogging(c => c.AddXUnit()); + + // Assert + var serviceProvider = services.BuildServiceProvider(); + serviceProvider.GetService().ShouldBeOfType(); + serviceProvider.GetService().ShouldBeOfType(); + } + + [Fact] + public static void AddXUnit_ITestOutputHelperAccessor_Registers_Services() + { + // Arrange + var services = new ServiceCollection(); + var accessor = Substitute.For(); + + // Act + services.AddLogging(c => c.AddXUnit(accessor)); + + // Assert + var serviceProvider = services.BuildServiceProvider(); + serviceProvider.GetService().ShouldBeOfType(); + serviceProvider.GetService().ShouldBe(accessor); + } + + [Fact] + public static void AddXUnit_IMessageSinkAccessor_Registers_Services() + { + // Arrange + var services = new ServiceCollection(); + var accessor = Substitute.For(); + + // Act + services.AddLogging(c => c.AddXUnit(accessor)); + + // Assert + var serviceProvider = services.BuildServiceProvider(); + serviceProvider.GetService().ShouldBeOfType(); + serviceProvider.GetService().ShouldBe(accessor); + } + + [Fact] + public static void AddXUnit_ITestOutputHelper_Registers_Services() + { + // Arrange + var services = new ServiceCollection(); + var testOutputHelper = Substitute.For(); + + // Act + services.AddLogging(c => c.AddXUnit(testOutputHelper)); + + // Assert + var serviceProvider = services.BuildServiceProvider(); + serviceProvider.GetService().ShouldBeOfType(); + } + + [Fact] + public static void AddXUnit_IMessageSink_Registers_Services() + { + // Arrange + var services = new ServiceCollection(); + var messageSink = Substitute.For(); + + // Act + services.AddLogging(c => c.AddXUnit(messageSink)); + + // Assert + var serviceProvider = services.BuildServiceProvider(); + serviceProvider.GetService().ShouldBeOfType(); + } + + [Fact] + public static void AddXUnit_IMessageSink_With_LogLevel_Works() + { + // Arrange + var factory = NullLoggerFactory.Instance; + var messageSink = Substitute.For(); + var minLevel = LogLevel.Debug; + + // Act + factory.AddXUnit(messageSink, minLevel); + + // Assert + ILogger logger = factory.CreateLogger("SomeLogger"); + logger.LogInformation("Some message"); + } + + [Fact] + public static void AddXUnit_IMessageSink_With_Filter_Works() + { + // Arrange + var factory = NullLoggerFactory.Instance; + var messageSink = Substitute.For(); + + // Act + factory.AddXUnit(messageSink, (_) => { }); + + // Assert + ILogger logger = factory.CreateLogger("SomeLogger"); + logger.LogInformation("Some message"); + } + + [Fact] + public static void AddXUnit_IMessageSink_With_Options_Works() + { + // Arrange + var factory = NullLoggerFactory.Instance; + var messageSink = Substitute.For(); + var options = new XUnitLoggerOptions(); + + // Act + factory.AddXUnit(messageSink, options); + + // Assert + ILogger logger = factory.CreateLogger("SomeLogger"); + logger.LogInformation("Some message"); + } + + private static void ConfigureAction(XUnitLoggerOptions options) + { + } + + private static XUnitLoggerOptions ConfigureFunction() => new(); + + private static bool Filter(string? categoryName, LogLevel level) => true; +} diff --git a/tests/Logging.XUnit.Tests/XUnitLoggerProviderTests.cs b/tests/Shared/XUnitLoggerProviderTests.cs similarity index 97% rename from tests/Logging.XUnit.Tests/XUnitLoggerProviderTests.cs rename to tests/Shared/XUnitLoggerProviderTests.cs index ffa7fe95..92bf4e43 100644 --- a/tests/Logging.XUnit.Tests/XUnitLoggerProviderTests.cs +++ b/tests/Shared/XUnitLoggerProviderTests.cs @@ -1,72 +1,72 @@ -// Copyright (c) Martin Costello, 2018. All rights reserved. -// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. - -using Microsoft.Extensions.Logging; -using NSubstitute; - -namespace MartinCostello.Logging.XUnit; - -public static class XUnitLoggerProviderTests -{ - [Fact] - public static void XUnitLoggerProvider_TestOutputHelper_Constructor_Validates_Parameters() - { - // Arrange - var outputHelper = Substitute.For(); - var accessor = Substitute.For(); - var options = new XUnitLoggerOptions(); - - // Act and Assert - Assert.Throws("outputHelper", () => new XUnitLoggerProvider((null as ITestOutputHelper)!, options)); - Assert.Throws("accessor", () => new XUnitLoggerProvider((null as ITestOutputHelperAccessor)!, options)); - Assert.Throws("options", () => new XUnitLoggerProvider(outputHelper, null!)); - Assert.Throws("options", () => new XUnitLoggerProvider(accessor, null!)); - } - - [Fact] - public static void XUnitLoggerProvider_MessageSink_Constructor_Validates_Parameters() - { - // Arrange - var messageSink = Substitute.For(); - var accessor = Substitute.For(); - var options = new XUnitLoggerOptions(); - - // Act and Assert - Assert.Throws("messageSink", () => new XUnitLoggerProvider((null as IMessageSink)!, options)); - Assert.Throws("accessor", () => new XUnitLoggerProvider((null as IMessageSinkAccessor)!, options)); - Assert.Throws("options", () => new XUnitLoggerProvider(messageSink, null!)); - Assert.Throws("options", () => new XUnitLoggerProvider(accessor, null!)); - } - - [Theory] - [InlineData(Constructor.ITestOutputHelper)] - [InlineData(Constructor.IMessageSink)] - public static void XUnitLoggerProvider_Creates_Logger(Constructor constructor) - { - // Arrange - var testOutputHelper = Substitute.For(); - var messageSink = Substitute.For(); - var options = new XUnitLoggerOptions(); - - string categoryName = "MyLogger"; - - using var target = constructor switch - { - Constructor.ITestOutputHelper => new XUnitLoggerProvider(testOutputHelper, options), - Constructor.IMessageSink => new XUnitLoggerProvider(messageSink, options), - _ => throw new ArgumentOutOfRangeException(nameof(constructor), constructor, null), - }; - - // Act - ILogger actual = target.CreateLogger(categoryName); - - // Assert - actual.ShouldNotBeNull(); - - var xunit = actual.ShouldBeOfType(); - xunit.Name.ShouldBe(categoryName); - xunit.Filter.ShouldBeSameAs(options.Filter); - xunit.MessageSinkMessageFactory.ShouldBeSameAs(options.MessageSinkMessageFactory); - xunit.IncludeScopes.ShouldBeFalse(); - } -} +// Copyright (c) Martin Costello, 2018. All rights reserved. +// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. + +using Microsoft.Extensions.Logging; +using NSubstitute; + +namespace MartinCostello.Logging.XUnit; + +public static class XUnitLoggerProviderTests +{ + [Fact] + public static void XUnitLoggerProvider_TestOutputHelper_Constructor_Validates_Parameters() + { + // Arrange + var outputHelper = Substitute.For(); + var accessor = Substitute.For(); + var options = new XUnitLoggerOptions(); + + // Act and Assert + Assert.Throws("outputHelper", () => new XUnitLoggerProvider((null as ITestOutputHelper)!, options)); + Assert.Throws("accessor", () => new XUnitLoggerProvider((null as ITestOutputHelperAccessor)!, options)); + Assert.Throws("options", () => new XUnitLoggerProvider(outputHelper, null!)); + Assert.Throws("options", () => new XUnitLoggerProvider(accessor, null!)); + } + + [Fact] + public static void XUnitLoggerProvider_MessageSink_Constructor_Validates_Parameters() + { + // Arrange + var messageSink = Substitute.For(); + var accessor = Substitute.For(); + var options = new XUnitLoggerOptions(); + + // Act and Assert + Assert.Throws("messageSink", () => new XUnitLoggerProvider((null as IMessageSink)!, options)); + Assert.Throws("accessor", () => new XUnitLoggerProvider((null as IMessageSinkAccessor)!, options)); + Assert.Throws("options", () => new XUnitLoggerProvider(messageSink, null!)); + Assert.Throws("options", () => new XUnitLoggerProvider(accessor, null!)); + } + + [Theory] + [InlineData(Constructor.ITestOutputHelper)] + [InlineData(Constructor.IMessageSink)] + public static void XUnitLoggerProvider_Creates_Logger(Constructor constructor) + { + // Arrange + var testOutputHelper = Substitute.For(); + var messageSink = Substitute.For(); + var options = new XUnitLoggerOptions(); + + string categoryName = "MyLogger"; + + using var target = constructor switch + { + Constructor.ITestOutputHelper => new XUnitLoggerProvider(testOutputHelper, options), + Constructor.IMessageSink => new XUnitLoggerProvider(messageSink, options), + _ => throw new ArgumentOutOfRangeException(nameof(constructor), constructor, null), + }; + + // Act + ILogger actual = target.CreateLogger(categoryName); + + // Assert + actual.ShouldNotBeNull(); + + var xunit = actual.ShouldBeOfType(); + xunit.Name.ShouldBe(categoryName); + xunit.Filter.ShouldBeSameAs(options.Filter); + xunit.MessageSinkMessageFactory.ShouldBeSameAs(options.MessageSinkMessageFactory); + xunit.IncludeScopes.ShouldBeFalse(); + } +} diff --git a/tests/Logging.XUnit.Tests/XUnitLoggerTests.cs b/tests/Shared/XUnitLoggerTests.cs similarity index 96% rename from tests/Logging.XUnit.Tests/XUnitLoggerTests.cs rename to tests/Shared/XUnitLoggerTests.cs index 61ef09ee..165232ec 100644 --- a/tests/Logging.XUnit.Tests/XUnitLoggerTests.cs +++ b/tests/Shared/XUnitLoggerTests.cs @@ -1,681 +1,681 @@ -// Copyright (c) Martin Costello, 2018. All rights reserved. -// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. - -using Microsoft.Extensions.Logging; -using NSubstitute; - -namespace MartinCostello.Logging.XUnit; - -public static class XUnitLoggerTests -{ - [Fact] - public static void XUnitLogger_Validates_Parameters() - { - // Arrange - string name = "MyName"; - var outputHelper = Substitute.For(); - - var options = new XUnitLoggerOptions() - { - Filter = FilterFalse, - }; - - // Act and Assert - Assert.Throws("name", () => new XUnitLogger(null!, outputHelper, options)); - Assert.Throws("outputHelper", () => new XUnitLogger(name, (null as ITestOutputHelper)!, options)); - Assert.Throws("messageSink", () => new XUnitLogger(name, (null as IMessageSink)!, options)); - Assert.Throws("accessor", () => new XUnitLogger(name, (null as ITestOutputHelperAccessor)!, options)); - Assert.Throws("accessor", () => new XUnitLogger(name, (null as IMessageSinkAccessor)!, options)); - - // Arrange - var logger = new XUnitLogger(name, outputHelper, options); - - // Act and Assert - Assert.Throws("value", () => logger.Filter = null!); - Assert.Throws("value", () => logger.MessageSinkMessageFactory = null!); - - // Arrange - Func filter = (_, _) => true; - - // Act - logger.Filter = filter; - - // Assert - logger.Filter.ShouldBeSameAs(filter); - } - - [Theory] - [InlineData(Constructor.ITestOutputHelper)] - [InlineData(Constructor.IMessageSink)] - public static void XUnitLogger_Constructor_Initializes_Instance(Constructor constructor) - { - // Arrange - string name = "MyName"; - var testOutputHelper = Substitute.For(); - var messageSink = Substitute.For(); - var options = new XUnitLoggerOptions() - { - Filter = FilterTrue, - MessageSinkMessageFactory = DiagnosticMessageFactory, - IncludeScopes = true, - }; - - XUnitLogger CreateLogger(XUnitLoggerOptions? opts) - { - return constructor switch - { - Constructor.ITestOutputHelper => new XUnitLogger(name, testOutputHelper, opts), - Constructor.IMessageSink => new XUnitLogger(name, messageSink, opts), - _ => throw new ArgumentOutOfRangeException(nameof(constructor), constructor, null), - }; - } - - // Act - var actual = CreateLogger(options); - - // Assert - actual.Filter.ShouldBeSameAs(options.Filter); - actual.MessageSinkMessageFactory.ShouldBeSameAs(options.MessageSinkMessageFactory); - actual.IncludeScopes.ShouldBeTrue(); - actual.Name.ShouldBe(name); - - // Act - actual = CreateLogger(null); - - // Assert - actual.Filter.ShouldNotBeNull(); - actual.Filter(null, LogLevel.None).ShouldBeTrue(); - actual.IncludeScopes.ShouldBeFalse(); - actual.Name.ShouldBe(name); - actual.MessageSinkMessageFactory.ShouldNotBeNull(); -#pragma warning disable xUnit3002 - actual.MessageSinkMessageFactory("message").ShouldBeOfType(); -#pragma warning restore xUnit3002 - } - - [Fact] - public static void XUnitLogger_BeginScope_Returns_Value() - { - // Arrange - string name = "MyName"; - var outputHelper = Substitute.For(); - var options = new XUnitLoggerOptions(); - var logger = new XUnitLogger(name, outputHelper, options); - - // Act - using var actual = logger.BeginScope(true); - - // Assert - actual.ShouldNotBeNull(); - } - - [Fact] - public static void XUnitLogger_BeginScope_Throws_If_State_Is_Null() - { - // Arrange - string name = "MyName"; - var outputHelper = Substitute.For(); - var options = new XUnitLoggerOptions(); - var logger = new XUnitLogger(name, outputHelper, options); - string state = null!; - - // Act - Assert.Throws("state", () => logger.BeginScope(state)); - } - - [Theory] - [InlineData(LogLevel.Critical, true)] - [InlineData(LogLevel.Debug, false)] - [InlineData(LogLevel.Error, true)] - [InlineData(LogLevel.Information, false)] - [InlineData(LogLevel.None, false)] - [InlineData(LogLevel.Trace, false)] - [InlineData(LogLevel.Warning, true)] - public static void XUnitLogger_IsEnabled_Returns_Correct_Result(LogLevel logLevel, bool expected) - { - // Arrange - string name = "MyName"; - var outputHelper = Substitute.For(); - - bool CustomFilter(string? categoryName, LogLevel level) - { - categoryName.ShouldBe(name); - level.ShouldBe(logLevel); - return level > LogLevel.Information; - } - - var options = new XUnitLoggerOptions() - { - Filter = CustomFilter, - }; - - var logger = new XUnitLogger(name, outputHelper, options); - - // Act - bool actual = logger.IsEnabled(logLevel); - - // Assert - actual.ShouldBe(expected); - } - - [Fact] - public static void XUnitLogger_Log_Throws_If_Formatter_Is_Null() - { - // Arrange - string name = "MyName"; - var outputHelper = Substitute.For(); - var options = new XUnitLoggerOptions(); - - var logger = new XUnitLogger(name, outputHelper, options); - - // Act and Assert - Assert.Throws("formatter", () => logger.Log(LogLevel.Information, new EventId(2), true, null, null!)); - } - - [Fact] - public static void XUnitLogger_Log_Throws_If_LogLevel_Is_Invalid() - { - // Arrange - string name = "MyName"; - var outputHelper = Substitute.For(); - var options = new XUnitLoggerOptions(); - - var logger = new XUnitLogger(name, outputHelper, options); - - // Act and Assert - Assert.Throws("logLevel", () => logger.Log((LogLevel)int.MaxValue, 0, "state", null, Formatter)); - } - - [Fact] - public static void XUnitLogger_Log_Does_Nothing_If_Not_Enabled() - { - // Arrange - var outputHelper = Substitute.For(); - string name = "MyName"; - - var options = new XUnitLoggerOptions() - { - Filter = FilterFalse, - }; - - var logger = new XUnitLogger(name, outputHelper, options); - - // Act - logger.Log(LogLevel.Information, new EventId(2), "state", null, Formatter); - - // Assert - outputHelper.DidNotReceiveWithAnyArgs().WriteLine(string.Empty); - } - - [Fact] - public static void XUnitLogger_Log_Does_Nothing_If_Null_Message_And_No_Exception() - { - // Arrange - var outputHelper = Substitute.For(); - string name = "MyName"; - - var options = new XUnitLoggerOptions() - { - Filter = FilterTrue, - }; - - var logger = new XUnitLogger(name, outputHelper, options); - - // Act - logger.Log(LogLevel.Information, new EventId(2), "state", null, FormatterNull); - - // Assert - outputHelper.DidNotReceiveWithAnyArgs().WriteLine(string.Empty); - } - - [Fact] - public static void XUnitLogger_Log_Does_Nothing_If_Empty_Message_And_No_Exception() - { - // Arrange - var outputHelper = Substitute.For(); - string name = "MyName"; - - var options = new XUnitLoggerOptions() - { - Filter = FilterTrue, - }; - - var logger = new XUnitLogger(name, outputHelper, options); - - // Act - logger.Log(LogLevel.Information, new EventId(2), "state", null, FormatterEmpty); - - // Assert - outputHelper.DidNotReceiveWithAnyArgs().WriteLine(string.Empty); - } - - [Fact] - public static void XUnitLogger_Log_Does_Nothing_If_No_OutputHelper() - { - // Arrange - string name = "MyName"; - var accessor = Substitute.For(); - - var options = new XUnitLoggerOptions() - { - Filter = FilterTrue, - }; - - var logger = new XUnitLogger(name, accessor, options); - - // Act (no Assert) - logger.Log(LogLevel.Information, new EventId(2), "state", null, Formatter); - } - - [Fact] - public static void XUnitLogger_Log_Logs_Message_If_Only_Exception() - { - // Arrange - var outputHelper = Substitute.For(); - string name = "MyName"; - - var options = new XUnitLoggerOptions() - { - Filter = FilterTrue, - }; - - var logger = new XUnitLogger(name, outputHelper, options) - { - Clock = StaticClock, - }; - - var exception = new InvalidOperationException("Invalid"); - - string expected = string.Join( - Environment.NewLine, - "[2018-08-19 16:12:16Z] info: MyName[2]", - "System.InvalidOperationException: Invalid"); - - // Act - logger.Log(LogLevel.Information, new EventId(2), "state", exception, FormatterNull); - - // Assert - outputHelper.Received(1).WriteLine(expected); - } - - [Fact] - public static void XUnitLogger_Log_Logs_Message_If_Message_And_Exception() - { - // Arrange - var outputHelper = Substitute.For(); - string name = "MyName"; - - var options = new XUnitLoggerOptions() - { - Filter = FilterTrue, - }; - - var logger = new XUnitLogger(name, outputHelper, options) - { - Clock = StaticClock, - }; - - var exception = new InvalidOperationException("Invalid"); - - string expected = string.Join( - Environment.NewLine, - "[2018-08-19 16:12:16Z] warn: MyName[3]", - " Message|False|True", - "System.InvalidOperationException: Invalid"); - - // Act - logger.Log(LogLevel.Warning, new EventId(3), null, exception, Formatter); - - // Assert - outputHelper.Received(1).WriteLine(expected); - } - - [Fact] - public static void XUnitLogger_Log_Logs_Message_If_Message_And_No_Exception() - { - // Arrange - var outputHelper = Substitute.For(); - string name = "MyName"; - - var options = new XUnitLoggerOptions() - { - Filter = FilterTrue, - }; - - var logger = new XUnitLogger(name, outputHelper, options) - { - Clock = StaticClock, - }; - - string expected = string.Join( - Environment.NewLine, - "[2018-08-19 16:12:16Z] fail: MyName[4]", - " Message|False|False"); - - // Act - logger.Log(LogLevel.Error, new EventId(4), null, null, Formatter); - - // Assert - outputHelper.Received(1).WriteLine(expected); - } - - [Theory] - [InlineData(LogLevel.Critical, "crit")] - [InlineData(LogLevel.Debug, "dbug")] - [InlineData(LogLevel.Error, "fail")] - [InlineData(LogLevel.Information, "info")] - [InlineData(LogLevel.Trace, "trce")] - [InlineData(LogLevel.Warning, "warn")] - public static void XUnitLogger_Log_Logs_Messages(LogLevel logLevel, string shortLevel) - { - // Arrange - var outputHelper = Substitute.For(); - string name = "Your Name"; - - var options = new XUnitLoggerOptions() - { - Filter = FilterTrue, - }; - - var logger = new XUnitLogger(name, outputHelper, options) - { - Clock = StaticClock, - }; - - string expected = string.Join( - Environment.NewLine, - $"[2018-08-19 16:12:16Z] {shortLevel}: Your Name[85]", - " Message|True|False"); - - // Act - logger.Log(logLevel, new EventId(85), "Martin", null, Formatter); - - // Assert - outputHelper.Received(1).WriteLine(expected); - } - - [Fact] - public static void XUnitLogger_Log_Logs_Very_Long_Messages() - { - // Arrange - var outputHelper = Substitute.For(); - string name = "MyName"; - - var options = new XUnitLoggerOptions() - { - Filter = FilterTrue, - }; - - var logger = new XUnitLogger(name, outputHelper, options); - - // Act - logger.Log(LogLevel.Information, 1, "state", null, FormatterLong); - - // Assert - outputHelper.Received(1).WriteLine(Arg.Is((r) => r.Length > 1024)); - } - - [Fact] - public static void XUnitLogger_Log_Logs_Message_If_Scopes_Included_But_There_Are_No_Scopes() - { - // Arrange - var outputHelper = Substitute.For(); - string name = "MyName"; - - var options = new XUnitLoggerOptions() - { - Filter = FilterTrue, - IncludeScopes = true, - }; - - var logger = new XUnitLogger(name, outputHelper, options) - { - Clock = StaticClock, - }; - - string expected = string.Join( - Environment.NewLine, - "[2018-08-19 16:12:16Z] info: MyName[0]", - " Message|False|False"); - - // Act - logger.Log(LogLevel.Information, 0, null, null, Formatter); - - // Assert - outputHelper.Received(1).WriteLine(expected); - } - - [Fact] - public static void XUnitLogger_Log_Logs_Message_If_Scopes_Included_And_There_Are_Scopes() - { - // Arrange - var outputHelper = Substitute.For(); - string name = "MyName"; - - var options = new XUnitLoggerOptions() - { - Filter = FilterTrue, - IncludeScopes = true, - }; - - var logger = new XUnitLogger(name, outputHelper, options) - { - Clock = StaticClock, - }; - - string expected = string.Join( - Environment.NewLine, - "[2018-08-19 16:12:16Z] info: MyName[0]", - " => _", - " => __", - " => ___", - " => {OriginalFormat}: [null]", - " Message|False|False"); - - // Act - using (logger.BeginScope("_")) - { - using (logger.BeginScope("__")) - { - using (logger.BeginScope("___")) - { -#pragma warning disable CA2254 - using (logger.BeginScope(null!)) -#pragma warning restore CA2254 - { - logger.Log(LogLevel.Information, 0, null, null, Formatter); - } - } - } - } - - // Assert - outputHelper.Received(1).WriteLine(expected); - } - - [Fact] - public static void XUnitLogger_Log_Logs_Message_If_Scopes_Included_And_There_Is_Scope_Of_KeyValuePair() - { - // Arrange - var outputHelper = Substitute.For(); - string name = "MyName"; - - var options = new XUnitLoggerOptions() - { - Filter = FilterTrue, - IncludeScopes = true, - }; - - var logger = new XUnitLogger(name, outputHelper, options) - { - Clock = StaticClock, - }; - - string expected = string.Join( - Environment.NewLine, - "[2018-08-19 16:12:16Z] info: MyName[0]", - " => ScopeKey: ScopeValue", - " Message|False|False"); - - // Act - using (logger.BeginScope(new[] - { - new KeyValuePair("ScopeKey", "ScopeValue"), - })) - { - logger.Log(LogLevel.Information, 0, null, null, Formatter); - } - - // Assert - outputHelper.Received(1).WriteLine(expected); - } - - [Fact] - public static void XUnitLogger_Log_Logs_Message_If_Scopes_Included_And_There_Is_Scope_Of_KeyValuePairs() - { - // Arrange - var outputHelper = Substitute.For(); - string name = "MyName"; - - var options = new XUnitLoggerOptions() - { - Filter = FilterTrue, - IncludeScopes = true, - }; - - var logger = new XUnitLogger(name, outputHelper, options) - { - Clock = StaticClock, - }; - - string expected = string.Join( - Environment.NewLine, - "[2018-08-19 16:12:16Z] info: MyName[0]", - " => ScopeKeyOne: ScopeValueOne", - " => ScopeKeyTwo: ScopeValueTwo", - " => ScopeKeyThree: ScopeValueThree", - " Message|False|False"); - - // Act - using (logger.BeginScope(new[] - { - new KeyValuePair("ScopeKeyOne", "ScopeValueOne"), - new KeyValuePair("ScopeKeyTwo", "ScopeValueTwo"), - new KeyValuePair("ScopeKeyThree", "ScopeValueThree"), - })) - { - logger.Log(LogLevel.Information, 0, null, null, Formatter); - } - - // Assert - outputHelper.Received(1).WriteLine(expected); - } - - [Fact] - public static void XUnitLogger_Log_Logs_Message_If_Scopes_Included_And_There_Are_Scopes_Of_KeyValuePairs() - { - // Arrange - var outputHelper = Substitute.For(); - string name = "MyName"; - - var options = new XUnitLoggerOptions() - { - Filter = FilterTrue, - IncludeScopes = true, - }; - - var logger = new XUnitLogger(name, outputHelper, options) - { - Clock = StaticClock, - }; - - string expected = string.Join( - Environment.NewLine, - "[2018-08-19 16:12:16Z] info: MyName[0]", - " => ScopeKeyOne: ScopeValueOne", - " => ScopeKeyTwo: ScopeValueTwo", - " => ScopeKeyThree: ScopeValueThree", - " => ScopeKeyFour: ScopeValueFour", - " => ScopeKeyFive: ScopeValueFive", - " => ScopeKeySix: ScopeValueSix", - " Message|False|False"); - - // Act - using (logger.BeginScope(new[] - { - new KeyValuePair("ScopeKeyOne", "ScopeValueOne"), - new KeyValuePair("ScopeKeyTwo", "ScopeValueTwo"), - new KeyValuePair("ScopeKeyThree", "ScopeValueThree"), - })) - { - using (logger.BeginScope(new[] - { - new KeyValuePair("ScopeKeyFour", "ScopeValueFour"), - new KeyValuePair("ScopeKeyFive", "ScopeValueFive"), - new KeyValuePair("ScopeKeySix", "ScopeValueSix"), - })) - { - logger.Log(LogLevel.Information, 0, null, null, Formatter); - } - } - - // Assert - outputHelper.Received(1).WriteLine(expected); - } - - [Fact] - public static void XUnitLogger_Log_Logs_Message_If_Scopes_Included_And_There_Is_Scope_Of_IEnumerable() - { - // Arrange - var outputHelper = Substitute.For(); - string name = "MyName"; - - var options = new XUnitLoggerOptions() - { - Filter = FilterTrue, - IncludeScopes = true, - }; - - var logger = new XUnitLogger(name, outputHelper, options) - { - Clock = StaticClock, - }; - - string expected = string.Join( - Environment.NewLine, - "[2018-08-19 16:12:16Z] info: MyName[0]", - " => ScopeKeyOne", - " => ScopeKeyTwo", - " => ScopeKeyThree", - " Message|False|False"); - - // Act - using (logger.BeginScope(new[] { "ScopeKeyOne", "ScopeKeyTwo", "ScopeKeyThree" })) - { - logger.Log(LogLevel.Information, 0, null, null, Formatter); - } - - // Assert - outputHelper.Received(1).WriteLine(expected); - } - - private static DateTimeOffset StaticClock() => new(2018, 08, 19, 17, 12, 16, TimeSpan.FromHours(1)); - - private static DiagnosticMessage DiagnosticMessageFactory(string message) => new(message); - - private static bool FilterTrue(string? categoryName, LogLevel level) => true; - - private static bool FilterFalse(string? categoryName, LogLevel level) => false; - - private static string Formatter(TState? state, Exception? exception) - where TState : class - { - return $"Message|{(state == null ? bool.FalseString : bool.TrueString)}|{(exception == null ? bool.FalseString : bool.TrueString)}"; - } - - private static string FormatterEmpty(TState? state, Exception? exception) => string.Empty; - - private static string FormatterLong(TState? state, Exception? exception) => new('a', 2048); - - private static string FormatterNull(TState? state, Exception? exception) => null!; -} +// Copyright (c) Martin Costello, 2018. All rights reserved. +// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. + +using Microsoft.Extensions.Logging; +using NSubstitute; + +namespace MartinCostello.Logging.XUnit; + +public static class XUnitLoggerTests +{ + [Fact] + public static void XUnitLogger_Validates_Parameters() + { + // Arrange + string name = "MyName"; + var outputHelper = Substitute.For(); + + var options = new XUnitLoggerOptions() + { + Filter = FilterFalse, + }; + + // Act and Assert + Assert.Throws("name", () => new XUnitLogger(null!, outputHelper, options)); + Assert.Throws("outputHelper", () => new XUnitLogger(name, (null as ITestOutputHelper)!, options)); + Assert.Throws("messageSink", () => new XUnitLogger(name, (null as IMessageSink)!, options)); + Assert.Throws("accessor", () => new XUnitLogger(name, (null as ITestOutputHelperAccessor)!, options)); + Assert.Throws("accessor", () => new XUnitLogger(name, (null as IMessageSinkAccessor)!, options)); + + // Arrange + var logger = new XUnitLogger(name, outputHelper, options); + + // Act and Assert + Assert.Throws("value", () => logger.Filter = null!); + Assert.Throws("value", () => logger.MessageSinkMessageFactory = null!); + + // Arrange + Func filter = (_, _) => true; + + // Act + logger.Filter = filter; + + // Assert + logger.Filter.ShouldBeSameAs(filter); + } + + [Theory] + [InlineData(Constructor.ITestOutputHelper)] + [InlineData(Constructor.IMessageSink)] + public static void XUnitLogger_Constructor_Initializes_Instance(Constructor constructor) + { + // Arrange + string name = "MyName"; + var testOutputHelper = Substitute.For(); + var messageSink = Substitute.For(); + var options = new XUnitLoggerOptions() + { + Filter = FilterTrue, + MessageSinkMessageFactory = DiagnosticMessageFactory, + IncludeScopes = true, + }; + + XUnitLogger CreateLogger(XUnitLoggerOptions? opts) + { + return constructor switch + { + Constructor.ITestOutputHelper => new XUnitLogger(name, testOutputHelper, opts), + Constructor.IMessageSink => new XUnitLogger(name, messageSink, opts), + _ => throw new ArgumentOutOfRangeException(nameof(constructor), constructor, null), + }; + } + + // Act + var actual = CreateLogger(options); + + // Assert + actual.Filter.ShouldBeSameAs(options.Filter); + actual.MessageSinkMessageFactory.ShouldBeSameAs(options.MessageSinkMessageFactory); + actual.IncludeScopes.ShouldBeTrue(); + actual.Name.ShouldBe(name); + + // Act + actual = CreateLogger(null); + + // Assert + actual.Filter.ShouldNotBeNull(); + actual.Filter(null, LogLevel.None).ShouldBeTrue(); + actual.IncludeScopes.ShouldBeFalse(); + actual.Name.ShouldBe(name); + actual.MessageSinkMessageFactory.ShouldNotBeNull(); +#pragma warning disable xUnit3002 + actual.MessageSinkMessageFactory("message").ShouldBeOfType(); +#pragma warning restore xUnit3002 + } + + [Fact] + public static void XUnitLogger_BeginScope_Returns_Value() + { + // Arrange + string name = "MyName"; + var outputHelper = Substitute.For(); + var options = new XUnitLoggerOptions(); + var logger = new XUnitLogger(name, outputHelper, options); + + // Act + using var actual = logger.BeginScope(true); + + // Assert + actual.ShouldNotBeNull(); + } + + [Fact] + public static void XUnitLogger_BeginScope_Throws_If_State_Is_Null() + { + // Arrange + string name = "MyName"; + var outputHelper = Substitute.For(); + var options = new XUnitLoggerOptions(); + var logger = new XUnitLogger(name, outputHelper, options); + string state = null!; + + // Act + Assert.Throws("state", () => logger.BeginScope(state)); + } + + [Theory] + [InlineData(LogLevel.Critical, true)] + [InlineData(LogLevel.Debug, false)] + [InlineData(LogLevel.Error, true)] + [InlineData(LogLevel.Information, false)] + [InlineData(LogLevel.None, false)] + [InlineData(LogLevel.Trace, false)] + [InlineData(LogLevel.Warning, true)] + public static void XUnitLogger_IsEnabled_Returns_Correct_Result(LogLevel logLevel, bool expected) + { + // Arrange + string name = "MyName"; + var outputHelper = Substitute.For(); + + bool CustomFilter(string? categoryName, LogLevel level) + { + categoryName.ShouldBe(name); + level.ShouldBe(logLevel); + return level > LogLevel.Information; + } + + var options = new XUnitLoggerOptions() + { + Filter = CustomFilter, + }; + + var logger = new XUnitLogger(name, outputHelper, options); + + // Act + bool actual = logger.IsEnabled(logLevel); + + // Assert + actual.ShouldBe(expected); + } + + [Fact] + public static void XUnitLogger_Log_Throws_If_Formatter_Is_Null() + { + // Arrange + string name = "MyName"; + var outputHelper = Substitute.For(); + var options = new XUnitLoggerOptions(); + + var logger = new XUnitLogger(name, outputHelper, options); + + // Act and Assert + Assert.Throws("formatter", () => logger.Log(LogLevel.Information, new EventId(2), true, null, null!)); + } + + [Fact] + public static void XUnitLogger_Log_Throws_If_LogLevel_Is_Invalid() + { + // Arrange + string name = "MyName"; + var outputHelper = Substitute.For(); + var options = new XUnitLoggerOptions(); + + var logger = new XUnitLogger(name, outputHelper, options); + + // Act and Assert + Assert.Throws("logLevel", () => logger.Log((LogLevel)int.MaxValue, 0, "state", null, Formatter)); + } + + [Fact] + public static void XUnitLogger_Log_Does_Nothing_If_Not_Enabled() + { + // Arrange + var outputHelper = Substitute.For(); + string name = "MyName"; + + var options = new XUnitLoggerOptions() + { + Filter = FilterFalse, + }; + + var logger = new XUnitLogger(name, outputHelper, options); + + // Act + logger.Log(LogLevel.Information, new EventId(2), "state", null, Formatter); + + // Assert + outputHelper.DidNotReceiveWithAnyArgs().WriteLine(string.Empty); + } + + [Fact] + public static void XUnitLogger_Log_Does_Nothing_If_Null_Message_And_No_Exception() + { + // Arrange + var outputHelper = Substitute.For(); + string name = "MyName"; + + var options = new XUnitLoggerOptions() + { + Filter = FilterTrue, + }; + + var logger = new XUnitLogger(name, outputHelper, options); + + // Act + logger.Log(LogLevel.Information, new EventId(2), "state", null, FormatterNull); + + // Assert + outputHelper.DidNotReceiveWithAnyArgs().WriteLine(string.Empty); + } + + [Fact] + public static void XUnitLogger_Log_Does_Nothing_If_Empty_Message_And_No_Exception() + { + // Arrange + var outputHelper = Substitute.For(); + string name = "MyName"; + + var options = new XUnitLoggerOptions() + { + Filter = FilterTrue, + }; + + var logger = new XUnitLogger(name, outputHelper, options); + + // Act + logger.Log(LogLevel.Information, new EventId(2), "state", null, FormatterEmpty); + + // Assert + outputHelper.DidNotReceiveWithAnyArgs().WriteLine(string.Empty); + } + + [Fact] + public static void XUnitLogger_Log_Does_Nothing_If_No_OutputHelper() + { + // Arrange + string name = "MyName"; + var accessor = Substitute.For(); + + var options = new XUnitLoggerOptions() + { + Filter = FilterTrue, + }; + + var logger = new XUnitLogger(name, accessor, options); + + // Act (no Assert) + logger.Log(LogLevel.Information, new EventId(2), "state", null, Formatter); + } + + [Fact] + public static void XUnitLogger_Log_Logs_Message_If_Only_Exception() + { + // Arrange + var outputHelper = Substitute.For(); + string name = "MyName"; + + var options = new XUnitLoggerOptions() + { + Filter = FilterTrue, + }; + + var logger = new XUnitLogger(name, outputHelper, options) + { + Clock = StaticClock, + }; + + var exception = new InvalidOperationException("Invalid"); + + string expected = string.Join( + Environment.NewLine, + "[2018-08-19 16:12:16Z] info: MyName[2]", + "System.InvalidOperationException: Invalid"); + + // Act + logger.Log(LogLevel.Information, new EventId(2), "state", exception, FormatterNull); + + // Assert + outputHelper.Received(1).WriteLine(expected); + } + + [Fact] + public static void XUnitLogger_Log_Logs_Message_If_Message_And_Exception() + { + // Arrange + var outputHelper = Substitute.For(); + string name = "MyName"; + + var options = new XUnitLoggerOptions() + { + Filter = FilterTrue, + }; + + var logger = new XUnitLogger(name, outputHelper, options) + { + Clock = StaticClock, + }; + + var exception = new InvalidOperationException("Invalid"); + + string expected = string.Join( + Environment.NewLine, + "[2018-08-19 16:12:16Z] warn: MyName[3]", + " Message|False|True", + "System.InvalidOperationException: Invalid"); + + // Act + logger.Log(LogLevel.Warning, new EventId(3), null, exception, Formatter); + + // Assert + outputHelper.Received(1).WriteLine(expected); + } + + [Fact] + public static void XUnitLogger_Log_Logs_Message_If_Message_And_No_Exception() + { + // Arrange + var outputHelper = Substitute.For(); + string name = "MyName"; + + var options = new XUnitLoggerOptions() + { + Filter = FilterTrue, + }; + + var logger = new XUnitLogger(name, outputHelper, options) + { + Clock = StaticClock, + }; + + string expected = string.Join( + Environment.NewLine, + "[2018-08-19 16:12:16Z] fail: MyName[4]", + " Message|False|False"); + + // Act + logger.Log(LogLevel.Error, new EventId(4), null, null, Formatter); + + // Assert + outputHelper.Received(1).WriteLine(expected); + } + + [Theory] + [InlineData(LogLevel.Critical, "crit")] + [InlineData(LogLevel.Debug, "dbug")] + [InlineData(LogLevel.Error, "fail")] + [InlineData(LogLevel.Information, "info")] + [InlineData(LogLevel.Trace, "trce")] + [InlineData(LogLevel.Warning, "warn")] + public static void XUnitLogger_Log_Logs_Messages(LogLevel logLevel, string shortLevel) + { + // Arrange + var outputHelper = Substitute.For(); + string name = "Your Name"; + + var options = new XUnitLoggerOptions() + { + Filter = FilterTrue, + }; + + var logger = new XUnitLogger(name, outputHelper, options) + { + Clock = StaticClock, + }; + + string expected = string.Join( + Environment.NewLine, + $"[2018-08-19 16:12:16Z] {shortLevel}: Your Name[85]", + " Message|True|False"); + + // Act + logger.Log(logLevel, new EventId(85), "Martin", null, Formatter); + + // Assert + outputHelper.Received(1).WriteLine(expected); + } + + [Fact] + public static void XUnitLogger_Log_Logs_Very_Long_Messages() + { + // Arrange + var outputHelper = Substitute.For(); + string name = "MyName"; + + var options = new XUnitLoggerOptions() + { + Filter = FilterTrue, + }; + + var logger = new XUnitLogger(name, outputHelper, options); + + // Act + logger.Log(LogLevel.Information, 1, "state", null, FormatterLong); + + // Assert + outputHelper.Received(1).WriteLine(Arg.Is((r) => r.Length > 1024)); + } + + [Fact] + public static void XUnitLogger_Log_Logs_Message_If_Scopes_Included_But_There_Are_No_Scopes() + { + // Arrange + var outputHelper = Substitute.For(); + string name = "MyName"; + + var options = new XUnitLoggerOptions() + { + Filter = FilterTrue, + IncludeScopes = true, + }; + + var logger = new XUnitLogger(name, outputHelper, options) + { + Clock = StaticClock, + }; + + string expected = string.Join( + Environment.NewLine, + "[2018-08-19 16:12:16Z] info: MyName[0]", + " Message|False|False"); + + // Act + logger.Log(LogLevel.Information, 0, null, null, Formatter); + + // Assert + outputHelper.Received(1).WriteLine(expected); + } + + [Fact] + public static void XUnitLogger_Log_Logs_Message_If_Scopes_Included_And_There_Are_Scopes() + { + // Arrange + var outputHelper = Substitute.For(); + string name = "MyName"; + + var options = new XUnitLoggerOptions() + { + Filter = FilterTrue, + IncludeScopes = true, + }; + + var logger = new XUnitLogger(name, outputHelper, options) + { + Clock = StaticClock, + }; + + string expected = string.Join( + Environment.NewLine, + "[2018-08-19 16:12:16Z] info: MyName[0]", + " => _", + " => __", + " => ___", + " => {OriginalFormat}: [null]", + " Message|False|False"); + + // Act + using (logger.BeginScope("_")) + { + using (logger.BeginScope("__")) + { + using (logger.BeginScope("___")) + { +#pragma warning disable CA2254 + using (logger.BeginScope(null!)) +#pragma warning restore CA2254 + { + logger.Log(LogLevel.Information, 0, null, null, Formatter); + } + } + } + } + + // Assert + outputHelper.Received(1).WriteLine(expected); + } + + [Fact] + public static void XUnitLogger_Log_Logs_Message_If_Scopes_Included_And_There_Is_Scope_Of_KeyValuePair() + { + // Arrange + var outputHelper = Substitute.For(); + string name = "MyName"; + + var options = new XUnitLoggerOptions() + { + Filter = FilterTrue, + IncludeScopes = true, + }; + + var logger = new XUnitLogger(name, outputHelper, options) + { + Clock = StaticClock, + }; + + string expected = string.Join( + Environment.NewLine, + "[2018-08-19 16:12:16Z] info: MyName[0]", + " => ScopeKey: ScopeValue", + " Message|False|False"); + + // Act + using (logger.BeginScope(new[] + { + new KeyValuePair("ScopeKey", "ScopeValue"), + })) + { + logger.Log(LogLevel.Information, 0, null, null, Formatter); + } + + // Assert + outputHelper.Received(1).WriteLine(expected); + } + + [Fact] + public static void XUnitLogger_Log_Logs_Message_If_Scopes_Included_And_There_Is_Scope_Of_KeyValuePairs() + { + // Arrange + var outputHelper = Substitute.For(); + string name = "MyName"; + + var options = new XUnitLoggerOptions() + { + Filter = FilterTrue, + IncludeScopes = true, + }; + + var logger = new XUnitLogger(name, outputHelper, options) + { + Clock = StaticClock, + }; + + string expected = string.Join( + Environment.NewLine, + "[2018-08-19 16:12:16Z] info: MyName[0]", + " => ScopeKeyOne: ScopeValueOne", + " => ScopeKeyTwo: ScopeValueTwo", + " => ScopeKeyThree: ScopeValueThree", + " Message|False|False"); + + // Act + using (logger.BeginScope(new[] + { + new KeyValuePair("ScopeKeyOne", "ScopeValueOne"), + new KeyValuePair("ScopeKeyTwo", "ScopeValueTwo"), + new KeyValuePair("ScopeKeyThree", "ScopeValueThree"), + })) + { + logger.Log(LogLevel.Information, 0, null, null, Formatter); + } + + // Assert + outputHelper.Received(1).WriteLine(expected); + } + + [Fact] + public static void XUnitLogger_Log_Logs_Message_If_Scopes_Included_And_There_Are_Scopes_Of_KeyValuePairs() + { + // Arrange + var outputHelper = Substitute.For(); + string name = "MyName"; + + var options = new XUnitLoggerOptions() + { + Filter = FilterTrue, + IncludeScopes = true, + }; + + var logger = new XUnitLogger(name, outputHelper, options) + { + Clock = StaticClock, + }; + + string expected = string.Join( + Environment.NewLine, + "[2018-08-19 16:12:16Z] info: MyName[0]", + " => ScopeKeyOne: ScopeValueOne", + " => ScopeKeyTwo: ScopeValueTwo", + " => ScopeKeyThree: ScopeValueThree", + " => ScopeKeyFour: ScopeValueFour", + " => ScopeKeyFive: ScopeValueFive", + " => ScopeKeySix: ScopeValueSix", + " Message|False|False"); + + // Act + using (logger.BeginScope(new[] + { + new KeyValuePair("ScopeKeyOne", "ScopeValueOne"), + new KeyValuePair("ScopeKeyTwo", "ScopeValueTwo"), + new KeyValuePair("ScopeKeyThree", "ScopeValueThree"), + })) + { + using (logger.BeginScope(new[] + { + new KeyValuePair("ScopeKeyFour", "ScopeValueFour"), + new KeyValuePair("ScopeKeyFive", "ScopeValueFive"), + new KeyValuePair("ScopeKeySix", "ScopeValueSix"), + })) + { + logger.Log(LogLevel.Information, 0, null, null, Formatter); + } + } + + // Assert + outputHelper.Received(1).WriteLine(expected); + } + + [Fact] + public static void XUnitLogger_Log_Logs_Message_If_Scopes_Included_And_There_Is_Scope_Of_IEnumerable() + { + // Arrange + var outputHelper = Substitute.For(); + string name = "MyName"; + + var options = new XUnitLoggerOptions() + { + Filter = FilterTrue, + IncludeScopes = true, + }; + + var logger = new XUnitLogger(name, outputHelper, options) + { + Clock = StaticClock, + }; + + string expected = string.Join( + Environment.NewLine, + "[2018-08-19 16:12:16Z] info: MyName[0]", + " => ScopeKeyOne", + " => ScopeKeyTwo", + " => ScopeKeyThree", + " Message|False|False"); + + // Act + using (logger.BeginScope(new[] { "ScopeKeyOne", "ScopeKeyTwo", "ScopeKeyThree" })) + { + logger.Log(LogLevel.Information, 0, null, null, Formatter); + } + + // Assert + outputHelper.Received(1).WriteLine(expected); + } + + private static DateTimeOffset StaticClock() => new(2018, 08, 19, 17, 12, 16, TimeSpan.FromHours(1)); + + private static DiagnosticMessage DiagnosticMessageFactory(string message) => new(message); + + private static bool FilterTrue(string? categoryName, LogLevel level) => true; + + private static bool FilterFalse(string? categoryName, LogLevel level) => false; + + private static string Formatter(TState? state, Exception? exception) + where TState : class + { + return $"Message|{(state == null ? bool.FalseString : bool.TrueString)}|{(exception == null ? bool.FalseString : bool.TrueString)}"; + } + + private static string FormatterEmpty(TState? state, Exception? exception) => string.Empty; + + private static string FormatterLong(TState? state, Exception? exception) => new('a', 2048); + + private static string FormatterNull(TState? state, Exception? exception) => null!; +} diff --git a/tests/Logging.XUnit.Tests/xunit.runner.json b/tests/Shared/xunit.runner.json similarity index 100% rename from tests/Logging.XUnit.Tests/xunit.runner.json rename to tests/Shared/xunit.runner.json From 9638e452ec6ce7af2612c9599e572b281092703c Mon Sep 17 00:00:00 2001 From: martincostello Date: Fri, 20 Dec 2024 08:31:18 +0000 Subject: [PATCH 08/11] Update PackageValidationBaselineVersion The package will be released as 0.5.0, so validate against that. --- src/Logging.XUnit.v3/MartinCostello.Logging.XUnit.v3.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Logging.XUnit.v3/MartinCostello.Logging.XUnit.v3.csproj b/src/Logging.XUnit.v3/MartinCostello.Logging.XUnit.v3.csproj index 24a499d8..8206dc64 100644 --- a/src/Logging.XUnit.v3/MartinCostello.Logging.XUnit.v3.csproj +++ b/src/Logging.XUnit.v3/MartinCostello.Logging.XUnit.v3.csproj @@ -9,7 +9,7 @@ Library MartinCostello.Logging.XUnit.v3 MartinCostello.Logging.XUnit $(Description) From 4d683204d10f85e8f8ed2b41dcc480475943f027 Mon Sep 17 00:00:00 2001 From: martincostello Date: Fri, 20 Dec 2024 08:41:29 +0000 Subject: [PATCH 09/11] Refactor assertions Refactor to make CodeQL happy. --- tests/Shared/XUnitLoggerExtensionsTests.cs | 104 ++++++++++++--------- 1 file changed, 59 insertions(+), 45 deletions(-) diff --git a/tests/Shared/XUnitLoggerExtensionsTests.cs b/tests/Shared/XUnitLoggerExtensionsTests.cs index 7e61a8ad..bee454a7 100644 --- a/tests/Shared/XUnitLoggerExtensionsTests.cs +++ b/tests/Shared/XUnitLoggerExtensionsTests.cs @@ -18,16 +18,20 @@ public static void AddXUnit_TestOutputHelper_For_ILoggerBuilder_Validates_Parame var outputHelper = Substitute.For(); var accessor = Substitute.For(); + ILoggingBuilder nullBuilder = null!; + ITestOutputHelperAccessor nullAccessor = null!; + ITestOutputHelper nullHelper = null!; + // Act and Assert - Assert.Throws("builder", () => (null as ILoggingBuilder)!.AddXUnit()); - Assert.Throws("builder", () => (null as ILoggingBuilder)!.AddXUnit(outputHelper)); - Assert.Throws("builder", () => (null as ILoggingBuilder)!.AddXUnit(outputHelper, ConfigureAction)); - Assert.Throws("builder", () => (null as ILoggingBuilder)!.AddXUnit(accessor)); - Assert.Throws("builder", () => (null as ILoggingBuilder)!.AddXUnit(accessor, ConfigureAction)); - Assert.Throws("accessor", () => builder.AddXUnit((null as ITestOutputHelperAccessor)!)); - Assert.Throws("accessor", () => builder.AddXUnit((null as ITestOutputHelperAccessor)!, ConfigureAction)); - Assert.Throws("outputHelper", () => builder.AddXUnit((null as ITestOutputHelper)!)); - Assert.Throws("outputHelper", () => builder.AddXUnit((null as ITestOutputHelper)!, ConfigureAction)); + Assert.Throws("builder", nullBuilder.AddXUnit); + Assert.Throws("builder", () => nullBuilder.AddXUnit(outputHelper)); + Assert.Throws("builder", () => nullBuilder.AddXUnit(outputHelper, ConfigureAction)); + Assert.Throws("builder", () => nullBuilder.AddXUnit(accessor)); + Assert.Throws("builder", () => nullBuilder.AddXUnit(accessor, ConfigureAction)); + Assert.Throws("accessor", () => builder.AddXUnit(nullAccessor)); + Assert.Throws("accessor", () => builder.AddXUnit(nullAccessor, ConfigureAction)); + Assert.Throws("outputHelper", () => builder.AddXUnit(nullHelper)); + Assert.Throws("outputHelper", () => builder.AddXUnit(nullHelper, ConfigureAction)); Assert.Throws("configure", () => builder.AddXUnit(outputHelper, null!)); Assert.Throws("configure", () => builder.AddXUnit(accessor, null!)); } @@ -40,15 +44,19 @@ public static void AddXUnit_MessageSink_For_ILoggerBuilder_Validates_Parameters( var messageSink = Substitute.For(); var accessor = Substitute.For(); + ILoggingBuilder nullBuilder = null!; + IMessageSinkAccessor nullAccessor = null!; + IMessageSink nullSink = null!; + // Act and Assert - Assert.Throws("builder", () => (null as ILoggingBuilder)!.AddXUnit(messageSink)); - Assert.Throws("builder", () => (null as ILoggingBuilder)!.AddXUnit(messageSink, ConfigureAction)); - Assert.Throws("builder", () => (null as ILoggingBuilder)!.AddXUnit(accessor)); - Assert.Throws("builder", () => (null as ILoggingBuilder)!.AddXUnit(accessor, ConfigureAction)); - Assert.Throws("accessor", () => builder.AddXUnit((null as IMessageSinkAccessor)!)); - Assert.Throws("accessor", () => builder.AddXUnit((null as IMessageSinkAccessor)!, ConfigureAction)); - Assert.Throws("messageSink", () => builder.AddXUnit((null as IMessageSink)!)); - Assert.Throws("messageSink", () => builder.AddXUnit((null as IMessageSink)!, ConfigureAction)); + Assert.Throws("builder", () => nullBuilder.AddXUnit(messageSink)); + Assert.Throws("builder", () => nullBuilder.AddXUnit(messageSink, ConfigureAction)); + Assert.Throws("builder", () => nullBuilder.AddXUnit(accessor)); + Assert.Throws("builder", () => nullBuilder.AddXUnit(accessor, ConfigureAction)); + Assert.Throws("accessor", () => builder.AddXUnit(nullAccessor)); + Assert.Throws("accessor", () => builder.AddXUnit(nullAccessor, ConfigureAction)); + Assert.Throws("messageSink", () => builder.AddXUnit(nullSink)); + Assert.Throws("messageSink", () => builder.AddXUnit(nullSink, ConfigureAction)); Assert.Throws("configure", () => builder.AddXUnit(messageSink, null!)); Assert.Throws("configure", () => builder.AddXUnit(accessor, null!)); } @@ -62,19 +70,22 @@ public static void AddXUnit_TestOutputHelper_For_ILoggerFactory_Validates_Parame var outputHelper = Substitute.For(); var options = new XUnitLoggerOptions(); + ILoggerFactory nullFactory = null!; + ITestOutputHelper nullHelper = null!; + // Act and Assert - Assert.Throws("factory", () => (null as ILoggerFactory)!.AddXUnit(outputHelper)); - Assert.Throws("factory", () => (null as ILoggerFactory)!.AddXUnit(outputHelper, options)); - Assert.Throws("factory", () => (null as ILoggerFactory)!.AddXUnit(outputHelper, ConfigureAction)); - Assert.Throws("factory", () => (null as ILoggerFactory)!.AddXUnit(outputHelper, ConfigureFunction)); - Assert.Throws("factory", () => (null as ILoggerFactory)!.AddXUnit(outputHelper, Filter)); - Assert.Throws("factory", () => (null as ILoggerFactory)!.AddXUnit(outputHelper, logLevel)); - Assert.Throws("outputHelper", () => factory.AddXUnit((null as ITestOutputHelper)!)); - Assert.Throws("outputHelper", () => factory.AddXUnit((null as ITestOutputHelper)!, ConfigureAction)); - Assert.Throws("outputHelper", () => factory.AddXUnit((null as ITestOutputHelper)!, ConfigureFunction)); - Assert.Throws("outputHelper", () => factory.AddXUnit((null as ITestOutputHelper)!, Filter)); - Assert.Throws("outputHelper", () => factory.AddXUnit((null as ITestOutputHelper)!, logLevel)); - Assert.Throws("outputHelper", () => factory.AddXUnit((null as ITestOutputHelper)!, options)); + Assert.Throws("factory", () => nullFactory.AddXUnit(outputHelper)); + Assert.Throws("factory", () => nullFactory.AddXUnit(outputHelper, options)); + Assert.Throws("factory", () => nullFactory.AddXUnit(outputHelper, ConfigureAction)); + Assert.Throws("factory", () => nullFactory.AddXUnit(outputHelper, ConfigureFunction)); + Assert.Throws("factory", () => nullFactory.AddXUnit(outputHelper, Filter)); + Assert.Throws("factory", () => nullFactory.AddXUnit(outputHelper, logLevel)); + Assert.Throws("outputHelper", () => factory.AddXUnit(nullHelper)); + Assert.Throws("outputHelper", () => factory.AddXUnit(nullHelper, ConfigureAction)); + Assert.Throws("outputHelper", () => factory.AddXUnit(nullHelper, ConfigureFunction)); + Assert.Throws("outputHelper", () => factory.AddXUnit(nullHelper, Filter)); + Assert.Throws("outputHelper", () => factory.AddXUnit(nullHelper, logLevel)); + Assert.Throws("outputHelper", () => factory.AddXUnit(nullHelper, options)); Assert.Throws("options", () => factory.AddXUnit(outputHelper, (null as XUnitLoggerOptions)!)); Assert.Throws("configure", () => factory.AddXUnit(outputHelper, (null as Action)!)); Assert.Throws("configure", () => factory.AddXUnit(outputHelper, (null as Func)!)); @@ -90,19 +101,22 @@ public static void AddXUnit_MessageSink_For_ILoggerFactory_Validates_Parameters( var messageSink = Substitute.For(); var options = new XUnitLoggerOptions(); + ILoggerFactory nullFactory = null!; + IMessageSink nullSink = null!; + // Act and Assert - Assert.Throws("factory", () => (null as ILoggerFactory)!.AddXUnit(messageSink)); - Assert.Throws("factory", () => (null as ILoggerFactory)!.AddXUnit(messageSink, options)); - Assert.Throws("factory", () => (null as ILoggerFactory)!.AddXUnit(messageSink, ConfigureAction)); - Assert.Throws("factory", () => (null as ILoggerFactory)!.AddXUnit(messageSink, ConfigureFunction)); - Assert.Throws("factory", () => (null as ILoggerFactory)!.AddXUnit(messageSink, Filter)); - Assert.Throws("factory", () => (null as ILoggerFactory)!.AddXUnit(messageSink, logLevel)); - Assert.Throws("messageSink", () => factory.AddXUnit((null as IMessageSink)!)); - Assert.Throws("messageSink", () => factory.AddXUnit((null as IMessageSink)!, ConfigureAction)); - Assert.Throws("messageSink", () => factory.AddXUnit((null as IMessageSink)!, ConfigureFunction)); - Assert.Throws("messageSink", () => factory.AddXUnit((null as IMessageSink)!, Filter)); - Assert.Throws("messageSink", () => factory.AddXUnit((null as IMessageSink)!, logLevel)); - Assert.Throws("messageSink", () => factory.AddXUnit((null as IMessageSink)!, options)); + Assert.Throws("factory", () => nullFactory.AddXUnit(messageSink)); + Assert.Throws("factory", () => nullFactory.AddXUnit(messageSink, options)); + Assert.Throws("factory", () => nullFactory.AddXUnit(messageSink, ConfigureAction)); + Assert.Throws("factory", () => nullFactory.AddXUnit(messageSink, ConfigureFunction)); + Assert.Throws("factory", () => nullFactory.AddXUnit(messageSink, Filter)); + Assert.Throws("factory", () => nullFactory.AddXUnit(messageSink, logLevel)); + Assert.Throws("messageSink", () => factory.AddXUnit(nullSink)); + Assert.Throws("messageSink", () => factory.AddXUnit(nullSink, ConfigureAction)); + Assert.Throws("messageSink", () => factory.AddXUnit(nullSink, ConfigureFunction)); + Assert.Throws("messageSink", () => factory.AddXUnit(nullSink, Filter)); + Assert.Throws("messageSink", () => factory.AddXUnit(nullSink, logLevel)); + Assert.Throws("messageSink", () => factory.AddXUnit(nullSink, options)); Assert.Throws("options", () => factory.AddXUnit(messageSink, (null as XUnitLoggerOptions)!)); Assert.Throws("configure", () => factory.AddXUnit(messageSink, (null as Action)!)); Assert.Throws("configure", () => factory.AddXUnit(messageSink, (null as Func)!)); @@ -113,12 +127,12 @@ public static void AddXUnit_MessageSink_For_ILoggerFactory_Validates_Parameters( public static void ToLoggerFactory_Validates_Parameters() { // Arrange - ITestOutputHelper? outputHelper = null; - IMessageSink? messageSink = null; + ITestOutputHelper outputHelper = null!; + IMessageSink messageSink = null!; // Act and Assert - Assert.Throws("outputHelper", () => outputHelper!.ToLoggerFactory()); - Assert.Throws("messageSink", () => messageSink!.ToLoggerFactory()); + Assert.Throws("outputHelper", outputHelper.ToLoggerFactory); + Assert.Throws("messageSink", messageSink.ToLoggerFactory); } [Fact] From cd634cae9f1981040d8ee8dac41d92814bdd94d7 Mon Sep 17 00:00:00 2001 From: martincostello Date: Fri, 20 Dec 2024 09:10:16 +0000 Subject: [PATCH 10/11] Update README Update README for new package. --- README.md | 68 ++++++++++++++++++++++++++++++++++++----------- package-readme.md | 12 ++++++--- 2 files changed, 60 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 45654e04..91419340 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,49 @@ # xunit Logging -[![NuGet](https://img.shields.io/nuget/v/MartinCostello.Logging.XUnit?logo=nuget&label=Latest&color=blue)](https://www.nuget.org/packages/MartinCostello.Logging.XUnit "Download MartinCostello.Logging.XUnit from NuGet") -[![NuGet Downloads](https://img.shields.io/nuget/dt/MartinCostello.Logging.XUnit?logo=nuget&label=Downloads&color=blue)](https://www.nuget.org/packages/MartinCostello.Logging.XUnit "Download MartinCostello.Logging.XUnit from NuGet") +[![Build status][build-badge]][build-status] +[![codecov][coverage-badge]][coverage-report] +[![OpenSSF Scorecard][scorecard-badge]][scorecard-report] -[![Build status](https://github.com/martincostello/xunit-logging/workflows/build/badge.svg?branch=main&event=push)](https://github.com/martincostello/xunit-logging/actions?query=workflow%3Abuild+branch%3Amain+event%3Apush) -[![codecov](https://codecov.io/gh/martincostello/xunit-logging/branch/main/graph/badge.svg)](https://codecov.io/gh/martincostello/xunit-logging) -[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/martincostello/xunit-logging/badge)](https://securityscorecards.dev/viewer/?uri=github.com/martincostello/xunit-logging) +| **xunit version** | **Package** | **NuGet Version** | +|:------------------|:------------|:------------------| +| xunit v2 | [MartinCostello.Logging.XUnit][package-download-v2] | [![NuGet][package-badge-version-v2]][package-download-v2] [![NuGet Downloads][package-badge-downloads-v2]][package-download-v2] | +| xunit v3 | [MartinCostello.Logging.XUnit.v3][package-download-v3] | [![NuGet][package-badge-version-v3]][package-download-v3] [![NuGet Downloads][package-badge-downloads-v3]][package-download-v3] | ## Introduction -`MartinCostello.Logging.XUnit` provides extensions to hook into the `ILogger` infrastructure to output logs from your xunit tests to the test output. +`MartinCostello.Logging.XUnit` and `MartinCostello.Logging.XUnit.v3` provide extensions to hook into +the `ILogger` infrastructure to output logs from your xunit tests to the test output. + +Projects using xunit v2 should use the `MartinCostello.Logging.XUnit` package, while projects using +xunit v3 should use the `MartinCostello.Logging.XUnit.v3` package. > [!NOTE] -> This library is designed for the Microsoft logging implementation of `ILoggerFactory`. For other logging implementations, such as [Serilog](https://serilog.net/), consider using packages such as [Serilog.Sinks.XUnit](https://github.com/trbenning/serilog-sinks-xunit) instead. +> This library is designed for the Microsoft logging implementation of `ILoggerFactory`. +> For other logging implementations, such as [Serilog][serilog], consider using packages such as [Serilog.Sinks.XUnit][serilog-sinks-xunit] instead. ### Installation -To install the library from [NuGet](https://www.nuget.org/packages/MartinCostello.Logging.XUnit/ "MartinCostello.Logging.XUnit on NuGet.org") using the .NET SDK run: +To install the library from NuGet using the .NET SDK run one of the following commands. + +#### For xunit v2 ```console dotnet add package MartinCostello.Logging.XUnit ``` +#### For xunit v3 + +```console +dotnet add package MartinCostello.Logging.XUnit.v3 +``` + ### Usage ```csharp using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Xunit; -using Xunit.Abstractions; +using Xunit.Abstractions; // For xunit v2 - not required for xunit v3 namespace MyApp.Calculator; @@ -68,29 +83,50 @@ public sealed class Calculator(ILogger logger) See below for links to more examples: -- [Unit tests](https://github.com/martincostello/xunit-logging/blob/main/tests/Logging.XUnit.Tests/Examples.cs "Unit test examples") -- [Integration tests for an ASP.NET Core HTTP application](https://github.com/martincostello/xunit-logging/blob/main/tests/Logging.XUnit.Tests/Integration/HttpApplicationTests.cs "Integration test examples") +- [Unit tests][example-unit-tests] +- [Integration tests for an ASP.NET Core HTTP application][example-integration-tests] ## Feedback -Any feedback or issues can be added to the issues for this project in [GitHub](https://github.com/martincostello/xunit-logging/issues "Issues for this project on GitHub.com"). +Any feedback or issues can be added to the issues for this project in [GitHub][issues]. ## Repository -The repository is hosted in [GitHub](https://github.com/martincostello/xunit-logging "This project on GitHub.com"): +The repository is hosted in [GitHub][repo]: ## License -This project is licensed under the [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt "The Apache 2.0 license") license. +This project is licensed under the [Apache 2.0][license] license. ## Building and Testing -Compiling the library yourself requires Git and the [.NET SDK](https://www.microsoft.com/net/download/core "Download the .NET SDK") to be installed (version `9.0.100` or later). +Compiling the solution yourself requires Git and the [.NET SDK][dotnet-sdk] to be installed (version `9.0.100` or later). -To build and test the library locally from a terminal/command-line, run one of the following set of commands: +To build and test the solution locally from a terminal/command-line, run the following set of commands: ```powershell git clone https://github.com/martincostello/xunit-logging.git cd xunit-logging ./build.ps1 ``` + +[build-badge]: https://github.com/martincostello/xunit-logging/actions/workflows/build.yml/badge.svg?branch=main&event=push +[build-status]: https://github.com/martincostello/xunit-logging/actions?query=workflow%3Abuild+branch%3Amain+event%3Apush "Continuous Integration for this project" +[coverage-badge]: https://codecov.io/gh/martincostello/xunit-logging/branch/main/graph/badge.svg +[coverage-report]: https://codecov.io/gh/martincostello/xunit-logging "Code coverage report for this project" +[scorecard-badge]: https://api.securityscorecards.dev/projects/github.com/martincostello/xunit-logging/badge +[scorecard-report]: https://securityscorecards.dev/viewer/?uri=github.com/martincostello/xunit-logging "OpenSSF Scorecard for this project" +[dotnet-sdk]: https://dot.net/download "Download the .NET SDK" +[example-integration-tests]: https://github.com/martincostello/xunit-logging/blob/main/tests/Shared/Integration/HttpApplicationTests.cs "Integration test examples" +[example-unit-tests]: https://github.com/martincostello/xunit-logging/blob/main/tests/Shared/Examples.cs "Unit test examples" +[issues]: https://github.com/martincostello/xunit-logging/issues "Issues for this project on GitHub.com" +[license]: https://www.apache.org/licenses/LICENSE-2.0.txt "The Apache 2.0 license" +[package-badge-downloads-v2]: https://img.shields.io/nuget/dt/MartinCostello.Logging.XUnit?logo=nuget&label=Downloads&color=blue +[package-badge-downloads-v3]: https://img.shields.io/nuget/dt/MartinCostello.Logging.XUnit.v3?logo=nuget&label=Downloads&color=blue +[package-badge-version-v2]: https://img.shields.io/nuget/v/MartinCostello.Logging.XUnit?logo=nuget&label=Latest&color=blue +[package-badge-version-v3]: https://img.shields.io/nuget/v/MartinCostello.Logging.XUnit.v3?logo=nuget&label=Latest&color=blue +[package-download-v2]: https://www.nuget.org/packages/MartinCostello.Logging.XUnit "Download MartinCostello.Logging.XUnit from NuGet" +[package-download-v3]: https://www.nuget.org/packages/MartinCostello.Logging.XUnit.v3 "Download MartinCostello.Logging.XUnit.v3 from NuGet" +[repo]: https://github.com/martincostello/xunit-loggingE "This project on GitHub.com" +[serilog]: https://serilog.net/ "Serilog website" +[serilog-sinks-xunit]: https://github.com/trbenning/serilog-sinks-xunit "Serilog.Sinks.XUnit on GitHub" diff --git a/package-readme.md b/package-readme.md index 30d21d77..04718da3 100644 --- a/package-readme.md +++ b/package-readme.md @@ -2,7 +2,8 @@ ## Introduction -`MartinCostello.Logging.XUnit` provides extensions to hook into the `ILogger` infrastructure to output logs from your xunit tests to the test output. +`MartinCostello.Logging.XUnit` and `MartinCostello.Logging.XUnit.v3` provide extensions to hook +into the `ILogger` infrastructure to output logs from your xunit tests to the test output. ### Usage @@ -10,7 +11,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Xunit; -using Xunit.Abstractions; +using Xunit.Abstractions; // For xunit v2 - not required for xunit v3 namespace MyApp.Calculator; @@ -50,8 +51,11 @@ public sealed class Calculator(ILogger logger) ## Feedback -Any feedback or issues can be added to the issues for this project in [GitHub](https://github.com/martincostello/xunit-logging/issues "Issues for this project on GitHub.com"). +Any feedback or issues can be added to the issues for this project in [GitHub][issues]. ## License -This project is licensed under the [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt "The Apache 2.0 license") license. +This project is licensed under the [Apache 2.0][license] license. + +[issues]: https://github.com/martincostello/xunit-logging/issues "Issues for this package on GitHub.com" +[license]: https://www.apache.org/licenses/LICENSE-2.0.txt "The Apache 2.0 license" From 3b77e4d5e2705936754cb935ba10e2d4390d1479 Mon Sep 17 00:00:00 2001 From: martincostello Date: Fri, 20 Dec 2024 09:17:18 +0000 Subject: [PATCH 11/11] Add package icon Add a package icon (xunit's own logo). --- Directory.Build.props | 2 +- package-icon.png | Bin 0 -> 1001 bytes 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 package-icon.png diff --git a/Directory.Build.props b/Directory.Build.props index ff9231db..bc2d5d70 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -28,7 +28,7 @@ en-US direct enable - + package-icon.png Apache-2.0 https://github.com/martincostello/xunit-logging package-readme.md diff --git a/package-icon.png b/package-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..fcf87f17c38dfe00aec2d50d3da112ef3930dfc6 GIT binary patch literal 1001 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H3?#oinD`S&%?$7faRt&8g8%>j>*?u9OG}H2 zikg_1yn6M@#>PfPMMXhD;memVpFVwh`0(NF+qW-YzWnapJ4Z*y3l}bY`0(NS_3QWU z-Fx%q&GYBaA3uIz`*p%)5S5Q;?|uTH~n5a2)JH6_CSCsQIEN?#qvzzkN^F< zgLLkCJ=ozr_pI>qttspO-dUM_-1?>X_To8Q=3996AN*_M^{y3WJzy>^_G?kYk3|Y! z_BQ;6kg2TfX^R+s>}yuquPb3t|6rfE%RU_-dBje?VE>v0M!~FizK1r>*98gf%g@~~ zX@6Q(=!4Hp-yb*K_|EjMy75~vYjo*aMeB5;J(bvDhVh;a5-xa%d`lozw z?14Wo+chrKUG_Pj!ua9*GT+5qTjpyr)TKM-c1y>a&18CFK3zFFW^K*p-#Lt*-se_s z{k`ZxnAn%kb@pGTbKhZSU0DD6-}xqGX4V_q7f<1HWc_80`Iolsic8_<`pd1Fv-}A64ta5#d8?Yvrq&1lT;5+PFP0!L9xz`#VZL~Qy@Zr7Ns~yvYv>(`M zSIlzx_k{DuiiY3&{^lG!^=}{dnucF{7=CPcP{|kLZ}sQ(&!01Y?7MZuZ_k3;_JPNv z1XeKo+LQ3-uYul&$b&n@|9m+=Q(vy{QPYk0sT?5(YT`av{d>QkX};JIyO^oE9DlV~ z5*R;M9=x9OjkPA^!zHmT|GYPz=T6IYe0{!wpKHZ=?oa>Xxqe*VD=E#$aoC`My=}pn ze~$x$jy~J)(c8y?i(}iD{gwMpX$#DGAU{8`LUi5Kn3jLxg@4nZ{I%|LU$mTg;(L#4 z=2NQrp6>1Lyq|S#|D39|&kG$EN=^767+A;DKSQs_>+=F5POq#PKSlf1u_grikwYMC#>ibyc)2|%H!zb7>hG) zk87$Ie4N7|B|5=RF6-&WIJul>7xU!uo`0;1D|jHvA6N9yR6efcQK)`g+2d0CxQZuJ p{o|^hZjFztd3H5Fu5SM2|N868{$0+tt~Cc`SWj0!mvv4FO#qeg+06g| literal 0 HcmV?d00001