test(analyzer): 收敛 Core.Tests 与 Cqrs.Tests 警告批次

- 拆分 GameContextTests、ArchitectureServicesTests、RegistryInitializationHookBaseTests 与 Cqrs 测试辅助类型,消除批次内 MA0048 热点
- 修复 Core.Tests 零散可空性、集合抽象和测试辅助 warning,使受影响 Release 构建清零
- 更新 analyzer-warning-reduction 跟踪与 trace,记录 236 条仓库根 warning 基线和 45/50 停止点
This commit is contained in:
gewuyou 2026-04-28 08:32:00 +08:00
parent 98afcbffb3
commit ba4ace8d40
18 changed files with 634 additions and 358 deletions

View File

@ -0,0 +1,8 @@
namespace GFramework.Core.Tests.Architectures;
/// <summary>
/// 表示用于验证上下文类型不匹配分支的测试架构上下文。
/// </summary>
public class AnotherTestArchitectureContext : TestArchitectureContext
{
}

View File

@ -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<TService>() where TService : class
{
return _container.Get<TService>();
}
public IReadOnlyList<TService> GetServices<TService>() where TService : class
{
return _container.GetAll<TService>();
}
public TModel? GetModel<TModel>() where TModel : class, IModel
{
return _container.Get<TModel>();
}
public IReadOnlyList<TModel> GetModels<TModel>() where TModel : class, IModel
{
return _container.GetAll<TModel>();
}
public TSystem? GetSystem<TSystem>() where TSystem : class, ISystem
{
return _container.Get<TSystem>();
}
public IReadOnlyList<TSystem> GetSystems<TSystem>() where TSystem : class, ISystem
{
return _container.GetAll<TSystem>();
}
public TUtility? GetUtility<TUtility>() where TUtility : class, IUtility
{
return _container.Get<TUtility>();
}
public IReadOnlyList<TUtility> GetUtilities<TUtility>() where TUtility : class, IUtility
{
return _container.GetAll<TUtility>();
}
public IReadOnlyList<TService> GetServicesByPriority<TService>() where TService : class
{
return _container.GetAllByPriority<TService>();
}
public IReadOnlyList<TSystem> GetSystemsByPriority<TSystem>() where TSystem : class, ISystem
{
return _container.GetAllByPriority<TSystem>();
}
public IReadOnlyList<TModel> GetModelsByPriority<TModel>() where TModel : class, IModel
{
return _container.GetAllByPriority<TModel>();
}
public IReadOnlyList<TUtility> GetUtilitiesByPriority<TUtility>() where TUtility : class, IUtility
{
return _container.GetAllByPriority<TUtility>();
}
public void SendEvent<TEvent>() where TEvent : new()
{
}
public void SendEvent<TEvent>(TEvent e) where TEvent : class
{
}
public IUnRegister RegisterEvent<TEvent>(Action<TEvent> handler)
{
return new DefaultUnRegister(() => { });
}
public void UnRegisterEvent<TEvent>(Action<TEvent> onEvent)
{
}
public ValueTask<TResponse> SendRequestAsync<TResponse>(IRequest<TResponse> request,
CancellationToken cancellationToken = default)
{
throw new NotSupportedException();
}
public TResponse SendRequest<TResponse>(IRequest<TResponse> request)
{
throw new NotSupportedException();
}
/// <summary>
/// 测试桩:异步发送 CQRS 命令并返回响应。
/// </summary>
/// <typeparam name="TResponse">命令响应类型。</typeparam>
/// <param name="command">要发送的命令。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>命令响应任务。</returns>
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
public ValueTask<TResponse> SendCommandAsync<TResponse>(
GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand<TResponse> command,
CancellationToken cancellationToken = default)
{
throw new NotSupportedException();
}
/// <summary>
/// 测试桩:同步发送 CQRS 命令并返回响应。
/// </summary>
/// <typeparam name="TResponse">命令响应类型。</typeparam>
/// <param name="command">要发送的命令。</param>
/// <returns>命令响应。</returns>
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
public TResponse SendCommand<TResponse>(GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand<TResponse> command)
{
throw new NotSupportedException();
}
/// <summary>
/// 测试桩:异步发送 CQRS 查询并返回结果。
/// </summary>
/// <typeparam name="TResponse">查询结果类型。</typeparam>
/// <param name="query">要发送的查询。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>查询结果任务。</returns>
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
public ValueTask<TResponse> SendQueryAsync<TResponse>(
GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery<TResponse> query,
CancellationToken cancellationToken = default)
{
throw new NotSupportedException();
}
/// <summary>
/// 测试桩:同步发送 CQRS 查询并返回结果。
/// </summary>
/// <typeparam name="TResponse">查询结果类型。</typeparam>
/// <param name="query">要发送的查询。</param>
/// <returns>查询结果。</returns>
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
public TResponse SendQuery<TResponse>(GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery<TResponse> query)
{
throw new NotSupportedException();
}
public ValueTask PublishAsync<TNotification>(TNotification notification,
CancellationToken cancellationToken = default) where TNotification : INotification
{
throw new NotSupportedException();
}
public IAsyncEnumerable<TResponse> CreateStream<TResponse>(
IStreamRequest<TResponse> request,
CancellationToken cancellationToken = default)
{
throw new NotSupportedException();
}
public ValueTask SendAsync<TCommand>(TCommand command, CancellationToken cancellationToken = default)
where TCommand : IRequest<Unit>
{
throw new NotSupportedException();
}
public ValueTask<TResponse> SendAsync<TResponse>(IRequest<TResponse> command,
CancellationToken cancellationToken = default)
{
throw new NotSupportedException();
}
public void SendCommand(ICommand command)
{
}
public TResult SendCommand<TResult>(Abstractions.Command.ICommand<TResult> command)
{
return default!;
}
public Task SendCommandAsync(IAsyncCommand command)
{
return Task.CompletedTask;
}
public Task<TResult> SendCommandAsync<TResult>(IAsyncCommand<TResult> command)
{
return (Task<TResult>)Task.CompletedTask;
}
public TResult SendQuery<TResult>(Abstractions.Query.IQuery<TResult> query)
{
return default!;
}
public Task<TResult> SendQueryAsync<TResult>(IAsyncQuery<TResult> query)
{
return (Task<TResult>)Task.CompletedTask;
}
public IEnvironment GetEnvironment()
{
return _environment;
}
}
#endregion

