generated from arcus-azure/arcus.github.template
-
Notifications
You must be signed in to change notification settings - Fork 6
feat(logging): add logging support for TUnit test framework #369
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
stijnmoreels
wants to merge
9
commits into
arcus-azure:main
Choose a base branch
from
stijnmoreels:feature/add-tunit-test-logging
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
ff6f1de
feat(logging): add logging support for TUnit test framework
stijnmoreels 18be50c
chore: remove unnecessary namespace
stijnmoreels 28fd22c
Merge branch 'main' into feature/add-tunit-test-logging
stijnmoreels 2eaa54d
fix(merge): finalize merge with 'main'
stijnmoreels 6a14363
Update Arcus.Testing.Logging.TUnit.csproj
stijnmoreels 4c28aba
Merge branch 'main' into feature/add-tunit-test-logging
stijnmoreels 7084b4f
chore(tunit): include scope + category names in tunit logging
stijnmoreels 2f6dd11
Merge branch 'feature/add-tunit-test-logging' of https://github.com/s…
stijnmoreels f7f9593
Merge branch 'main' into feature/add-tunit-test-logging
stijnmoreels File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
41 changes: 41 additions & 0 deletions
41
src/Arcus.Testing.Logging.TUnit/Arcus.Testing.Logging.TUnit.csproj
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
|
|
||
| <PropertyGroup> | ||
| <TargetFramework>net10.0</TargetFramework> | ||
| <RootNamespace>Arcus.Testing</RootNamespace> | ||
| <Authors>Arcus</Authors> | ||
| <Company>Arcus</Company> | ||
| <Description>Provides logging capabilities during testing within the TUnit framework</Description> | ||
| <Copyright>Copyright (c) Arcus</Copyright> | ||
| <PackageProjectUrl>https://github.com/arcus-azure/arcus.testing</PackageProjectUrl> | ||
| <RepositoryUrl>https://github.com/arcus-azure/arcus.testing</RepositoryUrl> | ||
| <PackageLicenseFile>LICENSE</PackageLicenseFile> | ||
| <PackageIcon>icon.png</PackageIcon> | ||
| <PackageReadmeFile>README.md</PackageReadmeFile> | ||
| <RepositoryType>Git</RepositoryType> | ||
| <PackageTags>Azure;Testing</PackageTags> | ||
| <PackageId>Arcus.Testing.Logging.TUnit</PackageId> | ||
| <GeneratePackageOnBuild>true</GeneratePackageOnBuild> | ||
| <GenerateDocumentationFile>true</GenerateDocumentationFile> | ||
| <TreatWarningsAsErrors>true</TreatWarningsAsErrors> | ||
| <WarningsNotAsErrors>$(WarningsNotAsErrors);NU1901;NU1902;NU1903;NU1904</WarningsNotAsErrors> | ||
| <NoWarn>S1133</NoWarn> | ||
| <AnalysisMode>All</AnalysisMode> | ||
| </PropertyGroup> | ||
|
|
||
| <ItemGroup> | ||
| <Compile Include="..\Arcus.Testing.Logging.Xunit.v3\LogMessageBuilder.cs" Link="LogMessageBuilder.cs" /> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <None Include="..\..\README.md" Pack="true" PackagePath="\" /> | ||
| <None Include="..\..\LICENSE" Pack="true" PackagePath="\" /> | ||
| <None Include="..\..\docs\static\img\icon.png" Pack="true" PackagePath="\" /> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="TUnit.Core" Version="[1.*,2.0.0)" /> | ||
| <PackageReference Include="Microsoft.Extensions.Logging" Version="[10.*,11.0.0)" /> | ||
| </ItemGroup> | ||
|
|
||
| </Project> |
72 changes: 72 additions & 0 deletions
72
src/Arcus.Testing.Logging.TUnit/Extensions/ILoggerBuilderExtensions.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| using System; | ||
| using Arcus.Testing; | ||
| using ITUnitLogger = TUnit.Core.Logging.ILogger; | ||
|
|
||
| // ReSharper disable once CheckNamespace | ||
| namespace Microsoft.Extensions.Logging | ||
| { | ||
| /// <summary> | ||
| /// Extensions on the <see cref="ILoggingBuilder"/> related to logging. | ||
| /// </summary> | ||
| // ReSharper disable once InconsistentNaming | ||
| public static class ILoggerBuilderExtensions | ||
| { | ||
| /// <summary> | ||
| /// Adds the logging messages from the given TUnit <paramref name="outputWriter"/> as a provider to the <paramref name="builder"/>. | ||
| /// </summary> | ||
| /// <param name="builder">The logging builder to add the NUnit logging test messages to.</param> | ||
| /// <param name="outputWriter">The TUnit test writer to write custom test output.</param> | ||
| /// <exception cref="ArgumentNullException">Thrown when the <paramref name="builder"/> or the <paramref name="outputWriter"/> is <c>null</c>.</exception> | ||
| public static ILoggingBuilder AddTUnitTestLogging(this ILoggingBuilder builder, ITUnitLogger outputWriter) | ||
| { | ||
| ArgumentNullException.ThrowIfNull(builder); | ||
| ArgumentNullException.ThrowIfNull(outputWriter); | ||
|
|
||
| #pragma warning disable CA2000 // Responsibility of disposing the created object is transferred to the caller | ||
| var provider = new TUnitLoggerProvider(outputWriter); | ||
| #pragma warning restore CA2000 | ||
| return builder.AddProvider(provider); | ||
| } | ||
|
|
||
| [ProviderAlias("TUnit")] | ||
| private sealed class TUnitLoggerProvider : ILoggerProvider, ISupportExternalScope | ||
| { | ||
| private readonly ITUnitLogger _testLogger; | ||
| private IExternalScopeProvider _scopeProvider; | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="TUnitLoggerProvider"/> class. | ||
| /// </summary> | ||
| internal TUnitLoggerProvider(ITUnitLogger testLogger) | ||
| { | ||
| _testLogger = testLogger; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Creates a new <see cref="ILogger" /> instance. | ||
| /// </summary> | ||
| /// <param name="categoryName">The category name for messages produced by the logger.</param> | ||
| /// <returns>The instance of <see cref="ILogger" /> that was created.</returns> | ||
| public ILogger CreateLogger(string categoryName) | ||
| { | ||
| return new TUnitTestLogger(_testLogger, _scopeProvider, categoryName); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Sets external scope information source for logger provider. | ||
| /// </summary> | ||
| /// <param name="scopeProvider">The provider of scope data.</param> | ||
| public void SetScopeProvider(IExternalScopeProvider scopeProvider) | ||
| { | ||
| _scopeProvider = scopeProvider; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. | ||
| /// </summary> | ||
| public void Dispose() | ||
| { | ||
| } | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,120 @@ | ||
| using System; | ||
| using Microsoft.Extensions.Logging; | ||
| using ITUnitLogger = TUnit.Core.Logging.ILogger; | ||
| using TUnitLogLevel = TUnit.Core.Logging.LogLevel; | ||
|
|
||
| namespace Arcus.Testing | ||
| { | ||
| /// <summary> | ||
| /// <see cref="ILogger"/> representation of a TUnit logger. | ||
| /// </summary> | ||
| public class TUnitTestLogger : ILogger | ||
| { | ||
| private readonly ITUnitLogger _outputWriter; | ||
| private readonly IExternalScopeProvider _scopeProvider; | ||
| private readonly string _categoryName; | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="TUnitTestLogger"/> class. | ||
| /// </summary> | ||
| /// <param name="outputWriter">The TUnit test writer to write custom test output.</param> | ||
| /// <exception cref="ArgumentNullException">Thrown when the <paramref name="outputWriter"/> is <c>null</c>.</exception> | ||
| public TUnitTestLogger(ITUnitLogger outputWriter) | ||
| : this(outputWriter, scopeProvider: null, categoryName: null) | ||
| { | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="TUnitTestLogger"/> class. | ||
| /// </summary> | ||
| /// <param name="outputWriter">The TUnit test writer to write custom test output.</param> | ||
| /// <param name="scopeProvider">The instance to provide logging scopes.</param> | ||
| /// <param name="categoryName">The category name for messages produced by the logger.</param> | ||
| /// <exception cref="ArgumentNullException">Thrown when the <paramref name="outputWriter"/> is <c>null</c>.</exception> | ||
| internal TUnitTestLogger(ITUnitLogger outputWriter, IExternalScopeProvider scopeProvider, string categoryName) | ||
| { | ||
| ArgumentNullException.ThrowIfNull(outputWriter); | ||
| _outputWriter = outputWriter; | ||
| _scopeProvider = scopeProvider; | ||
| _categoryName = categoryName; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Writes a log entry. | ||
| /// </summary> | ||
| /// <param name="logLevel">Entry will be written on this level.</param> | ||
| /// <param name="eventId">Id of the event.</param> | ||
| /// <param name="state">The entry to be written. Can be also an object.</param> | ||
| /// <param name="exception">The exception related to this entry.</param> | ||
| /// <param name="formatter">Function to create a <see cref="String" /> message of the <paramref name="state" /> and <paramref name="exception" />.</param> | ||
| /// <typeparam name="TState">The type of the object to be written.</typeparam> | ||
| public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter) | ||
| { | ||
| TUnitLogLevel level = ConvertToTUnitLogLevel(logLevel); | ||
|
|
||
| ArgumentNullException.ThrowIfNull(formatter); | ||
| string message = formatter(state, exception); | ||
|
|
||
| var builder = new LogMessageBuilder(logLevel); | ||
| builder.AddCategory(_categoryName) | ||
| .AddUserMessage(message) | ||
| .AddException(exception); | ||
|
|
||
| _scopeProvider?.ForEachScope((st, lb) => lb.AddScope(st), builder); | ||
|
|
||
| string result = builder.ToString(); | ||
| _outputWriter.Log(level, result, exception, formatter: (st, _) => st); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Checks if the given <paramref name="logLevel" /> is enabled. | ||
| /// </summary> | ||
| /// <param name="logLevel">Level to be checked.</param> | ||
| /// <returns><c>true</c> if enabled.</returns> | ||
| public bool IsEnabled(LogLevel logLevel) | ||
| { | ||
| TUnitLogLevel level = ConvertToTUnitLogLevel(logLevel); | ||
| return _outputWriter.IsEnabled(level); | ||
| } | ||
|
|
||
| private static TUnitLogLevel ConvertToTUnitLogLevel(LogLevel logLevel) | ||
| { | ||
| return logLevel switch | ||
| { | ||
| LogLevel.Critical => TUnitLogLevel.Critical, | ||
| LogLevel.Error => TUnitLogLevel.Error, | ||
| LogLevel.Warning => TUnitLogLevel.Warning, | ||
| LogLevel.Information => TUnitLogLevel.Information, | ||
| LogLevel.Debug => TUnitLogLevel.Debug, | ||
| LogLevel.Trace => TUnitLogLevel.Trace, | ||
| _ => TUnitLogLevel.None | ||
| }; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Begins a logical operation scope. | ||
| /// </summary> | ||
| /// <param name="state">The identifier for the scope.</param> | ||
| /// <typeparam name="TState">The type of the state to begin scope for.</typeparam> | ||
| /// <returns>An <see cref="IDisposable" /> that ends the logical operation scope on dispose.</returns> | ||
| public IDisposable BeginScope<TState>(TState state) where TState : notnull | ||
| { | ||
| return _scopeProvider?.Push(state); | ||
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// <see cref="ILogger"/> representation of a TUnit logger. | ||
| /// </summary> | ||
| /// <typeparam name="TCategoryName">The type whose name is used for the logger category name.</typeparam> | ||
| public class TUnitTestLogger<TCategoryName> : TUnitTestLogger, ILogger<TCategoryName> | ||
| { | ||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="TUnitTestLogger"/> class. | ||
| /// </summary> | ||
| public TUnitTestLogger(ITUnitLogger outputWriter) | ||
| : base(outputWriter, scopeProvider: null, categoryName: typeof(TCategoryName).FullName) | ||
| { | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
45 changes: 45 additions & 0 deletions
45
src/Arcus.Testing.Tests.Unit/Logging/Fixture/InMemoryTUnitTestLogger.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| using System; | ||
| using System.Collections.ObjectModel; | ||
| using System.Threading.Tasks; | ||
| using TUnit.Core.Logging; | ||
| using Xunit; | ||
|
|
||
| namespace Arcus.Testing.Tests.Unit.Logging.Fixture | ||
| { | ||
| public class MockTUnitTestLogger : ILogger | ||
| { | ||
| internal Collection<(LogLevel level, string message)> Logs { get; } = []; | ||
|
|
||
| public ValueTask LogAsync<TState>(LogLevel logLevel, TState state, Exception exception, Func<TState, Exception, string> formatter) | ||
| { | ||
| throw new NotImplementedException(); | ||
| } | ||
|
|
||
| public void Log<TState>(LogLevel logLevel, TState state, Exception exception, Func<TState, Exception, string> formatter) | ||
| { | ||
| Logs.Add((logLevel, formatter(state, exception))); | ||
| } | ||
|
|
||
| public bool IsEnabled(LogLevel logLevel) | ||
| { | ||
| throw new NotImplementedException(); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Verifies that there was a <paramref name="message"/> written for the given <paramref name="level"/> to this logger. | ||
| /// </summary> | ||
| public void VerifyWritten(Microsoft.Extensions.Logging.LogLevel level, string message, Exception exception = null, string state = null) | ||
| { | ||
| Assert.Contains(Logs, log => | ||
| { | ||
| bool hasException = exception is null || log.message.Contains(exception.Message); | ||
| bool hasState = state is null || log.message.Contains(state); | ||
|
|
||
| return (int) level == (int) log.level | ||
| && log.message.Contains(message) | ||
| && hasState | ||
| && hasException; | ||
| }); | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.