Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .nuget/Codebelt.Extensions.Xunit.App/PackageReleaseNotes.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
Version 9.1.0
Version 9.1.1
Availability: .NET 9 and .NET 8

# ALM
- CHANGED Dependencies to latest and greatest with respect to TFMs

Version 9.1.0
Availability: .NET 9 and .NET 8

# ALM
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
Version 9.1.0
Version 9.1.1
Availability: .NET 9 and .NET 8

# ALM
- CHANGED Dependencies to latest and greatest with respect to TFMs

# Bug Fixes
- FIXED AspNetCoreHostTest class in the Codebelt.Extensions.Xunit.Hosting.AspNetCore namespace to have same behavior as prior to 9.1.0 release (hereby being backward compatible as originally intended)

Version 9.1.0
Availability: .NET 9 and .NET 8

# ALM
Expand Down
18 changes: 17 additions & 1 deletion .nuget/Codebelt.Extensions.Xunit.Hosting/PackageReleaseNotes.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,20 @@
Version 9.1.0
Version 9.1.1
Availability: .NET 9, .NET 8 and .NET Standard 2.0

# ALM
- CHANGED Dependencies to latest and greatest with respect to TFMs

# Bug Fixes
- FIXED HostTest class in the Codebelt.Extensions.Xunit.Hosting namespace to have same behavior as prior to 9.1.0 release (hereby being backward compatible as originally intended)
- FIXED LoggerExtensions class in the Codebelt.Extensions.Xunit.Hosting namespace to have same behavior as prior to 9.1.0 release (hereby being backward compatible as originally intended)

# Improvements
- CHANGED HostFixture class in the Codebelt.Extensions.Xunit.Hosting namespace so that IHostEnvironment.ApplicationName is aligned with the equivalent logic found in AspNetCoreHostFixture class (e.g., the assembly name of the calling Test type is used as the default value for the ApplicationName property)

# New Features
- EXTENDED LoggerExtensions class in the Codebelt.Extensions.Xunit.Hosting namespace with one new extension method for the ILogger interface: An overload of GetTestStore that takes an optional string argument (categoryName)

Version 9.1.0
Availability: .NET 9, .NET 8 and .NET Standard 2.0

# ALM
Expand Down
8 changes: 7 additions & 1 deletion .nuget/Codebelt.Extensions.Xunit/PackageReleaseNotes.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
Version 9.1.0
Version 9.1.1
Availability: .NET 9, .NET 8 and .NET Standard 2.0

# ALM
- CHANGED Dependencies to latest and greatest with respect to TFMs

Version 9.1.0
Availability: .NET 9, .NET 8 and .NET Standard 2.0

# ALM
Expand Down
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,22 @@ For more details, please refer to `PackageReleaseNotes.txt` on a per assembly ba
> [!NOTE]
> Changelog entries prior to version 8.4.0 was migrated from previous versions of Cuemon.Extensions.Xunit, Cuemon.Extensions.Xunit.Hosting, and Cuemon.Extensions.Xunit.Hosting.AspNetCore.

## [9.1.1] - 2025-04-01

### Added

- LoggerExtensions class in the Codebelt.Extensions.Xunit.Hosting namespace received one new extension method for the ILogger interface: An overload of GetTestStore that takes an optional string argument (categoryName)

### Changed

- HostFixture class in the Codebelt.Extensions.Xunit.Hosting namespace so that IHostEnvironment.ApplicationName is aligned with the equivalent logic found in AspNetCoreHostFixture class (e.g., the assembly name of the calling Test type is used as the default value for the ApplicationName property)

### Fixed

- HostTest class in the Codebelt.Extensions.Xunit.Hosting namespace to have same behavior as prior to `9.1.0` release (hereby being backward compatible as originally intended)
- LoggerExtensions class in the Codebelt.Extensions.Xunit.Hosting namespace to have same behavior as prior to `9.1.0` release (hereby being backward compatible as originally intended)
- AspNetCoreHostTest class in the Codebelt.Extensions.Xunit.Hosting.AspNetCore namespace to have same behavior as prior to 9.1.0 release (hereby being backward compatible as originally intended)

## [9.1.0] - 2025-03-31

This is a service update that primarily focuses on package dependencies including DIP improvements and a new blocking implementation of the AspNetCoreHostFixture.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,34 @@ public abstract class AspNetCoreHostTest<T> : HostTest<T> where T : class, IAspN
/// <param name="hostFixture">An implementation of the <see cref="IAspNetCoreHostFixture"/> interface.</param>
/// <param name="output">An implementation of the <see cref="ITestOutputHelper"/> interface.</param>
/// <param name="callerType">The <see cref="Type"/> of caller that ends up invoking this instance.</param>
protected AspNetCoreHostTest(T hostFixture, ITestOutputHelper output = null, Type callerType = null) : base(hostFixture, output, callerType)
protected AspNetCoreHostTest(T hostFixture, ITestOutputHelper output = null, Type callerType = null) : this(false, hostFixture, output, callerType)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="AspNetCoreHostTest{T}"/> class.
/// </summary>
/// <param name="skipHostFixtureInitialization">A value indicating whether to skip the host fixture initialization.</param>
/// <param name="hostFixture">An implementation of the <see cref="IAspNetCoreHostFixture"/> interface.</param>
/// <param name="output">An implementation of the <see cref="ITestOutputHelper"/> interface.</param>
/// <param name="callerType">The <see cref="Type"/> of caller that ends up invoking this instance.</param>
protected AspNetCoreHostTest(bool skipHostFixtureInitialization, T hostFixture, ITestOutputHelper output = null, Type callerType = null) : base(skipHostFixtureInitialization, hostFixture, output, callerType)
{
if (skipHostFixtureInitialization) { return; }
if (!hostFixture.HasValidState())
{
hostFixture.ConfigureHostCallback = ConfigureHost;
hostFixture.ConfigureCallback = Configure;
hostFixture.ConfigureServicesCallback = ConfigureServices;
hostFixture.ConfigureApplicationCallback = ConfigureApplication;
hostFixture.ConfigureHost(this);
}
Host = hostFixture.Host;
ServiceProvider = hostFixture.Host.Services;
Application = hostFixture.Application;
Configure(hostFixture.Configuration, hostFixture.HostingEnvironment);
}

/// <summary>
/// Gets the <see cref="IApplicationBuilder"/> initialized by the <see cref="IHost"/>.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ internal sealed class WebHostTest : AspNetCoreHostTest<IAspNetCoreHostFixture>,
private readonly Action<IHostBuilder> _hostConfigurator;
private HostBuilderContext _hostBuilderContext;

internal WebHostTest(Action<IServiceCollection> serviceConfigurator, Action<IApplicationBuilder> pipelineConfigurator, Action<IHostBuilder> hostConfigurator, IAspNetCoreHostFixture hostFixture) : base(hostFixture, callerType: pipelineConfigurator?.Target?.GetType() ?? serviceConfigurator?.Target?.GetType() ?? hostConfigurator?.Target?.GetType())
internal WebHostTest(Action<IServiceCollection> serviceConfigurator, Action<IApplicationBuilder> pipelineConfigurator, Action<IHostBuilder> hostConfigurator, IAspNetCoreHostFixture hostFixture) : base(true, hostFixture, callerType: pipelineConfigurator?.Target?.GetType() ?? serviceConfigurator?.Target?.GetType() ?? hostConfigurator?.Target?.GetType())
{
_serviceConfigurator = serviceConfigurator;
_pipelineConfigurator = pipelineConfigurator;
_hostConfigurator = hostConfigurator;
InitializeHostFixture(hostFixture);
}

internal WebHostTest(Action<HostBuilderContext, IServiceCollection> serviceConfigurator, Action<HostBuilderContext, IApplicationBuilder> pipelineConfigurator, Action<IHostBuilder> hostConfigurator, IAspNetCoreHostFixture hostFixture) : base(hostFixture, callerType: pipelineConfigurator?.Target?.GetType() ?? serviceConfigurator?.Target?.GetType() ?? hostConfigurator?.Target?.GetType())
internal WebHostTest(Action<HostBuilderContext, IServiceCollection> serviceConfigurator, Action<HostBuilderContext, IApplicationBuilder> pipelineConfigurator, Action<IHostBuilder> hostConfigurator, IAspNetCoreHostFixture hostFixture) : base(true, hostFixture, callerType: pipelineConfigurator?.Target?.GetType() ?? serviceConfigurator?.Target?.GetType() ?? hostConfigurator?.Target?.GetType())
{
_serviceConfiguratorWithContext = serviceConfigurator;
_pipelineConfiguratorWithContext = pipelineConfigurator;
Expand Down
4 changes: 2 additions & 2 deletions src/Codebelt.Extensions.Xunit.Hosting/GenericHostTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ internal sealed class GenericHostTest : HostTest<IHostFixture>, IGenericHostTest
private readonly Action<IHostBuilder> _hostConfigurator;
private HostBuilderContext _hostBuilderContext;

internal GenericHostTest(Action<IServiceCollection> serviceConfigurator, Action<IHostBuilder> hostConfigurator, IHostFixture hostFixture) : base(hostFixture, callerType: serviceConfigurator?.Target?.GetType() ?? hostConfigurator?.Target?.GetType())
internal GenericHostTest(Action<IServiceCollection> serviceConfigurator, Action<IHostBuilder> hostConfigurator, IHostFixture hostFixture) : base(true, hostFixture, callerType: serviceConfigurator?.Target?.GetType() ?? hostConfigurator?.Target?.GetType())
{
_serviceConfigurator = serviceConfigurator;
_hostConfigurator = hostConfigurator;
InitializeHostFixture(hostFixture);
}

internal GenericHostTest(Action<HostBuilderContext, IServiceCollection> serviceConfigurator, Action<IHostBuilder> hostConfigurator, IHostFixture hostFixture) : base(hostFixture, callerType: serviceConfigurator?.Target?.GetType() ?? hostConfigurator?.Target?.GetType())
internal GenericHostTest(Action<HostBuilderContext, IServiceCollection> serviceConfigurator, Action<IHostBuilder> hostConfigurator, IHostFixture hostFixture) : base(true, hostFixture, callerType: serviceConfigurator?.Target?.GetType() ?? hostConfigurator?.Target?.GetType())
{
_serviceConfiguratorWithContext = serviceConfigurator;
_hostConfigurator = hostConfigurator;
Expand Down
18 changes: 13 additions & 5 deletions src/Codebelt.Extensions.Xunit.Hosting/HostFixture.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
Expand Down Expand Up @@ -56,14 +57,21 @@ public virtual void ConfigureHost(Test hostTest)
Configuration = context.Configuration;
HostingEnvironment = context.HostingEnvironment;
ConfigureServicesCallback(services);
})
.ConfigureHostConfiguration(builder =>
{
builder.AddInMemoryCollection(new Dictionary<string, string>
{
{ HostDefaults.ApplicationKey, hostTest.CallerType.Assembly.GetName().Name }
});
});

#if NET9_0_OR_GREATER
hb.UseDefaultServiceProvider(o =>
{
o.ValidateOnBuild = true;
o.ValidateScopes = true;
});
hb.UseDefaultServiceProvider(o =>
{
o.ValidateOnBuild = true;
o.ValidateScopes = true;
});
#endif

ConfigureHostCallback(hb);
Expand Down
30 changes: 29 additions & 1 deletion src/Codebelt.Extensions.Xunit.Hosting/HostTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,44 @@
/// <param name="hostFixture">An implementation of the <see cref="IHostFixture"/> interface.</param>
/// <param name="output">An implementation of the <see cref="ITestOutputHelper"/> interface.</param>
/// <param name="callerType">The <see cref="Type"/> of caller that ends up invoking this instance.</param>
protected HostTest(T hostFixture, ITestOutputHelper output = null, Type callerType = null) : base(output, callerType)
/// <exception cref="ArgumentNullException">
/// <paramref name="hostFixture"/> is null.
/// </exception>
protected HostTest(T hostFixture, ITestOutputHelper output = null, Type callerType = null) : this(false, hostFixture, output, callerType)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="HostTest{T}"/> class.
/// </summary>
/// <param name="skipHostFixtureInitialization">A value indicating whether to skip the host fixture initialization.</param>
/// <param name="hostFixture">An implementation of the <see cref="IHostFixture"/> interface.</param>
/// <param name="output">An implementation of the <see cref="ITestOutputHelper"/> interface.</param>
/// <param name="callerType">The <see cref="Type"/> of caller that ends up invoking this instance.</param>
/// <exception cref="ArgumentNullException">
/// <paramref name="hostFixture"/> is null.
/// </exception>
protected HostTest(bool skipHostFixtureInitialization, T hostFixture, ITestOutputHelper output = null, Type callerType = null) : base(output, callerType)
{
if (hostFixture == null) { throw new ArgumentNullException(nameof(hostFixture)); }
if (skipHostFixtureInitialization) { return; }
if (!hostFixture.HasValidState())
{
hostFixture.ConfigureHostCallback = ConfigureHost;
hostFixture.ConfigureCallback = Configure;
hostFixture.ConfigureServicesCallback = ConfigureServices;
hostFixture.ConfigureHost(this);
}
Host = hostFixture.Host;
ServiceProvider = hostFixture.Host.Services;
Configure(hostFixture.Configuration, hostFixture.HostingEnvironment);
}