View File

@ -147,10 +147,3 @@ public class ContextProviderTests
Assert.That(foundContext, Is.SameAs(context));
}
}
/// <summary>
/// 另一个测试用的架构上下文类,用于测试类型不匹配的情况
/// </summary>
public class AnotherTestArchitectureContext : TestArchitectureContext
{
}

View File

@ -9,6 +9,7 @@ namespace GFramework.Core.Tests.Architectures;
public abstract class TestArchitectureBase : Architecture
{
private Action<TestArchitectureBase>? _postRegistrationHook;
private readonly List<ArchitecturePhase> _phaseHistory = [];
/// <summary>
/// 获取就绪事件是否已触发的状态
@ -23,7 +24,7 @@ public abstract class TestArchitectureBase : Architecture
/// <summary>
/// 获取架构阶段历史记录列表
/// </summary>
public List<ArchitecturePhase> PhaseHistory { get; } = [];
public IReadOnlyList<ArchitecturePhase> PhaseHistory => _phaseHistory;
/// <summary>
/// 添加注册后钩子函数
@ -43,6 +44,6 @@ public abstract class TestArchitectureBase : Architecture
_postRegistrationHook?.Invoke(this);
// 订阅阶段变更事件以记录历史
PhaseChanged += (_, eventArgs) => PhaseHistory.Add(eventArgs.Phase);
PhaseChanged += (_, eventArgs) => _phaseHistory.Add(eventArgs.Phase);
}
}

View File

@ -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;
/// <summary>
/// 为 <see cref="ArchitectureServicesTests" /> 提供最小实现的架构上下文测试桩。
/// </summary>
/// <remarks>
/// 该类型仅用于验证 <see cref="ArchitectureServices" /> 的上下文传递行为,因此仅保留当前测试切片需要的容器、
/// 环境与接口实现。所有不在本测试范围内的 CQRS 调用均明确抛出 <see cref="NotSupportedException" />
/// 以避免误把测试桩当作真实运行时上下文使用。
/// </remarks>
public class TestArchitectureContextV3 : IArchitectureContext
{
private readonly MicrosoftDiContainer _container = new();
private readonly DefaultEnvironment _environment = new();
/// <summary>
/// 获取或初始化用于区分测试上下文实例的标识。
/// </summary>
public int Id { get; init; }
/// <summary>
/// 获取指定类型的服务实例。
/// </summary>
/// <typeparam name="TService">服务类型。</typeparam>
/// <returns>已注册的服务实例。</returns>
/// <exception cref="InvalidOperationException">未注册或存在多个同类型实例时抛出。</exception>
public TService GetService<TService>() where TService : class
{
return _container.GetRequired<TService>();
}
/// <summary>
/// 获取指定类型的所有服务实例。
/// </summary>
/// <typeparam name="TService">服务类型。</typeparam>
/// <returns>所有已注册的服务实例。</returns>
public IReadOnlyList<TService> GetServices<TService>() where TService : class
{
return _container.GetAll<TService>();
}
/// <summary>
/// 获取指定类型的模型实例。
/// </summary>
/// <typeparam name="TModel">模型类型。</typeparam>
/// <returns>已注册的模型实例。</returns>
/// <exception cref="InvalidOperationException">未注册或存在多个同类型实例时抛出。</exception>
public TModel GetModel<TModel>() where TModel : class, IModel
{
return _container.GetRequired<TModel>();
}
/// <summary>
/// 获取指定类型的所有模型实例。
/// </summary>
/// <typeparam name="TModel">模型类型。</typeparam>
/// <returns>所有已注册的模型实例。</returns>
public IReadOnlyList<TModel> GetModels<TModel>() where TModel : class, IModel
{
return _container.GetAll<TModel>();
}
/// <summary>
/// 获取指定类型的系统实例。
/// </summary>
/// <typeparam name="TSystem">系统类型。</typeparam>
/// <returns>已注册的系统实例。</returns>
/// <exception cref="InvalidOperationException">未注册或存在多个同类型实例时抛出。</exception>
public TSystem GetSystem<TSystem>() where TSystem : class, ISystem
{
return _container.GetRequired<TSystem>();
}
/// <summary>
/// 获取指定类型的所有系统实例。
/// </summary>
/// <typeparam name="TSystem">系统类型。</typeparam>
/// <returns>所有已注册的系统实例。</returns>
public IReadOnlyList<TSystem> GetSystems<TSystem>() where TSystem : class, ISystem
{
return _container.GetAll<TSystem>();
}
/// <summary>
/// 获取指定类型的工具实例。
/// </summary>
/// <typeparam name="TUtility">工具类型。</typeparam>
/// <returns>已注册的工具实例。</returns>
/// <exception cref="InvalidOperationException">未注册或存在多个同类型实例时抛出。</exception>
public TUtility GetUtility<TUtility>() where TUtility : class, IUtility
{
return _container.GetRequired<TUtility>();
}
/// <summary>
/// 获取指定类型的所有工具实例。
/// </summary>
/// <typeparam name="TUtility">工具类型。</typeparam>
/// <returns>所有已注册的工具实例。</returns>
public IReadOnlyList<TUtility> GetUtilities<TUtility>() where TUtility : class, IUtility
{
return _container.GetAll<TUtility>();
}
/// <summary>
/// 获取指定类型的所有服务实例,并按优先级排序。
/// </summary>
/// <typeparam name="TService">服务类型。</typeparam>
/// <returns>按优先级排序后的服务实例。</returns>
public IReadOnlyList<TService> GetServicesByPriority<TService>() where TService : class
{
return _container.GetAllByPriority<TService>();
}
/// <summary>
/// 获取指定类型的所有系统实例,并按优先级排序。
/// </summary>
/// <typeparam name="TSystem">系统类型。</typeparam>
/// <returns>按优先级排序后的系统实例。</returns>
public IReadOnlyList<TSystem> GetSystemsByPriority<TSystem>() where TSystem : class, ISystem
{
return _container.GetAllByPriority<TSystem>();
}
/// <summary>
/// 获取指定类型的所有模型实例,并按优先级排序。
/// </summary>
/// <typeparam name="TModel">模型类型。</typeparam>
/// <returns>按优先级排序后的模型实例。</returns>
public IReadOnlyList<TModel> GetModelsByPriority<TModel>() where TModel : class, IModel
{
return _container.GetAllByPriority<TModel>();
}
/// <summary>
/// 获取指定类型的所有工具实例,并按优先级排序。
/// </summary>
/// <typeparam name="TUtility">工具类型。</typeparam>
/// <returns>按优先级排序后的工具实例。</returns>
public IReadOnlyList<TUtility> GetUtilitiesByPriority<TUtility>() where TUtility : class, IUtility
{
return _container.GetAllByPriority<TUtility>();
}
/// <summary>
/// 发送无参数事件。
/// </summary>
/// <typeparam name="TEvent">事件类型。</typeparam>
public void SendEvent<TEvent>() where TEvent : new()
{
}
/// <summary>
/// 发送带参数事件。
/// </summary>
/// <typeparam name="TEvent">事件类型。</typeparam>
/// <param name="e">事件实例。</param>
public void SendEvent<TEvent>(TEvent e) where TEvent : class
{
}
/// <summary>
/// 注册事件处理器。
/// </summary>
/// <typeparam name="TEvent">事件类型。</typeparam>
/// <param name="handler">事件处理回调。</param>
/// <returns>用于注销回调的句柄。</returns>
public IUnRegister RegisterEvent<TEvent>(Action<TEvent> handler)
{
return new DefaultUnRegister(() => { });
}
/// <summary>
/// 取消事件处理器注册。
/// </summary>
/// <typeparam name="TEvent">事件类型。</typeparam>
/// <param name="onEvent">要取消的事件回调。</param>
public void UnRegisterEvent<TEvent>(Action<TEvent> onEvent)
{
}
/// <summary>
/// 异步发送新版 CQRS 请求。
/// </summary>
/// <typeparam name="TResponse">响应类型。</typeparam>
/// <param name="request">要发送的请求。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>请求响应任务。</returns>
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
public ValueTask<TResponse> SendRequestAsync<TResponse>(
IRequest<TResponse> request,
CancellationToken cancellationToken = default)
{
throw new NotSupportedException();
}
/// <summary>
/// 同步发送新版 CQRS 请求。
/// </summary>
/// <typeparam name="TResponse">响应类型。</typeparam>
/// <param name="request">要发送的请求。</param>
/// <returns>请求响应。</returns>
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
public TResponse SendRequest<TResponse>(IRequest<TResponse> request)
{
throw new NotSupportedException();
}
/// <summary>
/// 异步发送新版 CQRS 命令并返回响应。
/// </summary>
/// <typeparam name="TResponse">命令响应类型。</typeparam>
/// <param name="command">要发送的命令。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>命令响应任务。</returns>
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
public ValueTask<TResponse> SendCommandAsync<TResponse>(
GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand<TResponse> command,
CancellationToken cancellationToken = default)
{
throw new NotSupportedException();
}
/// <summary>
/// 同步发送新版 CQRS 命令并返回响应。
/// </summary>
/// <typeparam name="TResponse">命令响应类型。</typeparam>
/// <param name="command">要发送的命令。</param>
/// <returns>命令响应。</returns>
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
public TResponse SendCommand<TResponse>(GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand<TResponse> command)
{
throw new NotSupportedException();
}
/// <summary>
/// 异步发送新版 CQRS 查询并返回结果。
/// </summary>
/// <typeparam name="TResponse">查询结果类型。</typeparam>
/// <param name="query">要发送的查询。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>查询结果任务。</returns>
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
public ValueTask<TResponse> SendQueryAsync<TResponse>(
GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery<TResponse> query,
CancellationToken cancellationToken = default)
{
throw new NotSupportedException();
}
/// <summary>
/// 同步发送新版 CQRS 查询并返回结果。
/// </summary>
/// <typeparam name="TResponse">查询结果类型。</typeparam>
/// <param name="query">要发送的查询。</param>
/// <returns>查询结果。</returns>
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
public TResponse SendQuery<TResponse>(GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery<TResponse> query)
{
throw new NotSupportedException();
}
/// <summary>
/// 发布通知消息。
/// </summary>
/// <typeparam name="TNotification">通知类型。</typeparam>
/// <param name="notification">通知实例。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>通知发布任务。</returns>
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
public ValueTask PublishAsync<TNotification>(
TNotification notification,
CancellationToken cancellationToken = default)
where TNotification : INotification
{
throw new NotSupportedException();
}
/// <summary>
/// 创建流式请求结果。
/// </summary>
/// <typeparam name="TResponse">流元素类型。</typeparam>
/// <param name="request">流式请求。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>流式响应序列。</returns>
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
public IAsyncEnumerable<TResponse> CreateStream<TResponse>(
IStreamRequest<TResponse> request,
CancellationToken cancellationToken = default)
{
throw new NotSupportedException();
}
/// <summary>
/// 异步发送无返回值请求命令。
/// </summary>
/// <typeparam name="TCommand">命令类型。</typeparam>
/// <param name="command">要发送的命令。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>命令发送任务。</returns>
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
public ValueTask SendAsync<TCommand>(TCommand command, CancellationToken cancellationToken = default)
where TCommand : IRequest<Unit>
{
throw new NotSupportedException();
}
/// <summary>
/// 异步发送带返回值请求。
/// </summary>
/// <typeparam name="TResponse">响应类型。</typeparam>
/// <param name="command">要发送的请求。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>请求响应任务。</returns>
/// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>
public ValueTask<TResponse> SendAsync<TResponse>(
IRequest<TResponse> command,
CancellationToken cancellationToken = default)
{
throw new NotSupportedException();
}
/// <summary>
/// 发送旧版无返回值命令。
/// </summary>
/// <param name="command">要发送的命令。</param>
public void SendCommand(ICommand command)
{
}
/// <summary>
/// 发送旧版带返回值命令。
/// </summary>
/// <typeparam name="TResult">命令响应类型。</typeparam>
/// <param name="command">要发送的命令。</param>
/// <returns>默认响应值。</returns>
public TResult SendCommand<TResult>(GFramework.Core.Abstractions.Command.ICommand<TResult> command)
{
return default!;
}
/// <summary>
/// 异步发送旧版无返回值命令。
/// </summary>
/// <param name="command">要发送的命令。</param>
/// <returns>已完成任务。</returns>
public Task SendCommandAsync(IAsyncCommand command)
{
return Task.CompletedTask;
}
/// <summary>
/// 异步发送旧版带返回值命令。
/// </summary>
/// <typeparam name="TResult">命令响应类型。</typeparam>
/// <param name="command">要发送的命令。</param>
/// <returns>占位任务。</returns>
public Task<TResult> SendCommandAsync<TResult>(IAsyncCommand<TResult> command)
{
return (Task<TResult>)Task.CompletedTask;
}
/// <summary>
/// 发送旧版查询请求。
/// </summary>
/// <typeparam name="TResult">查询结果类型。</typeparam>
/// <param name="query">要发送的查询。</param>
/// <returns>默认查询结果。</returns>
public TResult SendQuery<TResult>(GFramework.Core.Abstractions.Query.IQuery<TResult> query)
{
return default!;
}
/// <summary>
/// 异步发送旧版查询请求。
/// </summary>
/// <typeparam name="TResult">查询结果类型。</typeparam>
/// <param name="query">要发送的查询。</param>
/// <returns>占位任务。</returns>
public Task<TResult> SendQueryAsync<TResult>(IAsyncQuery<TResult> query)
{
return (Task<TResult>)Task.CompletedTask;
}
/// <summary>
/// 获取当前测试上下文绑定的环境实例。
/// </summary>
/// <returns>默认测试环境实例。</returns>
public IEnvironment GetEnvironment()
{
return _environment;
}
}

View File

@ -405,7 +405,7 @@ public class CommandCoroutineExtensionsTests
CommandCoroutineExtensions.SendCommandAndWaitEventCoroutine<TestCommand, TestEvent>(
contextAware,
command,
null,
_ => { },
-1.0f));
}
@ -421,7 +421,7 @@ public class CommandCoroutineExtensionsTests
// 设置上下文服务以返回null事件总线
contextAware._mockContext
.Setup(ctx => ctx.GetService<IEventBus>())
.Returns((IEventBus?)null);
.Returns((IEventBus)null!);
// 创建协程
var coroutine = CommandCoroutineExtensions.SendCommandAndWaitEventCoroutine<TestCommand, TestEvent>(

View File

@ -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()

View File

@ -0,0 +1,6 @@
namespace GFramework.Core.Tests.Events;
/// <summary>
/// 表示不携带任何负载的空测试事件。
/// </summary>
public sealed class EmptyEvent;

View File

@ -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
/// <summary>
/// 测试用的结构体事件
/// </summary>
[StructLayout(LayoutKind.Auto)]
private struct StructEvent
{
public int Id { get; set; }

View File

@ -1,8 +1,12 @@
namespace GFramework.Core.Tests.Events;
/// <summary>
/// 表示包含整型载荷的测试事件。
/// </summary>
public sealed class TestEvent
{
/// <summary>
/// 获取初始化阶段写入的接收值。
/// </summary>
public int ReceivedValue { get; init; }
}
public sealed class EmptyEvent;

View File

@ -419,44 +419,3 @@ public class LoggerTests
Assert.That(fatalLogger.Logs[0].Level, Is.EqualTo(LogLevel.Fatal));
}
}
/// <summary>
/// 测试用的日志记录器实现类继承自AbstractLogger
/// </summary>
public sealed class TestLogger : AbstractLogger
{
private readonly List<LogEntry> _logs = new();
/// <summary>
/// 初始化TestLogger的新实例
/// </summary>
/// <param name="name">日志记录器的名称默认为null</param>
/// <param name="minLevel">最小日志级别默认为LogLevel.Info</param>
public TestLogger(string? name = null, LogLevel minLevel = LogLevel.Info) : base(name, minLevel)
{
}
/// <summary>
/// 获取按写入顺序保存的日志条目只读视图
/// </summary>
public IReadOnlyList<LogEntry> Logs => _logs;
/// <summary>
/// 将日志信息写入内部存储
/// </summary>
/// <param name="level">日志级别</param>
/// <param name="message">日志消息</param>
/// <param name="exception">相关异常(可选)</param>
protected override void Write(LogLevel level, string message, Exception? exception)
{
_logs.Add(new LogEntry(level, message, exception));
}
/// <summary>
/// 表示单个日志条目的记录类型
/// </summary>
/// <param name="Level">日志级别</param>
/// <param name="Message">日志消息</param>
/// <param name="Exception">相关异常(可选)</param>
public sealed record LogEntry(LogLevel Level, string Message, Exception? Exception);
}

View File

@ -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
{

View File

@ -0,0 +1,45 @@
using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Logging;
namespace GFramework.Core.Tests.Logging;
/// <summary>
/// 表示供日志相关测试复用的内存日志记录器。
/// </summary>
public sealed class TestLogger : AbstractLogger
{
private readonly List<LogEntry> _logs = new();
/// <summary>
/// 初始化 <see cref="TestLogger" /> 的新实例。
/// </summary>
/// <param name="name">日志记录器的名称;未指定时沿用基类默认行为。</param>
/// <param name="minLevel">允许写入的最小日志级别。</param>
public TestLogger(string? name = null, LogLevel minLevel = LogLevel.Info) : base(name, minLevel)
{
}
/// <summary>
/// 获取按写入顺序保存的日志条目只读视图。
/// </summary>
public IReadOnlyList<LogEntry> Logs => _logs;
/// <summary>
/// 将日志信息追加到内存列表,供断言读取。
/// </summary>
/// <param name="level">日志级别。</param>
/// <param name="message">日志消息。</param>
/// <param name="exception">相关异常;没有异常时为 <see langword="null" />。</param>
protected override void Write(LogLevel level, string message, Exception? exception)
{
_logs.Add(new LogEntry(level, message, exception));
}
/// <summary>
/// 表示单个日志条目的不可变快照。
/// </summary>
/// <param name="Level">日志级别。</param>
/// <param name="Message">日志消息。</param>
/// <param name="Exception">相关异常;没有异常时为 <see langword="null" />。</param>
public sealed record LogEntry(LogLevel Level, string Message, Exception? Exception);
}

View File

@ -1,58 +1,8 @@
using System.IO;
using GFramework.Core.Abstractions.Resource;
using GFramework.Core.Resource;
using NUnit.Framework;
namespace GFramework.Core.Tests.Resource;
/// <summary>
/// 测试用的简单资源类
/// </summary>
public class TestResource
{
public string Content { get; set; } = string.Empty;
public bool IsDisposed { get; set; }
}
/// <summary>
/// 测试用的资源加载器
/// </summary>
public class TestResourceLoader : IResourceLoader<TestResource>
{
private readonly Dictionary<string, string> _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<TestResource> 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;
}
}
/// <summary>
/// ResourceManager 功能测试类
/// </summary>

