Skip to content

Fix/analyzer warning reduction batch#288

Merged
GeWuYou merged 30 commits intomainfrom
fix/analyzer-warning-reduction-batch
Apr 25, 2026
Merged

Fix/analyzer warning reduction batch#288
GeWuYou merged 30 commits intomainfrom
fix/analyzer-warning-reduction-batch

Conversation

@GeWuYou
Copy link
Copy Markdown
Owner

@GeWuYou GeWuYou commented Apr 25, 2026

Summary by CodeRabbit

  • 改进

    • 增强异步操作的上下文处理,提升并发场景的性能表现。
    • 改进空值检查逻辑,使错误处理更加一致和可靠。
    • 优化测试框架的异步断言机制,提高测试稳定性。
  • 重构

    • 调整内部异步流程以避免不必要的同步上下文捕获。

GeWuYou added 27 commits April 25, 2026 08:27
- 修复 YamlConfigLoaderTests 中 Assert.ThrowsAsync 的冗余 async/await 写法

- 补充 WaitForTaskWithinAsync 的 ConfigureAwait(false) 以消除剩余 MA0004

- 更新 analyzer-warning-reduction 的 RP-059 跟踪与验证记录
- 修复 Assert.ThrowsAsync 中机械型 async 包装\n- 保持锚点缺失场景的异常与未安装断言语义不变
- 修复三个配置测试文件中 Assert.ThrowsAsync 与 Assert.DoesNotThrowAsync 的冗余 async/await 包装
- 调整文本校验异步异常测试签名以匹配去包装后的同步断言写法
- 修复 Scene 与 UI 过渡管道中间件链的多余 async 包装

- 更新低风险 await 调用以显式使用 ConfigureAwait(false)
- 重构四个纯加载测试的固定布置为文件内私有 helper,缩短方法体长度而不改变断言语义

- 保持 schema 内容、异常路径与 item/monster 注册顺序不变,并避免触碰热重载测试
- 更新 analyzer-warning-reduction 的 RP-060 跟踪与 trace

- 记录并行 warning-reduction 批次的验证结果与当前分支体积

- 补充下一轮继续朝 75 文件阈值推进的恢复建议
- 修复 SettingsSystem 中不依赖上下文的 await,补充 ConfigureAwait(false)
- 修复 ScopedStorage.DeleteAsync 的 await,保持作用域前缀语义不变
- 重构 Replace、Push、Pop、Clear 的 around pipeline 核心委托,移除匿名 async 包装

- 补充 BeforeChange 与 AfterChange 的 ConfigureAwait(false),收敛明显低风险 MA0004

- 保留场景生命周期与栈操作相关 await 的默认上下文行为,并在代码中说明原因
- 修复 FileStorage 在锁获取与异步释放路径上的 ConfigureAwait(false) 缺失
- 保持文件锁、临时文件写入和原子替换流程不变
- 修复 RouterBase 进入守卫异步调用缺少 ConfigureAwait(false) 的机械型 MA0004
- 修复 RouterBase 离开守卫异步调用缺少 ConfigureAwait(false) 的机械型 MA0004
- 调整 UiRouterBase 的异步过渡调用以显式保留同步上下文

- 清理 Push Pop Replace Clear 流程中的低风险 MA0004

- 保持 UI 生命周期顺序与过渡阶段语义不变
- 优化 allOf、enum、not 测试中的 Assert.ThrowsAsync 委托写法,移除冗余 async/await 包装
- 保持 schema 用例语义、断言内容和异常路径不变
- 简化 YAML 配置加载测试中的 Assert.ThrowsAsync Task 包装

- 简化持久化测试中的异步异常断言包装并保持原有断言语义
- 简化 Architecture、Command、Query 与 AsyncArchitecture 测试中的机械型 async/await 异步断言包装
- 更新 AsyncKeyLockManagerTests 中的 Task 断言写法以消除低风险 analyzer 噪音
- 简化 StateMachineSystemTests 中的 Task 异常断言包装

- 简化 StateMachineTests 中的异步异常断言包装并保持测试语义不变
- 更新 analyzer-warning-reduction 跟踪文档到 RP-061 并记录当前 HEAD 与 branch diff 真值
- 补充最近批次的验证结论与当前 subagent 恢复入口
- 简化 ResultExtensionsTests 中的异步异常断言包装
- 更新 AsyncOperationTests 的 Task 断言写法以消除低风险 analyzer 噪音
- 简化 ArchitectureConfigIntegrationTests 中的异步异常断言包装
- 保持重复初始化场景的测试语义与验证路径不变
- 补充 LocalizationTableTests 中 string key Dictionary 的 Ordinal comparer

- 补充 QueryCoroutineExtensionsTests 中 Metadata 字典的 Ordinal comparer 并保持测试语义不变
- 优化 ContextAwareServiceExtensionsTests 中的字符串相等断言,显式使用 Ordinal 比较
- 优化 RollingFileAppenderTests 中的 StartsWith、EndsWith、排序比较与文件名判等写法,补充 Ordinal 比较并保持测试语义不变
- 为 LogEntryTests 与格式化器测试中的字符串字典补充 StringComparer.Ordinal
- 保持日志相关测试断言与行为路径不变
- 补齐 WaitForTask、ResourceManager、State 与 StateMachine 测试中的低风险 ConfigureAwait(false)
- 更新 AsyncKeyLock、ResultExtensions、ResultT 与 Pipe 测试中的 culture 和异步等待写法
- 更新 OptionTests 中的 culture-sensitive 转换与 TryParse 写法
- 修正 AsyncExtensionsTests 与 CollectionExtensionsTests 的低风险异步和字符串比较写法
- 更新 ArchitectureContext、ArchitectureServices、GameContext 与环境测试桩的异常类型以满足 analyzer 约束

- 补齐 AsyncTestModel 与 AsyncTestSystem 的异步等待配置并保持测试语义不变

- 调整 ResultTests 的异类异常断言样例以避免新增编译与 analyzer 噪音
- 更新 ContextAware、Store 与通用扩展中的参数空校验写法以满足 analyzer 约束

- 简化 coroutine、pause、log 与 async 测试中的等待和断言包装并保持测试语义不变

- 调整测试替身异常类型与 Result 系列断言样例以减少低风险 warning 噪音
- 更新 active tracking 与 trace 到 RP-062 并收口到当前真值

- 记录本轮已达到 75 files 阈值的停止结论与最新 branch 指标

- 补充 Core build 通过结果与 Core.Tests 的 MSB4276 环境阻塞说明
- 更新 active tracking 与 trace 中的当前 branch line 统计

- 将恢复点表述改为代码 stop commit 以避免后续 docs 提交导致失真
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 25, 2026

Warning

Rate limit exceeded

@GeWuYou has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 44 minutes and 27 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 44 minutes and 27 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4738ab5f-a763-4b40-8edd-5dd2b70894b9

📥 Commits

Reviewing files that changed from the base of the PR and between be336b2 and a7a3eca.

📒 Files selected for processing (21)
  • GFramework.Core.Tests/Architectures/ArchitectureLifecycleBehaviorTests.cs
  • GFramework.Core.Tests/Architectures/ArchitectureServicesTests.cs
  • GFramework.Core.Tests/Architectures/GameContextTests.cs
  • GFramework.Core.Tests/Architectures/RegistryInitializationHookBaseTests.cs
  • GFramework.Core.Tests/Concurrency/AsyncKeyLockManagerTests.cs
  • GFramework.Core.Tests/Coroutine/TaskCoroutineExtensionsTests.cs
  • GFramework.Core.Tests/Coroutine/WaitForTaskTests.cs
  • GFramework.Core.Tests/Extensions/AsyncExtensionsTests.cs
  • GFramework.Core.Tests/Logging/RollingFileAppenderTests.cs
  • GFramework.Core.Tests/State/StateMachineSystemTests.cs
  • GFramework.Core.Tests/Tests/AsyncArchitectureTests.cs
  • GFramework.Core/Extensions/ContextAwareEventExtensions.cs
  • GFramework.Core/Extensions/NumericExtensions.cs
  • GFramework.Core/Extensions/StringExtensions.cs
  • GFramework.Core/StateManagement/StoreBuilder.cs
  • GFramework.Core/StateManagement/StoreSelection.cs
  • GFramework.Game/Scene/SceneRouterBase.cs
  • GFramework.Game/Storage/FileStorage.cs
  • GFramework.Game/Storage/ScopedStorage.cs
  • ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md
  • ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md
📝 Walkthrough

Walkthrough

该PR通过将ArgumentNullException.ThrowIfNull替换为显式null检查、添加.ConfigureAwait配置、简化测试异步断言写法以及调整异常类型,对整个框架进行代码清理和优化。

Changes