/// <summary>
/// Initializes the specified host fixture.
/// </summary>
/// <param name="hostFixture">The host fixture to initialize.</param>
[Obsolete("This method is obsolete and will be removed in a future version. It remains only to support binary compatibility.")]

Check warning on line 62 in src/Codebelt.Extensions.Xunit.Hosting/HostTest.cs

View workflow job for this annotation

GitHub Actions / call-sonarcloud / 🔬 Code Quality Analysis

Do not forget to remove this deprecated code someday. (https://rules.sonarsource.com/csharp/RSPEC-1133)

Check warning on line 62 in src/Codebelt.Extensions.Xunit.Hosting/HostTest.cs

View workflow job for this annotation

GitHub Actions / call-sonarcloud / 🔬 Code Quality Analysis

Do not forget to remove this deprecated code someday. (https://rules.sonarsource.com/csharp/RSPEC-1133)

Check warning on line 62 in src/Codebelt.Extensions.Xunit.Hosting/HostTest.cs

View workflow job for this annotation

GitHub Actions / call-sonarcloud / 🔬 Code Quality Analysis

Do not forget to remove this deprecated code someday. (https://rules.sonarsource.com/csharp/RSPEC-1133)

Check warning on line 62 in src/Codebelt.Extensions.Xunit.Hosting/HostTest.cs

View workflow job for this annotation

GitHub Actions / call-sonarcloud / 🔬 Code Quality Analysis

Do not forget to remove this deprecated code someday. (https://rules.sonarsource.com/csharp/RSPEC-1133)

Check warning on line 62 in src/Codebelt.Extensions.Xunit.Hosting/HostTest.cs

View workflow job for this annotation

GitHub Actions / call-sonarcloud / 🔬 Code Quality Analysis

Do not forget to remove this deprecated code someday. (https://rules.sonarsource.com/csharp/RSPEC-1133)
protected virtual void InitializeHostFixture(T hostFixture)
{
}
Expand Down
14 changes: 10 additions & 4 deletions src/Codebelt.Extensions.Xunit.Hosting/LoggerExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.Extensions.Logging;
Expand All @@ -15,14 +16,18 @@
/// Returns the associated <see cref="ITestStore{T}"/> that is provided when settings up services from <see cref="ServiceCollectionExtensions.AddXunitTestLogging"/>.
/// </summary>
/// <param name="logger">The <see cref="ILogger{TCategoryName}"/> from which to retrieve the <see cref="ITestStore{T}"/>.</param>
/// <param name="categoryName">The category name for messages produced by the <paramref name="logger"/> -or- <c>null</c> for messages produced by all loggers.</param>
/// <returns>Returns an implementation of <see cref="ITestStore{T}"/> with all logged entries expressed as <see cref="XunitTestLoggerEntry"/>.</returns>
/// <exception cref="ArgumentNullException">
/// <paramref name="logger"/> cannot be null.
/// </exception>
/// <exception cref="ArgumentException">
/// <paramref name="logger"/> does not contain a test store.
/// </exception>
public static ITestStore<XunitTestLoggerEntry> GetTestStore(this ILogger logger)
/// <exception cref="KeyNotFoundException">
/// <paramref name="logger"/> does not contain a test store for the specified <paramref name="categoryName"/>.
/// </exception>
public static ITestStore<XunitTestLoggerEntry> GetTestStore(this ILogger logger, string categoryName = null)

Check warning on line 30 in src/Codebelt.Extensions.Xunit.Hosting/LoggerExtensions.cs

View workflow job for this annotation

GitHub Actions / call-sonarcloud / 🔬 Code Quality Analysis

Refactor this method to reduce its Cognitive Complexity from 21 to the 15 allowed. (https://rules.sonarsource.com/csharp/RSPEC-3776)

Check warning on line 30 in src/Codebelt.Extensions.Xunit.Hosting/LoggerExtensions.cs

View workflow job for this annotation

GitHub Actions / call-sonarcloud / 🔬 Code Quality Analysis

Refactor this method to reduce its Cognitive Complexity from 21 to the 15 allowed. (https://rules.sonarsource.com/csharp/RSPEC-3776)

Check warning on line 30 in src/Codebelt.Extensions.Xunit.Hosting/LoggerExtensions.cs

View workflow job for this annotation

GitHub Actions / call-sonarcloud / 🔬 Code Quality Analysis

Refactor this method to reduce its Cognitive Complexity from 21 to the 15 allowed. (https://rules.sonarsource.com/csharp/RSPEC-3776)

Check warning on line 30 in src/Codebelt.Extensions.Xunit.Hosting/LoggerExtensions.cs

View workflow job for this annotation

GitHub Actions / call-sonarcloud / 🔬 Code Quality Analysis

Refactor this method to reduce its Cognitive Complexity from 21 to the 15 allowed. (https://rules.sonarsource.com/csharp/RSPEC-3776)

Check warning on line 30 in src/Codebelt.Extensions.Xunit.Hosting/LoggerExtensions.cs

View workflow job for this annotation

GitHub Actions / call-sonarcloud / 🔬 Code Quality Analysis

Refactor this method to reduce its Cognitive Complexity from 21 to the 15 allowed. (https://rules.sonarsource.com/csharp/RSPEC-3776)
{
if (logger == null) { throw new ArgumentNullException(nameof(logger)); }
var loggerType = logger.GetType();
Expand All @@ -41,7 +46,9 @@
{
var xunitTestLogger = loggerInformationType.GetProperty("Logger")?.GetValue(loggerInformation) as XunitTestLogger;
if (xunitTestLogger == null) { continue; }
return xunitTestLogger.Provider;
return categoryName == null
? xunitTestLogger.Provider
: xunitTestLogger.Provider[categoryName];
}
}
}
Expand All @@ -60,10 +67,9 @@
/// <exception cref="ArgumentException">
/// <paramref name="logger"/> does not contain a test store.
/// </exception>
[Obsolete($"This method will be removed in a future version. Please use non-generic equivalent {nameof(GetTestStore)}.")]
public static ITestStore<XunitTestLoggerEntry> GetTestStore<T>(this ILogger<T> logger)
{
return GetTestStore((ILogger)logger);
return GetTestStore(logger, typeof(T).FullName);
}
}
}
4 changes: 3 additions & 1 deletion src/Codebelt.Extensions.Xunit.Hosting/XunitTestLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

namespace Codebelt.Extensions.Xunit.Hosting
{
internal sealed class XunitTestLogger : ILogger, IDisposable
internal sealed class XunitTestLogger : InMemoryTestStore<XunitTestLoggerEntry>, ILogger, IDisposable
{
private readonly ITestOutputHelperAccessor _accessor;
private readonly ITestOutputHelper _output;
Expand All @@ -32,6 +32,8 @@ public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Except

_provider.WriteLoggerEntry(logLevel, eventId, message);

Add(new XunitTestLoggerEntry(logLevel, eventId, message));

if (_accessor != null)
{
if (_accessor.TestOutput == null) { throw new InvalidOperationException($"{nameof(ITestOutputHelperAccessor)}.{nameof(ITestOutputHelperAccessor.TestOutput)} is null."); }
Expand Down
22 changes: 18 additions & 4 deletions src/Codebelt.Extensions.Xunit.Hosting/XunitTestLoggerProvider.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
using System.Collections.Concurrent;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using Xunit.Abstractions;

namespace Codebelt.Extensions.Xunit.Hosting
{
internal sealed class XunitTestLoggerProvider : InMemoryTestStore<XunitTestLoggerEntry>, ILoggerProvider
{
private readonly ConcurrentDictionary<string, XunitTestLogger> _loggers = new();
private readonly ConcurrentDictionary<string, XunitTestLogger> _loggers = new(StringComparer.OrdinalIgnoreCase);
private readonly ITestOutputHelperAccessor _accessor;
private readonly ITestOutputHelper _output;

Expand All @@ -26,12 +28,24 @@ public ILogger CreateLogger(string categoryName)
? new XunitTestLogger(this, _accessor)
: new XunitTestLogger(this, _output));
}

public void WriteLoggerEntry(LogLevel logLevel, EventId eventId, string message)
{
Add(new XunitTestLoggerEntry(logLevel, eventId, message));
}

public ITestStore<XunitTestLoggerEntry> this[string categoryName]
{
get
{
if (_loggers.TryGetValue(categoryName, out var logger))
{
return logger;
}
throw new KeyNotFoundException($"Logger for category '{categoryName}' not found.");
}
}

public void Dispose()
{
}
Expand Down
Loading
Loading