View File

@ -0,0 +1,17 @@
namespace GFramework.Core.Tests.Resource;
/// <summary>
/// 表示 ResourceManager 测试使用的简单资源对象。
/// </summary>
public class TestResource
{
/// <summary>
/// 获取或设置资源内容。
/// </summary>
public string Content { get; set; } = string.Empty;
/// <summary>
/// 获取或设置一个值,指示资源是否已经被测试加载器标记为已卸载。
/// </summary>
public bool IsDisposed { get; set; }
}

View File

@ -0,0 +1,52 @@
using System.IO;
using GFramework.Core.Abstractions.Resource;
namespace GFramework.Core.Tests.Resource;
/// <summary>
/// 为 ResourceManager 测试提供可控数据源的资源加载器。
/// </summary>
public class TestResourceLoader : IResourceLoader<TestResource>
{
private readonly Dictionary<string, string> _resourceData = new(StringComparer.Ordinal);
/// <inheritdoc />
public TestResource Load(string path)
{
if (_resourceData.TryGetValue(path, out var content))
{
return new TestResource { Content = content };
}
throw new FileNotFoundException($"Resource not found: {path}");
}
/// <inheritdoc />
public async Task<TestResource> LoadAsync(string path)
{
await Task.Delay(10).ConfigureAwait(false); // 模拟异步加载
return Load(path);
}
/// <inheritdoc />
public void Unload(TestResource resource)
{
resource.IsDisposed = true;
}
/// <inheritdoc />
public bool CanLoad(string path)
{
return _resourceData.ContainsKey(path);
}
/// <summary>
/// 向测试加载器注册一条可返回的资源数据。
/// </summary>
/// <param name="path">资源路径。</param>
/// <param name="content">资源内容。</param>
public void AddTestData(string path, string content)
{
_resourceData[path] = content;
}
}

View File

@ -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*` 保持为独立高耦合波次,必要时先由主线程局部切分再决定是否并行

View File

@ -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