Cohort / File(s) Summary
参数验证模式替换
GFramework.Core/Extensions/AsyncExtensions.cs, GFramework.Core/Extensions/CollectionExtensions.cs, GFramework.Core/Extensions/ContextAware*Extensions.cs, GFramework.Core/Extensions/GuardExtensions.cs, GFramework.Core/Extensions/NumericExtensions.cs, GFramework.Core/Extensions/StoreEventBusExtensions.cs, GFramework.Core/Extensions/StringExtensions.cs, GFramework.Core/StateManagement/StoreBuilder.cs, GFramework.Core/StateManagement/StoreSelection.cs
将所有ArgumentNullException.ThrowIfNull(...)调用替换为显式的if (x is null) throw new ArgumentNullException(nameof(x))检查,保持相同的异常类型和参数命名。
ConfigureAwait配置添加
GFramework.Core/Routing/RouterBase.cs, GFramework.Game/Routing/RouterBase.cs, GFramework.Game/Scene/SceneRouterBase.cs, GFramework.Game/Scene/SceneTransitionPipeline.cs, GFramework.Game/Setting/SettingsSystem.cs, GFramework.Game/Storage/FileStorage.cs, GFramework.Game/Storage/ScopedStorage.cs, GFramework.Game/UI/UiRouterBase.cs, GFramework.Game/UI/UiTransitionPipeline.cs
在async/await调用中添加.ConfigureAwait(false).ConfigureAwait(true),控制同步上下文的捕获行为,并优化中间件链构建。
FileStorage存储引擎重构
GFramework.Game/Storage/FileStorage.cs
重构async锁获取模式,使用显式配置的await using变量替代内联模式,修改ReadAsync<T>的FileStream处理和WriteAsync<T>的try/catch结构,保持整体控制流不变。
异步测试断言简化
GFramework.Core.Tests/Architectures/*, GFramework.Core.Tests/Command/*, GFramework.Core.Tests/Concurrency/AsyncKeyLockManagerTests.cs, GFramework.Core.Tests/Coroutine/*, GFramework.Core.Tests/Extensions/*, GFramework.Core.Tests/Functional/*, GFramework.Core.Tests/Logging/*, GFramework.Core.Tests/Pause/PauseStackManagerTests.cs, GFramework.Core.Tests/Query/*, GFramework.Core.Tests/Resource/ResourceManagerTests.cs, GFramework.Core.Tests/Rule/*, GFramework.Core.Tests/State/*, GFramework.Core.Tests/Systems/AsyncTestSystem.cs, GFramework.Core.Tests/Tests/AsyncArchitectureTests.cs, GFramework.Game.Tests/Config/*, GFramework.Game.Tests/Data/PersistenceTests.cs, GFramework.Godot.Tests/Architectures/AbstractArchitectureModuleInstallationTests.cs
移除Assert.ThrowsAsyncAssert.DoesNotThrowAsync中的async/await包装,改为直接传递任务返回的委托,调整test方法签名从async Task转为void(适当情况下),添加ConfigureAwait(false)到测试中的await调用。
异常类型调整
GFramework.Core.Tests/Architectures/ArchitectureServicesTests.cs, GFramework.Core.Tests/Architectures/GameContextTests.cs, GFramework.Core.Tests/Architectures/RegistryInitializationHookBaseTests.cs, GFramework.Core.Tests/Rule/ContextAwareEnvironmentExtensionsTests.cs
将测试桩实现中的NotImplementedException替换为NotSupportedException,更新相关XML文档注释。
文化和字符串比较一致性
GFramework.Core.Tests/Coroutine/QueryCoroutineExtensionsTests.cs, GFramework.Core.Tests/Extensions/CollectionExtensionsTests.cs, GFramework.Core.Tests/Extensions/ResultExtensionsTests.cs, GFramework.Core.Tests/Functional/OptionTests.cs, GFramework.Core.Tests/Functional/Pipe/PipeExtensionsTests.cs, GFramework.Core.Tests/Functional/ResultTTests.cs, GFramework.Core.Tests/Functional/ResultTests.cs, GFramework.Core.Tests/Localization/LocalizationTableTests.cs, GFramework.Core.Tests/Logging/DefaultLogFormatterTests.cs, GFramework.Core.Tests/Logging/JsonLogFormatterTests.cs, GFramework.Core.Tests/Logging/LogEntryTests.cs, GFramework.Core.Tests/Logging/RollingFileAppenderTests.cs, GFramework.Core.Tests/Rule/ContextAwareServiceExtensionsTests.cs
使用StringComparer.OrdinalCultureInfo.InvariantCultureStringComparison.Ordinal确保文化不变的字符串处理,使测试行为确定性。
协程测试优化
GFramework.Core.Tests/Coroutine/CommandCoroutineExtensionsTests.cs, GFramework.Core.Tests/Coroutine/TaskCoroutineExtensionsTests.cs, GFramework.Core.Tests/Coroutine/WaitForTaskTTests.cs, GFramework.Core.Tests/Coroutine/WaitForTaskTests.cs
移除固定Task.Delay(10)延迟,使用MoveNext()确定性步进、SpinWait.SpinUntil轮询或ConfigureAwait(false)配置,引入WaitForTaskAsync辅助方法和1秒超时。
日志和并发测试
GFramework.Core.Tests/Logging/LogContextTests.cs, GFramework.Core.Tests/Concurrency/AsyncKeyLockManagerTests.cs
更新异步隔离测试,使用await Task.Delay(...)替代同步阻塞,简化异常断言,添加.ConfigureAwait(false)到锁获取和延迟操作。
PauseStackManager生命周期
GFramework.Core.Tests/Pause/PauseStackManagerTests.cs
将test fixture的TearDown从同步void转换为异步Task,确保DestroyAsync()完成,添加.ConfigureAwait(false)到相关await调用。
文档和追踪更新
ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md, ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md
推进恢复点从RP-058到RP-062,记录GFramework.Core Release构建成功(0警告/0错误),更新验证记录和环境阻断详情(MSB4276/NU1201),调整后续步骤。

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 56.36% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 标题准确总结了变更集的主要内容:批量修复分析器警告。
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/analyzer-warning-reduction-batch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 25, 2026

⚠️MegaLinter analysis: Success with warnings

Descriptor Linter Files Fixed Errors Warnings Elapsed time
⚠️ CSHARP dotnet-format yes 1 no 5.33s
✅ REPOSITORY gitleaks yes no no 7.48s
✅ REPOSITORY trufflehog yes no no 6.43s

Detailed Issues

⚠️ CSHARP / dotnet-format - 1 error
Welcome to .NET 9.0!
---------------------
SDK Version: 9.0.114

----------------
Installed an ASP.NET Core HTTPS development certificate.
To trust the certificate, run 'dotnet dev-certs https --trust'
Learn about HTTPS: https://aka.ms/dotnet-https

----------------
Write your first app: https://aka.ms/dotnet-hello-world
Find out what's new: https://aka.ms/dotnet-whats-new
Explore documentation: https://aka.ms/dotnet-docs
Report issues and find source on GitHub: https://github.com/dotnet/core
Use 'dotnet --help' to see available commands or visit: https://aka.ms/dotnet-cli
--------------------------------------------------------------------------------------
Unhandled exception: System.Exception: Restore operation failed.
   at Microsoft.CodeAnalysis.Tools.CodeFormatter.OpenMSBuildWorkspaceAsync(String solutionOrProjectPath, WorkspaceType workspaceType, Boolean noRestore, Boolean requiresSemantics, String binaryLogPath, Boolean logWorkspaceWarnings, ILogger logger, CancellationToken cancellationToken)
   at Microsoft.CodeAnalysis.Tools.CodeFormatter.FormatWorkspaceAsync(FormatOptions formatOptions, ILogger logger, CancellationToken cancellationToken, String binaryLogPath)
   at Microsoft.CodeAnalysis.Tools.FormatCommandCommon.FormatAsync(FormatOptions formatOptions, ILogger`1 logger, CancellationToken cancellationToken)
   at Microsoft.CodeAnalysis.Tools.Commands.RootFormatCommand.FormatCommandDefaultHandler.InvokeAsync(ParseResult parseResult, CancellationToken cancellationToken)
   at System.CommandLine.Invocation.InvocationPipeline.InvokeAsync(ParseResult parseResult, CancellationToken cancellationToken)

See detailed reports in MegaLinter artifacts
Set VALIDATE_ALL_CODEBASE: true in mega-linter.yml to validate all sources, not only the diff

MegaLinter is graciously provided by OX Security
Show us your support by starring ⭐ the repository

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Apr 25, 2026

Greptile Summary

This batch PR reduces analyzer warnings across ~72 files by replacing ArgumentNullException.ThrowIfNull calls with explicit if (x is null) throw patterns, adding ConfigureAwait(false/true) annotations throughout async call sites, and extracting inline async lambdas in SceneRouterBase into named helper methods. The FileStorage disposal chain is also corrected using leaveOpen: true on StreamReader/StreamWriter to avoid double-disposal of the underlying FileStream, and StoreSelection gains a #if NET9_0_OR_GREATER conditional to use the dedicated System.Threading.Lock type on .NET 9+.

Confidence Score: 5/5

Safe to merge — all changes are functionally equivalent null-check refactors, correct ConfigureAwait annotations, or cleanup with no runtime regressions.

No P0/P1 findings. The FileStorage double-disposal concern from the previous thread has been addressed with leaveOpen: true. The SceneRouterBase/UiRouterBase ConfigureAwait alignment (both now use ConfigureAwait(true) on outer pipeline calls) resolves the previously noted inconsistency. Remaining items are all minor style observations.

No files require special attention; pipeline files (SceneTransitionPipeline.cs, UiTransitionPipeline.cs) carry forward the pre-existing design decision of ConfigureAwait(false) on internal handler awaits, which was already discussed in previous review threads.

Important Files Changed

Filename Overview
GFramework.Game/Storage/FileStorage.cs Refactored await using scoped blocks to await using var pattern with leaveOpen: true to fix double-disposal; previous thread concern addressed. ConfigureAwait(false) added throughout.
GFramework.Game/Scene/SceneRouterBase.cs Inline lambdas extracted to named helper methods (ExecuteReplaceCoreAsync, etc.) with explicit ConfigureAwait(true) throughout; outer pipeline call now uses ConfigureAwait(true), aligning with UiRouterBase.
GFramework.Game/Scene/SceneTransitionPipeline.cs Added ConfigureAwait(false) to all internal awaits; async () => await lambda pattern replaced with direct method group to remove unnecessary state-machine allocation.
GFramework.Game/UI/UiRouterBase.cs Added explicit ConfigureAwait(true) to all awaits inside lambdas and on outer pipeline calls; consistent with scene router intent.
GFramework.Game/UI/UiTransitionPipeline.cs Same ConfigureAwait(false) additions and async lambda simplification as SceneTransitionPipeline.
GFramework.Core/StateManagement/StoreSelection.cs Introduced #if NET9_0_OR_GREATER to use System.Threading.Lock on .NET 9+ and fall back to object on .NET 8; null checks expanded from ThrowIfNull to verbose if-throw pattern.
GFramework.Core/Extensions/CollectionExtensions.cs Replaced ArgumentNullException.ThrowIfNull with explicit if (x is null) throw blocks — functionally equivalent, reduces analyzer warnings.
GFramework.Core/Extensions/GuardExtensions.cs Same ThrowIfNull → explicit null check replacement; uses single-line if without braces (minor style inconsistency vs. multi-line blocks in other changed files).
GFramework.Game/Storage/ScopedStorage.cs DeleteAsync simplified from async/await wrapper to direct return inner.DeleteAsync(Key(key)), eliminating an unnecessary state machine allocation.
GFramework.Core.Tests/Architectures/ArchitectureLifecycleBehaviorTests.cs Test method converted from async Task to void; Assert.ThrowsAsync correctly called without async () => await wrapper (NUnit internally blocks until the task completes).

Sequence Diagram

sequenceDiagram
    participant Caller as Caller (engine thread)
    participant Router as SceneRouterBase
    participant Pipeline as SceneTransitionPipeline
    participant Handler as Around Handler
    participant Core as ExecuteReplaceCoreAsync

    Caller->>Router: ReplaceAsync()
    Router->>Pipeline: ExecuteAroundAsync(event, coreAction) ConfigureAwait(true)
    alt No around handlers
        Pipeline->>Core: coreAction() ConfigureAwait(false)
        Note over Core: BeforeChangeAsync().ConfigureAwait(true)<br/>ClearInternalAsync().ConfigureAwait(true)<br/>PushInternalAsync().ConfigureAwait(true)<br/>AfterChangeAsync().ConfigureAwait(true)
        Core-->>Pipeline: Task completes (engine thread inside)
        Pipeline-->>Router: returns
    else Has around handlers
        Pipeline->>Handler: HandleAsync(event, next, ct) ConfigureAwait(false)
        Handler->>Core: next() → coreAction()
        Note over Core: Runs with ConfigureAwait(true) internally
        Core-->>Handler: completes
        Note over Handler: Post-next code resumes on thread-pool
        Handler-->>Pipeline: completes
        Pipeline-->>Router: Task completes
    end
    Router-->>Caller: resumes on engine thread (ConfigureAwait(true))
Loading

Reviews (4): Last reviewed commit: "fix(pr-review): 收敛PR建议并修复构建验证" | Re-trigger Greptile

Comment thread GFramework.Game/Storage/FileStorage.cs
Comment thread GFramework.Game/Scene/SceneRouterBase.cs
@GeWuYou
Copy link
Copy Markdown
Owner Author

GeWuYou commented Apr 25, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 25, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (9)
GFramework.Core/StateManagement/StoreSelection.cs (1)

30-30: ⚠️ Potential issue | 🟠 Major

修复 MA0158:将 object 锁替换为 System.Threading.Lock

第 30 行的 private readonly object _lock = new(); 触发了 Meziantou.Analyzer 的 MA0158 告警。项目目标框架包含 net9.0,System.Threading.Lock 完全可用。根据编码规范,应遵循 Meziantou.Analyzer 建议,直接改用 System.Threading.Lock,现有的 lock (_lock) 调用无需改动。

建议修改
-    private readonly object _lock = new();
+    private readonly System.Threading.Lock _lock = new();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@GFramework.Core/StateManagement/StoreSelection.cs` at line 30, 在
StoreSelection 类中将当前字段 private readonly object _lock = new(); 替换为使用
System.Threading.Lock(例如 private readonly System.Threading.Lock _lock =
new();),并保留现有的 lock (_lock) 调用不变;定位到 StoreSelection 类和字段名 _lock,改用
System.Threading.Lock 类型以消除 MA0158 警告,确保构造时初始化并保持 readonly 修饰符。
GFramework.Core.Tests/Concurrency/AsyncKeyLockManagerTests.cs (2)

282-306: ⚠️ Potential issue | 🟡 Minor

与第 126 行同类问题:async Task 方法体没有 await

CleanupDuringAcquire_Should_NotCauseRaceCondition 同样是 async Task 但没有任何 await(外层循环中的 await 都在 Task.Run 的 Lambda 内),会触发 CS1998。建议把第 305 行的 Assert.DoesNotThrowAsync(() => Task.WhenAll(tasks)) 替换为直接 await Task.WhenAll(tasks).ConfigureAwait(false),与本文件其它并发测试一致。

♻️ 建议修改
-        // Assert
-        Assert.DoesNotThrowAsync(() => Task.WhenAll(tasks));
+        // Assert - 任意未捕获异常都会导致 await 失败,从而使测试失败
+        await Task.WhenAll(tasks).ConfigureAwait(false);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@GFramework.Core.Tests/Concurrency/AsyncKeyLockManagerTests.cs` around lines
282 - 306, The test method CleanupDuringAcquire_Should_NotCauseRaceCondition is
declared async Task but contains no await (all awaits are inside Task.Run
lambdas), causing CS1998; replace the Assert.DoesNotThrowAsync(() =>
Task.WhenAll(tasks)) call with a direct await of
Task.WhenAll(tasks).ConfigureAwait(false) so the outer method actually awaits
the tasks and removes the compiler warning, keeping the rest of the test
(AsyncKeyLockManager creation and Task.Run loop) unchanged.

107-127: ⚠️ Potential issue | 🟡 Minor

async Task 测试方法缺少 await,会触发 CS1998 警告,与本 PR 目标相悖

ConcurrentAcquire_Should_NotThrowException 声明为 async Task,但方法体内没有任何 awaitAssert.DoesNotThrowAsync 在 NUnit 中返回 void,且这里也未对其 await)。这会触发 CS1998: This async method lacks 'await' operators,恰好是本批次想要消除的分析器警告类型。同时由于断言不会向上抛出 Task 异常,逻辑上也只是同步调用。

建议直接 await Task.WhenAll(tasks)(与本文件其它测试保持一致),让真实的并发异常自然冒泡,断言改为隐式(无异常即通过);或将方法签名改为 void

♻️ 建议修改
     [Test]
-    public async Task ConcurrentAcquire_Should_NotThrowException()
+    public async Task ConcurrentAcquire_Should_NotThrowException()
     {
         // Arrange
         using var manager = new AsyncKeyLockManager();
         var tasks = new List<Task>();

         // Act
         for (var i = 0; i < 100; i++)
         {
             var key = $"key-{i % 10}";
             tasks.Add(Task.Run(async () =>
             {
                 await using var handle = await manager.AcquireLockAsync(key).ConfigureAwait(false);
                 await Task.Delay(1).ConfigureAwait(false);
             }));
         }

-        // Assert
-        Assert.DoesNotThrowAsync(() => Task.WhenAll(tasks));
+        // Assert - 任意未捕获异常都会导致 await 失败,从而使测试失败
+        await Task.WhenAll(tasks).ConfigureAwait(false);
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@GFramework.Core.Tests/Concurrency/AsyncKeyLockManagerTests.cs` around lines
107 - 127, The test ConcurrentAcquire_Should_NotThrowException is declared async
Task but never awaits, causing CS1998; replace the Assert.DoesNotThrowAsync(...)
call with an actual await of Task.WhenAll(tasks) so the test awaits the
concurrent tasks (use await Task.WhenAll(tasks).ConfigureAwait(false)) and let
exceptions bubble implicitly; locate this change around the AsyncKeyLockManager
usage and the manager.AcquireLockAsync calls.
GFramework.Core.Tests/Architectures/ArchitectureServicesTests.cs (2)

369-414: ⚠️ Potential issue | 🟡 Minor

XML 文档与实际抛出的异常类型不一致

SendCommandAsync / SendCommand / SendQueryAsync / SendQuery 这几处实现已改为抛出 NotSupportedException,但对应的 <exception cref="NotImplementedException"> 文档仍未更新,会误导读者并产生不一致的 API 文档。同样的问题出现在第 369、383、396、410 行。

📝 建议的修正(示例)
-    /// <exception cref="NotImplementedException">该测试桩未实现此成员。</exception>
+    /// <exception cref="NotSupportedException">该测试桩不支持此成员。</exception>

每处 <exception cref="NotImplementedException"> 均需同步替换。

依据编码指南:「Comments MUST NOT be trivial, redundant, or misleading」。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@GFramework.Core.Tests/Architectures/ArchitectureServicesTests.cs` around
lines 369 - 414, Update the XML <exception> tags for the stub methods
SendCommandAsync, SendCommand, SendQueryAsync, and SendQuery to reflect that
they throw NotSupportedException (replace cref="NotImplementedException" with
cref="NotSupportedException") so the documentation matches the actual thrown
exception; ensure the descriptive text (e.g., "该测试桩未实现此成员。") is updated if
needed to avoid implying NotImplementedException.

274-307: ⚠️ Potential issue | 🟡 Minor

修复 TestArchitectureContextV3 中的可空返回类型不匹配

IArchitectureContext 接口声明的 GetServiceGetModelGetSystemGetUtility 方法返回非可空类型(TServiceTModelTSystemTUtility),并且接口文档明确说明这些方法"如果不存在则抛出异常"。TestArchitectureContextV3 的实现将返回类型改为可空(TService? 等),引发 CS8766 警告。应删除返回类型中的 ? 以对齐接口签名。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@GFramework.Core.Tests/Architectures/ArchitectureServicesTests.cs` around
lines 274 - 307, The implementation in TestArchitectureContextV3 has nullable
return types for methods GetService<TService>(), GetModel<TModel>(),
GetSystem<TSystem>(), and GetUtility<TUtility>() (e.g. TService?) which
mismatches the IArchitectureContext interface and causes CS8766; update those
method signatures to return non-nullable types (remove the '?' from TService?,
TModel?, TSystem?, TUtility?) so they match the interface declarations and
semantics that missing items should throw.
GFramework.Core.Tests/Architectures/GameContextTests.cs (1)

404-532: ⚠️ Potential issue | 🟡 Minor

XML 文档中的 <exception cref="NotImplementedException"> 与实际抛出的 NotSupportedException 不一致

本次将 10 处 throw new NotImplementedException(); 改为 throw new NotSupportedException();,但这些方法上方的 XML 注释仍然写着:

/// <exception cref="NotImplementedException">该测试桩未实现此成员。</exception>

具体涉及行:404、417、430、444、457、471、484、498、513、527。同 PR 内的 RegistryInitializationHookBaseTests.cs 已经把 <exception> 同步更新为 NotSupportedException(参见其行 193/203/338/348),本文件应保持一致。

按照《coding guidelines》中“Any change to public API, lifecycle semantics, module behavior, or extension points MUST update the related XML docs”的要求,建议同步更新文档:

♻️ 建议修改(每处类似)
-    /// <exception cref="NotImplementedException">该测试桩未实现此成员。</exception>
+    /// <exception cref="NotSupportedException">该测试桩未实现此成员。</exception>

As per coding guidelines: "Any change to public API, lifecycle semantics, module behavior, or extension points MUST update the related XML docs".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@GFramework.Core.Tests/Architectures/GameContextTests.cs` around lines 404 -
532, The XML <exception> tags above the test-stub methods (SendRequestAsync,
SendRequest, SendCommandAsync, SendCommand, SendQueryAsync, SendQuery,
PublishAsync, CreateStream, SendAsync<TCommand>, SendAsync<TResponse>) are out
of sync— they reference NotImplementedException while the methods throw
NotSupportedException; update each method's XML <exception cref="..."> to
cref="System.NotSupportedException" and adjust the description text if needed so
the docs match the thrown exception.
GFramework.Core.Tests/Architectures/RegistryInitializationHookBaseTests.cs (1)

290-298: ⚠️ Potential issue | 🟠 Major

修复 CS8603 可空性返回告警

CI 流水线报告 CS8603: Possible null reference return.(行 294、297)。该 override 的返回类型为 TUtility(非可空),但:

  • 第 294 行 return _registry as TUtility; 中,as 运算符在不匹配时返回 null
  • 第 297 行 return base.GetUtility<TUtility>(); 调用的基类方法签名为 TUtility? GetUtility<TUtility>()(来自 TestArchitectureContext),返回类型本就是可空。

这两个返回点都可能产生 null,与当前签名声明不匹配。

按照《coding guidelines》中“Write null-safe code that respects nullable annotations instead of suppressing warnings by default”的要求,建议让 override 与基类签名保持一致:

♻️ 建议修复
-    public override TUtility GetUtility<TUtility>()
+    public override TUtility? GetUtility<TUtility>() where TUtility : class
     {
         if (typeof(TUtility) == typeof(TestRegistry))
         {
             return _registry as TUtility;
         }

         return base.GetUtility<TUtility>();
     }

As per coding guidelines: "Write null-safe code that respects nullable annotations instead of suppressing warnings by default."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@GFramework.Core.Tests/Architectures/RegistryInitializationHookBaseTests.cs`
around lines 290 - 298, The override GetUtility<TUtility>() currently returns a
non-nullable TUtility but uses nullable-returning expressions (_registry as
TUtility and base.GetUtility<TUtility>()), causing CS8603; change the method
signature to match the base and return a nullable by declaring public override
TUtility? GetUtility<TUtility>() so the compiler and callers see the nullable
contract, keep the existing cast (return _registry as TUtility) and the base
call (return base.GetUtility<TUtility>()) unchanged, and update any callers if
they assumed a non-null result to handle possible nulls.
GFramework.Core.Tests/Tests/AsyncArchitectureTests.cs (1)

92-102: ⚠️ Potential issue | 🟡 Minor

测试方法保留 async 但不再 await,可能触发 CS1998 告警

行 96 已将 Assert.ThrowsAsync 的委托从 async () => await ... 简化为 () => Architecture.InitializeAsync(),但方法签名仍为 async Task 且方法体内不再包含任何 await。这会触发 CS1998: This async method lacks 'await' operators,与 PR "减少分析器告警"的目标相反。

同样的问题在 InitializeAsync_Should_Handle_Exception_Correctly(行 137-146)也存在。

建议二选一处理:

♻️ 方案 A:去掉 async 关键字
-    public async Task Architecture_Should_Stop_Initialization_When_Model_Init_Fails()
+    public void Architecture_Should_Stop_Initialization_When_Model_Init_Fails()
     {
         Architecture!.AddPostRegistrationHook(a => { a.RegisterModel(new FailingModel()); });

         Assert.ThrowsAsync<InvalidOperationException>(() => Architecture.InitializeAsync());

         Assert.That(
             Architecture.CurrentPhase,
             Is.EqualTo(ArchitecturePhase.FailedInitialization)
         );
     }
♻️ 方案 B:保留 async,补充 await
-        Assert.ThrowsAsync<InvalidOperationException>(() => Architecture.InitializeAsync());
+        await Task.Run(() => Assert.ThrowsAsync<InvalidOperationException>(
+            () => Architecture.InitializeAsync()));

(更推荐方案 A)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@GFramework.Core.Tests/Tests/AsyncArchitectureTests.cs` around lines 92 - 102,
The test methods Architecture_Should_Stop_Initialization_When_Model_Init_Fails
and InitializeAsync_Should_Handle_Exception_Correctly are declared async but
contain no await (triggering CS1998); fix by removing the async modifier and
keeping the return type Task for both test methods (e.g., change "public async
Task ..." to "public Task ..."), or alternatively retain async and await the
call inside Assert.ThrowsAsync (e.g., use "await Assert.ThrowsAsync(...)" with
an async lambda that awaits Architecture.InitializeAsync()); update the
corresponding method signatures and bodies for the chosen option so there are no
async methods without awaits.
GFramework.Core.Tests/Architectures/ArchitectureLifecycleBehaviorTests.cs (1)

97-106: ⚠️ Potential issue | 🟡 Minor

改后该测试方法不再包含任何 await,会触发 CS1998 告警,与本 PR“减少分析器告警”的目标相悖。

InitializeAsync_When_OnInitialize_Throws_Should_Mark_FailedInitialization 仍声明为 async Task,但在去掉 Assert.ThrowsAsync 内部的 async/await 包装后,方法体里已经没有任何 awaitAssert.ThrowsAsync 同步返回捕获的异常,无需在外层方法再异步化。

建议将其改为同步签名:

♻️ 建议变更
-    public async Task InitializeAsync_When_OnInitialize_Throws_Should_Mark_FailedInitialization()
+    public void InitializeAsync_When_OnInitialize_Throws_Should_Mark_FailedInitialization()
     {
         var architecture = new PhaseTrackingArchitecture(() => throw new InvalidOperationException("boom"));

         var exception = Assert.ThrowsAsync<InvalidOperationException>(() => architecture.InitializeAsync());
         Assert.That(exception, Is.Not.Null);
         Assert.That(architecture.CurrentPhase, Is.EqualTo(ArchitecturePhase.FailedInitialization));
         Assert.ThrowsAsync<InvalidOperationException>(() => architecture.WaitUntilReadyAsync());
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@GFramework.Core.Tests/Architectures/ArchitectureLifecycleBehaviorTests.cs`
around lines 97 - 106, The test
InitializeAsync_When_OnInitialize_Throws_Should_Mark_FailedInitialization is
declared async Task but contains no awaits (uses Assert.ThrowsAsync which
returns the exception synchronously), causing CS1998; change the test to a
synchronous test by removing the async modifier and changing the signature to
public void
InitializeAsync_When_OnInitialize_Throws_Should_Mark_FailedInitialization(),
keep the existing calls to Assert.ThrowsAsync(() =>
architecture.InitializeAsync()) and Assert.ThrowsAsync(() =>
architecture.WaitUntilReadyAsync()), and keep the PhaseTrackingArchitecture(...)
setup and the assertion that architecture.CurrentPhase ==
ArchitecturePhase.FailedInitialization.
🧹 Nitpick comments (10)
GFramework.Core.Tests/Logging/RollingFileAppenderTests.cs (1)

71-72: 排序对该断言无实际意义,可考虑移除以减少噪音。

此处仅断言 files.Length > 1OrderBy(..., StringComparer.Ordinal) 的排序结果未被使用。虽然加上 StringComparer.Ordinal 能消除 CA1304/CA1311 等分析器告警,但更简洁的做法是直接去掉 OrderBy 调用。如果保留是为了与第 111-114 行保持一致风格,则当前写法也可接受。

♻️ 建议简化
-        var files = Directory.GetFiles(_testDir, "*.log").OrderBy(f => f, System.StringComparer.Ordinal).ToArray();
+        var files = Directory.GetFiles(_testDir, "*.log");
         Assert.That(files.Length, Is.GreaterThan(1));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@GFramework.Core.Tests/Logging/RollingFileAppenderTests.cs` around lines 71 -
72, The assertion collects log files into the local variable files but the
subsequent OrderBy(..., StringComparer.Ordinal) has no effect on the assertion;
remove the unnecessary OrderBy call in the RollingFileAppenderTests method
(replace Directory.GetFiles(_testDir, "*.log").OrderBy(...).ToArray() with
Directory.GetFiles(_testDir, "*.log").ToArray()) so the test still asserts
files.Length > 1 without the redundant sorting step.
GFramework.Core.Tests/Concurrency/AsyncKeyLockManagerTests.cs (1)

263-265: 残留的 async () => await ... 包装与 PR 整体目标不一致

本 PR 的目标之一是去除 Assert.ThrowsAsync / Assert.CatchAsync / Assert.DoesNotThrowAsync 委托中多余的 async/await 包装(参见第 246 行已改为 () => ... .AsTask()),但此处仍保留了 async () => await manager.AcquireLockAsync(...).ConfigureAwait(false)。建议与第 246 行保持一致,直接传递 ValueTask.AsTask()

注意:去掉 async 包装后,ConfigureAwait 也随之失去意义(它仅影响 await 的续约线程行为),因此一并移除。

♻️ 建议修改
-        Assert.CatchAsync<OperationCanceledException>(async () =>
-            await manager.AcquireLockAsync("test-key", cts.Token).ConfigureAwait(false));
+        Assert.CatchAsync<OperationCanceledException>(
+            () => manager.AcquireLockAsync("test-key", cts.Token).AsTask());
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@GFramework.Core.Tests/Concurrency/AsyncKeyLockManagerTests.cs` around lines
263 - 265, Replace the unnecessary async/await wrapper in the Assert.CatchAsync
call: instead of using async () => await manager.AcquireLockAsync("test-key",
cts.Token).ConfigureAwait(false), pass a non-async delegate that returns the
ValueTask as a Task by calling manager.AcquireLockAsync("test-key",
cts.Token).AsTask(); remove ConfigureAwait since there is no await in the
delegate and ensure Assert.CatchAsync<OperationCanceledException>(() =>
manager.AcquireLockAsync(...).AsTask()) uses the same pattern as the other test
at line ~246.
GFramework.Core.Tests/Functional/OptionTests.cs (1)

494-496: 建议考虑为 Option<T>.ToString() 显式指定文化设置

测试代码在第 494-496 行期望 Option<int>.Some(42).ToString() 返回 "Some(42)"。当前的 Option<T>.ToString() 实现使用字符串插值隐式调用值的 ToString() 方法,这依赖于当前的区域性设置。虽然整数 42 在所有文化中都格式化为相同的字符串,因此测试本身是确定性的,但为与项目中其他文化无关的改进保持一致,建议在实现中显式使用 InvariantCulture

public override string ToString() =>
    _isSome ? $"Some({_value?.ToString(System.Globalization.CultureInfo.InvariantCulture) ?? _value})" : "None";

这样可以确保所有值类型的格式化都与文化无关,提高代码的一致性和可预测性。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@GFramework.Core.Tests/Functional/OptionTests.cs` around lines 494 - 496, The
test assumes Option<int>.ToString() yields a culture-independent representation;
update the Option<T>.ToString() implementation (the method that checks _isSome
and formats _value) to call
_value?.ToString(System.Globalization.CultureInfo.InvariantCulture) (falling
back to _value if null) when formatting the Some(...) branch so that value
formatting is culture-invariant and consistent across locales.
GFramework.Game/Storage/ScopedStorage.cs (1)

103-106: 建议:与同类方法保持一致,使用 Task 直返而非 async/await

本类的 ExistsAsync/ReadAsync/WriteAsync 等都是直接 return inner.XxxAsync(Key(key));(任务消隐 / task elision),不包 async-await,自然也无需 ConfigureAwait。当前 DeleteAsync 单独使用了 async + await + ConfigureAwait(false),与本 PR “去除冗余 async/await 包装” 的整体方向相反,且在该方法中没有捕获异常或 finally 等需要状态机的逻辑。

♻️ 建议改动
-    public async Task DeleteAsync(string key)
-    {
-        await inner.DeleteAsync(Key(key)).ConfigureAwait(false);
-    }
+    public Task DeleteAsync(string key)
+    {
+        return inner.DeleteAsync(Key(key));
+    }

如果保留 async 是为了未来加入装饰逻辑(计时、日志等),可忽略此建议。

As per coding guidelines: "Match existing async patterns and naming conventions with Async suffix for asynchronous methods".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@GFramework.Game/Storage/ScopedStorage.cs` around lines 103 - 106, DeleteAsync
currently uses async/await with ConfigureAwait(false) while other methods
(ExistsAsync/ReadAsync/WriteAsync) use task elision; change DeleteAsync to
directly return inner.DeleteAsync(Key(key)) (remove async/await and
ConfigureAwait) so it matches the existing pattern and avoids creating an
unnecessary state machine; keep Key(key) and inner.DeleteAsync as the referenced
symbols.
GFramework.Game/Storage/FileStorage.cs (1)

245-256: 可选:StreamReader 与外层 await using 的所有权重叠

new StreamReader(fs, Encoding.UTF8) 默认 leaveOpen: false,会在 sr.Dispose() 时关闭 fs;随后 configuredFileStream 再次 DisposeAsync(fs)FileStream 的 Dispose 是幂等的,目前不会出错,但出现了同一资源被两层 using 同时拥有的语义。若想让所有权更清晰,可让 StreamReader 不接管底层流:

♻️ 建议改动
-        using var sr = new StreamReader(fs, Encoding.UTF8);
+        using var sr = new StreamReader(fs, Encoding.UTF8, detectEncodingFromByteOrderMarks: true, bufferSize: 1024, leaveOpen: true);

属于风格性建议,不是必须改动。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@GFramework.Game/Storage/FileStorage.cs` around lines 245 - 256, The
FileStream created (fs) and the configuredFileStream awaitable are both
disposing the same stream while the StreamReader (sr) is constructed with
default leaveOpen=false, causing overlapping ownership; update the StreamReader
construction in the Read method (where fs, configuredFileStream and sr are used)
to pass leaveOpen: true so the StreamReader does not close the underlying
FileStream, and let the outer await using configuredFileStream handle disposing
the FileStream.
GFramework.Core.Tests/Coroutine/TaskCoroutineExtensionsTests.cs (2)

67-72: 第 69 行的同步等待无实际作用,建议移除。

taskTask.FromResult(42),构造时已完成;ConfigureAwait(false).GetAwaiter().GetResult() 既不影响 instruction.Result 的可用性,结果也被丢弃,等同于一次空操作。该行仅是从原 .Wait() 模式机械替换而来,建议直接删除以减少误导。

♻️ 建议变更
-        task.ConfigureAwait(false).GetAwaiter().GetResult();
-
         Assert.That(instruction.Result, Is.EqualTo(42));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@GFramework.Core.Tests/Coroutine/TaskCoroutineExtensionsTests.cs` around lines
67 - 72, 在 TaskCoroutineExtensionsTests 的该测试中,变量 task 是 Task.FromResult(42),因此
调用 task.ConfigureAwait(false).GetAwaiter().GetResult() 是多余的并且不影响
instruction.Result;请删除该调用行(即移除
ConfigureAwait(false).GetAwaiter().GetResult())以避免误导并保持测试简洁,保留前后的
AsCoroutineInstruction() 和 Assert.That(instruction.Result, Is.EqualTo(42)) 不变。

168-168: ConfigureAwait(false).GetAwaiter().GetResult().Wait() 等价,仅形式更冗长。

这几处均是在测试线程上同步阻塞等待 Task.Delay,本质上仍是 sync-over-async;将 .Wait() 替换为 ConfigureAwait(false).GetAwaiter().GetResult() 并未改善行为,反而增加噪声。如果此改动是为了消除某条具体的分析器告警(例如 VSTHRD002/MA0042),建议改用 await Task.Delay(50).ConfigureAwait(false); 并将测试方法标记为 async Task,或者使用 TaskCompletionSource 直接驱动调度器,避免依赖时序。

Also applies to: 192-192, 268-268

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@GFramework.Core.Tests/Coroutine/TaskCoroutineExtensionsTests.cs` at line 168,
The test code is synchronously blocking on Task.Delay via
ConfigureAwait(false).GetAwaiter().GetResult(), which is redundant and causes
sync-over-async; replace these calls in TaskCoroutineExtensionsTests (the
Task.Delay(...) occurrences) with asynchronous awaits (await
Task.Delay(50).ConfigureAwait(false)) and make the enclosing test methods return
async Task, or alternatively drive the scheduler deterministically using a
TaskCompletionSource to avoid timing-dependent sleeps; update each test method
that currently calls ConfigureAwait(false).GetAwaiter().GetResult() (the
instances around the current occurrences) accordingly.
GFramework.Core.Tests/Coroutine/WaitForTaskTests.cs (1)

282-299: 该测试中 ConfigureAwait(false) 改动与其他测试不一致,建议保持风格统一。

整个文件其它测试仍在使用 Task.Delay(...).Wait() 阻塞写法(如第 45、58、62、78、93、97、136、152、165、169、186、273、295、312 行),仅此测试改为 ConfigureAwait(false)。本测试方法已经是 async Task,第 295 行又用 .Wait() 阻塞,整体风格不一致:

  • 若目标是统一为异步等待,建议把第 295 行也改成 await Task.Delay(100).ConfigureAwait(false);,并对文件其它测试做同样处理;
  • 若目标只是消除某条特定分析器告警,建议在改动文件内保持单一风格,避免出现混合写法对后续维护者产生困惑。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@GFramework.Core.Tests/Coroutine/WaitForTaskTests.cs` around lines 282 - 299,
In WaitForTaskOfT_Should_Get_Result_From_Async_Operation replace the blocking
Task.Delay(100).Wait() with an asynchronous await (await
Task.Delay(100).ConfigureAwait(false)); keep the test method async and maintain
the existing ConfigureAwait(false) style used inside the Task.Run; this makes
the waiting consistent with the earlier await
Task.Delay(50).ConfigureAwait(false) and matches the file’s async-await style.
GFramework.Game/Scene/SceneRouterBase.cs (1)

375-426: 建议:补齐其它三个 Core 方法的设计注释,或提升为 region 级说明

行 380 在 ExecuteReplaceCoreAsync 中已注明"场景生命周期回调可能依赖引擎线程,因此这里保留默认 await 行为",但 ExecutePushCoreAsync/ExecutePopCoreAsync/ExecuteClearCoreAsync 依赖的是同一设计意图却未注释,未来读者容易把这些裸 await 误读为遗漏。建议把说明提升为 Helper Methods region 顶端的统一注释,或在每个 Core 方法上各自标注一行。

可选项:若启用了 Meziantou MA0004,将这些 await 显式改为 .ConfigureAwait(true) 既能消除告警,也能与 UiRouterBase 的写法保持一致;若 MA0004 不强制要求,则保持默认 await + 集中注释即可。

♻️ 建议变更示例(任选其一)

集中注释:

     `#region` Helper Methods
 
+    // 注:以下 ExecuteXCoreAsync 内的 await 故意保留默认 ConfigureAwait(true),
+    // 因为场景生命周期回调(OnLoad/OnEnter/OnExit/OnUnload/OnPause/OnResume)
+    // 通常需要回到引擎主线程执行。
+
     /// <summary>
     /// 执行 Replace 的核心切换顺序。
     /// </summary>
@@
     private async Task ExecuteReplaceCoreAsync(
         SceneTransitionEvent `@event`,
         string sceneKey,
         ISceneEnterParam? param)
     {
-        // 场景生命周期回调可能依赖引擎线程,因此这里保留默认 await 行为。
         await BeforeChangeAsync(`@event`);
         await ClearInternalAsync();
         await PushInternalAsync(sceneKey, param);
         await AfterChangeAsync(`@event`);
     }

或显式 ConfigureAwait(true)

-        await BeforeChangeAsync(`@event`);
-        await ClearInternalAsync();
-        await PushInternalAsync(sceneKey, param);
-        await AfterChangeAsync(`@event`);
+        await BeforeChangeAsync(`@event`).ConfigureAwait(true);
+        await ClearInternalAsync().ConfigureAwait(true);
+        await PushInternalAsync(sceneKey, param).ConfigureAwait(true);
+        await AfterChangeAsync(`@event`).ConfigureAwait(true);

As per coding guidelines, "Add inline comments for non-trivial logic, concurrency or threading behavior" 以及 "Treat Meziantou.Analyzer feedback as part of the coding standard"。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@GFramework.Game/Scene/SceneRouterBase.cs` around lines 375 - 426, The comment
notes that the threading rationale currently documented in
ExecuteReplaceCoreAsync should be applied to the other core switch methods to
avoid confusion or MA0004 warnings; update ExecutePushCoreAsync,
ExecutePopCoreAsync, and ExecuteClearCoreAsync (or add a single region-level
summary above these Helper Methods) to include the same note about lifecycle
callbacks depending on the engine/UI thread and intentionally keeping the
default await behavior, or alternatively change each await to use
.ConfigureAwait(true) to silence Meziantou MA0004 and match UiRouterBase;
reference the methods ExecutePushCoreAsync, ExecutePopCoreAsync,
ExecuteClearCoreAsync and the existing ExecuteReplaceCoreAsync when making the
change.
GFramework.Game/UI/UiRouterBase.cs (1)

102-235: LGTM:显式 ConfigureAwait(true) 表达 UI 线程亲和意图

_pipeline 内部对 lambda 的 await 使用 ConfigureAwait(false),而 lambda 自身续延的上下文由其内部 await 的 ConfigureAwait 决定,所以这里在 lambda 内逐个 await 与外层 ExecuteAroundAsync 的 await 都标注 ConfigureAwait(true) 是必要的,可在保留引擎线程亲和的同时满足 Meziantou MA0004。BeforeChangeAsync/AfterChangeAsync 中的 _pipeline.ExecuteAsync(...).ConfigureAwait(true)SceneRouterBase 中的 ConfigureAwait(false) 形成对照(场景生命周期与 UI 线程亲和需求不同),是预期的差异。

可选项(不强制):与 SceneRouterBase.cs 一致地把 Push/Replace/Pop/Clear 中的 inline async () => { ... } 抽取为命名的 ExecuteXCoreAsync 私有方法,可降低嵌套 lambda 的状态机开销并提升可读性。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@GFramework.Game/UI/UiRouterBase.cs` around lines 102 - 235, Summary: The
ConfigureAwait(true) usage inside the _pipeline.ExecuteAroundAsync lambdas is
intentional to preserve UI-thread affinity, so keep them and optionally refactor
lambdas to named private methods to reduce state-machine overhead. Leave the
current ConfigureAwait(true) on awaits of BeforeChangeAsync/AfterChangeAsync and
the outer ExecuteAroundAsync await as-is; if you want to refactor, extract each
inline async () => { ... } block into a private method (e.g.,
ExecutePushCoreAsync, ExecuteReplaceCoreAsync, ExecutePopCoreAsync,
ExecuteClearCoreAsync) that performs the same sequence (BeforeChangeAsync,
Do*Internal/DoPushPageInternal, AfterChangeAsync) and pass that method to
_pipeline.ExecuteAroundAsync to improve readability and avoid nested lambda
state machines.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md`:
- Line 51: Replace the ambiguous term "缺件" with a clearer phrase like "缺失组件" or
"缺失工作负载组件" wherever it appears in the analyzer-warning-reduction tracking note
mentioning Microsoft.NET.SDK.WorkloadAutoImportPropsLocator; update the sentence
"优先修复或绕过 `Microsoft.NET.SDK.WorkloadAutoImportPropsLocator` 缺件" to read e.g.
"优先修复或绕过 `Microsoft.NET.SDK.WorkloadAutoImportPropsLocator` 的缺失工作负载组件" and
ensure the surrounding tracking text follows the guideline by explicitly stating
completed actions, discovered issues, validation results, and the recommended
next recovery point.

In `@GFramework.Core.Tests/Extensions/AsyncExtensionsTests.cs`:
- Around line 124-128: The test uses a non-async lambda that returns a
ConfiguredTaskAwaitable (ct => Task.Delay(..., ct).ConfigureAwait(false)), which
cannot be converted to the expected Func<CancellationToken, Task> for
AsyncExtensions.WithTimeoutAsync; remove the unnecessary ConfigureAwait(false)
so the lambda returns a Task (e.g., use ct =>
Task.Delay(TimeSpan.FromSeconds(2), ct)), or alternatively make the lambda async
(async ct => { await Task.Delay(..., ct); }) — update the assertion in
AsyncExtensionsTests to pass a Func<CancellationToken, Task> to
WithTimeoutAsync.

In `@GFramework.Core.Tests/State/StateMachineSystemTests.cs`:
- Around line 138-142: The test method DestroyAsync_Should_Not_Throw_Exception
is declared with the async modifier but contains no awaits (it uses
Assert.That(() => _stateMachine!.DestroyAsync(), Throws.Nothing)), which
triggers CS1998; remove the async keyword and make the method return Task (i.e.,
change the signature of DestroyAsync_Should_Not_Throw_Exception from "async
Task" to "Task") so the test returns the Task produced by
_stateMachine!.DestroyAsync() without introducing an unused async state machine.

In `@GFramework.Core/Extensions/ContextAwareCommandExtensions.cs`:
- Around line 22-30: The explicit null-checks in
ContextAwareCommandExtensions.cs (and other changed files like
ContextAwareQueryExtensions.cs, AsyncExtensions.cs,
ContextAwareEventExtensions.cs, NumericExtensions.cs) replace
ArgumentNullException.ThrowIfNull(...) and therefore conflict with
CA1510/Meziantou.Analyzer; revert those explicit if-throws back to using
ArgumentNullException.ThrowIfNull(contextAware) and
ArgumentNullException.ThrowIfNull(command) (and equivalent parameters in the
other files) so the analyzer's recommended pattern is followed, or alternatively
add an .editorconfig entry to explicitly configure CA1510 if you intend to keep
the explicit checks.

In `@GFramework.Core/Extensions/NumericExtensions.cs`:
- Around line 28-35: The three null-checks in Between<T> (checking value, min,
max) use single-line if statements; change them to use Allman-style braces with
4-space indentation (e.g., if (value is null) { throw new
ArgumentNullException(nameof(value)); }) for each of value, min, and max to
comply with the project's brace style and avoid the CA1510 reverse-refactor
issue; update the method Between<T> accordingly so all three checks consistently
use braces.

In `@GFramework.Core/Extensions/StringExtensions.cs`:
- Around line 41-46: Replace single-line if statements in
GFramework.Core.Extensions.StringExtensions (e.g., the null checks for str and
suffix) with Allman-style braces; specifically change the single-line checks "if
(str is null) throw new ArgumentNullException(nameof(str));" and "if (suffix is
null) throw new ArgumentNullException(nameof(suffix));" and the other
single-line ifs around the later checks (the ones at the 72-76 range) to use
block braces on their own lines (open brace on the same line as the if, close
brace on its own line) and keep existing indentation to conform to the
repository's Allman C# brace style.

In `@GFramework.Core/StateManagement/StoreBuilder.cs`:
- Around line 42-44: The null-checks in StoreBuilder.cs (notably in
UseMiddleware and the two AddReducer overloads AddReducer<TAction>(Func<...>)
and AddReducer<TAction>(IReducer<...>)) use single-line if statements without
braces which violates the Allman brace style and triggers the CA1510
reverse-refactor issue; update these checks to match the PR's other files by
either replacing the single-line ifs with braced Allman-style blocks or,
preferably for consistency with ContextAwareCommandExtensions.cs, simplify them
to ArgumentNullException.ThrowIfNull(...) for each parameter so the null checks
are uniform across the codebase.

---

Outside diff comments:
In `@GFramework.Core.Tests/Architectures/ArchitectureLifecycleBehaviorTests.cs`:
- Around line 97-106: The test
InitializeAsync_When_OnInitialize_Throws_Should_Mark_FailedInitialization is
declared async Task but contains no awaits (uses Assert.ThrowsAsync which
returns the exception synchronously), causing CS1998; change the test to a
synchronous test by removing the async modifier and changing the signature to
public void
InitializeAsync_When_OnInitialize_Throws_Should_Mark_FailedInitialization(),
keep the existing calls to Assert.ThrowsAsync(() =>
architecture.InitializeAsync()) and Assert.ThrowsAsync(() =>
architecture.WaitUntilReadyAsync()), and keep the PhaseTrackingArchitecture(...)
setup and the assertion that architecture.CurrentPhase ==
ArchitecturePhase.FailedInitialization.

In `@GFramework.Core.Tests/Architectures/ArchitectureServicesTests.cs`:
- Around line 369-414: Update the XML <exception> tags for the stub methods
SendCommandAsync, SendCommand, SendQueryAsync, and SendQuery to reflect that
they throw NotSupportedException (replace cref="NotImplementedException" with
cref="NotSupportedException") so the documentation matches the actual thrown
exception; ensure the descriptive text (e.g., "该测试桩未实现此成员。") is updated if
needed to avoid implying NotImplementedException.
- Around line 274-307: The implementation in TestArchitectureContextV3 has
nullable return types for methods GetService<TService>(), GetModel<TModel>(),
GetSystem<TSystem>(), and GetUtility<TUtility>() (e.g. TService?) which
mismatches the IArchitectureContext interface and causes CS8766; update those
method signatures to return non-nullable types (remove the '?' from TService?,
TModel?, TSystem?, TUtility?) so they match the interface declarations and
semantics that missing items should throw.

In `@GFramework.Core.Tests/Architectures/GameContextTests.cs`:
- Around line 404-532: The XML <exception> tags above the test-stub methods
(SendRequestAsync, SendRequest, SendCommandAsync, SendCommand, SendQueryAsync,
SendQuery, PublishAsync, CreateStream, SendAsync<TCommand>,
SendAsync<TResponse>) are out of sync— they reference NotImplementedException
while the methods throw NotSupportedException; update each method's XML
<exception cref="..."> to cref="System.NotSupportedException" and adjust the
description text if needed so the docs match the thrown exception.

In `@GFramework.Core.Tests/Architectures/RegistryInitializationHookBaseTests.cs`:
- Around line 290-298: The override GetUtility<TUtility>() currently returns a
non-nullable TUtility but uses nullable-returning expressions (_registry as
TUtility and base.GetUtility<TUtility>()), causing CS8603; change the method
signature to match the base and return a nullable by declaring public override
TUtility? GetUtility<TUtility>() so the compiler and callers see the nullable
contract, keep the existing cast (return _registry as TUtility) and the base
call (return base.GetUtility<TUtility>()) unchanged, and update any callers if
they assumed a non-null result to handle possible nulls.

In `@GFramework.Core.Tests/Concurrency/AsyncKeyLockManagerTests.cs`:
- Around line 282-306: The test method
CleanupDuringAcquire_Should_NotCauseRaceCondition is declared async Task but
contains no await (all awaits are inside Task.Run lambdas), causing CS1998;
replace the Assert.DoesNotThrowAsync(() => Task.WhenAll(tasks)) call with a
direct await of Task.WhenAll(tasks).ConfigureAwait(false) so the outer method
actually awaits the tasks and removes the compiler warning, keeping the rest of
the test (AsyncKeyLockManager creation and Task.Run loop) unchanged.
- Around line 107-127: The test ConcurrentAcquire_Should_NotThrowException is
declared async Task but never awaits, causing CS1998; replace the
Assert.DoesNotThrowAsync(...) call with an actual await of Task.WhenAll(tasks)
so the test awaits the concurrent tasks (use await
Task.WhenAll(tasks).ConfigureAwait(false)) and let exceptions bubble implicitly;
locate this change around the AsyncKeyLockManager usage and the
manager.AcquireLockAsync calls.

In `@GFramework.Core.Tests/Tests/AsyncArchitectureTests.cs`:
- Around line 92-102: The test methods
Architecture_Should_Stop_Initialization_When_Model_Init_Fails and
InitializeAsync_Should_Handle_Exception_Correctly are declared async but contain
no await (triggering CS1998); fix by removing the async modifier and keeping the
return type Task for both test methods (e.g., change "public async Task ..." to
"public Task ..."), or alternatively retain async and await the call inside
Assert.ThrowsAsync (e.g., use "await Assert.ThrowsAsync(...)" with an async
lambda that awaits Architecture.InitializeAsync()); update the corresponding
method signatures and bodies for the chosen option so there are no async methods
without awaits.

In `@GFramework.Core/StateManagement/StoreSelection.cs`:
- Line 30: 在 StoreSelection 类中将当前字段 private readonly object _lock = new(); 替换为使用
System.Threading.Lock(例如 private readonly System.Threading.Lock _lock =
new();),并保留现有的 lock (_lock) 调用不变;定位到 StoreSelection 类和字段名 _lock,改用
System.Threading.Lock 类型以消除 MA0158 警告,确保构造时初始化并保持 readonly 修饰符。

---

Nitpick comments:
In `@GFramework.Core.Tests/Concurrency/AsyncKeyLockManagerTests.cs`:
- Around line 263-265: Replace the unnecessary async/await wrapper in the
Assert.CatchAsync call: instead of using async () => await
manager.AcquireLockAsync("test-key", cts.Token).ConfigureAwait(false), pass a
non-async delegate that returns the ValueTask as a Task by calling
manager.AcquireLockAsync("test-key", cts.Token).AsTask(); remove ConfigureAwait
since there is no await in the delegate and ensure
Assert.CatchAsync<OperationCanceledException>(() =>
manager.AcquireLockAsync(...).AsTask()) uses the same pattern as the other test
at line ~246.

In `@GFramework.Core.Tests/Coroutine/TaskCoroutineExtensionsTests.cs`:
- Around line 67-72: 在 TaskCoroutineExtensionsTests 的该测试中,变量 task 是
Task.FromResult(42),因此 调用 task.ConfigureAwait(false).GetAwaiter().GetResult()
是多余的并且不影响 instruction.Result;请删除该调用行(即移除
ConfigureAwait(false).GetAwaiter().GetResult())以避免误导并保持测试简洁,保留前后的
AsCoroutineInstruction() 和 Assert.That(instruction.Result, Is.EqualTo(42)) 不变。
- Line 168: The test code is synchronously blocking on Task.Delay via
ConfigureAwait(false).GetAwaiter().GetResult(), which is redundant and causes
sync-over-async; replace these calls in TaskCoroutineExtensionsTests (the
Task.Delay(...) occurrences) with asynchronous awaits (await
Task.Delay(50).ConfigureAwait(false)) and make the enclosing test methods return
async Task, or alternatively drive the scheduler deterministically using a
TaskCompletionSource to avoid timing-dependent sleeps; update each test method
that currently calls ConfigureAwait(false).GetAwaiter().GetResult() (the
instances around the current occurrences) accordingly.

In `@GFramework.Core.Tests/Coroutine/WaitForTaskTests.cs`:
- Around line 282-299: In WaitForTaskOfT_Should_Get_Result_From_Async_Operation
replace the blocking Task.Delay(100).Wait() with an asynchronous await (await
Task.Delay(100).ConfigureAwait(false)); keep the test method async and maintain
the existing ConfigureAwait(false) style used inside the Task.Run; this makes
the waiting consistent with the earlier await
Task.Delay(50).ConfigureAwait(false) and matches the file’s async-await style.

In `@GFramework.Core.Tests/Functional/OptionTests.cs`:
- Around line 494-496: The test assumes Option<int>.ToString() yields a
culture-independent representation; update the Option<T>.ToString()
implementation (the method that checks _isSome and formats _value) to call
_value?.ToString(System.Globalization.CultureInfo.InvariantCulture) (falling
back to _value if null) when formatting the Some(...) branch so that value
formatting is culture-invariant and consistent across locales.

In `@GFramework.Core.Tests/Logging/RollingFileAppenderTests.cs`:
- Around line 71-72: The assertion collects log files into the local variable
files but the subsequent OrderBy(..., StringComparer.Ordinal) has no effect on
the assertion; remove the unnecessary OrderBy call in the
RollingFileAppenderTests method (replace Directory.GetFiles(_testDir,
"*.log").OrderBy(...).ToArray() with Directory.GetFiles(_testDir,
"*.log").ToArray()) so the test still asserts files.Length > 1 without the
redundant sorting step.

In `@GFramework.Game/Scene/SceneRouterBase.cs`:
- Around line 375-426: The comment notes that the threading rationale currently
documented in ExecuteReplaceCoreAsync should be applied to the other core switch
methods to avoid confusion or MA0004 warnings; update ExecutePushCoreAsync,
ExecutePopCoreAsync, and ExecuteClearCoreAsync (or add a single region-level
summary above these Helper Methods) to include the same note about lifecycle
callbacks depending on the engine/UI thread and intentionally keeping the
default await behavior, or alternatively change each await to use
.ConfigureAwait(true) to silence Meziantou MA0004 and match UiRouterBase;
reference the methods ExecutePushCoreAsync, ExecutePopCoreAsync,
ExecuteClearCoreAsync and the existing ExecuteReplaceCoreAsync when making the
change.

In `@GFramework.Game/Storage/FileStorage.cs`:
- Around line 245-256: The FileStream created (fs) and the configuredFileStream
awaitable are both disposing the same stream while the StreamReader (sr) is
constructed with default leaveOpen=false, causing overlapping ownership; update
the StreamReader construction in the Read method (where fs, configuredFileStream
and sr are used) to pass leaveOpen: true so the StreamReader does not close the
underlying FileStream, and let the outer await using configuredFileStream handle
disposing the FileStream.

In `@GFramework.Game/Storage/ScopedStorage.cs`:
- Around line 103-106: DeleteAsync currently uses async/await with
ConfigureAwait(false) while other methods (ExistsAsync/ReadAsync/WriteAsync) use
task elision; change DeleteAsync to directly return inner.DeleteAsync(Key(key))
(remove async/await and ConfigureAwait) so it matches the existing pattern and
avoids creating an unnecessary state machine; keep Key(key) and
inner.DeleteAsync as the referenced symbols.

In `@GFramework.Game/UI/UiRouterBase.cs`:
- Around line 102-235: Summary: The ConfigureAwait(true) usage inside the
_pipeline.ExecuteAroundAsync lambdas is intentional to preserve UI-thread
affinity, so keep them and optionally refactor lambdas to named private methods
to reduce state-machine overhead. Leave the current ConfigureAwait(true) on
awaits of BeforeChangeAsync/AfterChangeAsync and the outer ExecuteAroundAsync
await as-is; if you want to refactor, extract each inline async () => { ... }
block into a private method (e.g., ExecutePushCoreAsync,
ExecuteReplaceCoreAsync, ExecutePopCoreAsync, ExecuteClearCoreAsync) that
performs the same sequence (BeforeChangeAsync, Do*Internal/DoPushPageInternal,
AfterChangeAsync) and pass that method to _pipeline.ExecuteAroundAsync to
improve readability and avoid nested lambda state machines.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d2fce200-1636-4233-be9b-4a9050ee5fac

📥 Commits

Reviewing files that changed from the base of the PR and between 984fb21 and be336b2.

📒 Files selected for processing (75)
  • GFramework.Core.Tests/Architectures/ArchitectureAdditionalCqrsHandlersTests.cs
  • GFramework.Core.Tests/Architectures/ArchitectureContextTests.cs
  • GFramework.Core.Tests/Architectures/ArchitectureLifecycleBehaviorTests.cs
  • GFramework.Core.Tests/Architectures/ArchitectureServicesTests.cs
  • GFramework.Core.Tests/Architectures/GameContextTests.cs
  • GFramework.Core.Tests/Architectures/RegistryInitializationHookBaseTests.cs
  • GFramework.Core.Tests/Command/AbstractAsyncCommandTests.cs
  • GFramework.Core.Tests/Command/CommandExecutorTests.cs
  • GFramework.Core.Tests/Concurrency/AsyncKeyLockManagerTests.cs
  • GFramework.Core.Tests/Coroutine/AsyncOperationTests.cs
  • GFramework.Core.Tests/Coroutine/CommandCoroutineExtensionsTests.cs
  • GFramework.Core.Tests/Coroutine/QueryCoroutineExtensionsTests.cs
  • GFramework.Core.Tests/Coroutine/TaskCoroutineExtensionsTests.cs
  • GFramework.Core.Tests/Coroutine/WaitForTaskTTests.cs
  • GFramework.Core.Tests/Coroutine/WaitForTaskTests.cs
  • GFramework.Core.Tests/Extensions/AsyncExtensionsTests.cs
  • GFramework.Core.Tests/Extensions/CollectionExtensionsTests.cs
  • GFramework.Core.Tests/Extensions/ResultExtensionsTests.cs
  • GFramework.Core.Tests/Functional/OptionTests.cs
  • GFramework.Core.Tests/Functional/Pipe/PipeExtensionsTests.cs
  • GFramework.Core.Tests/Functional/ResultTTests.cs
  • GFramework.Core.Tests/Functional/ResultTests.cs
  • GFramework.Core.Tests/Localization/LocalizationTableTests.cs
  • GFramework.Core.Tests/Logging/DefaultLogFormatterTests.cs
  • GFramework.Core.Tests/Logging/JsonLogFormatterTests.cs
  • GFramework.Core.Tests/Logging/LogContextTests.cs
  • GFramework.Core.Tests/Logging/LogEntryTests.cs
  • GFramework.Core.Tests/Logging/RollingFileAppenderTests.cs
  • GFramework.Core.Tests/Model/AsyncTestModel.cs
  • GFramework.Core.Tests/Pause/PauseStackManagerTests.cs
  • GFramework.Core.Tests/Query/AbstractAsyncQueryTests.cs
  • GFramework.Core.Tests/Query/AsyncQueryExecutorTests.cs
  • GFramework.Core.Tests/Resource/ResourceManagerTests.cs
  • GFramework.Core.Tests/Rule/ContextAwareEnvironmentExtensionsTests.cs
  • GFramework.Core.Tests/Rule/ContextAwareServiceExtensionsTests.cs
  • GFramework.Core.Tests/State/StateMachineSystemTests.cs
  • GFramework.Core.Tests/State/StateMachineTests.cs
  • GFramework.Core.Tests/State/StateTests.cs
  • GFramework.Core.Tests/Systems/AsyncTestSystem.cs
  • GFramework.Core.Tests/Tests/AsyncArchitectureTests.cs
  • GFramework.Core/Extensions/AsyncExtensions.cs
  • GFramework.Core/Extensions/CollectionExtensions.cs
  • GFramework.Core/Extensions/ContextAwareCommandExtensions.cs
  • GFramework.Core/Extensions/ContextAwareEnvironmentExtensions.cs
  • GFramework.Core/Extensions/ContextAwareEventExtensions.cs
  • GFramework.Core/Extensions/ContextAwareQueryExtensions.cs
  • GFramework.Core/Extensions/ContextAwareServiceExtensions.cs
  • GFramework.Core/Extensions/GuardExtensions.cs
  • GFramework.Core/Extensions/NumericExtensions.cs
  • GFramework.Core/Extensions/StoreEventBusExtensions.cs
  • GFramework.Core/Extensions/StringExtensions.cs
  • GFramework.Core/StateManagement/StoreBuilder.cs
  • GFramework.Core/StateManagement/StoreSelection.cs
  • GFramework.Game.Tests/Config/ArchitectureConfigIntegrationTests.cs
  • GFramework.Game.Tests/Config/GameConfigBootstrapTests.cs
  • GFramework.Game.Tests/Config/YamlConfigLoaderAllOfTests.cs
  • GFramework.Game.Tests/Config/YamlConfigLoaderDependentRequiredTests.cs
  • GFramework.Game.Tests/Config/YamlConfigLoaderDependentSchemasTests.cs
  • GFramework.Game.Tests/Config/YamlConfigLoaderEnumTests.cs
  • GFramework.Game.Tests/Config/YamlConfigLoaderIfThenElseTests.cs
  • GFramework.Game.Tests/Config/YamlConfigLoaderNegationTests.cs
  • GFramework.Game.Tests/Config/YamlConfigLoaderTests.cs
  • GFramework.Game.Tests/Config/YamlConfigTextValidatorTests.cs
  • GFramework.Game.Tests/Data/PersistenceTests.cs
  • GFramework.Game/Routing/RouterBase.cs
  • GFramework.Game/Scene/SceneRouterBase.cs
  • GFramework.Game/Scene/SceneTransitionPipeline.cs
  • GFramework.Game/Setting/SettingsSystem.cs
  • GFramework.Game/Storage/FileStorage.cs
  • GFramework.Game/Storage/ScopedStorage.cs
  • GFramework.Game/UI/UiRouterBase.cs
  • GFramework.Game/UI/UiTransitionPipeline.cs
  • GFramework.Godot.Tests/Architectures/AbstractArchitectureModuleInstallationTests.cs
  • ai-plan/public/analyzer-warning-reduction/todos/analyzer-warning-reduction-tracking.md
  • ai-plan/public/analyzer-warning-reduction/traces/analyzer-warning-reduction-trace.md

Comment thread GFramework.Core.Tests/Extensions/AsyncExtensionsTests.cs
Comment thread GFramework.Core.Tests/State/StateMachineSystemTests.cs
Comment thread GFramework.Core/Extensions/ContextAwareCommandExtensions.cs Outdated
Comment thread GFramework.Core/Extensions/NumericExtensions.cs Outdated
Comment thread GFramework.Core/Extensions/StringExtensions.cs Outdated
Comment thread GFramework.Core/StateManagement/StoreBuilder.cs Outdated
- 修复 AsyncExtensionsTests 中错误返回 ConfiguredTaskAwaitable 导致的测试编译失败

- 收敛多处测试中的冗余 async/await 与 ValueTask 断言包装,减少 PR review 指出的告警

- 更新 StoreSelection 的 net9+ 锁实现与 analyzer-warning-reduction 跟踪文档,记录 PR #288 与当前 MSB4018 环境阻塞
@github-actions
Copy link
Copy Markdown

Summary

Tests 📝 Passed ✅ Failed ❌ Skipped ⏭️ Other ❓ Flaky 🍂 Duration ⏱️
2156 2156 0 0 0 0 36.3s    ↑98ms

Test Results

passed 2156 passed

Details

tests 2156 tests
clock 36.3s ↑98ms
tool nunit
build CI - Build & Test arrow-right build-and-test link #967
pull-request Fix/analyzer warning reduction batch link #288

Insights

Average Tests per Run Total Flaky Tests Total Failed Slowest Test (p95)
2147 0 2 4.7s

Fail Rate

Fail Rate 0.00%
Test 📝 Results 📊 Passed ✅ Failed ❌ Fail Rate (%) 📈
ILogAppender_Flush_Should_Raise_OnFlushCompleted_Only_Once 22 21 1 4.55    ↓0.21
Run_Should_Assign_Globally_Unique_Reference_Metadata_Member_Names 30 29 1 3.33    ↓0.12

build-and-test: Run #967

Tests 📝 Passed ✅ Failed ❌ Skipped ⏭️ Pending ⏳ Other ❓ Flaky 🍂 Duration ⏱️
2156 2156 0 0 0 0 0 36.3s

🎉 All tests passed!

Slowest Tests

Test 📝 Results 📊 Duration (avg) ⏱️ Duration (p95) ⏱️
SendRequestAsync_Should_ResolveCqrsRuntime_OnlyOnce_When_AccessedConcurrently 30 4.0s 4.7s
Does_Not_Report_When_FieldInjectedModel_Is_Registered 30 2.2s 2.4s
Generates_Scene_Behavior_Boilerplate 30 1.9s 2.0s
CleanupDuringAcquire_Should_NotCauseRaceCondition 30 1.1s 1.1s
Append_ShouldNotBlock 30 1.0s 1.0s
Flush_Should_Raise_OnFlushCompleted_With_Sender_And_Result 23 87ms 1.0s
Context_Caching_Should_Improve_Performance 30 781ms 791ms
PendingCount_ShouldReflectQueuedEntries 30 501ms 501ms
Cleanup_Should_NotRemoveActiveLocks 30 404ms 406ms
Cleanup_Should_RemoveUnusedLocks 30 401ms 402ms

± Comparison with run #963 at eaf80f1 | 🍂 No flaky tests detected across all runs. | ⏱️ Measured over 30 runs.

Github Test Reporter by CTRF 💚

- 将 ConfigureAwait 参数从 false 修改为 true
- 确保异步操作在正确的上下文中继续执行
- 避免潜在的死锁或性能问题
@github-actions
Copy link
Copy Markdown

Summary

Tests 📝 Passed ✅ Failed ❌ Skipped ⏭️ Other ❓ Flaky 🍂 Duration ⏱️
2156 2156 0 0 0 0 35.8s    ↓462ms

Test Results

passed 2156 passed

Details

tests 2156 tests
clock 35.8s ↓462ms
tool nunit
build CI - Build & Test arrow-right build-and-test link #968
pull-request Fix/analyzer warning reduction batch link #288

Insights

Average Tests per Run Total Flaky Tests Total Failed Slowest Test (p95)
2147 0 2 4.7s

Fail Rate

Fail Rate 0.00%
Test 📝 Results 📊 Passed ✅ Failed ❌ Fail Rate (%) 📈
ILogAppender_Flush_Should_Raise_OnFlushCompleted_Only_Once 23 22 1 4.35    ↓0.20
Run_Should_Assign_Globally_Unique_Reference_Metadata_Member_Names 31 30 1 3.23    ↓0.10

build-and-test: Run #968

Tests 📝 Passed ✅ Failed ❌ Skipped ⏭️ Pending ⏳ Other ❓ Flaky 🍂 Duration ⏱️
2156 2156 0 0 0 0 0 35.8s

🎉 All tests passed!

Slowest Tests

Test 📝 Results 📊 Duration (avg) ⏱️ Duration (p95) ⏱️
SendRequestAsync_Should_ResolveCqrsRuntime_OnlyOnce_When_AccessedConcurrently 31 4.0s 4.7s
Does_Not_Report_When_FieldInjectedModel_Is_Registered 31 2.2s 2.4s
Generates_Scene_Behavior_Boilerplate 31 1.9s 2.0s
CleanupDuringAcquire_Should_NotCauseRaceCondition 31 1.1s 1.1s
Append_ShouldNotBlock 31 1.0s 1.0s
Flush_Should_Raise_OnFlushCompleted_With_Sender_And_Result 24 84ms 1.0s
Context_Caching_Should_Improve_Performance 31 782ms 791ms
PendingCount_ShouldReflectQueuedEntries 31 501ms 501ms
Cleanup_Should_NotRemoveActiveLocks 31 404ms 406ms
Cleanup_Should_RemoveUnusedLocks 31 401ms 402ms

± Comparison with run #967 at f33a4e8 | 🍂 No flaky tests detected across all runs. | ⏱️ Measured over 31 runs.

Github Test Reporter by CTRF 💚

- 修复 PR #288 中经本地复核后仍成立的 Core、Game 与测试建议

- 更新 WSL 标准 dotnet build 验证路径并确认 Release 构建可通过

- 补充 analyzer-warning-reduction 跟踪文档记录本轮结论与恢复点
@github-actions
Copy link
Copy Markdown

Summary

Tests 📝 Passed ✅ Failed ❌ Skipped ⏭️ Other ❓ Flaky 🍂 Duration ⏱️
2156 2156 0 0 0 0 37.4s    ↑1.5s

Test Results

passed 2156 passed

Details

tests 2156 tests
clock 37.4s ↑1.5s
tool nunit
build CI - Build & Test arrow-right build-and-test link #970
pull-request Fix/analyzer warning reduction batch link #288

Insights

Average Tests per Run Total Flaky Tests Total Failed Slowest Test (p95)
2148 0 2 4.7s

Fail Rate

Fail Rate 0.00%
Test 📝 Results 📊 Passed ✅ Failed ❌ Fail Rate (%) 📈
ILogAppender_Flush_Should_Raise_OnFlushCompleted_Only_Once 24 23 1 4.17    ↓0.18
Run_Should_Assign_Globally_Unique_Reference_Metadata_Member_Names 32 31 1 3.13    ↓0.10

build-and-test: Run #970

Tests 📝 Passed ✅ Failed ❌ Skipped ⏭️ Pending ⏳ Other ❓ Flaky 🍂 Duration ⏱️
2156 2156 0 0 0 0 0 37.4s

🎉 All tests passed!

Slowest Tests

Test 📝 Results 📊 Duration (avg) ⏱️ Duration (p95) ⏱️
SendRequestAsync_Should_ResolveCqrsRuntime_OnlyOnce_When_AccessedConcurrently 32 4.0s 4.7s
Does_Not_Report_When_FieldInjectedModel_Is_Registered 32 2.2s 2.4s
Generates_Scene_Behavior_Boilerplate 32 1.9s 2.0s
CleanupDuringAcquire_Should_NotCauseRaceCondition 32 1.1s 1.1s
Append_ShouldNotBlock 32 1.0s 1.0s
Flush_Should_Raise_OnFlushCompleted_With_Sender_And_Result 25 81ms 1.0s
Context_Caching_Should_Improve_Performance 32 782ms 791ms
PendingCount_ShouldReflectQueuedEntries 32 501ms 501ms
Cleanup_Should_NotRemoveActiveLocks 32 404ms 406ms
Cleanup_Should_RemoveUnusedLocks 32 401ms 402ms

± Comparison with run #968 at c7e4b3c | 🍂 No flaky tests detected across all runs. | ⏱️ Measured over 32 runs.

Github Test Reporter by CTRF 💚

@GeWuYou GeWuYou merged commit 4ad880c into main Apr 25, 2026
7 checks passed
@GeWuYou GeWuYou deleted the fix/analyzer-warning-reduction-batch branch April 25, 2026 06:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant