diff --git a/GFramework.Core.Tests/Architectures/AnotherTestArchitectureContext.cs b/GFramework.Core.Tests/Architectures/AnotherTestArchitectureContext.cs new file mode 100644 index 00000000..850a3723 --- /dev/null +++ b/GFramework.Core.Tests/Architectures/AnotherTestArchitectureContext.cs @@ -0,0 +1,8 @@ +namespace GFramework.Core.Tests.Architectures; + +/// +/// 表示用于验证上下文类型不匹配分支的测试架构上下文。 +/// +public class AnotherTestArchitectureContext : TestArchitectureContext +{ +} diff --git a/GFramework.Core.Tests/Architectures/ArchitectureServicesTests.cs b/GFramework.Core.Tests/Architectures/ArchitectureServicesTests.cs index f1474403..2c060b5b 100644 --- a/GFramework.Core.Tests/Architectures/ArchitectureServicesTests.cs +++ b/GFramework.Core.Tests/Architectures/ArchitectureServicesTests.cs @@ -262,215 +262,3 @@ public class ArchitectureServicesTests Assert.That(_services!.ModuleManager, Is.Not.Null); } } - -#region Test Classes - -public class TestArchitectureContextV3 : IArchitectureContext -{ - private readonly MicrosoftDiContainer _container = new(); - private readonly DefaultEnvironment _environment = new(); - public int Id { get; init; } - - public TService? GetService() where TService : class - { - return _container.Get(); - } - - public IReadOnlyList GetServices() where TService : class - { - return _container.GetAll(); - } - - public TModel? GetModel() where TModel : class, IModel - { - return _container.Get(); - } - - public IReadOnlyList GetModels() where TModel : class, IModel - { - return _container.GetAll(); - } - - public TSystem? GetSystem() where TSystem : class, ISystem - { - return _container.Get(); - } - - public IReadOnlyList GetSystems() where TSystem : class, ISystem - { - return _container.GetAll(); - } - - public TUtility? GetUtility() where TUtility : class, IUtility - { - return _container.Get(); - } - - public IReadOnlyList GetUtilities() where TUtility : class, IUtility - { - return _container.GetAll(); - } - - public IReadOnlyList GetServicesByPriority() where TService : class - { - return _container.GetAllByPriority(); - } - - public IReadOnlyList GetSystemsByPriority() where TSystem : class, ISystem - { - return _container.GetAllByPriority(); - } - - public IReadOnlyList GetModelsByPriority() where TModel : class, IModel - { - return _container.GetAllByPriority(); - } - - public IReadOnlyList GetUtilitiesByPriority() where TUtility : class, IUtility - { - return _container.GetAllByPriority(); - } - - public void SendEvent() where TEvent : new() - { - } - - public void SendEvent(TEvent e) where TEvent : class - { - } - - public IUnRegister RegisterEvent(Action handler) - { - return new DefaultUnRegister(() => { }); - } - - public void UnRegisterEvent(Action onEvent) - { - } - - public ValueTask SendRequestAsync(IRequest request, - CancellationToken cancellationToken = default) - { - throw new NotSupportedException(); - } - - public TResponse SendRequest(IRequest request) - { - throw new NotSupportedException(); - } - - /// - /// 测试桩:异步发送 CQRS 命令并返回响应。 - /// - /// 命令响应类型。 - /// 要发送的命令。 - /// 取消令牌。 - /// 命令响应任务。 - /// 该测试桩不支持此成员。 - public ValueTask SendCommandAsync( - GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand command, - CancellationToken cancellationToken = default) - { - throw new NotSupportedException(); - } - - /// - /// 测试桩:同步发送 CQRS 命令并返回响应。 - /// - /// 命令响应类型。 - /// 要发送的命令。 - /// 命令响应。 - /// 该测试桩不支持此成员。 - public TResponse SendCommand(GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand command) - { - throw new NotSupportedException(); - } - - /// - /// 测试桩:异步发送 CQRS 查询并返回结果。 - /// - /// 查询结果类型。 - /// 要发送的查询。 - /// 取消令牌。 - /// 查询结果任务。 - /// 该测试桩不支持此成员。 - public ValueTask SendQueryAsync( - GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery query, - CancellationToken cancellationToken = default) - { - throw new NotSupportedException(); - } - - /// - /// 测试桩:同步发送 CQRS 查询并返回结果。 - /// - /// 查询结果类型。 - /// 要发送的查询。 - /// 查询结果。 - /// 该测试桩不支持此成员。 - public TResponse SendQuery(GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery query) - { - throw new NotSupportedException(); - } - - public ValueTask PublishAsync(TNotification notification, - CancellationToken cancellationToken = default) where TNotification : INotification - { - throw new NotSupportedException(); - } - - public IAsyncEnumerable CreateStream( - IStreamRequest request, - CancellationToken cancellationToken = default) - { - throw new NotSupportedException(); - } - - public ValueTask SendAsync(TCommand command, CancellationToken cancellationToken = default) - where TCommand : IRequest - { - throw new NotSupportedException(); - } - - public ValueTask SendAsync(IRequest command, - CancellationToken cancellationToken = default) - { - throw new NotSupportedException(); - } - - public void SendCommand(ICommand command) - { - } - - public TResult SendCommand(Abstractions.Command.ICommand command) - { - return default!; - } - - public Task SendCommandAsync(IAsyncCommand command) - { - return Task.CompletedTask; - } - - public Task SendCommandAsync(IAsyncCommand command) - { - return (Task)Task.CompletedTask; - } - - public TResult SendQuery(Abstractions.Query.IQuery query) - { - return default!; - } - - public Task SendQueryAsync(IAsyncQuery query) - { - return (Task)Task.CompletedTask; - } - - public IEnvironment GetEnvironment() - { - return _environment; - } -} - -#endregion diff --git a/GFramework.Core.Tests/Architectures/ContextProviderTests.cs b/GFramework.Core.Tests/Architectures/ContextProviderTests.cs index 5fc205ce..ee196cdf 100644 --- a/GFramework.Core.Tests/Architectures/ContextProviderTests.cs +++ b/GFramework.Core.Tests/Architectures/ContextProviderTests.cs @@ -147,10 +147,3 @@ public class ContextProviderTests Assert.That(foundContext, Is.SameAs(context)); } } - -/// -/// 另一个测试用的架构上下文类,用于测试类型不匹配的情况 -/// -public class AnotherTestArchitectureContext : TestArchitectureContext -{ -} \ No newline at end of file diff --git a/GFramework.Core.Tests/Architectures/TestArchitectureBase.cs b/GFramework.Core.Tests/Architectures/TestArchitectureBase.cs index 57cf9a71..fdd144f7 100644 --- a/GFramework.Core.Tests/Architectures/TestArchitectureBase.cs +++ b/GFramework.Core.Tests/Architectures/TestArchitectureBase.cs @@ -9,6 +9,7 @@ namespace GFramework.Core.Tests.Architectures; public abstract class TestArchitectureBase : Architecture { private Action? _postRegistrationHook; + private readonly List _phaseHistory = []; /// /// 获取就绪事件是否已触发的状态 @@ -23,7 +24,7 @@ public abstract class TestArchitectureBase : Architecture /// /// 获取架构阶段历史记录列表 /// - public List PhaseHistory { get; } = []; + public IReadOnlyList PhaseHistory => _phaseHistory; /// /// 添加注册后钩子函数 @@ -43,6 +44,6 @@ public abstract class TestArchitectureBase : Architecture _postRegistrationHook?.Invoke(this); // 订阅阶段变更事件以记录历史 - PhaseChanged += (_, eventArgs) => PhaseHistory.Add(eventArgs.Phase); + PhaseChanged += (_, eventArgs) => _phaseHistory.Add(eventArgs.Phase); } } diff --git a/GFramework.Core.Tests/Architectures/TestArchitectureContextV3.cs b/GFramework.Core.Tests/Architectures/TestArchitectureContextV3.cs new file mode 100644 index 00000000..6fda6f2d --- /dev/null +++ b/GFramework.Core.Tests/Architectures/TestArchitectureContextV3.cs @@ -0,0 +1,412 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using GFramework.Core.Abstractions.Architectures; +using GFramework.Core.Abstractions.Command; +using GFramework.Core.Abstractions.Environment; +using GFramework.Core.Abstractions.Events; +using GFramework.Core.Abstractions.Model; +using GFramework.Core.Abstractions.Query; +using GFramework.Core.Abstractions.Systems; +using GFramework.Core.Abstractions.Utility; +using GFramework.Core.Environment; +using GFramework.Core.Events; +using GFramework.Core.Ioc; +using GFramework.Cqrs; +using GFramework.Cqrs.Abstractions.Cqrs; +using ICommand = GFramework.Core.Abstractions.Command.ICommand; + +namespace GFramework.Core.Tests.Architectures; + +/// +/// 为 提供最小实现的架构上下文测试桩。 +/// +/// +/// 该类型仅用于验证 的上下文传递行为,因此仅保留当前测试切片需要的容器、 +/// 环境与接口实现。所有不在本测试范围内的 CQRS 调用均明确抛出 , +/// 以避免误把测试桩当作真实运行时上下文使用。 +/// +public class TestArchitectureContextV3 : IArchitectureContext +{ + private readonly MicrosoftDiContainer _container = new(); + private readonly DefaultEnvironment _environment = new(); + + /// + /// 获取或初始化用于区分测试上下文实例的标识。 + /// + public int Id { get; init; } + + /// + /// 获取指定类型的服务实例。 + /// + /// 服务类型。 + /// 已注册的服务实例。 + /// 未注册或存在多个同类型实例时抛出。 + public TService GetService() where TService : class + { + return _container.GetRequired(); + } + + /// + /// 获取指定类型的所有服务实例。 + /// + /// 服务类型。 + /// 所有已注册的服务实例。 + public IReadOnlyList GetServices() where TService : class + { + return _container.GetAll(); + } + + /// + /// 获取指定类型的模型实例。 + /// + /// 模型类型。 + /// 已注册的模型实例。 + /// 未注册或存在多个同类型实例时抛出。 + public TModel GetModel() where TModel : class, IModel + { + return _container.GetRequired(); + } + + /// + /// 获取指定类型的所有模型实例。 + /// + /// 模型类型。 + /// 所有已注册的模型实例。 + public IReadOnlyList GetModels() where TModel : class, IModel + { + return _container.GetAll(); + } + + /// + /// 获取指定类型的系统实例。 + /// + /// 系统类型。 + /// 已注册的系统实例。 + /// 未注册或存在多个同类型实例时抛出。 + public TSystem GetSystem() where TSystem : class, ISystem + { + return _container.GetRequired(); + } + + /// + /// 获取指定类型的所有系统实例。 + /// + /// 系统类型。 + /// 所有已注册的系统实例。 + public IReadOnlyList GetSystems() where TSystem : class, ISystem + { + return _container.GetAll(); + } + + /// + /// 获取指定类型的工具实例。 + /// + /// 工具类型。 + /// 已注册的工具实例。 + /// 未注册或存在多个同类型实例时抛出。 + public TUtility GetUtility() where TUtility : class, IUtility + { + return _container.GetRequired(); + } + + /// + /// 获取指定类型的所有工具实例。 + /// + /// 工具类型。 + /// 所有已注册的工具实例。 + public IReadOnlyList GetUtilities() where TUtility : class, IUtility + { + return _container.GetAll(); + } + + /// + /// 获取指定类型的所有服务实例,并按优先级排序。 + /// + /// 服务类型。 + /// 按优先级排序后的服务实例。 + public IReadOnlyList GetServicesByPriority() where TService : class + { + return _container.GetAllByPriority(); + } + + /// + /// 获取指定类型的所有系统实例,并按优先级排序。 + /// + /// 系统类型。 + /// 按优先级排序后的系统实例。 + public IReadOnlyList GetSystemsByPriority() where TSystem : class, ISystem + { + return _container.GetAllByPriority(); + } + + /// + /// 获取指定类型的所有模型实例,并按优先级排序。 + /// + /// 模型类型。 + /// 按优先级排序后的模型实例。 + public IReadOnlyList GetModelsByPriority() where TModel : class, IModel + { + return _container.GetAllByPriority(); + } + + /// + /// 获取指定类型的所有工具实例,并按优先级排序。 + /// + /// 工具类型。 + /// 按优先级排序后的工具实例。 + public IReadOnlyList GetUtilitiesByPriority() where TUtility : class, IUtility + { + return _container.GetAllByPriority(); + } + + /// + /// 发送无参数事件。 + /// + /// 事件类型。 + public void SendEvent() where TEvent : new() + { + } + + /// + /// 发送带参数事件。 + /// + /// 事件类型。 + /// 事件实例。 + public void SendEvent(TEvent e) where TEvent : class + { + } + + /// + /// 注册事件处理器。 + /// + /// 事件类型。 + /// 事件处理回调。 + /// 用于注销回调的句柄。 + public IUnRegister RegisterEvent(Action handler) + { + return new DefaultUnRegister(() => { }); + } + + /// + /// 取消事件处理器注册。 + /// + /// 事件类型。 + /// 要取消的事件回调。 + public void UnRegisterEvent(Action onEvent) + { + } + + /// + /// 异步发送新版 CQRS 请求。 + /// + /// 响应类型。 + /// 要发送的请求。 + /// 取消令牌。 + /// 请求响应任务。 + /// 该测试桩不支持此成员。 + public ValueTask SendRequestAsync( + IRequest request, + CancellationToken cancellationToken = default) + { + throw new NotSupportedException(); + } + + /// + /// 同步发送新版 CQRS 请求。 + /// + /// 响应类型。 + /// 要发送的请求。 + /// 请求响应。 + /// 该测试桩不支持此成员。 + public TResponse SendRequest(IRequest request) + { + throw new NotSupportedException(); + } + + /// + /// 异步发送新版 CQRS 命令并返回响应。 + /// + /// 命令响应类型。 + /// 要发送的命令。 + /// 取消令牌。 + /// 命令响应任务。 + /// 该测试桩不支持此成员。 + public ValueTask SendCommandAsync( + GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand command, + CancellationToken cancellationToken = default) + { + throw new NotSupportedException(); + } + + /// + /// 同步发送新版 CQRS 命令并返回响应。 + /// + /// 命令响应类型。 + /// 要发送的命令。 + /// 命令响应。 + /// 该测试桩不支持此成员。 + public TResponse SendCommand(GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand command) + { + throw new NotSupportedException(); + } + + /// + /// 异步发送新版 CQRS 查询并返回结果。 + /// + /// 查询结果类型。 + /// 要发送的查询。 + /// 取消令牌。 + /// 查询结果任务。 + /// 该测试桩不支持此成员。 + public ValueTask SendQueryAsync( + GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery query, + CancellationToken cancellationToken = default) + { + throw new NotSupportedException(); + } + + /// + /// 同步发送新版 CQRS 查询并返回结果。 + /// + /// 查询结果类型。 + /// 要发送的查询。 + /// 查询结果。 + /// 该测试桩不支持此成员。 + public TResponse SendQuery(GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery query) + { + throw new NotSupportedException(); + } + + /// + /// 发布通知消息。 + /// + /// 通知类型。 + /// 通知实例。 + /// 取消令牌。 + /// 通知发布任务。 + /// 该测试桩不支持此成员。 + public ValueTask PublishAsync( + TNotification notification, + CancellationToken cancellationToken = default) + where TNotification : INotification + { + throw new NotSupportedException(); + } + + /// + /// 创建流式请求结果。 + /// + /// 流元素类型。 + /// 流式请求。 + /// 取消令牌。 + /// 流式响应序列。 + /// 该测试桩不支持此成员。 + public IAsyncEnumerable CreateStream( + IStreamRequest request, + CancellationToken cancellationToken = default) + { + throw new NotSupportedException(); + } + + /// + /// 异步发送无返回值请求命令。 + /// + /// 命令类型。 + /// 要发送的命令。 + /// 取消令牌。 + /// 命令发送任务。 + /// 该测试桩不支持此成员。 + public ValueTask SendAsync(TCommand command, CancellationToken cancellationToken = default) + where TCommand : IRequest + { + throw new NotSupportedException(); + } + + /// + /// 异步发送带返回值请求。 + /// + /// 响应类型。 + /// 要发送的请求。 + /// 取消令牌。 + /// 请求响应任务。 + /// 该测试桩不支持此成员。 + public ValueTask SendAsync( + IRequest command, + CancellationToken cancellationToken = default) + { + throw new NotSupportedException(); + } + + /// + /// 发送旧版无返回值命令。 + /// + /// 要发送的命令。 + public void SendCommand(ICommand command) + { + } + + /// + /// 发送旧版带返回值命令。 + /// + /// 命令响应类型。 + /// 要发送的命令。 + /// 默认响应值。 + public TResult SendCommand(GFramework.Core.Abstractions.Command.ICommand command) + { + return default!; + } + + /// + /// 异步发送旧版无返回值命令。 + /// + /// 要发送的命令。 + /// 已完成任务。 + public Task SendCommandAsync(IAsyncCommand command) + { + return Task.CompletedTask; + } + + /// + /// 异步发送旧版带返回值命令。 + /// + /// 命令响应类型。 + /// 要发送的命令。 + /// 占位任务。 + public Task SendCommandAsync(IAsyncCommand command) + { + return (Task)Task.CompletedTask; + } + + /// + /// 发送旧版查询请求。 + /// + /// 查询结果类型。 + /// 要发送的查询。 + /// 默认查询结果。 + public TResult SendQuery(GFramework.Core.Abstractions.Query.IQuery query) + { + return default!; + } + + /// + /// 异步发送旧版查询请求。 + /// + /// 查询结果类型。 + /// 要发送的查询。 + /// 占位任务。 + public Task SendQueryAsync(IAsyncQuery query) + { + return (Task)Task.CompletedTask; + } + + /// + /// 获取当前测试上下文绑定的环境实例。 + /// + /// 默认测试环境实例。 + public IEnvironment GetEnvironment() + { + return _environment; + } +} diff --git a/GFramework.Core.Tests/Coroutine/CommandCoroutineExtensionsTests.cs b/GFramework.Core.Tests/Coroutine/CommandCoroutineExtensionsTests.cs index 494688bd..84378a6b 100644 --- a/GFramework.Core.Tests/Coroutine/CommandCoroutineExtensionsTests.cs +++ b/GFramework.Core.Tests/Coroutine/CommandCoroutineExtensionsTests.cs @@ -405,7 +405,7 @@ public class CommandCoroutineExtensionsTests CommandCoroutineExtensions.SendCommandAndWaitEventCoroutine( contextAware, command, - null, + _ => { }, -1.0f)); } @@ -421,7 +421,7 @@ public class CommandCoroutineExtensionsTests // 设置上下文服务以返回null事件总线 contextAware._mockContext .Setup(ctx => ctx.GetService()) - .Returns((IEventBus?)null); + .Returns((IEventBus)null!); // 创建协程 var coroutine = CommandCoroutineExtensions.SendCommandAndWaitEventCoroutine( diff --git a/GFramework.Core.Tests/Coroutine/WaitForMultipleEventsTests.cs b/GFramework.Core.Tests/Coroutine/WaitForMultipleEventsTests.cs index 1d0588d0..88baf123 100644 --- a/GFramework.Core.Tests/Coroutine/WaitForMultipleEventsTests.cs +++ b/GFramework.Core.Tests/Coroutine/WaitForMultipleEventsTests.cs @@ -20,7 +20,7 @@ namespace GFramework.Core.Tests.Coroutine (eventBus as IDisposable)?.Dispose(); } - private IEventBus eventBus; + private IEventBus eventBus = null!; [Test] public void Constructor_RegistersBothEventTypes() @@ -138,4 +138,4 @@ namespace GFramework.Core.Tests.Coroutine public string Data { get; set; } = string.Empty; } } -} \ No newline at end of file +} diff --git a/GFramework.Core.Tests/Events/EmptyEvent.cs b/GFramework.Core.Tests/Events/EmptyEvent.cs new file mode 100644 index 00000000..60d479e2 --- /dev/null +++ b/GFramework.Core.Tests/Events/EmptyEvent.cs @@ -0,0 +1,6 @@ +namespace GFramework.Core.Tests.Events; + +/// +/// 表示不携带任何负载的空测试事件。 +/// +public sealed class EmptyEvent; diff --git a/GFramework.Core.Tests/Events/EventListenerScopeTests.cs b/GFramework.Core.Tests/Events/EventListenerScopeTests.cs index a88edba6..2c6b9569 100644 --- a/GFramework.Core.Tests/Events/EventListenerScopeTests.cs +++ b/GFramework.Core.Tests/Events/EventListenerScopeTests.cs @@ -1,3 +1,4 @@ +using System.Runtime.InteropServices; using GFramework.Core.Abstractions.Events; using GFramework.Core.Events; using Moq; @@ -303,6 +304,7 @@ public class EventListenerScopeTests /// /// 测试用的结构体事件 /// + [StructLayout(LayoutKind.Auto)] private struct StructEvent { public int Id { get; set; } @@ -318,4 +320,4 @@ public class EventListenerScopeTests using var scope = new EventListenerScope(eventBusMock.Object); // 作用域内部不验证 } -} \ No newline at end of file +} diff --git a/GFramework.Core.Tests/Events/TestEvent.cs b/GFramework.Core.Tests/Events/TestEvent.cs index c7e47ed6..d3806918 100644 --- a/GFramework.Core.Tests/Events/TestEvent.cs +++ b/GFramework.Core.Tests/Events/TestEvent.cs @@ -1,8 +1,12 @@ namespace GFramework.Core.Tests.Events; +/// +/// 表示包含整型载荷的测试事件。 +/// public sealed class TestEvent { + /// + /// 获取初始化阶段写入的接收值。 + /// public int ReceivedValue { get; init; } } - -public sealed class EmptyEvent; \ No newline at end of file diff --git a/GFramework.Core.Tests/Logging/LoggerTests.cs b/GFramework.Core.Tests/Logging/LoggerTests.cs index 3bba60a4..f26e68b6 100644 --- a/GFramework.Core.Tests/Logging/LoggerTests.cs +++ b/GFramework.Core.Tests/Logging/LoggerTests.cs @@ -419,44 +419,3 @@ public class LoggerTests Assert.That(fatalLogger.Logs[0].Level, Is.EqualTo(LogLevel.Fatal)); } } - -/// -/// 测试用的日志记录器实现类,继承自AbstractLogger -/// -public sealed class TestLogger : AbstractLogger -{ - private readonly List _logs = new(); - - /// - /// 初始化TestLogger的新实例 - /// - /// 日志记录器的名称,默认为null - /// 最小日志级别,默认为LogLevel.Info - public TestLogger(string? name = null, LogLevel minLevel = LogLevel.Info) : base(name, minLevel) - { - } - - /// - /// 获取按写入顺序保存的日志条目只读视图 - /// - public IReadOnlyList Logs => _logs; - - /// - /// 将日志信息写入内部存储 - /// - /// 日志级别 - /// 日志消息 - /// 相关异常(可选) - protected override void Write(LogLevel level, string message, Exception? exception) - { - _logs.Add(new LogEntry(level, message, exception)); - } - - /// - /// 表示单个日志条目的记录类型 - /// - /// 日志级别 - /// 日志消息 - /// 相关异常(可选) - public sealed record LogEntry(LogLevel Level, string Message, Exception? Exception); -} diff --git a/GFramework.Core.Tests/Logging/LoggingConfigurationTests.cs b/GFramework.Core.Tests/Logging/LoggingConfigurationTests.cs index 9586533f..533d368c 100644 --- a/GFramework.Core.Tests/Logging/LoggingConfigurationTests.cs +++ b/GFramework.Core.Tests/Logging/LoggingConfigurationTests.cs @@ -307,8 +307,9 @@ public class LoggingConfigurationTests }}"; var config = LoggingConfigurationLoader.LoadFromJsonString(json); - Assert.That(config.Appenders[0].Filter, Is.Not.Null); - Assert.That(config.Appenders[0].Filter.Type, Is.EqualTo("Namespace")); + var filter = config.Appenders[0].Filter; + Assert.That(filter, Is.Not.Null); + Assert.That(filter!.Type, Is.EqualTo("Namespace")); } finally { diff --git a/GFramework.Core.Tests/Logging/TestLogger.cs b/GFramework.Core.Tests/Logging/TestLogger.cs new file mode 100644 index 00000000..594b6de7 --- /dev/null +++ b/GFramework.Core.Tests/Logging/TestLogger.cs @@ -0,0 +1,45 @@ +using GFramework.Core.Abstractions.Logging; +using GFramework.Core.Logging; + +namespace GFramework.Core.Tests.Logging; + +/// +/// 表示供日志相关测试复用的内存日志记录器。 +/// +public sealed class TestLogger : AbstractLogger +{ + private readonly List _logs = new(); + + /// + /// 初始化 的新实例。 + /// + /// 日志记录器的名称;未指定时沿用基类默认行为。 + /// 允许写入的最小日志级别。 + public TestLogger(string? name = null, LogLevel minLevel = LogLevel.Info) : base(name, minLevel) + { + } + + /// + /// 获取按写入顺序保存的日志条目只读视图。 + /// + public IReadOnlyList Logs => _logs; + + /// + /// 将日志信息追加到内存列表,供断言读取。 + /// + /// 日志级别。 + /// 日志消息。 + /// 相关异常;没有异常时为 。 + protected override void Write(LogLevel level, string message, Exception? exception) + { + _logs.Add(new LogEntry(level, message, exception)); + } + + /// + /// 表示单个日志条目的不可变快照。 + /// + /// 日志级别。 + /// 日志消息。 + /// 相关异常;没有异常时为 。 + public sealed record LogEntry(LogLevel Level, string Message, Exception? Exception); +} diff --git a/GFramework.Core.Tests/Resource/ResourceManagerTests.cs b/GFramework.Core.Tests/Resource/ResourceManagerTests.cs index dba15f62..d91f447b 100644 --- a/GFramework.Core.Tests/Resource/ResourceManagerTests.cs +++ b/GFramework.Core.Tests/Resource/ResourceManagerTests.cs @@ -1,58 +1,8 @@ -using System.IO; -using GFramework.Core.Abstractions.Resource; using GFramework.Core.Resource; using NUnit.Framework; namespace GFramework.Core.Tests.Resource; -/// -/// 测试用的简单资源类 -/// -public class TestResource -{ - public string Content { get; set; } = string.Empty; - public bool IsDisposed { get; set; } -} - -/// -/// 测试用的资源加载器 -/// -public class TestResourceLoader : IResourceLoader -{ - private readonly Dictionary _resourceData = new(); - - public TestResource Load(string path) - { - if (_resourceData.TryGetValue(path, out var content)) - { - return new TestResource { Content = content }; - } - - throw new FileNotFoundException($"Resource not found: {path}"); - } - - public async Task LoadAsync(string path) - { - await Task.Delay(10).ConfigureAwait(false); // 模拟异步加载 - return Load(path); - } - - public void Unload(TestResource resource) - { - resource.IsDisposed = true; - } - - public bool CanLoad(string path) - { - return _resourceData.ContainsKey(path); - } - - public void AddTestData(string path, string content) - { - _resourceData[path] = content; - } -} - /// /// ResourceManager 功能测试类 /// diff --git a/GFramework.Core.Tests/Resource/TestResource.cs b/GFramework.Core.Tests/Resource/TestResource.cs new file mode 100644 index 00000000..a9f78cae --- /dev/null +++ b/GFramework.Core.Tests/Resource/TestResource.cs @@ -0,0 +1,17 @@ +namespace GFramework.Core.Tests.Resource; + +/// +/// 表示 ResourceManager 测试使用的简单资源对象。 +/// +public class TestResource +{ + /// + /// 获取或设置资源内容。 + /// + public string Content { get; set; } = string.Empty; + + /// + /// 获取或设置一个值,指示资源是否已经被测试加载器标记为已卸载。 + /// + public bool IsDisposed { get; set; } +} diff --git a/GFramework.Core.Tests/Resource/TestResourceLoader.cs b/GFramework.Core.Tests/Resource/TestResourceLoader.cs new file mode 100644 index 00000000..5dcf6a14 --- /dev/null +++ b/GFramework.Core.Tests/Resource/TestResourceLoader.cs @@ -0,0 +1,52 @@ +using System.IO; +using GFramework.Core.Abstractions.Resource; + +namespace GFramework.Core.Tests.Resource; + +/// +/// 为 ResourceManager 测试提供可控数据源的资源加载器。 +/// +public class TestResourceLoader : IResourceLoader +{ + private readonly Dictionary _resourceData = new(StringComparer.Ordinal); + + /// + public TestResource Load(string path) + { + if (_resourceData.TryGetValue(path, out var content)) + { + return new TestResource { Content = content }; + } + + throw new FileNotFoundException($"Resource not found: {path}"); + } + + /// + public async Task LoadAsync(string path) + { + await Task.Delay(10).ConfigureAwait(false); // 模拟异步加载 + return Load(path); + } + + /// + public void Unload(TestResource resource) + { + resource.IsDisposed = true; + } + + /// + public bool CanLoad(string path) + { + return _resourceData.ContainsKey(path); + } + + /// + /// 向测试加载器注册一条可返回的资源数据。 + /// + /// 资源路径。 + /// 资源内容。 + public void AddTestData(string path, string content) + { + _resourceData[path] = content; + } +} diff --git a/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md b/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md index 13d194c0..4c823765 100644 --- a/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md +++ b/ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md @@ -6,53 +6,41 @@ ## 当前恢复点 -- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-086` -- 当前阶段:`Phase 86` +- 恢复点编号:`ANALYZER-WARNING-REDUCTION-RP-087` +- 当前阶段:`Phase 87` - 当前焦点: - - `2026-04-27` 已按 `$gframework-batch-boot 100` 连续执行多波 `MA0048` 小切片,当前以 `GFramework.Core.Tests` 的测试辅助类型拆分为主 - - `2026-04-27` 已按 `$gframework-pr-review` 收敛 `PR #298` 的有效 nitpick,修复测试辅助类型的只读暴露、线程安全、空安全与文档一致性问题 - - 本轮已完成 `ArchitectureContextTests`、`AsyncQueryExecutorTests`、`CommandExecutorTests`、`StateTests`、`StateMachineTests`、`StateMachineSystemTests`、`ArchitectureModulesBehaviorTests`、`ArchitectureAdditionalCqrsHandlersTests`、`QueryCoroutineExtensionsTests`、`ObjectPoolTests`、`AbstractContextUtilityTests` 等低风险单文件切片 - - 当前仓库根权威基线已从 `353 Warning(s)` / `279` 个唯一位点下降到 `288 Warning(s)` / `214` 个唯一位点 - - 当前分支下一波更适合转向 `GameContextTests.cs`、`ArchitectureServicesTests.cs`、`RegistryInitializationHookBaseTests.cs` 这类仍在 `GFramework.Core.Tests` 内、但已混入 `CS8766` / `MA0016` 的小型混合切片 + - `2026-04-28` 已按 `$gframework-batch-boot 50` 先执行仓库根 `dotnet clean` + `dotnet build`,建立本轮权威基线 `288 Warning(s)` / `214` 个唯一位点 + - 本轮已并行收敛 `GameContextTests.cs`、`ArchitectureServicesTests.cs`、`RegistryInitializationHookBaseTests.cs`、`CqrsDispatcherCacheTests.cs` 与 `CqrsHandlerRegistrarTests.cs` + - 主线程已补齐 `ResourceManagerTests.cs`、`TestEvent.cs`、`LoggerTests.cs`、`ContextProviderTests.cs`、`TestArchitectureBase.cs`、`CommandCoroutineExtensionsTests.cs` 等 `Core.Tests` 零散 warning + - 当前 `GFramework.Core.Tests` 与 `GFramework.Cqrs.Tests` 的受影响项目 Release 构建都已恢复到 `0 Warning(s)` / `0 Error(s)` + - 当前仓库根权威基线已从本轮开始时的 `288 Warning(s)` / `214` 个唯一位点下降到 `236 Warning(s)` / `162` 个唯一位点;剩余 warning 只集中在 `Mediator/*` 与 `YamlConfigSchemaValidator*` ## 当前活跃事实 -- 当前 `origin/main` 基线提交为 `7cfdd2c`(`2026-04-27T16:59:57+08:00`)。 +- 当前 `origin/main` 基线提交为 `6cc87a9`(`2026-04-27T20:28:50+08:00`)。 - 当前直接验证结果: - - `dotnet build GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release` - - 最新结果:成功;`28 Warning(s)`、`0 Error(s)`;当前 warning 来自 `GameContextTests.cs`、`ArchitectureServicesTests.cs`、`RegistryInitializationHookBaseTests.cs` 等既有热点 - - `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --no-build` - - 最新结果:成功;`1610` 通过、`0` 失败 - `dotnet clean` - - 最新结果:成功;已刷新仓库根 non-incremental 基线 + - 最新结果:成功;已刷新本轮 final non-incremental 仓库根基线 - `dotnet build` - - 最新结果:成功;`288 Warning(s)`、`0 Error(s)`,唯一位点 `214` - - `dotnet build GFramework.Game/GFramework.Game.csproj -c Release` + - 最新结果:成功;`236 Warning(s)`、`0 Error(s)`,唯一位点 `162` + - `dotnet build GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release` + - 最新结果:成功;`0 Warning(s)`、`0 Error(s)` + - `dotnet build GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release` - 最新结果:成功;`0 Warning(s)`、`0 Error(s)` - - `dotnet test GFramework.Game.Tests/GFramework.Game.Tests.csproj -c Release --filter "FullyQualifiedName~YamlConfigLoaderTests.ReadYamlAsync_Should_Preserve_OperationCanceledException_When_Cancellation_Is_Requested"` - - 最新结果:成功;`1` 通过、`0` 失败 - - `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --filter "FullyQualifiedName~MicrosoftDiContainerTests.GetAllByPriority_Should_Sort_By_Priority_Ascending"` - - 最新结果:成功;`1` 通过、`0` 失败 - - `dotnet format GFramework.sln --verify-no-changes --include GFramework.Game/Config/YamlConfigLoader.cs GFramework.Game.Tests/Config/YamlConfigLoaderTests.cs GFramework.Core.Tests/Ioc/IMixedService.cs GFramework.Core.Tests/Ioc/IPrioritizedService.cs GFramework.Core.Tests/Ioc/PrioritizedService.cs GFramework.Core.Tests/Query/TestAsyncQueryWithExceptionV4.cs` - - 最新结果:成功;本次 PR follow-up 改动文件无需额外格式化 - 当前批次摘要: - - 本轮通过多批并行 worker 共完成 `20+` 个 `GFramework.Core.Tests` 文件的测试辅助类型拆分,集中消化纯 `MA0048` warning 热点 - - 本轮停止时共享工作树共有 `61` 个变更条目,仍低于 `$gframework-batch-boot 100` 的文件停止线 - - 本轮仓库根权威 warning 已从开始时的 `353` 下降到 `288`,且 `GFramework.Core.Tests` 受影响项目的 Release 构建已恢复到 `0 Warning(s)` / `0 Error(s)` + - 本轮接受并集成 `GameContextTests.cs`、`ArchitectureServicesTests.cs`、`RegistryInitializationHookBaseTests.cs`、`CqrsDispatcherCacheTests.cs`、`CqrsHandlerRegistrarTests.cs` 五个并行 worker 切片 + - 主线程补齐 `Core.Tests` 内剩余零散 warning,使 `GFramework.Core.Tests` 项目级 Release 构建回到 `0 Warning(s)` / `0 Error(s)` + - 当前 `origin/main...HEAD` 已提交 branch diff 仍为 `21` 个文件;计入当前待提交工作树后的并集 footprint 为 `45 / 50` 个文件,已接近本轮停止线 - 当前建议保留到下一波次的候选: - - `GFramework.Core.Tests/Architectures/GameContextTests.cs` 的 `4` 个 `CS8766` 与 `2` 个 `MA0048` - - `GFramework.Core.Tests/Architectures/ArchitectureServicesTests.cs` 的 `4` 个 `CS8766` 与 `1` 个 `MA0048` - - `GFramework.Core.Tests/Architectures/RegistryInitializationHookBaseTests.cs` 的 `1` 个 `MA0016` 与 `5` 个 `MA0048` + - `GFramework.Cqrs.Tests/Mediator/MediatorArchitectureIntegrationTests.cs`、`MediatorComprehensiveTests.cs`、`MediatorAdvancedFeaturesTests.cs` 的高密度 `MA0048` / `MA0004` - `GFramework.Game/Config/YamlConfigSchemaValidator.cs` 与 `YamlConfigSchemaValidator.ObjectKeywords.cs` 的高耦合 warning 热点 ## 当前风险 -- `GFramework.Cqrs.Tests/Mediator/*` 仍有 `47` / `44` / `34` 个唯一 warning 位点,属于高 changed-file 风险的 `MA0048` 大波次。 - - 缓解措施:优先继续处理 `6-7` 个 warning 的小文件切片,避免一次性推高文件数。 -- `GameContextTests.cs`、`ArchitectureServicesTests.cs` 这类混合 `CS8766` / `MA0048` 文件不再适合继续用“纯拆分”模式批量下发。 - - 缓解措施:下一波由主线程先局部修正可空签名,再决定是否继续并行拆分。 -- `YamlConfigSchemaValidator*` 仍然聚集多类高耦合 warning。 - - 缓解措施:继续把它们留在独立波次,不与测试项目的低风险拆分混提。 +- `GFramework.Cqrs.Tests/Mediator/*` 仍有 `94` / `88` / `68` 条输出 warning,属于高 changed-file 风险的 `MA0048` 大波次。 + - 缓解措施:当前 footprint 已到 `45 / 50`,下一轮应在新提交基础上单独规划 `Mediator*` 波次,而不是继续叠在本轮工作树上。 +- `YamlConfigSchemaValidator*` 仍然聚集 `222` 条输出 warning,且同时混有 `MA0048`、`MA0009`、`MA0051`、`MA0006`。 + - 缓解措施:保持为独立高耦合波次,不与测试项目拆分混提。 ## 活跃文档 @@ -72,11 +60,11 @@ ## 验证说明 - 权威验证结果统一维护在“当前活跃事实”。 -- `GFramework.Core.Tests` 项目级 Release 构建已在本轮清零,但仓库根 non-incremental 构建仍保留大量既有 warning。 +- `GFramework.Core.Tests` 与 `GFramework.Cqrs.Tests` 的当前受影响项目 Release 构建都已在本轮清零,但仓库根 non-incremental 构建仍保留 `Mediator/*` 与 `YamlConfigSchemaValidator*` 既有 warning。 - warning reduction 的仓库级真值只以同轮 `dotnet clean` 后的 `dotnet build` 为准。 ## 下一步建议 -1. 提交本轮多批 `MA0048` warning reduction 与 `ai-plan` 同步。 -2. 下一波由主线程先处理 `GameContextTests.cs` / `ArchitectureServicesTests.cs` 的 `CS8766`,再决定是否继续拆分剩余 `MA0048`。 -3. 继续将 `YamlConfigSchemaValidator*` 与 `GFramework.Cqrs.Tests/Mediator/*` 作为独立高风险波次处理。 +1. 提交本轮 `Core.Tests` / `Cqrs.Tests` warning reduction 与 `ai-plan` 同步。 +2. 下一轮在新提交基础上单独规划 `Mediator/*` 波次,避免在 `45 / 50` footprint 状态继续扩批。 +3. 将 `YamlConfigSchemaValidator*` 保持为独立高耦合波次,必要时先由主线程局部切分再决定是否并行。 diff --git a/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md b/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md index b57f4b67..edbc3a06 100644 --- a/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md +++ b/ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md @@ -1,5 +1,55 @@ # Analyzer Warning Reduction 追踪 +## 2026-04-28 — RP-087 + +### 阶段:按 `$gframework-batch-boot 50` 并行收敛 `Core.Tests` / `Cqrs.Tests` 低风险切片 + +- 触发背景: + - 用户再次要求先拿仓库根构建 warning,再把可切分的 warning 批次分派给多个 subagent,以降低主线程上下文压力 + - 当前分支与 `origin/main@6cc87a9` 无提交差异,适合从单文件和小型混合 warning 切片重新起步 +- 主线程实施: + - 先执行仓库规则要求的 non-incremental 基线:`dotnet restore GFramework.sln -p:RestoreFallbackFolders=` 以修复当前 WSL 环境里的旧 fallback 资产,再执行仓库根 `dotnet clean` + `dotnet build` + - 基线结果:`288 Warning(s)`、唯一位点 `214` + - 并行下发四个 disjoint worker: + - `GameContextTests.cs` + - `ArchitectureServicesTests.cs` + - `RegistryInitializationHookBaseTests.cs` + - `CqrsDispatcherCacheTests.cs` + - 主线程补齐不与 worker 重叠的零散切片: + - `ResourceManagerTests.cs` + - `TestEvent.cs` + - `EventListenerScopeTests.cs` + - `TestArchitectureBase.cs` + - `ContextProviderTests.cs` + - `LoggerTests.cs` + - `LoggingConfigurationTests.cs` + - `WaitForMultipleEventsTests.cs` + - `CommandCoroutineExtensionsTests.cs` + - 集成 `GameContextTests`、`CqrsDispatcherCacheTests` 与 `CqrsHandlerRegistrarTests` worker 产出的 commit `a7be413` / `9098490` / `98afcbf`,并接受已直接落到共享工作树的 `ArchitectureServicesTests` / `RegistryInitializationHookBaseTests` 切片 + - 重新执行仓库根 `dotnet clean` + `dotnet build` + - final 结果:`236 Warning(s)`、唯一位点 `162` +- 验证里程碑: + - `dotnet build GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release` + - 结果:成功;`0 Warning(s)`、`0 Error(s)` + - `dotnet build GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release` + - 结果:成功;`0 Warning(s)`、`0 Error(s)` + - `dotnet clean` + - 结果:成功 + - `dotnet build` + - 结果:成功;`236 Warning(s)`、`0 Error(s)`,唯一位点 `162` + - `git diff --name-only refs/remotes/origin/main...HEAD | wc -l` + - 结果:`21` + - 已提交 diff 与当前工作树变更并集文件数 + - 结果:`45 / 50` +- 当前结论: + - 本轮已经把剩余 `Core.Tests` warning 全部清空,并把 `CqrsDispatcherCacheTests.cs` 与 `CqrsHandlerRegistrarTests.cs` 这两处 `Cqrs.Tests` 单文件 `MA0048` 热点从仓库根中移除 + - 仓库根权威 warning 从 `288` 下降到 `236`,唯一位点从 `214` 下降到 `162` + - 在 `45 / 50` footprint 下,继续扩批将明显压缩 review 余量;剩余切片也只剩 `Mediator*` 与 `YamlConfigSchemaValidator*` 这两组高风险热点,因此本轮应在这里收口 +- 下一步: + 1. 提交本轮已验证的 warning reduction 与 `ai-plan` 同步。 + 2. 下一轮在新提交基础上单独规划 `Mediator*` 波次。 + 3. 将 `YamlConfigSchemaValidator*` 保持为独立高耦合波次,再决定是否需要新的并行切法。 + ## 2026-04-27 — RP-086 ### 阶段:收敛 PR #298 的 CodeRabbit nitpick follow-up