From 95343e2a81bfb8cabfb20998d82718354727e946 Mon Sep 17 00:00:00 2001 From: "Eric J. Smith" Date: Mon, 12 Feb 2024 21:53:10 -0600 Subject: [PATCH 1/4] XUnit test logger improvements --- .../Queue/QueueTestBase.cs | 6 +- src/Foundatio.Xunit/Foundatio.Xunit.csproj | 5 +- src/Foundatio.Xunit/Logging/LogEntry.cs | 2 +- .../Logging/LoggingExtensions.cs | 84 +++++++++++++ src/Foundatio.Xunit/Logging/TestLogger.cs | 119 +++++++----------- .../Logging/TestLoggerFactory.cs | 82 ------------ .../Logging/TestLoggerLogger.cs | 112 +++++++++++++++++ .../Logging/TestLoggerOptions.cs | 19 +++ .../Logging/TestLoggerProvider.cs | 35 ++++++ .../Logging/TestWithLoggingBase.cs | 4 +- .../Caching/InMemoryCacheClientTests.cs | 2 +- .../Caching/InMemoryHybridCacheClientTests.cs | 2 +- .../Locks/InMemoryLockTests.cs | 2 +- .../Metrics/DiagnosticsMetricsTests.cs | 2 +- .../Queue/InMemoryQueueTests.cs | 2 +- .../Utility/ScheduledTimerTests.cs | 2 +- .../Utility/TestLoggerTests.cs | 61 +++++++++ 17 files changed, 370 insertions(+), 171 deletions(-) create mode 100644 src/Foundatio.Xunit/Logging/LoggingExtensions.cs delete mode 100644 src/Foundatio.Xunit/Logging/TestLoggerFactory.cs create mode 100644 src/Foundatio.Xunit/Logging/TestLoggerLogger.cs create mode 100644 src/Foundatio.Xunit/Logging/TestLoggerOptions.cs create mode 100644 src/Foundatio.Xunit/Logging/TestLoggerProvider.cs create mode 100644 tests/Foundatio.Tests/Utility/TestLoggerTests.cs diff --git a/src/Foundatio.TestHarness/Queue/QueueTestBase.cs b/src/Foundatio.TestHarness/Queue/QueueTestBase.cs index 1bf7a243..05416721 100644 --- a/src/Foundatio.TestHarness/Queue/QueueTestBase.cs +++ b/src/Foundatio.TestHarness/Queue/QueueTestBase.cs @@ -631,7 +631,7 @@ public virtual async Task WillNotWaitForItemAsync() public virtual async Task WillWaitForItemAsync() { - Log.MinimumLevel = LogLevel.Trace; + Log.Options.DefaultMinimumLevel = LogLevel.Trace; var queue = GetQueue(); if (queue == null) return; @@ -792,7 +792,7 @@ await queue.StartWorkingAsync(w => public virtual async Task WorkItemsWillTimeoutAsync() { - Log.MinimumLevel = LogLevel.Trace; + Log.Options.DefaultMinimumLevel = LogLevel.Trace; Log.SetLogLevel("Foundatio.Queues.RedisQueue", LogLevel.Trace); var queue = GetQueue(retryDelay: TimeSpan.Zero, workItemTimeout: TimeSpan.FromMilliseconds(50)); if (queue == null) @@ -1313,7 +1313,7 @@ protected async Task CanDequeueWithLockingImpAsync(CacheLockProvider distributed await queue.DeleteQueueAsync(); await AssertEmptyQueueAsync(queue); - Log.MinimumLevel = LogLevel.Trace; + Log.Options.DefaultMinimumLevel = LogLevel.Trace; using var metrics = new InMemoryMetricsClient(new InMemoryMetricsClientOptions { Buffered = false, LoggerFactory = Log }); queue.AttachBehavior(new MetricsQueueBehavior(metrics, loggerFactory: Log)); diff --git a/src/Foundatio.Xunit/Foundatio.Xunit.csproj b/src/Foundatio.Xunit/Foundatio.Xunit.csproj index f881eaee..f9db1e39 100644 --- a/src/Foundatio.Xunit/Foundatio.Xunit.csproj +++ b/src/Foundatio.Xunit/Foundatio.Xunit.csproj @@ -7,7 +7,8 @@ - - + + + diff --git a/src/Foundatio.Xunit/Logging/LogEntry.cs b/src/Foundatio.Xunit/Logging/LogEntry.cs index 6f68b732..85f51762 100644 --- a/src/Foundatio.Xunit/Logging/LogEntry.cs +++ b/src/Foundatio.Xunit/Logging/LogEntry.cs @@ -6,7 +6,7 @@ namespace Foundatio.Xunit; public class LogEntry { - public DateTime Date { get; set; } + public DateTimeOffset Date { get; set; } public string CategoryName { get; set; } public LogLevel LogLevel { get; set; } public object[] Scopes { get; set; } diff --git a/src/Foundatio.Xunit/Logging/LoggingExtensions.cs b/src/Foundatio.Xunit/Logging/LoggingExtensions.cs new file mode 100644 index 00000000..001bf79c --- /dev/null +++ b/src/Foundatio.Xunit/Logging/LoggingExtensions.cs @@ -0,0 +1,84 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Logging; +using Xunit.Abstractions; + +namespace Foundatio.Xunit; + +public static class LoggingExtensions +{ + public static TestLogger GetTestLogger(this IServiceProvider serviceProvider) + { + return serviceProvider.GetRequiredService(); + } + + public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, ITestOutputHelper outputHelper, + Action configure = null) + { + + var options = new TestLoggerOptions { + WriteLogEntryFunc = logEntry => + { + outputHelper.WriteLine(logEntry.ToString(false)); + } + }; + + configure?.Invoke(options); + + return builder.AddXUnit(options); + } + + public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, Action configure) + { + var options = new TestLoggerOptions(); + configure?.Invoke(options); + return builder.AddXUnit(options); + } + + public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, TestLoggerOptions options = null) + { + if (builder == null) + throw new ArgumentNullException(nameof(builder)); + + var loggerProvider = new TestLoggerProvider(options); + builder.AddProvider(loggerProvider); + builder.Services.TryAddSingleton(loggerProvider.Log); + + return builder; + } + + public static ILoggerFactory AddXUnit(this ILoggerFactory factory, Action configure = null) + { + if (factory == null) + throw new ArgumentNullException(nameof(factory)); + + var options = new TestLoggerOptions(); + configure?.Invoke(options); + + factory.AddProvider(new TestLoggerProvider(options)); + + return factory; + } + + public static TestLogger ToTestLogger(this ITestOutputHelper outputHelper, Action configure = null) + { + if (outputHelper == null) + throw new ArgumentNullException(nameof(outputHelper)); + + var options = new TestLoggerOptions(); + options.WriteLogEntryFunc = logEntry => + { + outputHelper.WriteLine(logEntry.ToString()); + }; + + configure?.Invoke(options); + + var testLogger = new TestLogger(options); + + return testLogger; + } + + public static ILogger ToLogger(this ITestOutputHelper outputHelper, Action configure = null) + => outputHelper.ToTestLogger(configure).CreateLogger(); +} diff --git a/src/Foundatio.Xunit/Logging/TestLogger.cs b/src/Foundatio.Xunit/Logging/TestLogger.cs index 2d525cad..065551f5 100644 --- a/src/Foundatio.Xunit/Logging/TestLogger.cs +++ b/src/Foundatio.Xunit/Logging/TestLogger.cs @@ -1,112 +1,81 @@ -using System; +using System; using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; using System.Threading; using Foundatio.Utility; using Microsoft.Extensions.Logging; namespace Foundatio.Xunit; -internal class TestLogger : ILogger +public class TestLogger : ILoggerFactory { - private readonly TestLoggerFactory _loggerFactory; - private readonly string _categoryName; + private readonly Dictionary _logLevels = new(); + private readonly Queue _logEntries = new(); - public TestLogger(string categoryName, TestLoggerFactory loggerFactory) + public TestLogger() { - _loggerFactory = loggerFactory; - _categoryName = categoryName; + Options = new TestLoggerOptions(); } - public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + public TestLogger(TestLoggerOptions options) { - if (!_loggerFactory.IsEnabled(_categoryName, logLevel)) - return; + Options = options ?? new TestLoggerOptions(); + } - object[] scopes = CurrentScopeStack.Reverse().ToArray(); - var logEntry = new LogEntry - { - Date = SystemClock.UtcNow, - LogLevel = logLevel, - EventId = eventId, - State = state, - Exception = exception, - Formatter = (s, e) => formatter((TState)s, e), - CategoryName = _categoryName, - Scopes = scopes - }; - - switch (state) - { - //case LogData logData: - // logEntry.Properties["CallerMemberName"] = logData.MemberName; - // logEntry.Properties["CallerFilePath"] = logData.FilePath; - // logEntry.Properties["CallerLineNumber"] = logData.LineNumber; - - // foreach (var property in logData.Properties) - // logEntry.Properties[property.Key] = property.Value; - // break; - case IDictionary logDictionary: - foreach (var property in logDictionary) - logEntry.Properties[property.Key] = property.Value; - break; - } + public TestLoggerOptions Options { get; } + public IReadOnlyList LogEntries => _logEntries.ToArray(); + + public void Clear() => _logEntries.Clear(); - foreach (object scope in scopes) + internal void AddLogEntry(LogEntry logEntry) + { + lock (_logEntries) { - if (!(scope is IDictionary scopeData)) - continue; + _logEntries.Enqueue(logEntry); - foreach (var property in scopeData) - logEntry.Properties[property.Key] = property.Value; + if (_logEntries.Count > Options.MaxLogEntriesToStore) + _logEntries.Dequeue(); } - _loggerFactory.AddLogEntry(logEntry); - } + if (Options.WriteLogEntryFunc == null || _logEntriesWritten >= Options.MaxLogEntriesToWrite) + return; - public bool IsEnabled(LogLevel logLevel) - { - return _loggerFactory.IsEnabled(_categoryName, logLevel); + try + { + Options.WriteLogEntry(logEntry); + Interlocked.Increment(ref _logEntriesWritten); + } + catch (Exception) + { + // ignored + } } - public IDisposable BeginScope(TState state) - { - if (state == null) - throw new ArgumentNullException(nameof(state)); - - return Push(state); - } + private int _logEntriesWritten = 0; - public IDisposable BeginScope(Func scopeFactory, TState state) + public ILogger CreateLogger(string categoryName) { - if (state == null) - throw new ArgumentNullException(nameof(state)); - - return Push(scopeFactory(state)); + return new TestLoggerLogger(categoryName, this); } - private static readonly AsyncLocal _currentScopeStack = new(); + public void AddProvider(ILoggerProvider loggerProvider) { } - private sealed class Wrapper + public bool IsEnabled(string category, LogLevel logLevel) { - public ImmutableStack Value { get; set; } - } + if (_logLevels.TryGetValue(category, out var categoryLevel)) + return logLevel >= categoryLevel; - private static ImmutableStack CurrentScopeStack - { - get => _currentScopeStack.Value?.Value ?? ImmutableStack.Create(); - set => _currentScopeStack.Value = new Wrapper { Value = value }; + return logLevel >= Options.DefaultMinimumLevel; } - private static IDisposable Push(object state) + public void SetLogLevel(string category, LogLevel minLogLevel) { - CurrentScopeStack = CurrentScopeStack.Push(state); - return new DisposableAction(Pop); + _logLevels[category] = minLogLevel; } - private static void Pop() + public void SetLogLevel(LogLevel minLogLevel) { - CurrentScopeStack = CurrentScopeStack.Pop(); + SetLogLevel(TypeHelper.GetTypeDisplayName(typeof(T)), minLogLevel); } + + public void Dispose() { } } diff --git a/src/Foundatio.Xunit/Logging/TestLoggerFactory.cs b/src/Foundatio.Xunit/Logging/TestLoggerFactory.cs deleted file mode 100644 index 7379a3f6..00000000 --- a/src/Foundatio.Xunit/Logging/TestLoggerFactory.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using Foundatio.Utility; -using Microsoft.Extensions.Logging; -using Xunit.Abstractions; - -namespace Foundatio.Xunit; - -public class TestLoggerFactory : ILoggerFactory -{ - private readonly Dictionary _logLevels = new(); - private readonly Queue _logEntries = new(); - private readonly Action _writeLogEntryFunc; - - public TestLoggerFactory() - { - _writeLogEntryFunc = e => { }; - } - - public TestLoggerFactory(Action writeLogEntryFunc) - { - _writeLogEntryFunc = writeLogEntryFunc; - } - - public TestLoggerFactory(ITestOutputHelper output) : this(e => output.WriteLine(e.ToString(false))) { } - - public LogLevel MinimumLevel { get; set; } = LogLevel.Information; - public IReadOnlyList LogEntries => _logEntries.ToArray(); - public int MaxLogEntriesToStore = 100; - public int MaxLogEntriesToWrite = 1000; - - internal void AddLogEntry(LogEntry logEntry) - { - lock (_logEntries) - { - _logEntries.Enqueue(logEntry); - - if (_logEntries.Count > MaxLogEntriesToStore) - _logEntries.Dequeue(); - } - - if (_writeLogEntryFunc == null || _logEntriesWritten >= MaxLogEntriesToWrite) - return; - - try - { - _writeLogEntryFunc(logEntry); - Interlocked.Increment(ref _logEntriesWritten); - } - catch (Exception) { } - } - - private int _logEntriesWritten = 0; - - public ILogger CreateLogger(string categoryName) - { - return new TestLogger(categoryName, this); - } - - public void AddProvider(ILoggerProvider loggerProvider) { } - - public bool IsEnabled(string category, LogLevel logLevel) - { - if (_logLevels.TryGetValue(category, out var categoryLevel)) - return logLevel >= categoryLevel; - - return logLevel >= MinimumLevel; - } - - public void SetLogLevel(string category, LogLevel minLogLevel) - { - _logLevels[category] = minLogLevel; - } - - public void SetLogLevel(LogLevel minLogLevel) - { - SetLogLevel(TypeHelper.GetTypeDisplayName(typeof(T)), minLogLevel); - } - - public void Dispose() { } -} diff --git a/src/Foundatio.Xunit/Logging/TestLoggerLogger.cs b/src/Foundatio.Xunit/Logging/TestLoggerLogger.cs new file mode 100644 index 00000000..d7cff822 --- /dev/null +++ b/src/Foundatio.Xunit/Logging/TestLoggerLogger.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Threading; +using Foundatio.Utility; +using Microsoft.Extensions.Logging; + +namespace Foundatio.Xunit; + +internal class TestLoggerLogger : ILogger +{ + private readonly TestLogger _logger; + private readonly string _categoryName; + + public TestLoggerLogger(string categoryName, TestLogger logger) + { + _logger = logger; + _categoryName = categoryName; + } + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + { + if (!_logger.IsEnabled(_categoryName, logLevel)) + return; + + object[] scopes = _logger.Options.IncludeScopes ? CurrentScopeStack.Reverse().ToArray() : Array.Empty(); + var logEntry = new LogEntry + { + Date = _logger.Options.GetNow(), + LogLevel = logLevel, + EventId = eventId, + State = state, + Exception = exception, + Formatter = (s, e) => formatter((TState)s, e), + CategoryName = _categoryName, + Scopes = scopes + }; + + switch (state) + { + //case LogData logData: + // logEntry.Properties["CallerMemberName"] = logData.MemberName; + // logEntry.Properties["CallerFilePath"] = logData.FilePath; + // logEntry.Properties["CallerLineNumber"] = logData.LineNumber; + + // foreach (var property in logData.Properties) + // logEntry.Properties[property.Key] = property.Value; + // break; + case IDictionary logDictionary: + foreach (var property in logDictionary) + logEntry.Properties[property.Key] = property.Value; + break; + } + + foreach (object scope in scopes) + { + if (!(scope is IDictionary scopeData)) + continue; + + foreach (var property in scopeData) + logEntry.Properties[property.Key] = property.Value; + } + + _logger.AddLogEntry(logEntry); + } + + public bool IsEnabled(LogLevel logLevel) + { + return _logger.IsEnabled(_categoryName, logLevel); + } + + public IDisposable BeginScope(TState state) + { + if (state == null) + throw new ArgumentNullException(nameof(state)); + + return Push(state); + } + + public IDisposable BeginScope(Func scopeFactory, TState state) + { + if (state == null) + throw new ArgumentNullException(nameof(state)); + + return Push(scopeFactory(state)); + } + + private static readonly AsyncLocal _currentScopeStack = new(); + + private sealed class Wrapper + { + public ImmutableStack Value { get; set; } + } + + private static ImmutableStack CurrentScopeStack + { + get => _currentScopeStack.Value?.Value ?? ImmutableStack.Create(); + set => _currentScopeStack.Value = new Wrapper { Value = value }; + } + + private static IDisposable Push(object state) + { + CurrentScopeStack = CurrentScopeStack.Push(state); + return new DisposableAction(Pop); + } + + private static void Pop() + { + CurrentScopeStack = CurrentScopeStack.Pop(); + } +} diff --git a/src/Foundatio.Xunit/Logging/TestLoggerOptions.cs b/src/Foundatio.Xunit/Logging/TestLoggerOptions.cs new file mode 100644 index 00000000..f8489c66 --- /dev/null +++ b/src/Foundatio.Xunit/Logging/TestLoggerOptions.cs @@ -0,0 +1,19 @@ +using System; +using Foundatio.Utility; +using Microsoft.Extensions.Logging; + +namespace Foundatio.Xunit; + +public class TestLoggerOptions +{ + public LogLevel DefaultMinimumLevel { get; set; } = LogLevel.Information; + public int MaxLogEntriesToStore { get; set; } = 100; + public int MaxLogEntriesToWrite { get; set; } = 1000; + public bool IncludeScopes { get; set; } = true; + + public Action WriteLogEntryFunc { get; set; } + internal void WriteLogEntry(LogEntry logEntry) => WriteLogEntryFunc?.Invoke(logEntry); + + public Func NowFunc { get; set; } + internal DateTimeOffset GetNow() => NowFunc?.Invoke() ?? SystemClock.OffsetNow; +} diff --git a/src/Foundatio.Xunit/Logging/TestLoggerProvider.cs b/src/Foundatio.Xunit/Logging/TestLoggerProvider.cs new file mode 100644 index 00000000..41335a5f --- /dev/null +++ b/src/Foundatio.Xunit/Logging/TestLoggerProvider.cs @@ -0,0 +1,35 @@ +using System; +using Microsoft.Extensions.Logging; + +namespace Foundatio.Xunit; + +[ProviderAlias("Test")] +public class TestLoggerProvider : ILoggerProvider +{ + public TestLoggerProvider(TestLoggerOptions options) + { + Log = new TestLogger(options); + } + + public TestLogger Log { get; } + + public virtual ILogger CreateLogger(string categoryName) + { + return Log.CreateLogger(categoryName); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + } + + ~TestLoggerProvider() + { + Dispose(false); + } +} diff --git a/src/Foundatio.Xunit/Logging/TestWithLoggingBase.cs b/src/Foundatio.Xunit/Logging/TestWithLoggingBase.cs index a3e91ba7..5de2cdba 100644 --- a/src/Foundatio.Xunit/Logging/TestWithLoggingBase.cs +++ b/src/Foundatio.Xunit/Logging/TestWithLoggingBase.cs @@ -9,9 +9,9 @@ public abstract class TestWithLoggingBase protected TestWithLoggingBase(ITestOutputHelper output) { - Log = new TestLoggerFactory(output); + Log = output.ToTestLogger(); _logger = Log.CreateLogger(GetType()); } - protected TestLoggerFactory Log { get; } + protected TestLogger Log { get; } } diff --git a/tests/Foundatio.Tests/Caching/InMemoryCacheClientTests.cs b/tests/Foundatio.Tests/Caching/InMemoryCacheClientTests.cs index 7dc699eb..6bcf9aa2 100644 --- a/tests/Foundatio.Tests/Caching/InMemoryCacheClientTests.cs +++ b/tests/Foundatio.Tests/Caching/InMemoryCacheClientTests.cs @@ -155,7 +155,7 @@ public override Task CanManageListsAsync() [Fact] public async Task CanSetMaxItems() { - Log.MinimumLevel = LogLevel.Trace; + Log.Options.DefaultMinimumLevel = LogLevel.Trace; // run in tight loop so that the code is warmed up and we can catch timing issues for (int x = 0; x < 5; x++) diff --git a/tests/Foundatio.Tests/Caching/InMemoryHybridCacheClientTests.cs b/tests/Foundatio.Tests/Caching/InMemoryHybridCacheClientTests.cs index d2e9b197..7a1e56ea 100644 --- a/tests/Foundatio.Tests/Caching/InMemoryHybridCacheClientTests.cs +++ b/tests/Foundatio.Tests/Caching/InMemoryHybridCacheClientTests.cs @@ -81,7 +81,7 @@ public override Task WillExpireRemoteItems() [Fact(Skip = "Skip because cache invalidation loops on this with 2 in memory cache client instances")] public override Task WillWorkWithSets() { - Log.MinimumLevel = LogLevel.Trace; + Log.Options.DefaultMinimumLevel = LogLevel.Trace; return base.WillWorkWithSets(); } diff --git a/tests/Foundatio.Tests/Locks/InMemoryLockTests.cs b/tests/Foundatio.Tests/Locks/InMemoryLockTests.cs index af162c2a..3f010eaa 100644 --- a/tests/Foundatio.Tests/Locks/InMemoryLockTests.cs +++ b/tests/Foundatio.Tests/Locks/InMemoryLockTests.cs @@ -59,7 +59,7 @@ public override Task CanAcquireMultipleResources() return base.CanAcquireMultipleResources(); } - [Fact] + [RetryFact] public override Task CanAcquireLocksInParallel() { return base.CanAcquireLocksInParallel(); diff --git a/tests/Foundatio.Tests/Metrics/DiagnosticsMetricsTests.cs b/tests/Foundatio.Tests/Metrics/DiagnosticsMetricsTests.cs index b0f9012d..d70c416b 100644 --- a/tests/Foundatio.Tests/Metrics/DiagnosticsMetricsTests.cs +++ b/tests/Foundatio.Tests/Metrics/DiagnosticsMetricsTests.cs @@ -16,7 +16,7 @@ public class DiagnosticsMetricsTests : TestWithLoggingBase, IDisposable public DiagnosticsMetricsTests(ITestOutputHelper output) : base(output) { - Log.MinimumLevel = LogLevel.Trace; + Log.Options.DefaultMinimumLevel = LogLevel.Trace; _client = new DiagnosticsMetricsClient(o => o.MeterName("Test")); } diff --git a/tests/Foundatio.Tests/Queue/InMemoryQueueTests.cs b/tests/Foundatio.Tests/Queue/InMemoryQueueTests.cs index 969ece92..4e14d983 100644 --- a/tests/Foundatio.Tests/Queue/InMemoryQueueTests.cs +++ b/tests/Foundatio.Tests/Queue/InMemoryQueueTests.cs @@ -290,7 +290,7 @@ public override Task VerifyDelayedRetryAttemptsAsync() [Fact] public override Task CanHandleAutoAbandonInWorker() { - Log.MinimumLevel = LogLevel.Trace; + Log.Options.DefaultMinimumLevel = LogLevel.Trace; return base.CanHandleAutoAbandonInWorker(); } diff --git a/tests/Foundatio.Tests/Utility/ScheduledTimerTests.cs b/tests/Foundatio.Tests/Utility/ScheduledTimerTests.cs index 09d7b0ec..b7d25114 100644 --- a/tests/Foundatio.Tests/Utility/ScheduledTimerTests.cs +++ b/tests/Foundatio.Tests/Utility/ScheduledTimerTests.cs @@ -47,7 +47,7 @@ public Task CanRunWithMinimumInterval() private async Task CanRunConcurrentlyAsync(TimeSpan? minimumIntervalTime = null) { - Log.MinimumLevel = LogLevel.Trace; + Log.Options.DefaultMinimumLevel = LogLevel.Trace; const int iterations = 2; var countdown = new AsyncCountdownEvent(iterations); diff --git a/tests/Foundatio.Tests/Utility/TestLoggerTests.cs b/tests/Foundatio.Tests/Utility/TestLoggerTests.cs new file mode 100644 index 00000000..390df56a --- /dev/null +++ b/tests/Foundatio.Tests/Utility/TestLoggerTests.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using Foundatio.Serializer; +using Foundatio.Utility; +using Foundatio.Xunit; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json.Linq; +using Xunit; +using Xunit.Abstractions; + +namespace Foundatio.Tests.Utility; + +public class TestLoggerTests +{ + private readonly ITestOutputHelper _output; + + public TestLoggerTests(ITestOutputHelper output) + { + _output = output; + } + + [Fact] + public void CanUseTestLogger() + { + var services = new ServiceCollection() + .AddLogging(b => b.AddDebug().AddXUnit(_output)) + .AddSingleton(); + + IServiceProvider provider = services.BuildServiceProvider(); + + var someClass = provider.GetRequiredService(); + + someClass.DoSomething(); + + var testLogger = provider.GetTestLogger(); + Assert.Single(testLogger.LogEntries); + Assert.Contains("Doing something", testLogger.LogEntries[0].Message); + + testLogger.Clear(); + testLogger.SetLogLevel(LogLevel.Error); + + someClass.DoSomething(); + Assert.Empty(testLogger.LogEntries); + } +} + +public class SomeClass +{ + private readonly ILogger _logger; + + public SomeClass(ILogger logger) + { + _logger = logger; + } + + public void DoSomething() + { + _logger.LogInformation("Doing something"); + } +} From 76cd0b50405b666418a38c09a242cdf8c67f7c00 Mon Sep 17 00:00:00 2001 From: "Eric J. Smith" Date: Mon, 12 Feb 2024 21:55:11 -0600 Subject: [PATCH 2/4] Missed one --- src/Foundatio.TestHarness/Locks/LockTestBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Foundatio.TestHarness/Locks/LockTestBase.cs b/src/Foundatio.TestHarness/Locks/LockTestBase.cs index 01d7513a..6801081f 100644 --- a/src/Foundatio.TestHarness/Locks/LockTestBase.cs +++ b/src/Foundatio.TestHarness/Locks/LockTestBase.cs @@ -271,7 +271,7 @@ private Task DoLockedWorkAsync(ILockProvider locker) public virtual async Task WillThrottleCallsAsync() { - Log.MinimumLevel = LogLevel.Trace; + Log.Options.DefaultMinimumLevel = LogLevel.Trace; Log.SetLogLevel(LogLevel.Information); Log.SetLogLevel(LogLevel.Trace); From f69b1b6968e5d91fed244f3f9483976f9fe96ec0 Mon Sep 17 00:00:00 2001 From: "Eric J. Smith" Date: Tue, 13 Feb 2024 00:15:54 -0600 Subject: [PATCH 3/4] More updates and compat --- .../Foundatio.HostingSample.csproj | 1 + .../Caching/CacheClientTestsBase.cs | 2 +- .../Jobs/JobQueueTestsBase.cs | 2 +- .../Locks/LockTestBase.cs | 4 +- .../Messaging/MessageBusTestBase.cs | 2 +- .../Metrics/MetricsClientTestBase.cs | 2 +- .../Queue/QueueTestBase.cs | 8 +- .../Serializer/SerializerTestsBase.cs | 2 +- .../Storage/FileStorageTestsBase.cs | 2 +- src/Foundatio.Xunit/Foundatio.Xunit.csproj | 4 +- .../Logging/LoggingExtensions.cs | 14 ++-- src/Foundatio.Xunit/Logging/TestLogger.cs | 43 +++++++++- src/Foundatio.Xunit/Logging/TestLoggerBase.cs | 61 +++++++++++++++ .../Logging/TestLoggerFixture.cs | 78 +++++++++++++++++++ .../Logging/TestWithLoggingBase.cs | 6 +- .../Caching/InMemoryCacheClientTests.cs | 2 +- .../Caching/InMemoryHybridCacheClientTests.cs | 2 +- tests/Foundatio.Tests/Hosting/HostingTests.cs | 19 +++-- tests/Foundatio.Tests/Jobs/JobTests.cs | 2 +- .../Foundatio.Tests/Jobs/WorkItemJobTests.cs | 2 +- .../Metrics/DiagnosticsMetricsTests.cs | 4 +- .../Queue/InMemoryQueueTests.cs | 2 +- tests/Foundatio.Tests/Utility/CloneTests.cs | 2 +- .../Utility/DataDictionaryTests.cs | 2 +- tests/Foundatio.Tests/Utility/RunTests.cs | 2 +- .../Utility/ScheduledTimerTests.cs | 4 +- .../Utility/SystemClockTests.cs | 2 +- .../Utility/TestLoggerTests.cs | 32 ++++++-- 28 files changed, 257 insertions(+), 51 deletions(-) create mode 100644 src/Foundatio.Xunit/Logging/TestLoggerBase.cs create mode 100644 src/Foundatio.Xunit/Logging/TestLoggerFixture.cs diff --git a/samples/Foundatio.HostingSample/Foundatio.HostingSample.csproj b/samples/Foundatio.HostingSample/Foundatio.HostingSample.csproj index 45456a13..8272d996 100644 --- a/samples/Foundatio.HostingSample/Foundatio.HostingSample.csproj +++ b/samples/Foundatio.HostingSample/Foundatio.HostingSample.csproj @@ -1,6 +1,7 @@  + diff --git a/src/Foundatio.TestHarness/Caching/CacheClientTestsBase.cs b/src/Foundatio.TestHarness/Caching/CacheClientTestsBase.cs index 928eea88..def4a62c 100644 --- a/src/Foundatio.TestHarness/Caching/CacheClientTestsBase.cs +++ b/src/Foundatio.TestHarness/Caching/CacheClientTestsBase.cs @@ -13,7 +13,7 @@ namespace Foundatio.Tests.Caching; -public abstract class CacheClientTestsBase : TestWithLoggingBase +public abstract class CacheClientTestsBase : TestLoggerBase { protected CacheClientTestsBase(ITestOutputHelper output) : base(output) { diff --git a/src/Foundatio.TestHarness/Jobs/JobQueueTestsBase.cs b/src/Foundatio.TestHarness/Jobs/JobQueueTestsBase.cs index 8af588f6..71239077 100644 --- a/src/Foundatio.TestHarness/Jobs/JobQueueTestsBase.cs +++ b/src/Foundatio.TestHarness/Jobs/JobQueueTestsBase.cs @@ -18,7 +18,7 @@ namespace Foundatio.Tests.Jobs; -public abstract class JobQueueTestsBase : TestWithLoggingBase +public abstract class JobQueueTestsBase : TestLoggerBase { private readonly ActivitySource _activitySource = new(nameof(JobQueueTestsBase)); diff --git a/src/Foundatio.TestHarness/Locks/LockTestBase.cs b/src/Foundatio.TestHarness/Locks/LockTestBase.cs index 6801081f..a145c955 100644 --- a/src/Foundatio.TestHarness/Locks/LockTestBase.cs +++ b/src/Foundatio.TestHarness/Locks/LockTestBase.cs @@ -14,7 +14,7 @@ namespace Foundatio.Tests.Locks; -public abstract class LockTestBase : TestWithLoggingBase +public abstract class LockTestBase : TestLoggerBase { protected LockTestBase(ITestOutputHelper output) : base(output) { } @@ -271,7 +271,7 @@ private Task DoLockedWorkAsync(ILockProvider locker) public virtual async Task WillThrottleCallsAsync() { - Log.Options.DefaultMinimumLevel = LogLevel.Trace; + Log.DefaultMinimumLevel = LogLevel.Trace; Log.SetLogLevel(LogLevel.Information); Log.SetLogLevel(LogLevel.Trace); diff --git a/src/Foundatio.TestHarness/Messaging/MessageBusTestBase.cs b/src/Foundatio.TestHarness/Messaging/MessageBusTestBase.cs index fc68da43..57eef070 100644 --- a/src/Foundatio.TestHarness/Messaging/MessageBusTestBase.cs +++ b/src/Foundatio.TestHarness/Messaging/MessageBusTestBase.cs @@ -16,7 +16,7 @@ namespace Foundatio.Tests.Messaging; -public abstract class MessageBusTestBase : TestWithLoggingBase +public abstract class MessageBusTestBase : TestLoggerBase { protected MessageBusTestBase(ITestOutputHelper output) : base(output) { diff --git a/src/Foundatio.TestHarness/Metrics/MetricsClientTestBase.cs b/src/Foundatio.TestHarness/Metrics/MetricsClientTestBase.cs index dd8cde1e..7acaa0b8 100644 --- a/src/Foundatio.TestHarness/Metrics/MetricsClientTestBase.cs +++ b/src/Foundatio.TestHarness/Metrics/MetricsClientTestBase.cs @@ -14,7 +14,7 @@ namespace Foundatio.Tests.Metrics; -public abstract class MetricsClientTestBase : TestWithLoggingBase +public abstract class MetricsClientTestBase : TestLoggerBase { public MetricsClientTestBase(ITestOutputHelper output) : base(output) { } diff --git a/src/Foundatio.TestHarness/Queue/QueueTestBase.cs b/src/Foundatio.TestHarness/Queue/QueueTestBase.cs index 05416721..c4774647 100644 --- a/src/Foundatio.TestHarness/Queue/QueueTestBase.cs +++ b/src/Foundatio.TestHarness/Queue/QueueTestBase.cs @@ -23,7 +23,7 @@ namespace Foundatio.Tests.Queue; -public abstract class QueueTestBase : TestWithLoggingBase, IDisposable +public abstract class QueueTestBase : TestLoggerBase, IDisposable { protected QueueTestBase(ITestOutputHelper output) : base(output) { @@ -631,7 +631,7 @@ public virtual async Task WillNotWaitForItemAsync() public virtual async Task WillWaitForItemAsync() { - Log.Options.DefaultMinimumLevel = LogLevel.Trace; + Log.DefaultMinimumLevel = LogLevel.Trace; var queue = GetQueue(); if (queue == null) return; @@ -792,7 +792,7 @@ await queue.StartWorkingAsync(w => public virtual async Task WorkItemsWillTimeoutAsync() { - Log.Options.DefaultMinimumLevel = LogLevel.Trace; + Log.DefaultMinimumLevel = LogLevel.Trace; Log.SetLogLevel("Foundatio.Queues.RedisQueue", LogLevel.Trace); var queue = GetQueue(retryDelay: TimeSpan.Zero, workItemTimeout: TimeSpan.FromMilliseconds(50)); if (queue == null) @@ -1313,7 +1313,7 @@ protected async Task CanDequeueWithLockingImpAsync(CacheLockProvider distributed await queue.DeleteQueueAsync(); await AssertEmptyQueueAsync(queue); - Log.Options.DefaultMinimumLevel = LogLevel.Trace; + Log.DefaultMinimumLevel = LogLevel.Trace; using var metrics = new InMemoryMetricsClient(new InMemoryMetricsClientOptions { Buffered = false, LoggerFactory = Log }); queue.AttachBehavior(new MetricsQueueBehavior(metrics, loggerFactory: Log)); diff --git a/src/Foundatio.TestHarness/Serializer/SerializerTestsBase.cs b/src/Foundatio.TestHarness/Serializer/SerializerTestsBase.cs index 85e9651d..6935d2c6 100644 --- a/src/Foundatio.TestHarness/Serializer/SerializerTestsBase.cs +++ b/src/Foundatio.TestHarness/Serializer/SerializerTestsBase.cs @@ -9,7 +9,7 @@ namespace Foundatio.Tests.Serializer; -public abstract class SerializerTestsBase : TestWithLoggingBase +public abstract class SerializerTestsBase : TestLoggerBase { protected SerializerTestsBase(ITestOutputHelper output) : base(output) { } diff --git a/src/Foundatio.TestHarness/Storage/FileStorageTestsBase.cs b/src/Foundatio.TestHarness/Storage/FileStorageTestsBase.cs index ea3294a0..772b9b7d 100644 --- a/src/Foundatio.TestHarness/Storage/FileStorageTestsBase.cs +++ b/src/Foundatio.TestHarness/Storage/FileStorageTestsBase.cs @@ -16,7 +16,7 @@ namespace Foundatio.Tests.Storage; -public abstract class FileStorageTestsBase : TestWithLoggingBase +public abstract class FileStorageTestsBase : TestLoggerBase { protected FileStorageTestsBase(ITestOutputHelper output) : base(output) { } diff --git a/src/Foundatio.Xunit/Foundatio.Xunit.csproj b/src/Foundatio.Xunit/Foundatio.Xunit.csproj index f9db1e39..5b974843 100644 --- a/src/Foundatio.Xunit/Foundatio.Xunit.csproj +++ b/src/Foundatio.Xunit/Foundatio.Xunit.csproj @@ -7,8 +7,8 @@ - - + + diff --git a/src/Foundatio.Xunit/Logging/LoggingExtensions.cs b/src/Foundatio.Xunit/Logging/LoggingExtensions.cs index 001bf79c..526434a8 100644 --- a/src/Foundatio.Xunit/Logging/LoggingExtensions.cs +++ b/src/Foundatio.Xunit/Logging/LoggingExtensions.cs @@ -13,7 +13,7 @@ public static TestLogger GetTestLogger(this IServiceProvider serviceProvider) return serviceProvider.GetRequiredService(); } - public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, ITestOutputHelper outputHelper, + public static ILoggingBuilder AddTestLogger(this ILoggingBuilder builder, ITestOutputHelper outputHelper, Action configure = null) { @@ -26,17 +26,17 @@ public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, ITestOutput configure?.Invoke(options); - return builder.AddXUnit(options); + return builder.AddTestLogger(options); } - public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, Action configure) + public static ILoggingBuilder AddTestLogger(this ILoggingBuilder builder, Action configure) { var options = new TestLoggerOptions(); configure?.Invoke(options); - return builder.AddXUnit(options); + return builder.AddTestLogger(options); } - public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, TestLoggerOptions options = null) + public static ILoggingBuilder AddTestLogger(this ILoggingBuilder builder, TestLoggerOptions options = null) { if (builder == null) throw new ArgumentNullException(nameof(builder)); @@ -48,7 +48,7 @@ public static ILoggingBuilder AddXUnit(this ILoggingBuilder builder, TestLoggerO return builder; } - public static ILoggerFactory AddXUnit(this ILoggerFactory factory, Action configure = null) + public static ILoggerFactory AddTestLogger(this ILoggerFactory factory, Action configure = null) { if (factory == null) throw new ArgumentNullException(nameof(factory)); @@ -79,6 +79,6 @@ public static TestLogger ToTestLogger(this ITestOutputHelper outputHelper, Actio return testLogger; } - public static ILogger ToLogger(this ITestOutputHelper outputHelper, Action configure = null) + public static ILogger ToTestLogger(this ITestOutputHelper outputHelper, Action configure = null) => outputHelper.ToTestLogger(configure).CreateLogger(); } diff --git a/src/Foundatio.Xunit/Logging/TestLogger.cs b/src/Foundatio.Xunit/Logging/TestLogger.cs index 065551f5..8b5ee870 100644 --- a/src/Foundatio.Xunit/Logging/TestLogger.cs +++ b/src/Foundatio.Xunit/Logging/TestLogger.cs @@ -3,6 +3,7 @@ using System.Threading; using Foundatio.Utility; using Microsoft.Extensions.Logging; +using Xunit.Abstractions; namespace Foundatio.Xunit; @@ -11,9 +12,22 @@ public class TestLogger : ILoggerFactory private readonly Dictionary _logLevels = new(); private readonly Queue _logEntries = new(); - public TestLogger() + public TestLogger(Action configure = null) { Options = new TestLoggerOptions(); + configure?.Invoke(Options); + } + + public TestLogger(ITestOutputHelper output, Action configure = null) + { + Options = new TestLoggerOptions { + WriteLogEntryFunc = logEntry => + { + output.WriteLine(logEntry.ToString(false)); + } + }; + + configure?.Invoke(Options); } public TestLogger(TestLoggerOptions options) @@ -22,8 +36,35 @@ public TestLogger(TestLoggerOptions options) } public TestLoggerOptions Options { get; } + + [Obsolete("Use DefaultMinimumLevel instead.")] + public LogLevel MinimumLevel + { + get => Options.DefaultMinimumLevel; + set => Options.DefaultMinimumLevel = value; + } + + public LogLevel DefaultMinimumLevel + { + get => Options.DefaultMinimumLevel; + set => Options.DefaultMinimumLevel = value; + } + + public int MaxLogEntriesToStore + { + get => Options.MaxLogEntriesToStore; + set => Options.MaxLogEntriesToStore = value; + } + + public int MaxLogEntriesToWrite + { + get => Options.MaxLogEntriesToWrite; + set => Options.MaxLogEntriesToWrite = value; + } + public IReadOnlyList LogEntries => _logEntries.ToArray(); + public void Clear() => _logEntries.Clear(); internal void AddLogEntry(LogEntry logEntry) diff --git a/src/Foundatio.Xunit/Logging/TestLoggerBase.cs b/src/Foundatio.Xunit/Logging/TestLoggerBase.cs new file mode 100644 index 00000000..049c3935 --- /dev/null +++ b/src/Foundatio.Xunit/Logging/TestLoggerBase.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Xunit.Abstractions; + +namespace Foundatio.Xunit; + +public abstract class TestLoggerBase : IAsyncDisposable +{ + protected readonly ILogger _logger; + protected readonly List _disposables = []; + + protected TestLoggerBase(ITestOutputHelper output) + { + var services = new ServiceCollection(); + RegisterRequiredServices(services, output); + var sp = services.BuildServiceProvider(); + _disposables.Add(sp); + Services = sp; + Log = Services.GetTestLogger(); + _logger = Log.CreateLogger(GetType()); + } + + protected IServiceProvider Services { get; } + + protected TService GetService() where TService : notnull + { + return Services.GetRequiredService(); + } + + protected TestLogger Log { get; } + + private void RegisterRequiredServices(IServiceCollection services, ITestOutputHelper output) + { + services.AddLogging(c => c.AddTestLogger(output)); + RegisterServices(services); + } + + protected virtual void RegisterServices(IServiceCollection services) + { + } + + public virtual ValueTask DisposeAsync() + { + foreach (var disposable in _disposables) + { + try + { + disposable.Dispose(); + } + catch (Exception ex) + { + _logger?.LogError(ex, "Error disposing resource."); + } + } + + return new ValueTask(Task.CompletedTask); + } +} diff --git a/src/Foundatio.Xunit/Logging/TestLoggerFixture.cs b/src/Foundatio.Xunit/Logging/TestLoggerFixture.cs new file mode 100644 index 00000000..7f8747eb --- /dev/null +++ b/src/Foundatio.Xunit/Logging/TestLoggerFixture.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Xunit; +using Xunit.Abstractions; + +namespace Foundatio.Xunit; + +public class TestLoggerFixture : IAsyncLifetime +{ + private readonly List _disposables = []; + private readonly List> _serviceRegistrations = []; + private readonly Lazy _serviceProvider; + private readonly Lazy _testLogger; + private readonly Lazy _log; + + public TestLoggerFixture() + { + _serviceProvider = new Lazy(BuildServiceProvider); + _testLogger = new Lazy(() => Services.GetTestLogger()); + _log = new Lazy(() => Services.GetRequiredService().CreateLogger(GetType())); + } + + public ITestOutputHelper Output { get; set; } + + public void AddServiceRegistrations(Action registerServices) + { + _serviceRegistrations.Add(registerServices); + } + + public IServiceProvider Services => _serviceProvider.Value; + + public TestLogger TestLogger => _testLogger.Value; + public ILogger Log => _log.Value; + + protected virtual void RegisterServices(IServiceCollection services) + { + if (Output == null) + throw new InvalidOperationException("Output should be set before registering services."); + + services.AddLogging(c => c.AddTestLogger(Output)); + foreach (var registration in _serviceRegistrations) + registration(services); + } + + protected virtual IServiceProvider BuildServiceProvider() + { + var services = new ServiceCollection(); + RegisterServices(services); + var sp = services.BuildServiceProvider(); + _disposables.Add(sp); + return sp; + } + + public virtual Task InitializeAsync() + { + return Task.CompletedTask; + } + + public virtual Task DisposeAsync() + { + foreach (var disposable in _disposables) + { + try + { + disposable.Dispose(); + } + catch (Exception ex) + { + Log?.LogError(ex, "Error disposing resource."); + } + } + + return Task.CompletedTask; + } +} diff --git a/src/Foundatio.Xunit/Logging/TestWithLoggingBase.cs b/src/Foundatio.Xunit/Logging/TestWithLoggingBase.cs index 5de2cdba..9c4e1978 100644 --- a/src/Foundatio.Xunit/Logging/TestWithLoggingBase.cs +++ b/src/Foundatio.Xunit/Logging/TestWithLoggingBase.cs @@ -1,15 +1,17 @@ -using Microsoft.Extensions.Logging; +using System; +using Microsoft.Extensions.Logging; using Xunit.Abstractions; namespace Foundatio.Xunit; +[Obsolete("Use TestLoggerBase instead.")] public abstract class TestWithLoggingBase { protected readonly ILogger _logger; protected TestWithLoggingBase(ITestOutputHelper output) { - Log = output.ToTestLogger(); + Log = new TestLogger(output); _logger = Log.CreateLogger(GetType()); } diff --git a/tests/Foundatio.Tests/Caching/InMemoryCacheClientTests.cs b/tests/Foundatio.Tests/Caching/InMemoryCacheClientTests.cs index 6bcf9aa2..dabaff30 100644 --- a/tests/Foundatio.Tests/Caching/InMemoryCacheClientTests.cs +++ b/tests/Foundatio.Tests/Caching/InMemoryCacheClientTests.cs @@ -155,7 +155,7 @@ public override Task CanManageListsAsync() [Fact] public async Task CanSetMaxItems() { - Log.Options.DefaultMinimumLevel = LogLevel.Trace; + Log.DefaultMinimumLevel = LogLevel.Trace; // run in tight loop so that the code is warmed up and we can catch timing issues for (int x = 0; x < 5; x++) diff --git a/tests/Foundatio.Tests/Caching/InMemoryHybridCacheClientTests.cs b/tests/Foundatio.Tests/Caching/InMemoryHybridCacheClientTests.cs index 7a1e56ea..3003f60c 100644 --- a/tests/Foundatio.Tests/Caching/InMemoryHybridCacheClientTests.cs +++ b/tests/Foundatio.Tests/Caching/InMemoryHybridCacheClientTests.cs @@ -81,7 +81,7 @@ public override Task WillExpireRemoteItems() [Fact(Skip = "Skip because cache invalidation loops on this with 2 in memory cache client instances")] public override Task WillWorkWithSets() { - Log.Options.DefaultMinimumLevel = LogLevel.Trace; + Log.DefaultMinimumLevel = LogLevel.Trace; return base.WillWorkWithSets(); } diff --git a/tests/Foundatio.Tests/Hosting/HostingTests.cs b/tests/Foundatio.Tests/Hosting/HostingTests.cs index 2b81859a..b5eb71e4 100644 --- a/tests/Foundatio.Tests/Hosting/HostingTests.cs +++ b/tests/Foundatio.Tests/Hosting/HostingTests.cs @@ -16,18 +16,23 @@ namespace Foundatio.Tests.Hosting; -public class HostingTests : TestWithLoggingBase +public class HostingTests { - public HostingTests(ITestOutputHelper output) : base(output) { } + private readonly ITestOutputHelper _output; + + public HostingTests(ITestOutputHelper output) + { + _output = output; + } [Fact] public async Task WillRunSyncStartupAction() { var resetEvent = new AsyncManualResetEvent(false); var builder = new WebHostBuilder() + .ConfigureLogging(l => l.AddTestLogger(_output)) .ConfigureServices(s => { - s.AddSingleton(Log); s.AddStartupAction("Hey", () => resetEvent.Set()); s.AddHealthChecks().AddCheckForStartupActions("Critical"); }) @@ -52,9 +57,9 @@ public async Task WillRunAsyncStartupAction() { var resetEvent = new AsyncManualResetEvent(false); var builder = new WebHostBuilder() + .ConfigureLogging(l => l.AddTestLogger(_output)) .ConfigureServices(s => { - s.AddSingleton(Log); s.AddStartupAction("Hey", () => { resetEvent.Set(); @@ -82,9 +87,9 @@ public async Task WillRunAsyncStartupAction() public async Task WillRunClassStartupAction() { var builder = new WebHostBuilder() + .ConfigureLogging(l => l.AddTestLogger(_output)) .ConfigureServices(s => { - s.AddSingleton(Log); s.AddStartupAction("Hey"); s.AddHealthChecks().AddCheckForStartupActions("Critical"); }) @@ -109,10 +114,10 @@ public async Task WillRunClassStartupAction() public async Task WillStopWaitingWhenStartupActionFails() { var builder = new WebHostBuilder() + .ConfigureLogging(l => l.AddTestLogger(_output)) .CaptureStartupErrors(true) .ConfigureServices(s => { - s.AddSingleton(Log); s.AddStartupAction("Boom", () => throw new ApplicationException("Boom")); s.AddHealthChecks().AddCheckForStartupActions("Critical"); }) @@ -134,11 +139,11 @@ public async Task WillStopWaitingWhenStartupActionFails() public async Task WillHandleNoRegisteredStartupActions() { var builder = new WebHostBuilder() + .ConfigureLogging(l => l.AddTestLogger(_output)) .UseEnvironment(Environments.Development) .CaptureStartupErrors(true) .ConfigureServices(s => { - s.AddSingleton(Log); s.AddHealthChecks().AddCheckForStartupActions("Critical"); }) .Configure(app => diff --git a/tests/Foundatio.Tests/Jobs/JobTests.cs b/tests/Foundatio.Tests/Jobs/JobTests.cs index a82cb52f..54ac43bf 100644 --- a/tests/Foundatio.Tests/Jobs/JobTests.cs +++ b/tests/Foundatio.Tests/Jobs/JobTests.cs @@ -15,7 +15,7 @@ namespace Foundatio.Tests.Jobs; -public class JobTests : TestWithLoggingBase +public class JobTests : TestLoggerBase { public JobTests(ITestOutputHelper output) : base(output) { } diff --git a/tests/Foundatio.Tests/Jobs/WorkItemJobTests.cs b/tests/Foundatio.Tests/Jobs/WorkItemJobTests.cs index 575f76f6..9de1408f 100644 --- a/tests/Foundatio.Tests/Jobs/WorkItemJobTests.cs +++ b/tests/Foundatio.Tests/Jobs/WorkItemJobTests.cs @@ -19,7 +19,7 @@ namespace Foundatio.Tests.Jobs; -public class WorkItemJobTests : TestWithLoggingBase +public class WorkItemJobTests : TestLoggerBase { public WorkItemJobTests(ITestOutputHelper output) : base(output) { } diff --git a/tests/Foundatio.Tests/Metrics/DiagnosticsMetricsTests.cs b/tests/Foundatio.Tests/Metrics/DiagnosticsMetricsTests.cs index d70c416b..e96e83f5 100644 --- a/tests/Foundatio.Tests/Metrics/DiagnosticsMetricsTests.cs +++ b/tests/Foundatio.Tests/Metrics/DiagnosticsMetricsTests.cs @@ -10,13 +10,13 @@ namespace Foundatio.Tests.Metrics; -public class DiagnosticsMetricsTests : TestWithLoggingBase, IDisposable +public class DiagnosticsMetricsTests : TestLoggerBase, IDisposable { private readonly DiagnosticsMetricsClient _client; public DiagnosticsMetricsTests(ITestOutputHelper output) : base(output) { - Log.Options.DefaultMinimumLevel = LogLevel.Trace; + Log.DefaultMinimumLevel = LogLevel.Trace; _client = new DiagnosticsMetricsClient(o => o.MeterName("Test")); } diff --git a/tests/Foundatio.Tests/Queue/InMemoryQueueTests.cs b/tests/Foundatio.Tests/Queue/InMemoryQueueTests.cs index 4e14d983..29893357 100644 --- a/tests/Foundatio.Tests/Queue/InMemoryQueueTests.cs +++ b/tests/Foundatio.Tests/Queue/InMemoryQueueTests.cs @@ -290,7 +290,7 @@ public override Task VerifyDelayedRetryAttemptsAsync() [Fact] public override Task CanHandleAutoAbandonInWorker() { - Log.Options.DefaultMinimumLevel = LogLevel.Trace; + Log.DefaultMinimumLevel = LogLevel.Trace; return base.CanHandleAutoAbandonInWorker(); } diff --git a/tests/Foundatio.Tests/Utility/CloneTests.cs b/tests/Foundatio.Tests/Utility/CloneTests.cs index 628da8ee..09cec44b 100644 --- a/tests/Foundatio.Tests/Utility/CloneTests.cs +++ b/tests/Foundatio.Tests/Utility/CloneTests.cs @@ -9,7 +9,7 @@ namespace Foundatio.Tests.Utility; -public class CloneTests : TestWithLoggingBase +public class CloneTests : TestLoggerBase { public CloneTests(ITestOutputHelper output) : base(output) { } diff --git a/tests/Foundatio.Tests/Utility/DataDictionaryTests.cs b/tests/Foundatio.Tests/Utility/DataDictionaryTests.cs index 96deaa5e..7cac547e 100644 --- a/tests/Foundatio.Tests/Utility/DataDictionaryTests.cs +++ b/tests/Foundatio.Tests/Utility/DataDictionaryTests.cs @@ -10,7 +10,7 @@ namespace Foundatio.Tests.Utility; -public class DataDictionaryTests : TestWithLoggingBase +public class DataDictionaryTests : TestLoggerBase { public DataDictionaryTests(ITestOutputHelper output) : base(output) { } diff --git a/tests/Foundatio.Tests/Utility/RunTests.cs b/tests/Foundatio.Tests/Utility/RunTests.cs index fa47098a..d32ca288 100644 --- a/tests/Foundatio.Tests/Utility/RunTests.cs +++ b/tests/Foundatio.Tests/Utility/RunTests.cs @@ -9,7 +9,7 @@ namespace Foundatio.Tests.Utility; -public class RunTests : TestWithLoggingBase +public class RunTests : TestLoggerBase { public RunTests(ITestOutputHelper output) : base(output) { } diff --git a/tests/Foundatio.Tests/Utility/ScheduledTimerTests.cs b/tests/Foundatio.Tests/Utility/ScheduledTimerTests.cs index b7d25114..947a1a9e 100644 --- a/tests/Foundatio.Tests/Utility/ScheduledTimerTests.cs +++ b/tests/Foundatio.Tests/Utility/ScheduledTimerTests.cs @@ -11,7 +11,7 @@ namespace Foundatio.Tests.Utility; -public class ScheduledTimerTests : TestWithLoggingBase +public class ScheduledTimerTests : TestLoggerBase { public ScheduledTimerTests(ITestOutputHelper output) : base(output) { @@ -47,7 +47,7 @@ public Task CanRunWithMinimumInterval() private async Task CanRunConcurrentlyAsync(TimeSpan? minimumIntervalTime = null) { - Log.Options.DefaultMinimumLevel = LogLevel.Trace; + Log.DefaultMinimumLevel = LogLevel.Trace; const int iterations = 2; var countdown = new AsyncCountdownEvent(iterations); diff --git a/tests/Foundatio.Tests/Utility/SystemClockTests.cs b/tests/Foundatio.Tests/Utility/SystemClockTests.cs index dd14b656..b0c05e1b 100644 --- a/tests/Foundatio.Tests/Utility/SystemClockTests.cs +++ b/tests/Foundatio.Tests/Utility/SystemClockTests.cs @@ -8,7 +8,7 @@ namespace Foundatio.Tests.Utility; -public class SystemClockTests : TestWithLoggingBase +public class SystemClockTests : TestLoggerBase { public SystemClockTests(ITestOutputHelper output) : base(output) { } diff --git a/tests/Foundatio.Tests/Utility/TestLoggerTests.cs b/tests/Foundatio.Tests/Utility/TestLoggerTests.cs index 390df56a..07f6548d 100644 --- a/tests/Foundatio.Tests/Utility/TestLoggerTests.cs +++ b/tests/Foundatio.Tests/Utility/TestLoggerTests.cs @@ -1,30 +1,30 @@ using System; -using System.Collections.Generic; -using Foundatio.Serializer; -using Foundatio.Utility; using Foundatio.Xunit; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; -using Newtonsoft.Json.Linq; using Xunit; using Xunit.Abstractions; namespace Foundatio.Tests.Utility; -public class TestLoggerTests +public class TestLoggerTests : IClassFixture { private readonly ITestOutputHelper _output; + private readonly TestLoggerFixture _fixture; - public TestLoggerTests(ITestOutputHelper output) + public TestLoggerTests(ITestOutputHelper output, TestLoggerFixture fixture) { _output = output; + _fixture = fixture; + _fixture.Output = output; + fixture.AddServiceRegistrations(s => s.AddSingleton()); } [Fact] public void CanUseTestLogger() { var services = new ServiceCollection() - .AddLogging(b => b.AddDebug().AddXUnit(_output)) + .AddLogging(b => b.AddDebug().AddTestLogger(_output)) .AddSingleton(); IServiceProvider provider = services.BuildServiceProvider(); @@ -43,6 +43,24 @@ public void CanUseTestLogger() someClass.DoSomething(); Assert.Empty(testLogger.LogEntries); } + + [Fact] + public void CanUseTestLoggerFixture() + { + var someClass = _fixture.Services.GetRequiredService(); + + someClass.DoSomething(); + + Assert.Single(_fixture.TestLogger.LogEntries); + Assert.Contains("Doing something", _fixture.TestLogger.LogEntries[0].Message); + + _fixture.TestLogger.Clear(); + _fixture.TestLogger.SetLogLevel(LogLevel.Error); + + someClass.DoSomething(); + Assert.Empty(_fixture.TestLogger.LogEntries); + } + } public class SomeClass From 7f77da34d47746b184a7f090048ab29edb48c662 Mon Sep 17 00:00:00 2001 From: "Eric J. Smith" Date: Tue, 13 Feb 2024 17:04:44 -0600 Subject: [PATCH 4/4] More updates --- .../Caching/CacheClientTestsBase.cs | 2 +- .../Jobs/JobQueueTestsBase.cs | 2 +- .../Locks/LockTestBase.cs | 2 +- .../Messaging/MessageBusTestBase.cs | 2 +- .../Metrics/MetricsClientTestBase.cs | 2 +- .../Queue/QueueTestBase.cs | 2 +- .../Serializer/SerializerTestsBase.cs | 2 +- .../Storage/FileStorageTestsBase.cs | 2 +- src/Foundatio.Xunit/Logging/TestLogger.cs | 12 +++- src/Foundatio.Xunit/Logging/TestLoggerBase.cs | 55 ++++++------------- .../Logging/TestLoggerFixture.cs | 1 - .../Logging/TestWithLoggingBase.cs | 4 +- tests/Foundatio.Tests/Jobs/JobTests.cs | 2 +- .../Foundatio.Tests/Jobs/WorkItemJobTests.cs | 2 +- .../Metrics/DiagnosticsMetricsTests.cs | 2 +- tests/Foundatio.Tests/Utility/CloneTests.cs | 2 +- .../Utility/DataDictionaryTests.cs | 2 +- tests/Foundatio.Tests/Utility/RunTests.cs | 2 +- .../Utility/ScheduledTimerTests.cs | 2 +- .../Utility/SystemClockTests.cs | 2 +- .../Utility/TestLoggerTests.cs | 54 ++++++++++++------ 21 files changed, 79 insertions(+), 79 deletions(-) diff --git a/src/Foundatio.TestHarness/Caching/CacheClientTestsBase.cs b/src/Foundatio.TestHarness/Caching/CacheClientTestsBase.cs index def4a62c..928eea88 100644 --- a/src/Foundatio.TestHarness/Caching/CacheClientTestsBase.cs +++ b/src/Foundatio.TestHarness/Caching/CacheClientTestsBase.cs @@ -13,7 +13,7 @@ namespace Foundatio.Tests.Caching; -public abstract class CacheClientTestsBase : TestLoggerBase +public abstract class CacheClientTestsBase : TestWithLoggingBase { protected CacheClientTestsBase(ITestOutputHelper output) : base(output) { diff --git a/src/Foundatio.TestHarness/Jobs/JobQueueTestsBase.cs b/src/Foundatio.TestHarness/Jobs/JobQueueTestsBase.cs index 71239077..8af588f6 100644 --- a/src/Foundatio.TestHarness/Jobs/JobQueueTestsBase.cs +++ b/src/Foundatio.TestHarness/Jobs/JobQueueTestsBase.cs @@ -18,7 +18,7 @@ namespace Foundatio.Tests.Jobs; -public abstract class JobQueueTestsBase : TestLoggerBase +public abstract class JobQueueTestsBase : TestWithLoggingBase { private readonly ActivitySource _activitySource = new(nameof(JobQueueTestsBase)); diff --git a/src/Foundatio.TestHarness/Locks/LockTestBase.cs b/src/Foundatio.TestHarness/Locks/LockTestBase.cs index a145c955..1b987aab 100644 --- a/src/Foundatio.TestHarness/Locks/LockTestBase.cs +++ b/src/Foundatio.TestHarness/Locks/LockTestBase.cs @@ -14,7 +14,7 @@ namespace Foundatio.Tests.Locks; -public abstract class LockTestBase : TestLoggerBase +public abstract class LockTestBase : TestWithLoggingBase { protected LockTestBase(ITestOutputHelper output) : base(output) { } diff --git a/src/Foundatio.TestHarness/Messaging/MessageBusTestBase.cs b/src/Foundatio.TestHarness/Messaging/MessageBusTestBase.cs index 57eef070..fc68da43 100644 --- a/src/Foundatio.TestHarness/Messaging/MessageBusTestBase.cs +++ b/src/Foundatio.TestHarness/Messaging/MessageBusTestBase.cs @@ -16,7 +16,7 @@ namespace Foundatio.Tests.Messaging; -public abstract class MessageBusTestBase : TestLoggerBase +public abstract class MessageBusTestBase : TestWithLoggingBase { protected MessageBusTestBase(ITestOutputHelper output) : base(output) { diff --git a/src/Foundatio.TestHarness/Metrics/MetricsClientTestBase.cs b/src/Foundatio.TestHarness/Metrics/MetricsClientTestBase.cs index 7acaa0b8..dd8cde1e 100644 --- a/src/Foundatio.TestHarness/Metrics/MetricsClientTestBase.cs +++ b/src/Foundatio.TestHarness/Metrics/MetricsClientTestBase.cs @@ -14,7 +14,7 @@ namespace Foundatio.Tests.Metrics; -public abstract class MetricsClientTestBase : TestLoggerBase +public abstract class MetricsClientTestBase : TestWithLoggingBase { public MetricsClientTestBase(ITestOutputHelper output) : base(output) { } diff --git a/src/Foundatio.TestHarness/Queue/QueueTestBase.cs b/src/Foundatio.TestHarness/Queue/QueueTestBase.cs index c4774647..be043688 100644 --- a/src/Foundatio.TestHarness/Queue/QueueTestBase.cs +++ b/src/Foundatio.TestHarness/Queue/QueueTestBase.cs @@ -23,7 +23,7 @@ namespace Foundatio.Tests.Queue; -public abstract class QueueTestBase : TestLoggerBase, IDisposable +public abstract class QueueTestBase : TestWithLoggingBase, IDisposable { protected QueueTestBase(ITestOutputHelper output) : base(output) { diff --git a/src/Foundatio.TestHarness/Serializer/SerializerTestsBase.cs b/src/Foundatio.TestHarness/Serializer/SerializerTestsBase.cs index 6935d2c6..85e9651d 100644 --- a/src/Foundatio.TestHarness/Serializer/SerializerTestsBase.cs +++ b/src/Foundatio.TestHarness/Serializer/SerializerTestsBase.cs @@ -9,7 +9,7 @@ namespace Foundatio.Tests.Serializer; -public abstract class SerializerTestsBase : TestLoggerBase +public abstract class SerializerTestsBase : TestWithLoggingBase { protected SerializerTestsBase(ITestOutputHelper output) : base(output) { } diff --git a/src/Foundatio.TestHarness/Storage/FileStorageTestsBase.cs b/src/Foundatio.TestHarness/Storage/FileStorageTestsBase.cs index 772b9b7d..ea3294a0 100644 --- a/src/Foundatio.TestHarness/Storage/FileStorageTestsBase.cs +++ b/src/Foundatio.TestHarness/Storage/FileStorageTestsBase.cs @@ -16,7 +16,7 @@ namespace Foundatio.Tests.Storage; -public abstract class FileStorageTestsBase : TestLoggerBase +public abstract class FileStorageTestsBase : TestWithLoggingBase { protected FileStorageTestsBase(ITestOutputHelper output) : base(output) { } diff --git a/src/Foundatio.Xunit/Logging/TestLogger.cs b/src/Foundatio.Xunit/Logging/TestLogger.cs index 8b5ee870..11554a43 100644 --- a/src/Foundatio.Xunit/Logging/TestLogger.cs +++ b/src/Foundatio.Xunit/Logging/TestLogger.cs @@ -11,6 +11,7 @@ public class TestLogger : ILoggerFactory { private readonly Dictionary _logLevels = new(); private readonly Queue _logEntries = new(); + private int _logEntriesWritten; public TestLogger(Action configure = null) { @@ -65,7 +66,14 @@ public int MaxLogEntriesToWrite public IReadOnlyList LogEntries => _logEntries.ToArray(); - public void Clear() => _logEntries.Clear(); + public void Clear() + { + lock (_logEntries) + { + _logEntries.Clear(); + Interlocked.Exchange(ref _logEntriesWritten, 0); + } + } internal void AddLogEntry(LogEntry logEntry) { @@ -91,8 +99,6 @@ internal void AddLogEntry(LogEntry logEntry) } } - private int _logEntriesWritten = 0; - public ILogger CreateLogger(string categoryName) { return new TestLoggerLogger(categoryName, this); diff --git a/src/Foundatio.Xunit/Logging/TestLoggerBase.cs b/src/Foundatio.Xunit/Logging/TestLoggerBase.cs index 049c3935..6aa2c907 100644 --- a/src/Foundatio.Xunit/Logging/TestLoggerBase.cs +++ b/src/Foundatio.Xunit/Logging/TestLoggerBase.cs @@ -1,61 +1,38 @@ using System; -using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Xunit; using Xunit.Abstractions; namespace Foundatio.Xunit; -public abstract class TestLoggerBase : IAsyncDisposable +public abstract class TestLoggerBase : IClassFixture, IAsyncLifetime { - protected readonly ILogger _logger; - protected readonly List _disposables = []; - - protected TestLoggerBase(ITestOutputHelper output) - { - var services = new ServiceCollection(); - RegisterRequiredServices(services, output); - var sp = services.BuildServiceProvider(); - _disposables.Add(sp); - Services = sp; - Log = Services.GetTestLogger(); - _logger = Log.CreateLogger(GetType()); - } - - protected IServiceProvider Services { get; } - - protected TService GetService() where TService : notnull + protected TestLoggerBase(ITestOutputHelper output, TestLoggerFixture fixture) { - return Services.GetRequiredService(); + Fixture = fixture; + fixture.Output = output; + fixture.AddServiceRegistrations(RegisterServices); } - protected TestLogger Log { get; } + protected TestLoggerFixture Fixture { get; } + protected IServiceProvider Services => Fixture.Services; + protected TestLogger TestLogger => Fixture.TestLogger; + protected ILogger Log => Fixture.Log; - private void RegisterRequiredServices(IServiceCollection services, ITestOutputHelper output) + protected virtual void RegisterServices(IServiceCollection services) { - services.AddLogging(c => c.AddTestLogger(output)); - RegisterServices(services); } - protected virtual void RegisterServices(IServiceCollection services) + public virtual Task InitializeAsync() { + return Task.CompletedTask; } - public virtual ValueTask DisposeAsync() + public virtual Task DisposeAsync() { - foreach (var disposable in _disposables) - { - try - { - disposable.Dispose(); - } - catch (Exception ex) - { - _logger?.LogError(ex, "Error disposing resource."); - } - } - - return new ValueTask(Task.CompletedTask); + Fixture.TestLogger.Clear(); + return Task.CompletedTask; } } diff --git a/src/Foundatio.Xunit/Logging/TestLoggerFixture.cs b/src/Foundatio.Xunit/Logging/TestLoggerFixture.cs index 7f8747eb..22388fdc 100644 --- a/src/Foundatio.Xunit/Logging/TestLoggerFixture.cs +++ b/src/Foundatio.Xunit/Logging/TestLoggerFixture.cs @@ -31,7 +31,6 @@ public void AddServiceRegistrations(Action registerServices) } public IServiceProvider Services => _serviceProvider.Value; - public TestLogger TestLogger => _testLogger.Value; public ILogger Log => _log.Value; diff --git a/src/Foundatio.Xunit/Logging/TestWithLoggingBase.cs b/src/Foundatio.Xunit/Logging/TestWithLoggingBase.cs index 9c4e1978..e3adbc67 100644 --- a/src/Foundatio.Xunit/Logging/TestWithLoggingBase.cs +++ b/src/Foundatio.Xunit/Logging/TestWithLoggingBase.cs @@ -1,10 +1,8 @@ -using System; -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging; using Xunit.Abstractions; namespace Foundatio.Xunit; -[Obsolete("Use TestLoggerBase instead.")] public abstract class TestWithLoggingBase { protected readonly ILogger _logger; diff --git a/tests/Foundatio.Tests/Jobs/JobTests.cs b/tests/Foundatio.Tests/Jobs/JobTests.cs index 54ac43bf..a82cb52f 100644 --- a/tests/Foundatio.Tests/Jobs/JobTests.cs +++ b/tests/Foundatio.Tests/Jobs/JobTests.cs @@ -15,7 +15,7 @@ namespace Foundatio.Tests.Jobs; -public class JobTests : TestLoggerBase +public class JobTests : TestWithLoggingBase { public JobTests(ITestOutputHelper output) : base(output) { } diff --git a/tests/Foundatio.Tests/Jobs/WorkItemJobTests.cs b/tests/Foundatio.Tests/Jobs/WorkItemJobTests.cs index 9de1408f..575f76f6 100644 --- a/tests/Foundatio.Tests/Jobs/WorkItemJobTests.cs +++ b/tests/Foundatio.Tests/Jobs/WorkItemJobTests.cs @@ -19,7 +19,7 @@ namespace Foundatio.Tests.Jobs; -public class WorkItemJobTests : TestLoggerBase +public class WorkItemJobTests : TestWithLoggingBase { public WorkItemJobTests(ITestOutputHelper output) : base(output) { } diff --git a/tests/Foundatio.Tests/Metrics/DiagnosticsMetricsTests.cs b/tests/Foundatio.Tests/Metrics/DiagnosticsMetricsTests.cs index e96e83f5..92c1c4f6 100644 --- a/tests/Foundatio.Tests/Metrics/DiagnosticsMetricsTests.cs +++ b/tests/Foundatio.Tests/Metrics/DiagnosticsMetricsTests.cs @@ -10,7 +10,7 @@ namespace Foundatio.Tests.Metrics; -public class DiagnosticsMetricsTests : TestLoggerBase, IDisposable +public class DiagnosticsMetricsTests : TestWithLoggingBase, IDisposable { private readonly DiagnosticsMetricsClient _client; diff --git a/tests/Foundatio.Tests/Utility/CloneTests.cs b/tests/Foundatio.Tests/Utility/CloneTests.cs index 09cec44b..628da8ee 100644 --- a/tests/Foundatio.Tests/Utility/CloneTests.cs +++ b/tests/Foundatio.Tests/Utility/CloneTests.cs @@ -9,7 +9,7 @@ namespace Foundatio.Tests.Utility; -public class CloneTests : TestLoggerBase +public class CloneTests : TestWithLoggingBase { public CloneTests(ITestOutputHelper output) : base(output) { } diff --git a/tests/Foundatio.Tests/Utility/DataDictionaryTests.cs b/tests/Foundatio.Tests/Utility/DataDictionaryTests.cs index 7cac547e..96deaa5e 100644 --- a/tests/Foundatio.Tests/Utility/DataDictionaryTests.cs +++ b/tests/Foundatio.Tests/Utility/DataDictionaryTests.cs @@ -10,7 +10,7 @@ namespace Foundatio.Tests.Utility; -public class DataDictionaryTests : TestLoggerBase +public class DataDictionaryTests : TestWithLoggingBase { public DataDictionaryTests(ITestOutputHelper output) : base(output) { } diff --git a/tests/Foundatio.Tests/Utility/RunTests.cs b/tests/Foundatio.Tests/Utility/RunTests.cs index d32ca288..fa47098a 100644 --- a/tests/Foundatio.Tests/Utility/RunTests.cs +++ b/tests/Foundatio.Tests/Utility/RunTests.cs @@ -9,7 +9,7 @@ namespace Foundatio.Tests.Utility; -public class RunTests : TestLoggerBase +public class RunTests : TestWithLoggingBase { public RunTests(ITestOutputHelper output) : base(output) { } diff --git a/tests/Foundatio.Tests/Utility/ScheduledTimerTests.cs b/tests/Foundatio.Tests/Utility/ScheduledTimerTests.cs index 947a1a9e..fae29893 100644 --- a/tests/Foundatio.Tests/Utility/ScheduledTimerTests.cs +++ b/tests/Foundatio.Tests/Utility/ScheduledTimerTests.cs @@ -11,7 +11,7 @@ namespace Foundatio.Tests.Utility; -public class ScheduledTimerTests : TestLoggerBase +public class ScheduledTimerTests : TestWithLoggingBase { public ScheduledTimerTests(ITestOutputHelper output) : base(output) { diff --git a/tests/Foundatio.Tests/Utility/SystemClockTests.cs b/tests/Foundatio.Tests/Utility/SystemClockTests.cs index b0c05e1b..dd14b656 100644 --- a/tests/Foundatio.Tests/Utility/SystemClockTests.cs +++ b/tests/Foundatio.Tests/Utility/SystemClockTests.cs @@ -8,7 +8,7 @@ namespace Foundatio.Tests.Utility; -public class SystemClockTests : TestLoggerBase +public class SystemClockTests : TestWithLoggingBase { public SystemClockTests(ITestOutputHelper output) : base(output) { } diff --git a/tests/Foundatio.Tests/Utility/TestLoggerTests.cs b/tests/Foundatio.Tests/Utility/TestLoggerTests.cs index 07f6548d..3e3f23f3 100644 --- a/tests/Foundatio.Tests/Utility/TestLoggerTests.cs +++ b/tests/Foundatio.Tests/Utility/TestLoggerTests.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using Foundatio.Xunit; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -7,16 +8,13 @@ namespace Foundatio.Tests.Utility; -public class TestLoggerTests : IClassFixture +public class TestLoggerTests : TestLoggerBase { private readonly ITestOutputHelper _output; - private readonly TestLoggerFixture _fixture; - public TestLoggerTests(ITestOutputHelper output, TestLoggerFixture fixture) + public TestLoggerTests(ITestOutputHelper output, TestLoggerFixture fixture) : base(output, fixture) { _output = output; - _fixture = fixture; - _fixture.Output = output; fixture.AddServiceRegistrations(s => s.AddSingleton()); } @@ -31,7 +29,7 @@ public void CanUseTestLogger() var someClass = provider.GetRequiredService(); - someClass.DoSomething(); + someClass.DoSomething(1); var testLogger = provider.GetTestLogger(); Assert.Single(testLogger.LogEntries); @@ -40,27 +38,49 @@ public void CanUseTestLogger() testLogger.Clear(); testLogger.SetLogLevel(LogLevel.Error); - someClass.DoSomething(); + someClass.DoSomething(2); Assert.Empty(testLogger.LogEntries); } [Fact] public void CanUseTestLoggerFixture() { - var someClass = _fixture.Services.GetRequiredService(); + var someClass = Services.GetRequiredService(); - someClass.DoSomething(); + for (int i = 1; i <= 9999; i++) + someClass.DoSomething(i); - Assert.Single(_fixture.TestLogger.LogEntries); - Assert.Contains("Doing something", _fixture.TestLogger.LogEntries[0].Message); + Log.LogInformation("Hello 1"); + Log.LogInformation("Hello 2"); - _fixture.TestLogger.Clear(); - _fixture.TestLogger.SetLogLevel(LogLevel.Error); + Assert.Equal(100, TestLogger.LogEntries.Count); + Assert.Contains("Hello 2", TestLogger.LogEntries.Last().Message); - someClass.DoSomething(); - Assert.Empty(_fixture.TestLogger.LogEntries); + Fixture.TestLogger.Clear(); + TestLogger.SetLogLevel(LogLevel.Error); + + someClass.DoSomething(1002); + + Assert.Empty(TestLogger.LogEntries); + TestLogger.SetLogLevel(LogLevel.Information); } + [Fact] + public void CanUseTestLoggerFixture2() + { + var someClass = Services.GetRequiredService(); + + someClass.DoSomething(1); + + Assert.Single(TestLogger.LogEntries); + Assert.Contains("Doing something", TestLogger.LogEntries[0].Message); + + TestLogger.Clear(); + TestLogger.SetLogLevel(LogLevel.Error); + + someClass.DoSomething(2); + Assert.Empty(TestLogger.LogEntries); + } } public class SomeClass @@ -72,8 +92,8 @@ public SomeClass(ILogger logger) _logger = logger; } - public void DoSomething() + public void DoSomething(int number) { - _logger.LogInformation("Doing something"); + _logger.LogInformation("Doing something {Number}", number); } }