using System.IO; using System.Reflection; using GFramework.Core.Abstractions.Logging; using GFramework.Core.Logging; namespace GFramework.Core.Tests.Logging; /// /// 测试 LoggerFactory 相关功能的测试类。 /// [TestFixture] [NonParallelizable] public class LoggerFactoryTests { /// /// 测试 ConsoleLoggerFactory 的 GetLogger 方法是否返回 ConsoleLogger 实例。 /// [Test] public void ConsoleLoggerFactory_GetLogger_ShouldReturnConsoleLogger() { var factory = new ConsoleLoggerFactory(); var logger = factory.GetLogger("TestLogger"); Assert.That(logger, Is.Not.Null); Assert.That(logger, Is.InstanceOf()); Assert.That(logger.Name(), Is.EqualTo("TestLogger")); } /// /// 测试 ConsoleLoggerFactory 使用不同名称获取不同的 logger 实例。 /// [Test] public void ConsoleLoggerFactory_GetLogger_WithDifferentNames_ShouldReturnDifferentLoggers() { var factory = new ConsoleLoggerFactory(); var logger1 = factory.GetLogger("Logger1"); var logger2 = factory.GetLogger("Logger2"); Assert.That(logger1.Name(), Is.EqualTo("Logger1")); Assert.That(logger2.Name(), Is.EqualTo("Logger2")); } /// /// 测试 ConsoleLoggerFactory 使用默认最小级别时的行为。 /// [Test] public void ConsoleLoggerFactory_GetLogger_WithDefaultMinLevel_ShouldUseInfo() { var factory = new ConsoleLoggerFactory(); _ = (ConsoleLogger)factory.GetLogger("TestLogger"); var stringWriter = new StringWriter(); var testLogger = new ConsoleLogger("TestLogger", LogLevel.Info, stringWriter, false); testLogger.Debug("Debug message"); testLogger.Info("Info message"); var output = stringWriter.ToString(); Assert.That(output, Does.Not.Contain("Debug message")); Assert.That(output, Does.Contain("Info message")); } /// /// 测试 ConsoleLoggerFactoryProvider 创建 logger 时使用提供者的最小级别设置。 /// [Test] public void ConsoleLoggerFactoryProvider_CreateLogger_ShouldReturnLoggerWithProviderMinLevel() { var provider = new ConsoleLoggerFactoryProvider { MinLevel = LogLevel.Debug }; _ = (ConsoleLogger)provider.CreateLogger("TestLogger"); var stringWriter = new StringWriter(); var testLogger = new ConsoleLogger("TestLogger", LogLevel.Debug, stringWriter, false); testLogger.Debug("Debug message"); testLogger.Trace("Trace message"); var output = stringWriter.ToString(); Assert.That(output, Does.Contain("Debug message")); Assert.That(output, Does.Not.Contain("Trace message")); } /// /// 测试 ConsoleLoggerFactoryProvider 创建 logger 时使用提供的名称。 /// [Test] public void ConsoleLoggerFactoryProvider_CreateLogger_ShouldUseProvidedName() { var provider = new ConsoleLoggerFactoryProvider(); var logger = provider.CreateLogger("MyLogger"); Assert.That(logger.Name(), Is.EqualTo("MyLogger")); } /// /// 测试 LoggerFactoryResolver 的 Provider 属性是否有默认值。 /// [Test] public void LoggerFactoryResolver_Provider_ShouldHaveDefaultValue() { Assert.That(LoggerFactoryResolver.Provider, Is.Not.Null); Assert.That(LoggerFactoryResolver.Provider, Is.InstanceOf()); } /// /// 测试 LoggerFactoryResolver 的 Provider 属性可以被更改。 /// [Test] public void LoggerFactoryResolver_Provider_CanBeChanged() { var customProvider = new ConsoleLoggerFactoryProvider { MinLevel = LogLevel.Debug }; var originalProvider = LoggerFactoryResolver.Provider; LoggerFactoryResolver.Provider = customProvider; Assert.That(LoggerFactoryResolver.Provider, Is.SameAs(customProvider)); LoggerFactoryResolver.Provider = originalProvider; } /// /// 测试 LoggerFactoryResolver 的 MinLevel 属性是否有默认值。 /// [Test] public void LoggerFactoryResolver_MinLevel_ShouldHaveDefaultValue() { Assert.That(LoggerFactoryResolver.MinLevel, Is.EqualTo(LogLevel.Info)); } /// /// 测试 LoggerFactoryResolver 的 MinLevel 属性可以被更改。 /// [Test] public void LoggerFactoryResolver_MinLevel_CanBeChanged() { var originalLevel = LoggerFactoryResolver.MinLevel; LoggerFactoryResolver.MinLevel = LogLevel.Debug; Assert.That(LoggerFactoryResolver.MinLevel, Is.EqualTo(LogLevel.Debug)); LoggerFactoryResolver.MinLevel = originalLevel; } /// /// 测试 ConsoleLoggerFactoryProvider 的 MinLevel 属性是否有默认值。 /// [Test] public void ConsoleLoggerFactoryProvider_MinLevel_ShouldHaveDefaultValue() { var provider = new ConsoleLoggerFactoryProvider(); Assert.That(provider.MinLevel, Is.EqualTo(LogLevel.Info)); } /// /// 测试 ConsoleLoggerFactoryProvider 的 MinLevel 属性可以被更改。 /// [Test] public void ConsoleLoggerFactoryProvider_MinLevel_CanBeChanged() { var provider = new ConsoleLoggerFactoryProvider(); provider.MinLevel = LogLevel.Debug; Assert.That(provider.MinLevel, Is.EqualTo(LogLevel.Debug)); } /// /// 测试 LoggerFactoryResolver 的 Provider 创建 logger 时使用提供者设置。 /// [Test] public void LoggerFactoryResolver_Provider_CreateLogger_ShouldUseProviderSettings() { var originalProvider = LoggerFactoryResolver.Provider; var provider = new ConsoleLoggerFactoryProvider { MinLevel = LogLevel.Warning }; LoggerFactoryResolver.Provider = provider; _ = (ConsoleLogger)provider.CreateLogger("TestLogger"); var stringWriter = new StringWriter(); var testLogger = new ConsoleLogger("TestLogger", LogLevel.Warning, stringWriter, false); testLogger.Warn("Warn message"); testLogger.Info("Info message"); var output = stringWriter.ToString(); Assert.That(output, Does.Contain("Warn message")); Assert.That(output, Does.Not.Contain("Info message")); LoggerFactoryResolver.Provider = originalProvider; } /// /// 测试 LoggerFactoryResolver 的 MinLevel 属性影响新创建的 logger。 /// [Test] public void LoggerFactoryResolver_MinLevel_AffectsNewLoggers() { var originalMinLevel = LoggerFactoryResolver.MinLevel; LoggerFactoryResolver.MinLevel = LogLevel.Error; var provider = LoggerFactoryResolver.Provider; _ = (ConsoleLogger)provider.CreateLogger("TestLogger"); var stringWriter = new StringWriter(); var testLogger = new ConsoleLogger("TestLogger", LogLevel.Error, stringWriter, false); testLogger.Error("Error message"); testLogger.Warn("Warn message"); var output = stringWriter.ToString(); Assert.That(output, Does.Contain("Error message")); Assert.That(output, Does.Not.Contain("Warn message")); LoggerFactoryResolver.MinLevel = originalMinLevel; } /// /// 验证默认 provider 激活失败时会回退到静默 provider。 /// [Test] public void LoggerFactoryResolver_Provider_Should_Fall_Back_To_SilentProvider_When_DefaultProvider_Activation_Fails() { var originalProvider = LoggerFactoryResolver.Provider; var originalTypeName = GetDefaultProviderTypeName(); try { ResetProvider(); SetDefaultProviderTypeName(typeof(ThrowingLoggerFactoryProvider).AssemblyQualifiedName!); var provider = LoggerFactoryResolver.Provider; var logger = provider.CreateLogger("Fallback"); Assert.Multiple(() => { Assert.That(provider.GetType().Name, Is.EqualTo("SilentLoggerFactoryProvider")); Assert.That(provider.MinLevel, Is.EqualTo(LogLevel.Info)); Assert.That(logger.IsEnabledForLevel(LogLevel.Error), Is.False); }); } finally { SetDefaultProviderTypeName(originalTypeName); LoggerFactoryResolver.Provider = originalProvider; } } /// /// 验证并发首次访问默认 provider 时只会创建一个实例,并向所有调用方返回相同引用。 /// [Test] public async Task LoggerFactoryResolver_Provider_Should_Create_A_Single_Default_Instance_When_Accessed_Concurrently() { var originalProvider = LoggerFactoryResolver.Provider; var originalTypeName = GetDefaultProviderTypeName(); try { BlockingLoggerFactoryProvider.Reset(); ResetProvider(); SetDefaultProviderTypeName(typeof(BlockingLoggerFactoryProvider).AssemblyQualifiedName!); var startGate = new ManualResetEventSlim(false); var tasks = Enumerable.Range(0, 8) .Select(_ => Task.Run(() => { startGate.Wait(); return LoggerFactoryResolver.Provider; })) .ToArray(); startGate.Set(); Assert.That( SpinWait.SpinUntil( () => BlockingLoggerFactoryProvider.ConstructionCount >= 1, TimeSpan.FromSeconds(2)), Is.True, "The test provider should start construction after concurrent access begins."); BlockingLoggerFactoryProvider.ReleaseConstruction(); var providers = await Task.WhenAll(tasks); Assert.Multiple(() => { Assert.That(BlockingLoggerFactoryProvider.ConstructionCount, Is.EqualTo(1)); Assert.That(providers.Distinct().Count(), Is.EqualTo(1)); Assert.That(LoggerFactoryResolver.Provider, Is.SameAs(providers[0])); }); } finally { BlockingLoggerFactoryProvider.ReleaseConstruction(); BlockingLoggerFactoryProvider.Reset(); SetDefaultProviderTypeName(originalTypeName); LoggerFactoryResolver.Provider = originalProvider; } } /// /// 测试 ConsoleLoggerFactory 创建的多个 logger 实例是独立的。 /// [Test] public void ConsoleLoggerFactory_MultipleLoggers_ShouldBeIndependent() { var factory = new ConsoleLoggerFactory(); var logger1 = factory.GetLogger("Logger1"); var logger2 = factory.GetLogger("Logger2", LogLevel.Debug); Assert.That(logger1.Name(), Is.EqualTo("Logger1")); Assert.That(logger2.Name(), Is.EqualTo("Logger2")); } /// /// 测试 ConsoleLoggerFactoryProvider 的 MinLevel 不会影响已创建的 logger。 /// [Test] public void ConsoleLoggerFactoryProvider_MinLevel_DoesNotAffectCreatedLogger() { var provider = new ConsoleLoggerFactoryProvider { MinLevel = LogLevel.Error }; provider.CreateLogger("TestLogger"); var stringWriter = new StringWriter(); var testLogger = new ConsoleLogger("TestLogger", LogLevel.Error, stringWriter, false); testLogger.Error("Error message"); testLogger.Fatal("Fatal message"); var output = stringWriter.ToString(); Assert.That(output, Does.Contain("Error message")); Assert.That(output, Does.Contain("Fatal message")); } private static string GetDefaultProviderTypeName() { return (string)GetResolverField("DefaultProviderTypeName").GetValue(null)!; } private static void SetDefaultProviderTypeName(string typeName) { GetResolverField("DefaultProviderTypeName").SetValue(null, typeName); } private static void ResetProvider() { GetResolverField("_provider").SetValue(null, null); } private static FieldInfo GetResolverField(string fieldName) { return typeof(LoggerFactoryResolver).GetField( fieldName, BindingFlags.NonPublic | BindingFlags.Static) ?? throw new InvalidOperationException( $"Failed to locate LoggerFactoryResolver.{fieldName}."); } /// /// 用于触发默认 provider 激活失败回退路径的测试桩。 /// public sealed class ThrowingLoggerFactoryProvider : ILoggerFactoryProvider { /// /// 初始化一个始终抛出异常的 provider。 /// /// 始终抛出,用于覆盖回退路径。 public ThrowingLoggerFactoryProvider() { throw new InvalidOperationException("Simulated provider activation failure."); } /// /// 获取或设置最小日志级别。 /// public LogLevel MinLevel { get; set; } = LogLevel.Info; /// /// 创建日志器。 /// /// 日志器名称。 /// 该测试桩永远不会成功创建日志器。 /// 始终抛出,因为该方法不应被调用。 public ILogger CreateLogger(string name) { throw new NotSupportedException(); } } /// /// 用于验证并发首次初始化路径只创建单个 provider 实例的测试桩。 /// public sealed class BlockingLoggerFactoryProvider : ILoggerFactoryProvider { private static int _constructionCount; private static ManualResetEventSlim _constructionGate = new(false); /// /// 初始化一个会阻塞构造完成的 provider,用于放大并发首次访问竞争窗口。 /// public BlockingLoggerFactoryProvider() { Interlocked.Increment(ref _constructionCount); _constructionGate.Wait(TimeSpan.FromSeconds(5)); } /// /// 获取已经发生的构造次数。 /// public static int ConstructionCount => Volatile.Read(ref _constructionCount); /// /// 获取或设置最小日志级别。 /// public LogLevel MinLevel { get; set; } = LogLevel.Info; /// /// 创建测试日志器。 /// /// 日志器名称。 /// 带有当前最小级别设置的控制台日志器。 public ILogger CreateLogger(string name) { return new ConsoleLogger(name, MinLevel, TextWriter.Null, false); } /// /// 重置该测试桩的并发观测状态。 /// public static void Reset() { _constructionGate = new ManualResetEventSlim(false); Interlocked.Exchange(ref _constructionCount, 0); } /// /// 释放当前被阻塞的 provider 构造过程。 /// public static void ReleaseConstruction() { _constructionGate.Set(); } } }