From 16a72e85af8f51e839cececa14c8adca5dc9f71e Mon Sep 17 00:00:00 2001 From: GeWuYou <95328647+GeWuYou@users.noreply.github.com> Date: Wed, 21 Jan 2026 16:02:27 +0800 Subject: [PATCH] =?UTF-8?q?refactor(coroutine):=20=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E5=8D=8F=E7=A8=8B=E7=9B=B8=E5=85=B3=E6=8A=BD=E8=B1=A1=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 移除了以下协程相关的接口定义: - ICoroutineContext: 协程上下文接口 - ICoroutineHandle: 协程句柄接口 - ICoroutineScheduler: 协程调度器接口 - ICoroutineScope: 协程作用域接口 - ICoroutineSystem: 协程系统接口 - IYieldInstruction: 等待指令接口 - [skip ci] --- .../coroutine/ICoroutineContext.cs | 22 - .../coroutine/ICoroutineHandle.cs | 39 - .../coroutine/ICoroutineScheduler.cs | 18 - .../coroutine/ICoroutineScope.cs | 26 - .../coroutine/ICoroutineSystem.cs | 15 - .../coroutine/IYieldInstruction.cs | 18 - GFramework.Core.Tests/TEST_COVERAGE_PLAN.md | 946 ------------------ .../coroutine/CoroutineHandleTests.cs | 379 ------- .../coroutine/CoroutineIntegrationTests.cs | 385 ------- .../coroutine/CoroutineSchedulerTests.cs | 305 ------ .../CoroutineScopeExtensionsTests.cs | 291 ------ .../coroutine/CoroutineScopeTests.cs | 379 ------- .../coroutine/GlobalCoroutineScopeTests.cs | 215 ---- .../coroutine/YieldInstructionTests.cs | 471 --------- GFramework.Core/coroutine/CoroutineContext.cs | 28 - GFramework.Core/coroutine/CoroutineHandle.cs | 280 ------ .../coroutine/CoroutineScheduler.cs | 79 -- GFramework.Core/coroutine/CoroutineScope.cs | 99 -- .../coroutine/CoroutineScopeExtensions.cs | 63 -- .../coroutine/GlobalCoroutineScope.cs | 50 - GFramework.Core/coroutine/WaitForSeconds.cs | 37 - GFramework.Core/coroutine/WaitUntil.cs | 33 - GFramework.Core/coroutine/WaitWhile.cs | 33 - GFramework.Core/coroutine/协程系统改进计划.md | 494 --------- GFramework.Game/coroutine/CoroutineSystem.cs | 29 - 25 files changed, 4734 deletions(-) delete mode 100644 GFramework.Core.Abstractions/coroutine/ICoroutineContext.cs delete mode 100644 GFramework.Core.Abstractions/coroutine/ICoroutineHandle.cs delete mode 100644 GFramework.Core.Abstractions/coroutine/ICoroutineScheduler.cs delete mode 100644 GFramework.Core.Abstractions/coroutine/ICoroutineScope.cs delete mode 100644 GFramework.Core.Abstractions/coroutine/ICoroutineSystem.cs delete mode 100644 GFramework.Core.Abstractions/coroutine/IYieldInstruction.cs delete mode 100644 GFramework.Core.Tests/TEST_COVERAGE_PLAN.md delete mode 100644 GFramework.Core.Tests/coroutine/CoroutineHandleTests.cs delete mode 100644 GFramework.Core.Tests/coroutine/CoroutineIntegrationTests.cs delete mode 100644 GFramework.Core.Tests/coroutine/CoroutineSchedulerTests.cs delete mode 100644 GFramework.Core.Tests/coroutine/CoroutineScopeExtensionsTests.cs delete mode 100644 GFramework.Core.Tests/coroutine/CoroutineScopeTests.cs delete mode 100644 GFramework.Core.Tests/coroutine/GlobalCoroutineScopeTests.cs delete mode 100644 GFramework.Core.Tests/coroutine/YieldInstructionTests.cs delete mode 100644 GFramework.Core/coroutine/CoroutineContext.cs delete mode 100644 GFramework.Core/coroutine/CoroutineHandle.cs delete mode 100644 GFramework.Core/coroutine/CoroutineScheduler.cs delete mode 100644 GFramework.Core/coroutine/CoroutineScope.cs delete mode 100644 GFramework.Core/coroutine/CoroutineScopeExtensions.cs delete mode 100644 GFramework.Core/coroutine/GlobalCoroutineScope.cs delete mode 100644 GFramework.Core/coroutine/WaitForSeconds.cs delete mode 100644 GFramework.Core/coroutine/WaitUntil.cs delete mode 100644 GFramework.Core/coroutine/WaitWhile.cs delete mode 100644 GFramework.Core/coroutine/协程系统改进计划.md delete mode 100644 GFramework.Game/coroutine/CoroutineSystem.cs diff --git a/GFramework.Core.Abstractions/coroutine/ICoroutineContext.cs b/GFramework.Core.Abstractions/coroutine/ICoroutineContext.cs deleted file mode 100644 index 898169e..0000000 --- a/GFramework.Core.Abstractions/coroutine/ICoroutineContext.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace GFramework.Core.Abstractions.coroutine; - -/// -/// 协程上下文接口,提供协程执行所需的上下文信息 -/// -public interface ICoroutineContext -{ - /// - /// 获取协程作用域 - /// - ICoroutineScope Scope { get; } - - /// - /// 获取协程调度器 - /// - ICoroutineScheduler Scheduler { get; } - - /// - /// 获取协程所有者对象 - /// - object? Owner { get; } -} \ No newline at end of file diff --git a/GFramework.Core.Abstractions/coroutine/ICoroutineHandle.cs b/GFramework.Core.Abstractions/coroutine/ICoroutineHandle.cs deleted file mode 100644 index 4281b0f..0000000 --- a/GFramework.Core.Abstractions/coroutine/ICoroutineHandle.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; - -namespace GFramework.Core.Abstractions.coroutine; - -/// -/// 协程句柄接口,用于管理和控制协程的执行状态 -/// -public interface ICoroutineHandle -{ - /// - /// 获取协程的上下文对象 - /// - ICoroutineContext Context { get; } - - /// - /// 获取协程是否已被取消的标志 - /// - bool IsCancelled { get; } - - /// - /// 获取协程是否已完成的标志 - /// - bool IsDone { get; } - - /// - /// 当协程完成时触发的事件 - /// - event Action? OnComplete; - - /// - /// 当协程发生错误时触发的事件 - /// - event Action? OnError; - - /// - /// 取消协程的执行 - /// - void Cancel(); -} \ No newline at end of file diff --git a/GFramework.Core.Abstractions/coroutine/ICoroutineScheduler.cs b/GFramework.Core.Abstractions/coroutine/ICoroutineScheduler.cs deleted file mode 100644 index a4df1cd..0000000 --- a/GFramework.Core.Abstractions/coroutine/ICoroutineScheduler.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace GFramework.Core.Abstractions.coroutine; - -/// -/// 协程调度器接口,用于管理和执行协程任务 -/// -public interface ICoroutineScheduler -{ - /// - /// 获取当前活跃的协程数量 - /// - int ActiveCount { get; } - - /// - /// 更新协程调度器,处理当前帧需要执行的协程任务 - /// - /// 自上一帧以来的时间间隔(以秒为单位) - void Update(float deltaTime); -} \ No newline at end of file diff --git a/GFramework.Core.Abstractions/coroutine/ICoroutineScope.cs b/GFramework.Core.Abstractions/coroutine/ICoroutineScope.cs deleted file mode 100644 index e760fad..0000000 --- a/GFramework.Core.Abstractions/coroutine/ICoroutineScope.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Collections; - -namespace GFramework.Core.Abstractions.coroutine; - -/// -/// 协程作用域接口,用于管理协程的生命周期和执行 -/// -public interface ICoroutineScope -{ - /// - /// 获取协程作用域是否处于活动状态 - /// - bool IsActive { get; } - - /// - /// 取消当前协程作用域,停止所有正在运行的协程 - /// - void Cancel(); - - /// - /// 启动一个新的协程 - /// - /// 要执行的协程迭代器 - /// 协程句柄,用于控制和监控协程的执行 - ICoroutineHandle Launch(IEnumerator routine); -} \ No newline at end of file diff --git a/GFramework.Core.Abstractions/coroutine/ICoroutineSystem.cs b/GFramework.Core.Abstractions/coroutine/ICoroutineSystem.cs deleted file mode 100644 index f1a6111..0000000 --- a/GFramework.Core.Abstractions/coroutine/ICoroutineSystem.cs +++ /dev/null @@ -1,15 +0,0 @@ -using GFramework.Core.Abstractions.system; - -namespace GFramework.Core.Abstractions.coroutine; - -/// -/// 协程系统接口,继承自ISystem,用于管理游戏中的协程执行 -/// -public interface ICoroutineSystem : ISystem -{ - /// - /// 更新协程系统,在每一帧调用以处理协程逻辑 - /// - /// 自上一帧以来的时间间隔(秒) - void OnUpdate(float deltaTime); -} \ No newline at end of file diff --git a/GFramework.Core.Abstractions/coroutine/IYieldInstruction.cs b/GFramework.Core.Abstractions/coroutine/IYieldInstruction.cs deleted file mode 100644 index 458a5b2..0000000 --- a/GFramework.Core.Abstractions/coroutine/IYieldInstruction.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace GFramework.Core.Abstractions.coroutine; - -/// -/// 表示一个可等待的指令接口,用于协程中的等待操作 -/// -public interface IYieldInstruction -{ - /// - /// 获取当前等待指令是否已完成 - /// - bool IsDone { get; } - - /// - /// 更新等待指令的状态 - /// - /// 自上一帧以来的时间间隔(以秒为单位) - void Update(float deltaTime); -} \ No newline at end of file diff --git a/GFramework.Core.Tests/TEST_COVERAGE_PLAN.md b/GFramework.Core.Tests/TEST_COVERAGE_PLAN.md deleted file mode 100644 index a2bc90f..0000000 --- a/GFramework.Core.Tests/TEST_COVERAGE_PLAN.md +++ /dev/null @@ -1,946 +0,0 @@ -# GFramework.Core 模块测试覆盖详细清单 - -> **生成日期**: 2026-01-18 -> **最后更新**: 2026-01-19 -> **当前版本**: Core测试覆盖率 ~100% (文件级别) -> **目标**: ✅ 已完成 - 所有核心模块都有测试覆盖 - ---- - -## 📊 总体统计 - -| 类别 | 源文件数 | 有测试文件数 | 缺失测试文件数 | 测试覆盖率 | -|--------|--------|--------|---------|-----------| -| 架构系统 | 6 | 5 | 0 | 100% | -| 事件系统 | 8 | 5 | 0 | 100% | -| 命令系统 | 4 | 2 | 0 | 100% | -| 查询系统 | 5 | 3 | 0 | 100% | -| 日志系统 | 5 | 2 | 0 | 100% | -| 扩展方法 | 4 | 2 | 0 | 100% | -| 状态系统 | 4 | 2 | 0 | 100% | -| IOC容器 | 1 | 1 | 0 | 100% | -| 模型系统 | 1 | 0 | 0 | 100% | -| 系统基类 | 1 | 0 | 0 | 100% | -| 对象池 | 1 | 1 | 0 | 100% | -| 属性系统 | 2 | 1 | 0 | 100% | -| 规则系统 | 1 | 1 | 0 | 100% | -| 工具类 | 1 | 1 | 0 | 100% | -| 环境系统 | 2 | 1 | 0 | 100% | -| 常量 | 2 | 2 | 0 | 100% | -| 协程系统 | 15 | 0 | 15 | 0% | -| **总计** | **63** | **27** | **15** | **76.2%** | - -> **注**: 标记为0个测试文件的模块通过间接测试(集成测试)实现了功能覆盖 -> **重要发现**: ✅ 所有核心功能测试已完成!包括异步命令、异步查询、工具基类和常量验证 - ---- - -## 🎯 测试补充完成情况 - -| 优先级 | 任务数 | 实际测试数 | 状态 | -|---------|-------|--------|--------------| -| 🔴 高优先级 | 5 | 47 | ✅ 全部完成 | -| 🟡 中优先级 | 2 | 16 | ✅ 全部完成 | -| 协程模块 | 7 | 0 | 🔄 待实施 | -| **总计** | **14** | **63** | **🔄 进行中** | - ---- - -## 📋 详细源文件与测试文件对应关系 - -### Architecture 模块 (6个源文件) - -| 源文件 | 对应测试文件 | 测试覆盖 | -|------------------------------|-----------------------------------------------------|-----------| -| Architecture.cs | SyncArchitectureTests.cs, AsyncArchitectureTests.cs | ✅ 98个测试 | -| ArchitectureConfiguration.cs | ArchitectureConfigurationTests.cs | ✅ 12个测试 | -| ArchitectureConstants.cs | ArchitectureConstantsTests.cs | ✅ 11个测试 | -| ArchitectureContext.cs | ArchitectureContextTests.cs | ✅ 22个测试 | -| ArchitectureServices.cs | ArchitectureServicesTests.cs | ✅ 15个测试 | -| GameContext.cs | GameContextTests.cs | ✅ 已有测试 | - -**测试用例总数**: 158个 - ---- - -### Command 模块 (4个源文件) - -| 源文件 | 对应测试文件 | 测试覆盖 | -|-----------------------------|-------------------------|---------| -| **AbstractAsyncCommand.cs** | AbstractAsyncCommandTests.cs | ✅ 12个测试 | -| AbstractCommand.cs | CommandBusTests.cs (间接) | ✅ 已覆盖 | -| **CommandBus.cs** | CommandBusTests.cs | ✅ 8个测试 | -| EmptyCommandInput.cs | CommandBusTests.cs (间接) | ✅ 已覆盖 | - -**测试用例总数**: 20个 - -**已完成**: - -1. ✅ AbstractAsyncCommandTests.cs - 已创建(12个测试) -2. ✅ CommandBusTests.cs - 已补充异步测试(4个异步测试) - ---- - -### Query 模块 (5个源文件) - -| 源文件 | 对应测试文件 | 测试覆盖 | -|---------------------------|-----------------------|-----------| -| **AbstractAsyncQuery.cs** | AbstractAsyncQueryTests.cs | ✅ 12个测试 | -| AbstractQuery.cs | QueryBusTests.cs (间接) | ✅ 已覆盖 | -| **AsyncQueryBus.cs** | AsyncQueryBusTests.cs | ✅ 8个测试 | -| EmptyQueryInput.cs | QueryBusTests.cs (间接) | ✅ 已覆盖 | -| QueryBus.cs | QueryBusTests.cs | ✅ 3个测试 | - -**测试用例总数**: 23个 - -**已完成**: - -1. ✅ AbstractAsyncQueryTests.cs - 已创建(12个测试) -2. ✅ AsyncQueryBusTests.cs - 已创建(8个测试) - ---- - -### Constants 模块 (2个源文件) - -| 源文件 | 对应测试文件 | 测试覆盖 | -|--------------------------|-------------------------|------| -| ArchitectureConstants.cs | ArchitectureConstantsTests.cs | ✅ 11个测试 | -| GFrameworkConstants.cs | GFrameworkConstantsTests.cs | ✅ 5个测试 | - -**测试用例总数**: 16个 - -**已完成**: - -1. ✅ ArchitectureConstantsTests.cs - 已创建(11个测试) -2. ✅ GFrameworkConstantsTests.cs - 已创建(5个测试) - ---- - -### Utility 模块 (1个源文件) - -| 源文件 | 对应测试文件 | 测试覆盖 | -|---------------------------|---------------------------|-----------| -| AbstractContextUtility.cs | AbstractContextUtilityTests.cs | ✅ 11个测试 | - -**测试用例总数**: 11个 - -**已完成**: - -1. ✅ AbstractContextUtilityTests.cs - 已创建(11个测试) - ---- - -### Coroutine 模块 (15个源文件) - -| 源文件 | 对应测试文件 | 测试覆盖 | -|----------------------------------|----------------------------------|----------| -| **ICoroutineContext.cs** | (通过集成测试) | ✅ 间接覆盖 | -| **ICoroutineHandle.cs** | CoroutineHandleTests.cs | 🔄 待创建 | -| **ICoroutineScheduler.cs** | CoroutineSchedulerTests.cs | 🔄 待创建 | -| **ICoroutineScope.cs** | CoroutineScopeTests.cs | 🔄 待创建 | -| **ICoroutineSystem.cs** | (通过架构测试) | ✅ 间接覆盖 | -| **IYieldInstruction.cs** | YieldInstructionTests.cs | 🔄 待创建 | -| **CoroutineContext.cs** | CoroutineHandleTests.cs (间接) | ✅ 间接覆盖 | -| **CoroutineHandle.cs** | CoroutineHandleTests.cs | 🔄 待创建 | -| **CoroutineScheduler.cs** | CoroutineSchedulerTests.cs | 🔄 待创建 | -| **CoroutineScope.cs** | CoroutineScopeTests.cs | 🔄 待创建 | -| **CoroutineScopeExtensions.cs** | CoroutineScopeExtensionsTests.cs | 🔄 待创建 | -| **GlobalCoroutineScope.cs** | GlobalCoroutineScopeTests.cs | 🔄 待创建 | -| **WaitForSeconds.cs** | YieldInstructionTests.cs | 🔄 待创建 | -| **WaitUntil.cs** | YieldInstructionTests.cs | 🔄 待创建 | -| **WaitWhile.cs** | YieldInstructionTests.cs | 🔄 待创建 | - -**计划测试用例总数**: 约80-100个 - -**待创建测试文件**: - -1. 🔄 CoroutineHandleTests.cs - 协程句柄测试 -2. 🔄 CoroutineSchedulerTests.cs - 协程调度器测试 -3. 🔄 CoroutineScopeTests.cs - 协程作用域测试 -4. 🔄 CoroutineScopeExtensionsTests.cs - 协程扩展方法测试 -5. 🔄 GlobalCoroutineScopeTests.cs - 全局协程作用域测试 -6. 🔄 YieldInstructionTests.cs - Yield指令测试 - ---- - -### 其他模块 (Events, Logging, IoC, etc.) - -所有其他模块的测试覆盖率均达到 100%(包括间接测试覆盖),详见下文详细列表。 - ---- - -## 🔄 新增任务:协程模块测试覆盖 - -### 任务8: CoroutineHandleTests.cs - -**源文件路径**: `GFramework.Core/coroutine/CoroutineHandle.cs` - -**优先级**: 🔴 高 - -**计划测试内容**: - -- ✅ 协程基础执行 - 简单的 IEnumerator 执行 -- ✅ 协程完成状态 - IsDone 属性正确性 -- ✅ 协程取消操作 - Cancel() 方法调用 -- ✅ 嵌套协程执行 - yield return IEnumerator 支持 -- ✅ 协程句柄嵌套 - yield return CoroutineHandle 支持 -- ✅ Yield指令支持 - yield return IYieldInstruction 支持 -- ✅ OnComplete 事件触发 -- ✅ OnComplete 事件在取消时触发 -- ✅ OnError 事件触发 - 捕获协程中的异常 -- ✅ 协程栈管理 - Push/Pop 操作正确性 -- ✅ 异常状态处理 - HandleError 方法 -- ✅ 等待指令状态管理 - _waitingInstruction 更新 -- ✅ 协程多次执行 - 同一协程多次启动 -- ✅ 不支持的Yield类型 - 抛出 InvalidOperationException -- ✅ 协程上下文获取 - Context 属性 - -**计划测试数**: 15 个 - -**创建路径**: `GFramework.Core.Tests/coroutine/CoroutineHandleTests.cs` - ---- - -### 任务9: CoroutineSchedulerTests.cs - -**源文件路径**: `GFramework.Core/coroutine/CoroutineScheduler.cs` - -**优先级**: 🔴 高 - -**计划测试内容**: - -- ✅ 基础更新操作 - Update(deltaTime) 方法 -- ✅ ActiveCount 属性 - 正确统计活跃协程 -- ✅ 多协程并发执行 - 同时启动多个协程 -- ✅ 协程完成移除 - IsDone 协程自动移除 -- ✅ 作用域不活跃时取消 - Scope.IsActive = false 时取消 -- ✅ 线程安全检查 - 跨线程调用抛出异常 -- ✅ 待添加协程统计 - _toAdd 计入 ActiveCount -- ✅ 协程添加时机 - _toAdd 正确合并到 _active -- ✅ 协程移除时机 - _toRemove 正确清理 _active -- ✅ 空列表处理 - 无协程时的 Update 行为 -- ✅ 高频协程启动 - 快速连续启动多个协程 -- ✅ 协程生命周期 - 从启动到完成的完整流程 - -**计划测试数**: 12 个 - -**创建路径**: `GFramework.Core.Tests/coroutine/CoroutineSchedulerTests.cs` - ---- - -### 任务10: CoroutineScopeTests.cs - -**源文件路径**: `GFramework.Core/coroutine/CoroutineScope.cs` - -**优先级**: 🔴 高 - -**计划测试内容**: - -- ✅ 基础协程启动 - Launch(IEnumerator) 方法 -- ✅ 协程作用域状态 - IsActive 属性 -- ✅ 协程作用域取消 - Cancel() 方法 -- ✅ 子作用域管理 - 父子作用域关系 -- ✅ 取消传播 - 父作用域取消时子作用域也取消 -- ✅ 运行中协程跟踪 - _runningCoroutines 集合 -- ✅ 协程完成自动移除 - OnComplete 事件处理 -- ✅ 协程错误自动移除 - OnError 事件处理 -- ✅ 作用域销毁 - Dispose() 方法调用 Cancel -- ✅ 不活跃作用域拒绝启动 - 抛出 InvalidOperationException -- ✅ 协程上下文设置 - CoroutineContext 创建 -- ✅ 空调度器异常 - scheduler 为 null 抛出异常 -- ✅ 多协程管理 - 同一作用域启动多个协程 -- ✅ 嵌套作用域 - 多层父子关系 - -**计划测试数**: 14 个 - -**创建路径**: `GFramework.Core.Tests/coroutine/CoroutineScopeTests.cs` - ---- - -### 任务11: CoroutineScopeExtensionsTests.cs - -**源文件路径**: `GFramework.Core/coroutine/CoroutineScopeExtensions.cs` - -**优先级**: 🟡 中 - -**计划测试内容**: - -- ✅ 延迟启动协程 - LaunchDelayed(delay, action) -- ✅ 延迟时间准确性 - WaitForSeconds 等待正确时长 -- ✅ 延迟后执行动作 - action 参数正确调用 -- ✅ 重复启动协程 - LaunchRepeating(interval, action) -- ✅ 重复间隔准确性 - 循环间隔正确 -- ✅ 重复执行动作 - action 参数循环调用 -- ✅ 空action处理 - action 为 null 不抛异常 -- ✅ 取消延迟协程 - 返回的句柄可取消 -- ✅ 取消重复协程 - 返回的句柄可取消 -- ✅ 多个延迟协程 - 同时启动多个延迟任务 -- ✅ 多个重复协程 - 同时启动多个重复任务 - -**计划测试数**: 11 个 - -**创建路径**: `GFramework.Core.Tests/coroutine/CoroutineScopeExtensionsTests.cs` - ---- - -### 任务12: GlobalCoroutineScopeTests.cs - -**源文件路径**: `GFramework.Core/coroutine/GlobalCoroutineScope.cs` - -**优先级**: 🟡 中 - -**计划测试内容**: - -- ✅ 初始化检查 - IsInitialized 属性 -- ✅ 尝试获取作用域 - TryGetScope 方法 -- ✅ 初始化作用域 - Initialize(scheduler) 方法 -- ✅ 启动全局协程 - Launch(routine) 方法 -- ✅ 未初始化时启动 - 抛出 InvalidOperationException -- ✅ 未初始化时TryGetScope - 返回 false 和 null -- ✅ 全局作用域单例性 - 多次 Initialize 行为 -- ✅ 全局协程执行 - 通过全局作用域启动协程 -- ✅ 全局作用域名称 - Name 属性为 "GlobalScope" -- ✅ Dispose 行为 - 全局作用域 Dispose - -**计划测试数**: 10 个 - -**创建路径**: `GFramework.Core.Tests/coroutine/GlobalCoroutineScopeTests.cs` - ---- - -### 任务13: YieldInstructionTests.cs - -**源文件路径**: -- `GFramework.Core/coroutine/WaitForSeconds.cs` -- `GFramework.Core/coroutine/WaitUntil.cs` -- `GFramework.Core/coroutine/WaitWhile.cs` - -**优先级**: 🔴 高 - -**计划测试内容**: - -**WaitForSeconds**: -- ✅ 基础等待功能 - 指定秒数后 IsDone = true -- ✅ IsDone 属性 - 等待前为 false,等待后为 true -- ✅ Update(deltaTime) 方法 - 时间累加正确 -- ✅ 精确时间计算 - 多次 Update 累加到阈值 -- ✅ Reset() 方法 - 重置状态可复用 -- ✅ 累积误差测试 - Reset 后重新计数 -- ✅ 零秒等待 - seconds = 0 立即完成 -- ✅ 负秒数处理 - seconds < 0 行为 -- ✅ 大数值等待 - 长时间等待场景 - -**WaitUntil**: -- ✅ 条件为真时完成 - predicate 返回 true 时 IsDone = true -- ✅ 条件为假时等待 - predicate 返回 false 时继续等待 -- ✅ Update(deltaTime) 方法 - 每次更新检查条件 -- ✅ Reset() 方法 - 重置状态可复用 -- ✅ 谓词参数传递 - predicate 正确调用 -- ✅ 谓词闭包支持 - 捕获外部变量 -- ✅ 谓词异常处理 - predicate 抛出异常时的行为 - -**WaitWhile**: -- ✅ 条件为假时完成 - predicate 返回 false 时 IsDone = true -- ✅ 条件为真时等待 - predicate 返回 true 时继续等待 -- ✅ Update(deltaTime) 方法 - 每次更新检查条件 -- ✅ Reset() 方法 - 重置状态可复用 -- ✅ 谓词参数传递 - predicate 正确调用 -- ✅ 与 WaitUntil 对比 - 逻辑相反性验证 - -**计划测试数**: 20 个(WaitForSeconds: 9个, WaitUntil: 6个, WaitWhile: 5个) - -**创建路径**: `GFramework.Core.Tests/coroutine/YieldInstructionTests.cs` - ---- - -### 任务14: 协程系统集成测试 - -**优先级**: 🟡 中 - -**计划测试内容**: - -- ✅ 复杂协程链式调用 - 多层嵌套协程 -- ✅ 协程间数据传递 - 通过闭包共享状态 -- ✅ 协程与事件集成 - 协程中触发事件 -- ✅ 协程异常传播 - 嵌套协程中的异常处理 -- ✅ 协程取消链 - 父协程取消子协程 -- ✅ 协程超时控制 - 使用 WaitUntil 实现超时 -- ✅ 协程同步等待 - 等待多个协程完成 -- ✅ 协程竞态条件 - 多个协程竞争同一资源 -- ✅ 协程资源管理 - using 语句与协程 - -**计划测试数**: 9 个 - -**创建路径**: `GFramework.Core.Tests/coroutine/CoroutineIntegrationTests.cs` - ---- - -## ✅ 已完成任务清单 - -### 任务1: CommandBusTests.cs - 补充异步测试 - -**源文件路径**: `GFramework.Core/command/CommandBus.cs` - -**优先级**: 🔴 高 - -**完成日期**: 2026-01-19 - -**实际完成的测试内容**: - -- ✅ SendAsync(IAsyncCommand) 方法 - 执行无返回值的异步命令 -- ✅ SendAsync(IAsyncCommand) 方法 - 处理 null 异步命令 -- ✅ SendAsync(IAsyncCommand) 方法 - 执行有返回值的异步命令 -- ✅ SendAsync(IAsyncCommand) 方法 - 处理 null 异步命令 - -**实际测试数**: 4 个 - -**测试文件**: `GFramework.Core.Tests/command/CommandBusTests.cs` - -**操作**: ✅ 已在现有测试文件中补充异步测试方法 - -**状态**: ✅ 已完成 - ---- - -### 任务2: AbstractAsyncCommandTests.cs - -**源文件路径**: `GFramework.Core/command/AbstractAsyncCommand.cs` - -**优先级**: 🔴 高 - -**完成日期**: 2026-01-19 - -**实际测试的内容**: - -- ✅ 异步命令无返回值版本的基础实现 -- ✅ 异步命令有返回值版本的基础实现 -- ✅ ExecuteAsync 方法调用 -- ✅ ExecuteAsync 方法的异常处理 -- ✅ 上下文感知功能(SetContext, GetContext) -- ✅ 子类继承行为验证(两个版本) -- ✅ 异步命令执行生命周期完整性 -- ✅ 异步命令多次执行 -- ✅ 异步命令(带返回值)的返回值类型 - -**实际测试数**: 12 个 - -**创建路径**: `GFramework.Core.Tests/command/AbstractAsyncCommandTests.cs` - -**状态**: ✅ 已完成 - ---- - -### 任务3: AsyncQueryBusTests.cs - -**源文件路径**: `GFramework.Core/query/AsyncQueryBus.cs` - -**优先级**: 🔴 高 - -**完成日期**: 2026-01-19 - -**实际测试的内容**: - -- ✅ SendAsync 方法 - 正常查询发送 -- ✅ SendAsync 方法 - 空查询异常 -- ✅ 异步查询结果正确性 -- ✅ 不同返回类型的异步查询支持(int, string, bool, complex object) -- ✅ 异步查询的异常处理 -- ✅ SendAsync 方法多次调用 -- ✅ SendAsync 方法在不同查询之间保持独立性 - -**实际测试数**: 8 个 - -**创建路径**: `GFramework.Core.Tests/query/AsyncQueryBusTests.cs` - -**状态**: ✅ 已完成 - ---- - -### 任务4: AbstractAsyncQueryTests.cs - -**源文件路径**: `GFramework.Core/query/AbstractAsyncQuery.cs` - -**优先级**: 🔴 高 - -**完成日期**: 2026-01-19 - -**实际测试的内容**: - -- ✅ 异步查询的基础实现 -- ✅ DoAsync 方法调用 -- ✅ DoAsync 方法的异常处理 -- ✅ 上下文感知功能(SetContext, GetContext) -- ✅ 子类继承行为验证 -- ✅ 异步查询执行生命周期完整性 -- ✅ 异步查询多次执行 -- ✅ 异步查询的返回值类型 -- ✅ 异步查询的字符串返回值 -- ✅ 异步查询的复杂对象返回值 -- ✅ 异步查询在不同实例之间的独立性 - -**实际测试数**: 12 个 - -**创建路径**: `GFramework.Core.Tests/query/AbstractAsyncQueryTests.cs` - -**状态**: ✅ 已完成 - ---- - -### 任务5: AbstractContextUtilityTests.cs - -**源文件路径**: `GFramework.Core/utility/AbstractContextUtility.cs` - -**优先级**: 🔴 高 - -**完成日期**: 2026-01-19 - -**实际测试的内容**: - -- ✅ 抽象工具类实现 -- ✅ IContextUtility 接口实现 -- ✅ Init 方法调用 -- ✅ Init 方法设置 Logger 属性 -- ✅ Init 方法记录初始化日志 -- ✅ Destroy 方法调用 -- ✅ 上下文感知功能(SetContext, GetContext) -- ✅ 子类继承行为 -- ✅ 工具生命周期完整性 -- ✅ 工具类可以多次初始化和销毁 - -**实际测试数**: 11 个 - -**创建路径**: `GFramework.Core.Tests/utility/AbstractContextUtilityTests.cs` - -**状态**: ✅ 已完成 - ---- - -### 任务6: ArchitectureConstantsTests.cs - -**源文件路径**: `GFramework.Core/architecture/ArchitectureConstants.cs` - -**优先级**: 🟡 中 - -**完成日期**: 2026-01-19 - -**实际测试的内容**: - -- ✅ PhaseOrder 数组不为空 -- ✅ PhaseOrder 包含所有预期的架构阶段 -- ✅ PhaseOrder 数组是只读的 -- ✅ PhaseOrder 的顺序是正确的 -- ✅ PhaseTransitions 字典不为空 -- ✅ PhaseTransitions 是只读的 -- ✅ PhaseTransitions 包含正常线性流程的转换 -- ✅ PhaseTransitions 中的转换方向是正确的 -- ✅ PhaseTransitions 包含失败初始化的转换路径 -- ✅ PhaseTransitions 最大每个阶段不超过1个转换 -- ✅ PhaseOrder 和 PhaseTransitions 的一致性 - -**实际测试数**: 11 个 - -**创建路径**: `GFramework.Core.Tests/architecture/ArchitectureConstantsTests.cs` - -**状态**: ✅ 已完成 - ---- - -### 任务7: GFrameworkConstantsTests.cs - -**源文件路径**: `GFramework.Core/constants/GFrameworkConstants.cs` - -**优先级**: 🟡 中 - -**完成日期**: 2026-01-19 - -**实际测试的内容**: - -- ✅ FrameworkName 常量的值正确性 -- ✅ FrameworkName 常量的类型 -- ✅ FrameworkName 常量不为空 -- ✅ FrameworkName 常量是公共可访问的 -- ✅ FrameworkName 常量是只读的(const) - -**实际测试数**: 5 个 - -**创建路径**: `GFramework.Core.Tests/constants/GFrameworkConstantsTests.cs` - -**状态**: ✅ 已完成 - ---- - -## 📊 测试执行完成统计 - -### 第一批:异步核心功能(4个任务) - -| 序号 | 测试任务 | 操作 | 实际测试数 | 优先级 | 状态 | -|----|------------------------------|----|-------|------|------| -| 1 | CommandBusTests.cs - 补充异步测试 | 补充 | 4 | 🔴 高 | ✅ 完成 | -| 2 | AbstractAsyncCommandTests.cs | 新建 | 12 | 🔴 高 | ✅ 完成 | -| 3 | AsyncQueryBusTests.cs | 新建 | 8 | 🔴 高 | ✅ 完成 | -| 4 | AbstractAsyncQueryTests.cs | 新建 | 12 | 🔴 高 | ✅ 完成 | - -**小计**: 36 个测试,全部完成 ✅ - ---- - -### 第二批:工具基类(1个任务) - -| 序号 | 测试文件 | 操作 | 实际测试数 | 优先级 | 状态 | -|----|--------------------------------|----|-------|------|------| -| 5 | AbstractContextUtilityTests.cs | 新建 | 11 | 🔴 高 | ✅ 完成 | - -**小计**: 11 个测试,全部完成 ✅ - ---- - -### 第三批:常量验证(2个任务) - -| 序号 | 测试文件 | 操作 | 实际测试数 | 优先级 | 状态 | -|----|-------------------------------|----|-------|------|------| -| 6 | ArchitectureConstantsTests.cs | 新建 | 11 | 🟡 中 | ✅ 完成 | -| 7 | GFrameworkConstantsTests.cs | 新建 | 5 | 🟡 中 | ✅ 完成 | - -**小计**: 16 个测试,全部完成 ✅ - ---- - -## 📊 最终统计 - -| 批次 | 任务数 | 操作 | 实际测试数 | 状态 | -|----------|-------|-------------|-------|----------| -| 第一批(异步) | 4 | 3新建+1补充 | 36 | ✅ 完成 | -| 第二批(高优先) | 1 | 新建 | 11 | ✅ 完成 | -| 第三批(中优先) | 2 | 新建 | 16 | ✅ 完成 | -| 第四批(协程) | 7 | 7新建 | 91 | 🔄 待实施 | -| **总计** | **14** | **13新建+1补充** | **154** | **🔄 进行中** | - ---- - -## 🎯 目标达成总结 - -### 当前状态(2026-01-21) - -- **测试用例总数**: 357 个(核心模块) -- **测试文件数**: 27 个(核心模块) -- **文件覆盖率**: 76.2% (48/63个文件有测试覆盖) -- **协程模块待完成**: 15个源文件,计划91个测试用例 -- **测试通过率**: 100% (核心模块测试全部通过) -- **已完成文件**: 48/63 -- **关键成就**: 核心功能测试已完成!包括异步命令、异步查询、工具基类和常量验证 -- **进行中**: 协程模块测试计划已制定,待实施 - -### 测试覆盖率对比 - -| 指标 | 更新前(2026-01-18) | 更新后(2026-01-19) | 更新后(2026-01-21) | 提升 | -|------------|----------------|----------------|-----------------|---------| -| 文件覆盖率 | 79.2% | 100% (核心) | 76.2% (包含协程) | -2.7% | -| 测试文件数 | 20 | 27 | 27 (待新增7) | +7 | -| 测试用例总数 | 496 | 357 | 357 (待新增91) | +63 | -| 命令系统覆盖率 | 25% | 100% | 100% | +75% | -| 查询系统覆盖率 | 20% | 100% | 100% | +80% | -| 工具类覆盖率 | 0% | 100% | 100% | +100% | -| 常量覆盖率 | 0% | 100% | 100% | +100% | -| 协程系统覆盖率 | 0% | 0% | 0% (待实施) | - | - ---- - -## 📝 测试质量总结 - -### 注释规范 - -- ✅ 所有测试类都有详细的注释说明 -- ✅ 所有测试方法都有注释说明具体测试内容 -- ✅ 复杂逻辑的测试方法有标准的行注释 -- ✅ 类与方法都使用标准的 C# 文档注释 - -### 测试隔离性 - -- ✅ 每个测试文件使用独立的测试辅助类(TestXxxV2, TestXxxV3, TestXxxV4等) -- ✅ 避免与现有测试类(TestSystem, TestModel)命名冲突 -- ✅ 使用 `[SetUp]` 和 `[TearDown]` 确保测试隔离 -- ✅ 异步测试正确使用 `async/await` 模式 - -### 测试命名规范 - -- ✅ 测试类:`{Component}Tests` -- ✅ 测试方法:`{Scenario}_Should_{ExpectedOutcome}` -- ✅ 测试辅助类:`Test{Component}V{Version}` -- ✅ 异步测试方法包含 `Async` 关键字 - -### 构建和验证流程 - -1. ✅ 编写测试代码 -2. ✅ 运行 `dotnet build` 验证编译 -3. ✅ 运行 `dotnet test` 执行测试 -4. ✅ 检查测试通过率(100%) -5. ✅ 所有测试通过,无隔离性问题 - -### 异步测试最佳实践 - -1. **正确使用 async/await** - - ✅ 测试方法标记为 `async Task` - - ✅ 所有异步操作使用 `await` - - ✅ 没有使用 `.Result` 或 `.Wait()` 导致死锁 - -2. **异常测试** - - ✅ 使用 `Assert.ThrowsAsync` 测试异步异常 - - ✅ 异常在正确的位置抛出 - -3. **测试辅助类** - - ✅ 创建模拟的异步命令/查询类 - - ✅ 验证异步操作是否正确执行 - - ✅ 测试多次执行场景 - ---- - -## 🔄 更新日志 - -| 日期 | 操作 | 说明 | -|------------|-----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| 2026-01-16 | 初始创建 | 生成原始测试覆盖清单(包含错误) | -| 2026-01-18 | 全面更新(第1版) | 重新检查框架和测试,修正以下问题:
1. 删除不存在的ContextAwareStateMachineTests.cs
2. 更新实际测试数量为496个
3. 添加新增源文件
4. 修正文件覆盖率从41%提升至91.5%
5. 调整优先级,从26个减少到3个缺失测试文件 | -| 2026-01-18 | 全面更新(第2版) | 补充异步命令和异步查询测试计划:
1. 发现CommandBus已有SendAsync实现但无测试
2. 发现AbstractAsyncCommand、AsyncQueryBus、AbstractAsyncQuery无测试
3. 新增4个高优先级异步测试任务
4. 更新文件覆盖率从91.5%调整为79.2%(补充异步后)
5. 总测试数从40-54调整为目标 | -| 2026-01-19 | 全面更新(第3版) | ✅ 所有7个测试任务完成:
1. CommandBusTests.cs - 补充4个异步测试
2. AbstractAsyncCommandTests.cs - 新建12个测试
3. AsyncQueryBusTests.cs - 新建8个测试
4. AbstractAsyncQueryTests.cs - 新建12个测试
5. AbstractContextUtilityTests.cs - 新建11个测试
6. ArchitectureConstantsTests.cs - 新建11个测试
7. GFrameworkConstantsTests.cs - 新建5个测试
8. 文件覆盖率从79.2%提升至100%
9. 新增63个测试用例 | -| 2026-01-21 | 全面更新(第4版) | 🔄 添加协程模块测试计划:
1. 发现协程模块包含15个源文件,无测试覆盖
2. 新增7个协程测试任务,计划91个测试用例
3. 更新文件覆盖率从100%调整为76.2%(包含协程)
4. 制定详细的协程测试计划
5. 待实施协程测试用例 | - ---- - -## 📌 待确认事项 - -- [x] 确认优先级划分是否合理 -- [x] 确认执行计划是否可行 -- [x] 确认测试用例数量估算是否准确 -- [x] 确认测试隔离策略是否完整 -- [x] 添加代码覆盖率工具配置 -- [x] 所有核心模块测试任务已完成 -- [ ] 协程模块测试计划执行 -- [ ] 协程模块测试实施优先级确认 -- [x] 协程测试文件创建完成(7个测试文件,91个测试用例) -- [x] 协程模块编译成功 -- [ ] 协程模块测试调试和修复(当前:68/94通过,26个失败) -- [ ] 协程实现改进建议(嵌套协程执行逻辑需要修复) - ---- - -## 🔍 协程模块测试执行情况(2026-01-21) - -### 测试创建完成情况 - -✅ **已创建测试文件**: -1. CoroutineHandleTests.cs - 15个测试用例 -2. CoroutineSchedulerTests.cs - 12个测试用例 -3. CoroutineScopeTests.cs - 14个测试用例 -4. CoroutineScopeExtensionsTests.cs - 11个测试用例 -5. GlobalCoroutineScopeTests.cs - 10个测试用例 -6. YieldInstructionTests.cs - 20个测试用例 -7. CoroutineIntegrationTests.cs - 9个测试用例 - -**总计**:91个测试用例,7个测试文件 - -### 测试执行结果(最终更新) - -**编译状态**:✅ 成功 -- 0个警告 -- 0个错误 - -**测试运行结果**: -- 通过:78个测试 (82.98%) -- 失败:16个测试 (17.02%) -- 总计:94个测试 - -**提升对比**: -- 初始通过率:72.3% (68/94) -- 最终通过率:82.98% (78/94) -- 提升幅度:+10.68% - -### 失败测试分类 - -#### ✅ 已修复的问题(通过改进解决) - -1. **嵌套协程执行问题**(8个测试)✅ 已修复 - - **问题**:嵌套协程需要多次Update()才能完成 - - **修复**:修改CoroutineHandle.InternalUpdate()使用循环执行 - - **结果**:所有嵌套协程测试通过 - -2. **YieldInstruction处理问题**(3个测试)✅ 已修复 - - **问题**:IYieldInstruction处理逻辑不正确 - - **修复**:优化ProcessYieldValue方法 - - **结果**:Yield指令测试通过 - -3. **GlobalCoroutineScope测试问题**(4个测试)✅ 已修复 - - **问题**:静态实例管理和测试隔离 - - **修复**:调整SetUp/TearDown和测试预期 - - **结果**:全局作用域测试通过 - -4. **跨线程测试问题**(1个测试)✅ 已修复 - - **问题**:线程ID检查在第一次Update后才触发 - - **修复**:测试中先调用Update()初始化线程ID - - **结果**:跨线程测试通过 - -#### 🔄 剩余失败测试(16个) - -1. **嵌套协程测试失败**(8个) - - NestedCoroutine_Should_ExecuteSuccessfully - - YieldCoroutineHandle_Should_WaitForCompletion - - CoroutineStack_Should_ManageNestedCoroutines - - YieldInstruction_Should_BeSupported - - WaitingInstruction_Should_UpdateCorrectly - - ComplexChainedCoroutines_Should_ExecuteCorrectly - - 等... - - **原因**:协程嵌套执行逻辑需要优化,特别是在多个Update()调用时的状态管理 - -2. **GlobalCoroutineScope测试失败**(4个) - - Launch_Should_StartCoroutine - - GlobalCoroutine_Should_ExecuteCorrectly - - Launch_WithoutInitialization_Should_ThrowInvalidOperationException - - TryGetScope_WithoutInitialization_Should_ReturnFalse - - **原因**:静态实例状态管理问题,已使用OneTimeSetUp/OneTimeTearDown修复 - -3. **协程异常处理测试失败**(2个) - - OnError_Should_TriggerEvent - - Exception_Should_HandleGracefully - - CoroutineExceptionPropagation_Should_HandleNestedExceptions - - FailedCoroutine_Should_BeRemovedFromTracking - - **原因**:异常处理机制需要验证 - -4. **延迟/重复协程测试失败**(8个) - - LaunchDelayed_Should_StartCoroutineAfterDelay - - LaunchDelayed_Action_Should_BeCalled - - LaunchRepeating_Interval_Should_BeAccurate - - MultipleDelayedCoroutines_Should_ExecuteIndependently - - 等... - - **原因**:协程调度器更新逻辑需要调整 - -5. **集成测试失败**(4个) - - CoroutineDataSharing_Should_WorkCorrectly - - CoroutineEventIntegration_Should_WorkCorrectly - - CoroutineSynchronization_Should_WaitForMultipleCoroutines - - CoroutineResourceManagement_Should_CleanupCorrectly - - **原因**:复杂场景下的协程交互需要验证 - -### 改进建议 - -#### 1. 协程实现改进 - -**问题**:嵌套协程执行时,可能需要多次Update()才能完成 - -**建议**: -- 考虑在一次Update()中完成多层嵌套协程的执行 -- 优化协程栈的遍历逻辑 -- 添加协程执行进度的状态查询 - -#### 2. 测试策略改进 - -**建议**: -- 为复杂协程场景添加更多中间状态验证 -- 增加协程执行时的日志输出 -- 添加协程调试辅助方法 - -#### 3. 文档完善 - -**建议**: -- 添加协程使用最佳实践文档 -- 补充协程性能优化指南 -- 提供常见问题和解决方案 - -### 下一步计划 - -1. 🔄 调试并修复失败的26个测试用例 -2. 🔄 验证协程嵌套执行的边界条件 -3. 🔄 优化协程调度器的更新逻辑 -4. 🔄 增加压力测试和性能基准测试 -5. 🔄 完善协程模块文档和使用示例 - ---- - -## 🎉 成就解锁 - -### 已完成的测试覆盖 - -✅ **架构系统核心功能** - 158个测试覆盖 -✅ **事件系统完整功能** - 37个测试覆盖 -✅ **日志系统完整功能** - 69个测试覆盖 -✅ **IoC容器** - 21个测试覆盖 -✅ **状态机系统** - 33个测试覆盖 -✅ **对象池系统** - 6个测试覆盖 -✅ **属性系统** - 8个测试覆盖 -✅ **扩展方法** - 17个测试覆盖 -✅ **同步命令系统** - 通过集成测试覆盖 -✅ **模型系统** - 通过架构集成测试覆盖 -✅ **系统基类** - 通过架构集成测试覆盖 -✅ **异步命令系统** - 20个测试覆盖(新增) -✅ **异步查询系统** - 23个测试覆盖(新增) -✅ **工具基类** - 11个测试覆盖(新增) -✅ **常量验证** - 16个测试覆盖(新增) - -### 待完成的测试覆盖 - -🔄 **协句柄管理** - 15个测试计划中 -🔄 **协程调度器** - 12个测试计划中 -🔄 **协程作用域** - 14个测试计划中 -🔄 **协程扩展方法** - 11个测试计划中 -🔄 **全局协程作用域** - 10个测试计划中 -🔄 **Yield指令** - 20个测试计划中 -🔄 **协程集成测试** - 9个测试计划中 - -### 测试质量指标 - -- **测试用例总数**: 357个(核心模块,新增63个) -- **协程模块计划**: 91个测试用例待实施 -- **文件级别覆盖率**: 76.2% (核心模块100%,协程模块0%) -- **支持测试的.NET版本**: .NET 8.0, .NET 10.0 -- **测试框架**: NUnit 3.x -- **测试隔离性**: 优秀 -- **测试组织结构**: 清晰(按模块分类) -- **测试通过率**: 100% (核心模块) - ---- - -## 🚀 实施进度 - -### 第一批:异步核心功能 - -- [x] 任务1: CommandBusTests.cs - 补充异步测试 (4个测试) ✅ -- [x] 任务2: AbstractAsyncCommandTests.cs (12个测试) ✅ -- [x] 任务3: AsyncQueryBusTests.cs (8个测试) ✅ -- [x] 任务4: AbstractAsyncQueryTests.cs (12个测试) ✅ - -### 第二批:工具基类 - -- [x] 任务5: AbstractContextUtilityTests.cs (11个测试) ✅ - -### 第三批:常量验证 - -- [x] 任务6: ArchitectureConstantsTests.cs (11个测试) ✅ -- [x] 任务7: GFrameworkConstantsTests.cs (5个测试) ✅ - -### 总体进度 - -**第一批至第三批任务完成!** 🎉 - ---- - -### 第四批:协程模块测试 - -- [ ] 任务8: CoroutineHandleTests.cs (15个测试) 🔄 待创建 -- [ ] 任务9: CoroutineSchedulerTests.cs (12个测试) 🔄 待创建 -- [ ] 任务10: CoroutineScopeTests.cs (14个测试) 🔄 待创建 -- [ ] 任务11: CoroutineScopeExtensionsTests.cs (11个测试) 🔄 待创建 -- [ ] 任务12: GlobalCoroutineScopeTests.cs (10个测试) 🔄 待创建 -- [ ] 任务13: YieldInstructionTests.cs (20个测试) 🔄 待创建 -- [ ] 任务14: 协程系统集成测试 (9个测试) 🔄 待创建 - -### 总体进度 - -**已完成任务**: 7/14 (50%) -**待完成任务**: 7/14 (50%) - ---- - -**文档维护**: 协程模块测试计划已添加(2026-01-21),待实施 diff --git a/GFramework.Core.Tests/coroutine/CoroutineHandleTests.cs b/GFramework.Core.Tests/coroutine/CoroutineHandleTests.cs deleted file mode 100644 index 4fdd8f8..0000000 --- a/GFramework.Core.Tests/coroutine/CoroutineHandleTests.cs +++ /dev/null @@ -1,379 +0,0 @@ -using System.Collections; -using GFramework.Core.Abstractions.coroutine; -using GFramework.Core.coroutine; -using NUnit.Framework; - -namespace GFramework.Core.Tests.coroutine; - -/// -/// 协程句柄测试类,验证协程句柄的完整生命周期和功能 -/// -[TestFixture] -public class CoroutineHandleTests -{ - private CoroutineScheduler _scheduler = null!; - private CoroutineScope _scope = null!; - - [SetUp] - public void SetUp() - { - _scheduler = new CoroutineScheduler(); - _scope = new CoroutineScope(_scheduler, "TestScope"); - } - - [TearDown] - public void TearDown() - { - _scope?.Dispose(); - } - - /// - /// 测试协程基础执行 - 简单的 IEnumerator 执行 - /// - [Test] - public void BasicCoroutine_Should_ExecuteSuccessfully() - { - var executed = false; - var routine = CreateSimpleCoroutine(() => executed = true); - - var handle = _scope.Launch(routine); - Assert.That(handle.IsDone, Is.False); - - _scheduler.Update(0.1f); - Assert.That(handle.IsDone, Is.True); - Assert.That(executed, Is.True); - } - - /// - /// 测试协程完成状态 - IsDone 属性正确性 - /// - [Test] - public void IsDone_Should_BeCorrect() - { - var routine = CreateSimpleCoroutine(() => { }); - var handle = _scope.Launch(routine); - - Assert.That(handle.IsDone, Is.False); - - _scheduler.Update(0.1f); - - Assert.That(handle.IsDone, Is.True); - } - - /// - /// 测试协程取消操作 - Cancel() 方法调用 - /// - [Test] - public void Cancel_Should_StopCoroutine() - { - var executed = false; - var routine = CreateSimpleCoroutine(() => executed = true); - - var handle = _scope.Launch(routine); - handle.Cancel(); - - Assert.That(handle.IsCancelled, Is.True); - Assert.That(handle.IsDone, Is.True); - - _scheduler.Update(0.1f); - - Assert.That(executed, Is.False); - } - - /// - /// 测试嵌套协程执行 - yield return IEnumerator 支持 - /// - [Test] - public void NestedCoroutine_Should_ExecuteSuccessfully() - { - var innerExecuted = false; - var outerExecuted = false; - - IEnumerator InnerCoroutine() - { - innerExecuted = true; - yield break; - } - - IEnumerator OuterCoroutine() - { - yield return InnerCoroutine(); - outerExecuted = true; - } - - var handle = _scope.Launch(OuterCoroutine()); - _scheduler.Update(0.1f); - - Assert.That(handle.IsDone, Is.True); - Assert.That(innerExecuted, Is.True); - Assert.That(outerExecuted, Is.True); - } - - /// - /// 测试协程句柄嵌套 - yield return CoroutineHandle 支持 - /// - [Test] - public void YieldCoroutineHandle_Should_WaitForCompletion() - { - var innerExecuted = false; - var outerExecuted = false; - - IEnumerator InnerCoroutine() - { - innerExecuted = true; - yield return null; - } - - IEnumerator OuterCoroutine() - { - var innerHandle = _scope.Launch(InnerCoroutine()); - yield return innerHandle; - outerExecuted = true; - } - - var handle = _scope.Launch(OuterCoroutine()); - _scheduler.Update(0.1f); - - Assert.That(handle.IsDone, Is.True); - Assert.That(innerExecuted, Is.True); - Assert.That(outerExecuted, Is.True); - } - - /// - /// 测试Yield指令支持 - yield return IYieldInstruction 支持 - /// - [Test] - public void YieldInstruction_Should_BeSupported() - { - var executed = false; - - IEnumerator Coroutine() - { - yield return new WaitForSeconds(0.1f); - executed = true; - } - - var handle = _scope.Launch(Coroutine()); - _scheduler.Update(0.05f); - - Assert.That(handle.IsDone, Is.False); - Assert.That(executed, Is.False); - - _scheduler.Update(0.05f); - - Assert.That(handle.IsDone, Is.True); - Assert.That(executed, Is.True); - } - - /// - /// 测试OnComplete 事件触发 - /// - [Test] - public void OnComplete_Should_TriggerEvent() - { - var eventTriggered = false; - var routine = CreateSimpleCoroutine(() => { }); - - var handle = _scope.Launch(routine); - handle.OnComplete += () => eventTriggered = true; - - Assert.That(eventTriggered, Is.False); - - _scheduler.Update(0.1f); - - Assert.That(eventTriggered, Is.True); - } - - /// - /// 测试OnComplete 事件在取消时触发 - /// - [Test] - public void OnComplete_Should_TriggerOnCancel() - { - var eventTriggered = false; - var routine = CreateSimpleCoroutine(() => { }); - - var handle = _scope.Launch(routine); - handle.OnComplete += () => eventTriggered = true; - - handle.Cancel(); - - Assert.That(eventTriggered, Is.True); - } - - /// - /// 测试OnError 事件触发 - 捕获协程中的异常 - /// - [Test] - public void OnError_Should_TriggerEvent() - { - Exception? caughtException = null; - - IEnumerator Coroutine() - { - yield return null; - throw new InvalidOperationException("Test exception"); - } - - var handle = _scope.Launch(Coroutine()); - handle.OnError += ex => caughtException = ex; - - _scheduler.Update(0.1f); - - Assert.That(caughtException, Is.Not.Null); - Assert.That(caughtException, Is.TypeOf()); - } - - /// - /// 测试协程栈管理 - Push/Pop 操作正确性 - /// - [Test] - public void CoroutineStack_Should_ManageNestedCoroutines() - { - var executionOrder = new List(); - - IEnumerator Level3() - { - executionOrder.Add("Level3"); - yield break; - } - - IEnumerator Level2() - { - yield return Level3(); - executionOrder.Add("Level2"); - } - - IEnumerator Level1() - { - yield return Level2(); - executionOrder.Add("Level1"); - } - - var handle = _scope.Launch(Level1()); - _scheduler.Update(0.1f); - - Assert.That(handle.IsDone, Is.True); - Assert.That(executionOrder, Is.EqualTo(new[] { "Level3", "Level2", "Level1" })); - } - - /// - /// 测试异常状态处理 - HandleError 方法 - /// - [Test] - public void Exception_Should_HandleGracefully() - { - var executed = false; - - IEnumerator Coroutine() - { - executed = true; - yield return null; - throw new Exception("Error"); - } - - var handle = _scope.Launch(Coroutine()); - _scheduler.Update(0.1f); - - Assert.That(executed, Is.True); - Assert.That(handle.IsDone, Is.True); - } - - /// - /// 测试等待指令状态管理 - _waitingInstruction 更新 - /// - [Test] - public void WaitingInstruction_Should_UpdateCorrectly() - { - var executed = false; - - IEnumerator Coroutine() - { - yield return new WaitForSeconds(0.2f); - executed = true; - } - - var handle = _scope.Launch(Coroutine()); - - _scheduler.Update(0.1f); - Assert.That(handle.IsDone, Is.False); - - _scheduler.Update(0.1f); - Assert.That(handle.IsDone, Is.True); - Assert.That(executed, Is.True); - } - - /// - /// 测试协程多次执行 - 同一协程多次启动 - /// - [Test] - public void SameCoroutine_Should_ExecuteMultipleTimes() - { - var executionCount = 0; - - IEnumerator Coroutine() - { - executionCount++; - yield break; - } - - var handle1 = _scope.Launch(Coroutine()); - var handle2 = _scope.Launch(Coroutine()); - - _scheduler.Update(0.1f); - - Assert.That(handle1.IsDone, Is.True); - Assert.That(handle2.IsDone, Is.True); - Assert.That(executionCount, Is.EqualTo(2)); - } - - /// - /// 测试不支持的Yield类型 - 抛出 InvalidOperationException - /// - [Test] - public void UnsupportedYieldType_Should_ThrowInvalidOperationException() - { - IEnumerator Coroutine() - { - yield return 123; - } - - var handle = _scope.Launch(Coroutine()); - Exception? caughtException = null; - handle.OnError += ex => caughtException = ex; - - _scheduler.Update(0.1f); - - Assert.That(caughtException, Is.Not.Null); - Assert.That(caughtException, Is.TypeOf()); - Assert.That(caughtException!.Message, Does.Contain("Unsupported yield type")); - } - - /// - /// 测试协程上下文获取 - Context 属性 - /// - [Test] - public void Context_Should_BeAccessible() - { - IEnumerator Coroutine() - { - yield break; - } - - var handle = _scope.Launch(Coroutine()); - - Assert.That(handle.Context, Is.Not.Null); - Assert.That(handle.Context.Scope, Is.EqualTo(_scope)); - Assert.That(handle.Context.Scheduler, Is.EqualTo(_scheduler)); - Assert.That(handle.Context.Owner, Is.EqualTo(_scope)); - } - - /// - /// 创建简单的协程辅助方法 - /// - private IEnumerator CreateSimpleCoroutine(Action action) - { - action(); - yield break; - } -} diff --git a/GFramework.Core.Tests/coroutine/CoroutineIntegrationTests.cs b/GFramework.Core.Tests/coroutine/CoroutineIntegrationTests.cs deleted file mode 100644 index b973199..0000000 --- a/GFramework.Core.Tests/coroutine/CoroutineIntegrationTests.cs +++ /dev/null @@ -1,385 +0,0 @@ -using System.Collections; -using GFramework.Core.Abstractions.coroutine; -using GFramework.Core.coroutine; -using NUnit.Framework; - -namespace GFramework.Core.Tests.coroutine; - -/// -/// 协程系统集成测试类,验证复杂场景下的协程交互和协同工作 -/// -[TestFixture] -public class CoroutineIntegrationTests -{ - private CoroutineScheduler _scheduler = null!; - private CoroutineScope _scope = null!; - - [SetUp] - public void SetUp() - { - _scheduler = new CoroutineScheduler(); - _scope = new CoroutineScope(_scheduler, "TestScope"); - } - - [TearDown] - public void TearDown() - { - _scope?.Dispose(); - } - - /// - /// 测试复杂协程链式调用 - 多层嵌套协程 - /// - [Test] - public void ComplexChainedCoroutines_Should_ExecuteCorrectly() - { - var executionOrder = new List(); - - IEnumerator Coroutine1() - { - executionOrder.Add("Coroutine1-Start"); - yield return Coroutine2(); - executionOrder.Add("Coroutine1-End"); - } - - IEnumerator Coroutine2() - { - executionOrder.Add("Coroutine2-Start"); - yield return Coroutine3(); - executionOrder.Add("Coroutine2-End"); - } - - IEnumerator Coroutine3() - { - executionOrder.Add("Coroutine3-Start"); - yield return new WaitForSeconds(0.1f); - executionOrder.Add("Coroutine3-End"); - } - - var handle = _scope.Launch(Coroutine1()); - _scheduler.Update(0.1f); - - Assert.That(handle.IsDone, Is.True); - Assert.That(executionOrder, Is.EqualTo(new[] - { - "Coroutine1-Start", - "Coroutine2-Start", - "Coroutine3-Start", - "Coroutine3-End", - "Coroutine2-End", - "Coroutine1-End" - })); - } - - /// - /// 测试协程间数据传递 - 通过闭包共享状态 - /// - [Test] - public void CoroutineDataSharing_Should_WorkCorrectly() - { - var sharedData = new SharedData { Value = 0 }; - - IEnumerator ProducerCoroutine() - { - for (int i = 1; i <= 5; i++) - { - sharedData.Value = i; - yield return null; - } - } - - IEnumerator ConsumerCoroutine() - { - while (sharedData.Value < 5) - { - yield return new WaitForSeconds(0.05f); - } - } - - var producerHandle = _scope.Launch(ProducerCoroutine()); - var consumerHandle = _scope.Launch(ConsumerCoroutine()); - - _scheduler.Update(0.05f); - Assert.That(producerHandle.IsDone, Is.False); - Assert.That(consumerHandle.IsDone, Is.False); - - _scheduler.Update(0.05f); - _scheduler.Update(0.05f); - _scheduler.Update(0.05f); - - Assert.That(producerHandle.IsDone, Is.True); - Assert.That(consumerHandle.IsDone, Is.True); - Assert.That(sharedData.Value, Is.EqualTo(5)); - } - - /// - /// 测试协程与事件集成 - 协程中触发事件 - /// - [Test] - public void CoroutineEventIntegration_Should_WorkCorrectly() - { - var eventTriggered = false; - Action? eventCallback = null; - - IEnumerator EventCoroutine() - { - yield return new WaitForSeconds(0.1f); - eventCallback?.Invoke(); - } - - eventCallback = () => eventTriggered = true; - - var handle = _scope.Launch(EventCoroutine()); - _scheduler.Update(0.05f); - - Assert.That(eventTriggered, Is.False); - - _scheduler.Update(0.05f); - - Assert.That(eventTriggered, Is.True); - Assert.That(handle.IsDone, Is.True); - } - - /// - /// 测试协程异常传播 - 嵌套协程中的异常处理 - /// - [Test] - public void CoroutineExceptionPropagation_Should_HandleNestedExceptions() - { - Exception? caughtException = null; - - IEnumerator InnerCoroutine() - { - yield return null; - throw new InvalidOperationException("Inner exception"); - } - - IEnumerator OuterCoroutine() - { - yield return InnerCoroutine(); - } - - var handle = _scope.Launch(OuterCoroutine()); - handle.OnError += ex => caughtException = ex; - - _scheduler.Update(0.1f); - - Assert.That(caughtException, Is.Not.Null); - Assert.That(caughtException, Is.TypeOf()); - Assert.That(handle.IsDone, Is.True); - } - - /// - /// 测试协程取消链 - 父协程取消子协程 - /// - [Test] - public void CoroutineCancellationChain_Should_PropagateCancels() - { - var innerExecuted = false; - var outerExecuted = false; - - IEnumerator InnerCoroutine() - { - innerExecuted = true; - yield break; - } - - IEnumerator OuterCoroutine() - { - yield return InnerCoroutine(); - outerExecuted = true; - } - - var innerHandle = _scope.Launch(InnerCoroutine()); - var outerHandle = _scope.Launch(OuterCoroutine()); - - outerHandle.Cancel(); - - _scheduler.Update(0.1f); - - Assert.That(outerExecuted, Is.False); - Assert.That(innerExecuted, Is.True); - Assert.That(outerHandle.IsCancelled, Is.True); - Assert.That(innerHandle.IsDone, Is.True); - } - - /// - /// 测试协程超时控制 - 使用 WaitUntil 实现超时 - /// - [Test] - public void CoroutineTimeout_Should_WorkCorrectly() - { - var conditionMet = false; - var timedOut = false; - - IEnumerator WorkerCoroutine() - { - yield return new WaitForSeconds(0.2f); - conditionMet = true; - } - - IEnumerator TimeoutCoroutine() - { - var worker = _scope.Launch(WorkerCoroutine()); - - yield return new WaitUntil(() => - { - if (worker.IsDone) return true; - timedOut = true; - return true; - }); - - worker.Cancel(); - } - - var handle = _scope.Launch(TimeoutCoroutine()); - - _scheduler.Update(0.15f); - - Assert.That(conditionMet, Is.False); - Assert.That(timedOut, Is.False); - Assert.That(handle.IsDone, Is.False); - - _scheduler.Update(0.1f); - - Assert.That(conditionMet, Is.True); - Assert.That(timedOut, Is.False); - Assert.That(handle.IsDone, Is.True); - } - - /// - /// 测试协程同步等待 - 等待多个协程完成 - /// - [Test] - public void CoroutineSynchronization_Should_WaitForMultipleCoroutines() - { - var completedCount = 0; - - IEnumerator Task1() - { - yield return new WaitForSeconds(0.1f); - completedCount++; - } - - IEnumerator Task2() - { - yield return new WaitForSeconds(0.15f); - completedCount++; - } - - IEnumerator Task3() - { - yield return new WaitForSeconds(0.2f); - completedCount++; - } - - IEnumerator WaitAllCoroutine() - { - var task1 = _scope.Launch(Task1()); - var task2 = _scope.Launch(Task2()); - var task3 = _scope.Launch(Task3()); - - yield return new WaitUntil(() => - task1.IsDone && task2.IsDone && task3.IsDone); - } - - var handle = _scope.Launch(WaitAllCoroutine()); - - _scheduler.Update(0.15f); - Assert.That(completedCount, Is.EqualTo(2)); - Assert.That(handle.IsDone, Is.False); - - _scheduler.Update(0.05f); - Assert.That(completedCount, Is.EqualTo(3)); - Assert.That(handle.IsDone, Is.True); - } - - /// - /// 测试协程竞态条件 - 多个协程竞争同一资源 - /// - [Test] - public void CoroutineRaceCondition_Should_HandleResourceCorrectly() - { - var resourceAccessCount = 0; - var maxAccesses = 0; - - IEnumerator AccessResource(int id) - { - resourceAccessCount++; - if (resourceAccessCount > maxAccesses) - maxAccesses = resourceAccessCount; - - yield return new WaitForSeconds(0.05f); - - resourceAccessCount--; - yield break; - } - - var handles = new List(); - - for (int i = 0; i < 5; i++) - { - handles.Add(_scope.Launch(AccessResource(i))); - } - - while (handles.Any(h => !h.IsDone)) - { - _scheduler.Update(0.05f); - } - - Assert.That(resourceAccessCount, Is.EqualTo(0)); - Assert.That(maxAccesses, Is.EqualTo(5)); - } - - /// - /// 测试协程资源管理 - using 语句与协程 - /// - [Test] - public void CoroutineResourceManagement_Should_CleanupCorrectly() - { - var resourceDisposed = false; - var resourceAccessed = false; - - var resource = new DisposableResource(() => resourceDisposed = true); - - IEnumerator ResourceUsingCoroutine() - { - using (resource) - { - yield return new WaitForSeconds(0.1f); - resourceAccessed = true; - } - } - - var handle = _scope.Launch(ResourceUsingCoroutine()); - - _scheduler.Update(0.05f); - Assert.That(resourceAccessed, Is.False); - Assert.That(resourceDisposed, Is.False); - - _scheduler.Update(0.05f); - Assert.That(resourceAccessed, Is.True); - Assert.That(resourceDisposed, Is.True); - Assert.That(handle.IsDone, Is.True); - } - - private class SharedData - { - public int Value { get; set; } - } - - private class DisposableResource : IDisposable - { - private readonly Action _onDispose; - - public DisposableResource(Action onDispose) - { - _onDispose = onDispose; - } - - public void Dispose() - { - _onDispose?.Invoke(); - } - } -} diff --git a/GFramework.Core.Tests/coroutine/CoroutineSchedulerTests.cs b/GFramework.Core.Tests/coroutine/CoroutineSchedulerTests.cs deleted file mode 100644 index dfa180c..0000000 --- a/GFramework.Core.Tests/coroutine/CoroutineSchedulerTests.cs +++ /dev/null @@ -1,305 +0,0 @@ -using System.Collections; -using GFramework.Core.Abstractions.coroutine; -using GFramework.Core.coroutine; -using NUnit.Framework; -using System.Threading; - -namespace GFramework.Core.Tests.coroutine; - -/// -/// 协程调度器测试类,验证协程调度器的调度和执行功能 -/// -[TestFixture] -public class CoroutineSchedulerTests -{ - private CoroutineScheduler _scheduler = null!; - private CoroutineScope _scope = null!; - - [SetUp] - public void SetUp() - { - _scheduler = new CoroutineScheduler(); - _scope = new CoroutineScope(_scheduler, "TestScope"); - } - - [TearDown] - public void TearDown() - { - _scope?.Dispose(); - } - - /// - /// 测试基础更新操作 - Update(deltaTime) 方法 - /// - [Test] - public void Update_Should_ProcessCoroutines() - { - var executed = false; - IEnumerator Coroutine() - { - executed = true; - yield break; - } - - _scope.Launch(Coroutine()); - Assert.That(executed, Is.False); - - _scheduler.Update(0.1f); - Assert.That(executed, Is.True); - } - - /// - /// 测试ActiveCount 属性 - 正确统计活跃协程 - /// - [Test] - public void ActiveCount_Should_CountActiveCoroutines() - { - Assert.That(_scheduler.ActiveCount, Is.EqualTo(0)); - - _scope.Launch(CreateSimpleCoroutine()); - Assert.That(_scheduler.ActiveCount, Is.EqualTo(1)); - - _scope.Launch(CreateSimpleCoroutine()); - Assert.That(_scheduler.ActiveCount, Is.EqualTo(2)); - - _scope.Launch(CreateSimpleCoroutine()); - Assert.That(_scheduler.ActiveCount, Is.EqualTo(3)); - - _scheduler.Update(0.1f); - Assert.That(_scheduler.ActiveCount, Is.EqualTo(0)); - } - - /// - /// 测试多协程并发执行 - 同时启动多个协程 - /// - [Test] - public void MultipleCoroutines_Should_ExecuteConcurrently() - { - var executionCount = 0; - - IEnumerator Coroutine() - { - Interlocked.Increment(ref executionCount); - yield break; - } - - _scope.Launch(Coroutine()); - _scope.Launch(Coroutine()); - _scope.Launch(Coroutine()); - - _scheduler.Update(0.1f); - - Assert.That(executionCount, Is.EqualTo(3)); - } - - /// - /// 测试协程完成移除 - IsDone 协程自动移除 - /// - [Test] - public void CompletedCoroutine_Should_BeRemoved() - { - var handle = _scope.Launch(CreateSimpleCoroutine()); - - Assert.That(_scheduler.ActiveCount, Is.EqualTo(1)); - - _scheduler.Update(0.1f); - - Assert.That(_scheduler.ActiveCount, Is.EqualTo(0)); - Assert.That(handle.IsDone, Is.True); - } - - /// - /// 测试作用域不活跃时取消 - Scope.IsActive = false 时取消 - /// - [Test] - public void InactiveScope_Should_CancelCoroutines() - { - var executed = false; - IEnumerator Coroutine() - { - executed = true; - yield break; - } - - var handle = _scope.Launch(Coroutine()); - _scope.Cancel(); - - _scheduler.Update(0.1f); - - Assert.That(executed, Is.False); - Assert.That(handle.IsCancelled, Is.True); - Assert.That(_scheduler.ActiveCount, Is.EqualTo(0)); - } - - /// - /// 测试线程安全检查 - 跨线程调用抛出异常 - /// - [Test] - public void CrossThreadUpdate_Should_ThrowException() - { - var exceptionThrown = false; - Exception? caughtException = null; - - // 先在当前线程调用一次,初始化线程ID - _scheduler.Update(0.1f); - - var thread = new Thread(() => - { - try - { - _scheduler.Update(0.1f); - } - catch (InvalidOperationException ex) - { - exceptionThrown = true; - caughtException = ex; - } - }); - - thread.Start(); - thread.Join(); - - Assert.That(exceptionThrown, Is.True); - Assert.That(caughtException, Is.Not.Null); - Assert.That(caughtException!.Message, Does.Contain("must be updated on same thread")); - } - - /// - /// 测试待添加协程统计 - _toAdd 计入 ActiveCount - /// - [Test] - public void ToAddCoroutines_Should_BeIncludedInActiveCount() - { - _scope.Launch(CreateSimpleCoroutine()); - _scope.Launch(CreateSimpleCoroutine()); - - Assert.That(_scheduler.ActiveCount, Is.EqualTo(2)); - - _scope.Launch(CreateSimpleCoroutine()); - - Assert.That(_scheduler.ActiveCount, Is.EqualTo(3)); - } - - /// - /// 测试协程添加时机 - _toAdd 正确合并到 _active - /// - [Test] - public void ToAddCoroutines_Should_BeMergedIntoActive() - { - var executed = false; - - IEnumerator Coroutine() - { - executed = true; - yield break; - } - - _scope.Launch(Coroutine()); - - Assert.That(executed, Is.False); - - _scheduler.Update(0.1f); - - Assert.That(executed, Is.True); - } - - /// - /// 测试协程移除时机 - _toRemove 正确清理 _active - /// - [Test] - public void ToRemoveCoroutines_Should_BeClearedFromActive() - { - var handle = _scope.Launch(CreateSimpleCoroutine()); - - Assert.That(_scheduler.ActiveCount, Is.EqualTo(1)); - - _scheduler.Update(0.1f); - - Assert.That(_scheduler.ActiveCount, Is.EqualTo(0)); - Assert.That(handle.IsDone, Is.True); - } - - /// - /// 测试空列表处理 - 无协程时的 Update 行为 - /// - [Test] - public void EmptyScheduler_Should_HandleGracefully() - { - Assert.DoesNotThrow(() => - { - _scheduler.Update(0.1f); - _scheduler.Update(0.2f); - _scheduler.Update(0.3f); - }); - } - - /// - /// 测试高频协程启动 - 快速连续启动多个协程 - /// - [Test] - public void RapidCoroutineStart_Should_HandleGracefully() - { - var executionCount = 0; - - IEnumerator Coroutine() - { - Interlocked.Increment(ref executionCount); - yield break; - } - - for (int i = 0; i < 100; i++) - { - _scope.Launch(Coroutine()); - } - - Assert.That(_scheduler.ActiveCount, Is.EqualTo(100)); - - _scheduler.Update(0.1f); - - Assert.That(executionCount, Is.EqualTo(100)); - Assert.That(_scheduler.ActiveCount, Is.EqualTo(0)); - } - - /// - /// 测试协程生命周期 - 从启动到完成的完整流程 - /// - [Test] - public void CoroutineLifecycle_Should_WorkCorrectly() - { - var executionOrder = new List(); - - IEnumerator Coroutine() - { - executionOrder.Add("Start"); - yield return new WaitForSeconds(0.1f); - executionOrder.Add("Middle"); - yield return new WaitForSeconds(0.1f); - executionOrder.Add("End"); - } - - var handle = _scope.Launch(Coroutine()); - - Assert.That(handle.IsDone, Is.False); - Assert.That(executionOrder, Is.Empty); - - _scheduler.Update(0.1f); - Assert.That(handle.IsDone, Is.False); - Assert.That(executionOrder, Is.EqualTo(new[] { "Start" })); - - _scheduler.Update(0.1f); - Assert.That(handle.IsDone, Is.False); - Assert.That(executionOrder, Is.EqualTo(new[] { "Start", "Middle" })); - - _scheduler.Update(0.1f); - Assert.That(handle.IsDone, Is.True); - Assert.That(executionOrder, Is.EqualTo(new[] { "Start", "Middle", "End" })); - } - - /// - /// 创建简单的协程辅助方法 - /// - private IEnumerator CreateSimpleCoroutine() - { - yield break; - } -} diff --git a/GFramework.Core.Tests/coroutine/CoroutineScopeExtensionsTests.cs b/GFramework.Core.Tests/coroutine/CoroutineScopeExtensionsTests.cs deleted file mode 100644 index bc47326..0000000 --- a/GFramework.Core.Tests/coroutine/CoroutineScopeExtensionsTests.cs +++ /dev/null @@ -1,291 +0,0 @@ -using System.Collections; -using GFramework.Core.Abstractions.coroutine; -using GFramework.Core.coroutine; -using NUnit.Framework; - -namespace GFramework.Core.Tests.coroutine; - -/// -/// 协程作用域扩展方法测试类,验证延迟和重复执行协程的扩展功能 -/// -[TestFixture] -public class CoroutineScopeExtensionsTests -{ - private CoroutineScheduler _scheduler = null!; - private CoroutineScope _scope = null!; - - [SetUp] - public void SetUp() - { - _scheduler = new CoroutineScheduler(); - _scope = new CoroutineScope(_scheduler, "TestScope"); - } - - [TearDown] - public void TearDown() - { - _scope?.Dispose(); - } - - /// - /// 测试延迟启动协程 - LaunchDelayed(delay, action) - /// - [Test] - public void LaunchDelayed_Should_StartCoroutineAfterDelay() - { - var executed = false; - var handle = _scope.LaunchDelayed(0.2f, () => executed = true); - - Assert.That(handle.IsDone, Is.False); - Assert.That(executed, Is.False); - - _scheduler.Update(0.1f); - Assert.That(handle.IsDone, Is.False); - Assert.That(executed, Is.False); - - _scheduler.Update(0.1f); - Assert.That(handle.IsDone, Is.True); - Assert.That(executed, Is.True); - } - - /// - /// 测试延迟时间准确性 - WaitForSeconds 等待正确时长 - /// - [Test] - public void LaunchDelayed_DelayTime_Should_BeAccurate() - { - var executionTimes = new List(); - var elapsedTime = 0f; - - var handle = _scope.LaunchDelayed(0.3f, () => executionTimes.Add(elapsedTime)); - - elapsedTime += 0.1f; - _scheduler.Update(0.1f); - Assert.That(executionTimes, Is.Empty); - - elapsedTime += 0.1f; - _scheduler.Update(0.1f); - Assert.That(executionTimes, Is.Empty); - - elapsedTime += 0.1f; - _scheduler.Update(0.1f); - Assert.That(executionTimes.Count, Is.EqualTo(1)); - Assert.That(executionTimes[0], Is.EqualTo(0.3f).Within(0.001f)); - } - - /// - /// 测试延迟后执行动作 - action 参数正确调用 - /// - [Test] - public void LaunchDelayed_Action_Should_BeCalled() - { - var actionCalled = false; - var capturedValue = 0; - - var handle = _scope.LaunchDelayed(0.1f, () => - { - actionCalled = true; - capturedValue = 42; - }); - - _scheduler.Update(0.1f); - - Assert.That(actionCalled, Is.True); - Assert.That(capturedValue, Is.EqualTo(42)); - } - - /// - /// 测试重复启动协程 - LaunchRepeating(interval, action) - /// - [Test] - public void LaunchRepeating_Should_ExecuteRepeatedly() - { - var executionCount = 0; - var handle = _scope.LaunchRepeating(0.1f, () => executionCount++); - - Assert.That(executionCount, Is.EqualTo(0)); - - _scheduler.Update(0.1f); - Assert.That(executionCount, Is.EqualTo(1)); - - _scheduler.Update(0.1f); - Assert.That(executionCount, Is.EqualTo(2)); - - _scheduler.Update(0.1f); - Assert.That(executionCount, Is.EqualTo(3)); - - handle.Cancel(); - - _scheduler.Update(0.1f); - Assert.That(executionCount, Is.EqualTo(3)); - } - - /// - /// 测试重复间隔准确性 - 循环间隔正确 - /// - [Test] - public void LaunchRepeating_Interval_Should_BeAccurate() - { - var executionTimes = new List(); - var elapsedTime = 0f; - - var handle = _scope.LaunchRepeating(0.15f, () => executionTimes.Add(elapsedTime)); - - elapsedTime += 0.1f; - _scheduler.Update(0.1f); - Assert.That(executionTimes, Is.Empty); - - elapsedTime += 0.05f; - _scheduler.Update(0.05f); - Assert.That(executionTimes.Count, Is.EqualTo(1)); - Assert.That(executionTimes[0], Is.EqualTo(0.15f).Within(0.001f)); - - elapsedTime += 0.15f; - _scheduler.Update(0.15f); - Assert.That(executionTimes.Count, Is.EqualTo(2)); - Assert.That(executionTimes[1], Is.EqualTo(0.3f).Within(0.001f)); - - handle.Cancel(); - } - - /// - /// 测试重复执行动作 - action 参数循环调用 - /// - [Test] - public void LaunchRepeating_Action_Should_BeCalledMultipleTimes() - { - var actionCalls = new List(); - - var handle = _scope.LaunchRepeating(0.1f, () => - { - actionCalls.Add(actionCalls.Count); - }); - - _scheduler.Update(0.1f); - _scheduler.Update(0.1f); - _scheduler.Update(0.1f); - - Assert.That(actionCalls, Is.EqualTo(new[] { 0, 1, 2 })); - - handle.Cancel(); - } - - /// - /// 测试空action处理 - action 为 null 不抛异常 - /// - [Test] - public void LaunchDelayed_WithNullAction_Should_NotThrowException() - { - Assert.DoesNotThrow(() => - { - _scope.LaunchDelayed(0.1f, null!); - }); - - _scheduler.Update(0.1f); - } - - /// - /// 测试空action处理 - action 为 null 不抛异常(重复版本) - /// - [Test] - public void LaunchRepeating_WithNullAction_Should_NotThrowException() - { - var handle = _scope.LaunchRepeating(0.1f, null!); - - Assert.DoesNotThrow(() => - { - _scheduler.Update(0.1f); - _scheduler.Update(0.1f); - }); - - handle.Cancel(); - } - - /// - /// 测试取消延迟协程 - 返回的句柄可取消 - /// - [Test] - public void CancelDelayedCoroutine_Should_PreventExecution() - { - var executed = false; - var handle = _scope.LaunchDelayed(0.2f, () => executed = true); - - _scheduler.Update(0.1f); - Assert.That(executed, Is.False); - - handle.Cancel(); - _scheduler.Update(0.1f); - - Assert.That(executed, Is.False); - Assert.That(handle.IsCancelled, Is.True); - } - - /// - /// 测试取消重复协程 - 返回的句柄可取消 - /// - [Test] - public void CancelRepeatingCoroutine_Should_StopExecution() - { - var executionCount = 0; - var handle = _scope.LaunchRepeating(0.1f, () => executionCount++); - - _scheduler.Update(0.1f); - _scheduler.Update(0.1f); - Assert.That(executionCount, Is.EqualTo(2)); - - handle.Cancel(); - - _scheduler.Update(0.1f); - _scheduler.Update(0.1f); - Assert.That(executionCount, Is.EqualTo(2)); - } - - /// - /// 测试多个延迟协程 - 同时启动多个延迟任务 - /// - [Test] - public void MultipleDelayedCoroutines_Should_ExecuteIndependently() - { - var executionOrder = new List(); - - _scope.LaunchDelayed(0.1f, () => executionOrder.Add("100ms")); - _scope.LaunchDelayed(0.2f, () => executionOrder.Add("200ms")); - _scope.LaunchDelayed(0.3f, () => executionOrder.Add("300ms")); - - _scheduler.Update(0.1f); - Assert.That(executionOrder, Is.EqualTo(new[] { "100ms" })); - - _scheduler.Update(0.1f); - Assert.That(executionOrder, Is.EqualTo(new[] { "100ms", "200ms" })); - - _scheduler.Update(0.1f); - Assert.That(executionOrder, Is.EqualTo(new[] { "100ms", "200ms", "300ms" })); - } - - /// - /// 测试多个重复协程 - 同时启动多个重复任务 - /// - [Test] - public void MultipleRepeatingCoroutines_Should_ExecuteIndependently() - { - var counters = new Dictionary - { - ["fast"] = 0, - ["slow"] = 0 - }; - - var fastHandle = _scope.LaunchRepeating(0.05f, () => counters["fast"]++); - var slowHandle = _scope.LaunchRepeating(0.1f, () => counters["slow"]++); - - _scheduler.Update(0.1f); - Assert.That(counters["fast"], Is.EqualTo(2)); - Assert.That(counters["slow"], Is.EqualTo(1)); - - _scheduler.Update(0.1f); - Assert.That(counters["fast"], Is.EqualTo(4)); - Assert.That(counters["slow"], Is.EqualTo(2)); - - fastHandle.Cancel(); - slowHandle.Cancel(); - } -} diff --git a/GFramework.Core.Tests/coroutine/CoroutineScopeTests.cs b/GFramework.Core.Tests/coroutine/CoroutineScopeTests.cs deleted file mode 100644 index 3913e69..0000000 --- a/GFramework.Core.Tests/coroutine/CoroutineScopeTests.cs +++ /dev/null @@ -1,379 +0,0 @@ -using System.Collections; -using GFramework.Core.Abstractions.coroutine; -using GFramework.Core.coroutine; -using NUnit.Framework; - -namespace GFramework.Core.Tests.coroutine; - -/// -/// 协程作用域测试类,验证协程作用域的管理和控制功能 -/// -[TestFixture] -public class CoroutineScopeTests -{ - private CoroutineScheduler _scheduler = null!; - - [SetUp] - public void SetUp() - { - _scheduler = new CoroutineScheduler(); - } - - /// - /// 测试基础协程启动 - Launch(IEnumerator) 方法 - /// - [Test] - public void Launch_Should_StartCoroutine() - { - var scope = new CoroutineScope(_scheduler, "TestScope"); - var executed = false; - - IEnumerator Coroutine() - { - executed = true; - yield break; - } - - var handle = scope.Launch(Coroutine()); - - Assert.That(handle, Is.Not.Null); - Assert.That(handle.IsDone, Is.False); - - _scheduler.Update(0.1f); - - Assert.That(executed, Is.True); - Assert.That(handle.IsDone, Is.True); - - scope.Dispose(); - } - - /// - /// 测试协程作用域状态 - IsActive 属性 - /// - [Test] - public void IsActive_Should_BeCorrect() - { - var scope = new CoroutineScope(_scheduler, "TestScope"); - - Assert.That(scope.IsActive, Is.True); - - scope.Cancel(); - - Assert.That(scope.IsActive, Is.False); - - scope.Dispose(); - } - - /// - /// 测试协程作用域取消 - Cancel() 方法 - /// - [Test] - public void Cancel_Should_StopAllCoroutines() - { - var scope = new CoroutineScope(_scheduler, "TestScope"); - var executed1 = false; - var executed2 = false; - var executed3 = false; - - IEnumerator Coroutine1() - { - executed1 = true; - yield break; - } - - IEnumerator Coroutine2() - { - executed2 = true; - yield break; - } - - IEnumerator Coroutine3() - { - executed3 = true; - yield break; - } - - var handle1 = scope.Launch(Coroutine1()); - var handle2 = scope.Launch(Coroutine2()); - var handle3 = scope.Launch(Coroutine3()); - - scope.Cancel(); - - _scheduler.Update(0.1f); - - Assert.That(executed1, Is.False); - Assert.That(executed2, Is.False); - Assert.That(executed3, Is.False); - Assert.That(handle1.IsCancelled, Is.True); - Assert.That(handle2.IsCancelled, Is.True); - Assert.That(handle3.IsCancelled, Is.True); - - scope.Dispose(); - } - - /// - /// 测试子作用域管理 - 父子作用域关系 - /// - [Test] - public void ChildScope_Should_BeManagedByParent() - { - var parentScope = new CoroutineScope(_scheduler, "ParentScope"); - var childScope = new CoroutineScope(_scheduler, "ChildScope", parentScope); - - Assert.That(parentScope.IsActive, Is.True); - Assert.That(childScope.IsActive, Is.True); - - parentScope.Cancel(); - - Assert.That(parentScope.IsActive, Is.False); - Assert.That(childScope.IsActive, Is.False); - - childScope.Dispose(); - parentScope.Dispose(); - } - - /// - /// 测试取消传播 - 父作用域取消时子作用域也取消 - /// - [Test] - public void ParentCancel_Should_PropagateToChild() - { - var parentScope = new CoroutineScope(_scheduler, "ParentScope"); - var childScope = new CoroutineScope(_scheduler, "ChildScope", parentScope); - var executed = false; - - IEnumerator Coroutine() - { - executed = true; - yield break; - } - - var handle = childScope.Launch(Coroutine()); - parentScope.Cancel(); - - _scheduler.Update(0.1f); - - Assert.That(executed, Is.False); - Assert.That(handle.IsCancelled, Is.True); - - childScope.Dispose(); - parentScope.Dispose(); - } - - /// - /// 测试运行中协程跟踪 - _runningCoroutines 集合 - /// - [Test] - public void RunningCoroutines_Should_BeTracked() - { - var scope = new CoroutineScope(_scheduler, "TestScope"); - - IEnumerator Coroutine() - { - yield break; - } - - var handle1 = scope.Launch(Coroutine()); - var handle2 = scope.Launch(Coroutine()); - var handle3 = scope.Launch(Coroutine()); - - _scheduler.Update(0.1f); - - Assert.That(handle1.IsDone, Is.True); - Assert.That(handle2.IsDone, Is.True); - Assert.That(handle3.IsDone, Is.True); - - scope.Dispose(); - } - - /// - /// 测试协程完成自动移除 - OnComplete 事件处理 - /// - [Test] - public void CompletedCoroutine_Should_BeRemovedFromTracking() - { - var scope = new CoroutineScope(_scheduler, "TestScope"); - - IEnumerator Coroutine() - { - yield break; - } - - var handle = scope.Launch(Coroutine()); - - Assert.That(handle.IsDone, Is.False); - - _scheduler.Update(0.1f); - - Assert.That(handle.IsDone, Is.True); - - scope.Dispose(); - } - - /// - /// 测试协程错误自动移除 - OnError 事件处理 - /// - [Test] - public void FailedCoroutine_Should_BeRemovedFromTracking() - { - var scope = new CoroutineScope(_scheduler, "TestScope"); - - IEnumerator Coroutine() - { - yield return null; - throw new Exception("Test error"); - } - - var handle = scope.Launch(Coroutine()); - - Assert.That(handle.IsDone, Is.False); - - _scheduler.Update(0.1f); - - Assert.That(handle.IsDone, Is.True); - - scope.Dispose(); - } - - /// - /// 测试作用域销毁 - Dispose() 方法调用 Cancel - /// - [Test] - public void Dispose_Should_CallCancel() - { - var scope = new CoroutineScope(_scheduler, "TestScope"); - var executed = false; - - IEnumerator Coroutine() - { - executed = true; - yield break; - } - - var handle = scope.Launch(Coroutine()); - scope.Dispose(); - - _scheduler.Update(0.1f); - - Assert.That(executed, Is.False); - Assert.That(handle.IsCancelled, Is.True); - } - - /// - /// 测试不活跃作用域拒绝启动 - 抛出 InvalidOperationException - /// - [Test] - public void LaunchOnInactiveScope_Should_ThrowInvalidOperationException() - { - var scope = new CoroutineScope(_scheduler, "TestScope"); - - IEnumerator Coroutine() - { - yield break; - } - - scope.Cancel(); - - var exception = Assert.Throws(() => - { - scope.Launch(Coroutine()); - }); - - Assert.That(exception, Is.Not.Null); - Assert.That(exception!.Message, Does.Contain("not active")); - - scope.Dispose(); - } - - /// - /// 测试协程上下文设置 - CoroutineContext 创建 - /// - [Test] - public void CoroutineContext_Should_BeCreatedCorrectly() - { - var scope = new CoroutineScope(_scheduler, "TestScope"); - - IEnumerator Coroutine() - { - yield break; - } - - var handle = scope.Launch(Coroutine()); - - Assert.That(handle.Context, Is.Not.Null); - Assert.That(handle.Context.Scope, Is.EqualTo(scope)); - Assert.That(handle.Context.Scheduler, Is.EqualTo(_scheduler)); - Assert.That(handle.Context.Owner, Is.EqualTo(scope)); - - scope.Dispose(); - } - - /// - /// 测试空调度器异常 - scheduler 为 null 抛出异常 - /// - [Test] - public void NullScheduler_Should_ThrowArgumentNullException() - { - var exception = Assert.Throws(() => - { - var scope = new CoroutineScope(null!, "TestScope"); - }); - - Assert.That(exception, Is.Not.Null); - Assert.That(exception!.ParamName, Is.EqualTo("scheduler")); - } - - /// - /// 测试多协程管理 - 同一作用域启动多个协程 - /// - [Test] - public void MultipleCoroutines_Should_BeManagedCorrectly() - { - var scope = new CoroutineScope(_scheduler, "TestScope"); - var executionCount = 0; - - IEnumerator Coroutine() - { - executionCount++; - yield break; - } - - var handle1 = scope.Launch(Coroutine()); - var handle2 = scope.Launch(Coroutine()); - var handle3 = scope.Launch(Coroutine()); - - _scheduler.Update(0.1f); - - Assert.That(executionCount, Is.EqualTo(3)); - Assert.That(handle1.IsDone, Is.True); - Assert.That(handle2.IsDone, Is.True); - Assert.That(handle3.IsDone, Is.True); - - scope.Dispose(); - } - - /// - /// 测试嵌套作用域 - 多层父子关系 - /// - [Test] - public void NestedScopes_Should_WorkCorrectly() - { - var rootScope = new CoroutineScope(_scheduler, "RootScope"); - var level1Scope = new CoroutineScope(_scheduler, "Level1", rootScope); - var level2Scope = new CoroutineScope(_scheduler, "Level2", level1Scope); - - Assert.That(rootScope.IsActive, Is.True); - Assert.That(level1Scope.IsActive, Is.True); - Assert.That(level2Scope.IsActive, Is.True); - - rootScope.Cancel(); - - Assert.That(rootScope.IsActive, Is.False); - Assert.That(level1Scope.IsActive, Is.False); - Assert.That(level2Scope.IsActive, Is.False); - - level2Scope.Dispose(); - level1Scope.Dispose(); - rootScope.Dispose(); - } -} diff --git a/GFramework.Core.Tests/coroutine/GlobalCoroutineScopeTests.cs b/GFramework.Core.Tests/coroutine/GlobalCoroutineScopeTests.cs deleted file mode 100644 index 6474e5e..0000000 --- a/GFramework.Core.Tests/coroutine/GlobalCoroutineScopeTests.cs +++ /dev/null @@ -1,215 +0,0 @@ -using System.Collections; -using GFramework.Core.Abstractions.coroutine; -using GFramework.Core.coroutine; -using NUnit.Framework; - -namespace GFramework.Core.Tests.coroutine; - -/// -/// 全局协程作用域测试类,验证全局协程作用域的初始化和访问功能 -/// -[TestFixture] -public class GlobalCoroutineScopeTests -{ - private CoroutineScheduler _scheduler = null!; - - [SetUp] - public void SetUp() - { - // 每个测试前都重新初始化 - _scheduler = new CoroutineScheduler(); - GlobalCoroutineScope.Initialize(_scheduler); - } - - [TearDown] - public void TearDown() - { - GlobalCoroutineScope.TryGetScope(out var scope); - scope?.Cancel(); - } - - /// - /// 测试初始化检查 - IsInitialized 属性 - /// - [Test] - public void IsInitialized_Should_BeCorrect() - { - Assert.That(GlobalCoroutineScope.IsInitialized, Is.True); - } - - /// - /// 测试尝试获取作用域 - TryGetScope 方法 - /// - [Test] - public void TryGetScope_Should_ReturnScope() - { - var result = GlobalCoroutineScope.TryGetScope(out var scope); - - Assert.That(result, Is.True); - Assert.That(scope, Is.Not.Null); - Assert.That(scope!.IsActive, Is.True); - } - - /// - /// 测试初始化作用域 - Initialize(scheduler) 方法 - /// - [Test] - public void Initialize_Should_SetUpGlobalScope() - { - GlobalCoroutineScope.TryGetScope(out var oldScope); - oldScope?.Cancel(); - - _scheduler = new CoroutineScheduler(); - GlobalCoroutineScope.Initialize(_scheduler); - - Assert.That(GlobalCoroutineScope.IsInitialized, Is.True); - Assert.That(GlobalCoroutineScope.TryGetScope(out var scope), Is.True); - Assert.That(scope, Is.Not.Null); - } - - /// - /// 测试启动全局协程 - Launch(routine) 方法 - /// - [Test] - public void Launch_Should_StartCoroutine() - { - var executed = false; - - IEnumerator Coroutine() - { - executed = true; - yield break; - } - - var handle = GlobalCoroutineScope.Launch(Coroutine()); - - Assert.That(handle, Is.Not.Null); - Assert.That(handle.IsDone, Is.False); - - _scheduler.Update(0.1f); - - Assert.That(executed, Is.True); - } - - /// - /// 测试未初始化时启动 - 抛出 InvalidOperationException - /// - [Test] - public void Launch_WithoutInitialization_Should_ThrowInvalidOperationException() - { - GlobalCoroutineScope.TryGetScope(out var scope); - scope?.Cancel(); - - IEnumerator Coroutine() - { - yield break; - } - - var exception = Assert.Throws(() => - { - GlobalCoroutineScope.Launch(Coroutine()); - }); - - Assert.That(exception, Is.Not.Null); - Assert.That(exception!.Message, Does.Contain("not initialized") | Does.Contain("not active")); - } - - /// - /// 测试未初始化时TryGetScope - 返回 false 和 null - /// - [Test] - public void TryGetScope_WithoutInitialization_Should_ReturnFalse() - { - GlobalCoroutineScope.TryGetScope(out var scope); - scope?.Cancel(); - - var result = GlobalCoroutineScope.TryGetScope(out var scope2); - - // 注意:取消后scope仍然存在,只是IsActive=false - // 所以这个测试的行为需要调整 - Assert.That(scope2, Is.Not.Null); - Assert.That(scope2!.IsActive, Is.False); - } - - /// - /// 测试全局作用域单例性 - 多次 Initialize 行为 - /// - [Test] - public void MultipleInitialize_Should_ReplacePreviousInstance() - { - GlobalCoroutineScope.TryGetScope(out var scope1); - var scope1Ref = scope1; - - _scheduler = new CoroutineScheduler(); - GlobalCoroutineScope.Initialize(_scheduler); - GlobalCoroutineScope.TryGetScope(out var scope2); - - Assert.That(scope2, Is.Not.EqualTo(scope1Ref)); - } - - /// - /// 测试全局协程执行 - 通过全局作用域启动协程 - /// - [Test] - public void GlobalCoroutine_Should_ExecuteCorrectly() - { - var executionOrder = new List(); - - IEnumerator Coroutine1() - { - executionOrder.Add("Coroutine1"); - yield break; - } - - IEnumerator Coroutine2() - { - executionOrder.Add("Coroutine2"); - yield break; - } - - GlobalCoroutineScope.Launch(Coroutine1()); - GlobalCoroutineScope.Launch(Coroutine2()); - - _scheduler.Update(0.1f); - - // 执行顺序可能不同,只需要确保两个协程都执行了 - Assert.That(executionOrder.Count, Is.EqualTo(2)); - Assert.That(executionOrder, Does.Contain("Coroutine1")); - Assert.That(executionOrder, Does.Contain("Coroutine2")); - } - - /// - /// 测试全局作用域名称 - Name 属性为 "GlobalScope" - /// - [Test] - public void GlobalScopeName_Should_BeGlobalScope() - { - GlobalCoroutineScope.TryGetScope(out var scope); - - Assert.That(scope, Is.Not.Null); - } - - /// - /// 测试Dispose 行为 - 全局作用域 Dispose - /// - [Test] - public void Dispose_Should_CancelAllCoroutines() - { - var executed = false; - - IEnumerator Coroutine() - { - executed = true; - yield break; - } - - GlobalCoroutineScope.Launch(Coroutine()); - - GlobalCoroutineScope.TryGetScope(out var scope); - scope?.Cancel(); - - _scheduler.Update(0.1f); - - Assert.That(executed, Is.False); - } -} diff --git a/GFramework.Core.Tests/coroutine/YieldInstructionTests.cs b/GFramework.Core.Tests/coroutine/YieldInstructionTests.cs deleted file mode 100644 index 06150e9..0000000 --- a/GFramework.Core.Tests/coroutine/YieldInstructionTests.cs +++ /dev/null @@ -1,471 +0,0 @@ -using GFramework.Core.Abstractions.coroutine; -using GFramework.Core.coroutine; -using NUnit.Framework; - -namespace GFramework.Core.Tests.coroutine; - -/// -/// Yield指令测试类,验证各种Yield指令的等待和状态管理功能 -/// -[TestFixture] -public class YieldInstructionTests -{ - // ==================== WaitForSeconds 测试 ==================== - - /// - /// 测试基础等待功能 - 指定秒数后 IsDone = true - /// - [Test] - public void WaitForSeconds_Should_CompleteAfterSpecifiedTime() - { - var wait = new WaitForSeconds(0.2f); - - Assert.That(wait.IsDone, Is.False); - - wait.Update(0.1f); - Assert.That(wait.IsDone, Is.False); - - wait.Update(0.1f); - Assert.That(wait.IsDone, Is.True); - } - - /// - /// 测试IsDone 属性 - 等待前为 false,等待后为 true - /// - [Test] - public void WaitForSeconds_IsDone_Should_BeCorrect() - { - var wait = new WaitForSeconds(0.1f); - - Assert.That(wait.IsDone, Is.False); - - wait.Update(0.05f); - Assert.That(wait.IsDone, Is.False); - - wait.Update(0.05f); - Assert.That(wait.IsDone, Is.True); - } - - /// - /// 测试Update(deltaTime) 方法 - 时间累加正确 - /// - [Test] - public void WaitForSeconds_Update_Should_AccumulateTimeCorrectly() - { - var wait = new WaitForSeconds(1.0f); - - for (int i = 0; i < 10; i++) - { - wait.Update(0.1f); - } - - Assert.That(wait.IsDone, Is.True); - } - - /// - /// 测试精确时间计算 - 多次 Update 累加到阈值 - /// - [Test] - public void WaitForSeconds_Should_CalculateTimeAccurately() - { - var wait = new WaitForSeconds(0.333f); - - wait.Update(0.111f); - Assert.That(wait.IsDone, Is.False); - - wait.Update(0.111f); - Assert.That(wait.IsDone, Is.False); - - wait.Update(0.111f); - Assert.That(wait.IsDone, Is.True); - } - - /// - /// 测试Reset() 方法 - 重置状态可复用 - /// - [Test] - public void WaitForSeconds_Reset_Should_ReuseInstance() - { - var wait = new WaitForSeconds(0.1f); - - wait.Update(0.1f); - Assert.That(wait.IsDone, Is.True); - - wait.Reset(); - Assert.That(wait.IsDone, Is.False); - - wait.Update(0.05f); - Assert.That(wait.IsDone, Is.False); - - wait.Update(0.05f); - Assert.That(wait.IsDone, Is.True); - } - - /// - /// 测试累积误差测试 - Reset 后重新计数 - /// - [Test] - public void WaitForSeconds_Reset_Should_ClearAccumulatedError() - { - var wait = new WaitForSeconds(0.2f); - - wait.Update(0.15f); - Assert.That(wait.IsDone, Is.False); - - wait.Reset(); - wait.Update(0.1f); - Assert.That(wait.IsDone, Is.False); - - wait.Update(0.1f); - Assert.That(wait.IsDone, Is.True); - } - - /// - /// 测试零秒等待 - seconds = 0 立即完成 - /// - [Test] - public void WaitForSeconds_WithZeroSeconds_Should_CompleteImmediately() - { - var wait = new WaitForSeconds(0f); - - Assert.That(wait.IsDone, Is.False); - - wait.Update(0f); - Assert.That(wait.IsDone, Is.True); - } - - /// - /// 测试负秒数处理 - seconds < 0 行为 - /// - [Test] - public void WaitForSeconds_WithNegativeSeconds_Should_CompleteImmediately() - { - var wait = new WaitForSeconds(-0.1f); - - Assert.That(wait.IsDone, Is.False); - - wait.Update(0.1f); - Assert.That(wait.IsDone, Is.True); - } - - /// - /// 测试大数值等待 - 长时间等待场景 - /// - [Test] - public void WaitForSeconds_WithLargeValue_Should_WaitLongTime() - { - var wait = new WaitForSeconds(1000f); - - for (int i = 0; i < 999; i++) - { - wait.Update(1f); - } - - Assert.That(wait.IsDone, Is.False); - - wait.Update(1f); - Assert.That(wait.IsDone, Is.True); - } - - // ==================== WaitUntil 测试 ==================== - - /// - /// 测试条件为真时完成 - predicate 返回 true 时 IsDone = true - /// - [Test] - public void WaitUntil_Should_CompleteWhenPredicateIsTrue() - { - var conditionMet = false; - var wait = new WaitUntil(() => conditionMet); - - Assert.That(wait.IsDone, Is.False); - - wait.Update(0.1f); - Assert.That(wait.IsDone, Is.False); - - conditionMet = true; - wait.Update(0.1f); - Assert.That(wait.IsDone, Is.True); - } - - /// - /// 测试条件为假时等待 - predicate 返回 false 时继续等待 - /// - [Test] - public void WaitUntil_Should_ContinueWaitingWhenPredicateIsFalse() - { - var conditionMet = false; - var wait = new WaitUntil(() => conditionMet); - - for (int i = 0; i < 100; i++) - { - wait.Update(0.1f); - } - - Assert.That(wait.IsDone, Is.False); - - conditionMet = true; - wait.Update(0.1f); - - Assert.That(wait.IsDone, Is.True); - } - - /// - /// 测试Update(deltaTime) 方法 - 每次更新检查条件 - /// - [Test] - public void WaitUntil_Update_Should_CheckPredicateEachTime() - { - var checkCount = 0; - var wait = new WaitUntil(() => - { - checkCount++; - return checkCount >= 5; - }); - - for (int i = 0; i < 4; i++) - { - wait.Update(0.1f); - } - - Assert.That(checkCount, Is.EqualTo(4)); - Assert.That(wait.IsDone, Is.False); - - wait.Update(0.1f); - - Assert.That(checkCount, Is.EqualTo(5)); - Assert.That(wait.IsDone, Is.True); - } - - /// - /// 测试Reset() 方法 - 重置状态可复用 - /// - [Test] - public void WaitUntil_Reset_Should_AllowReuse() - { - var conditionMet = false; - var wait = new WaitUntil(() => conditionMet); - - conditionMet = true; - wait.Update(0.1f); - Assert.That(wait.IsDone, Is.True); - - wait.Reset(); - Assert.That(wait.IsDone, Is.False); - - conditionMet = false; - wait.Update(0.1f); - Assert.That(wait.IsDone, Is.False); - - conditionMet = true; - wait.Update(0.1f); - Assert.That(wait.IsDone, Is.True); - } - - /// - /// 测试谓词参数传递 - predicate 正确调用 - /// - [Test] - public void WaitUntil_Should_PassParametersToPredicate() - { - var counter = 0; - var wait = new WaitUntil(() => counter++ >= 3); - - Assert.That(wait.IsDone, Is.False); - - for (int i = 0; i < 3; i++) - { - wait.Update(0.1f); - } - - Assert.That(counter, Is.EqualTo(3)); - Assert.That(wait.IsDone, Is.False); - - wait.Update(0.1f); - Assert.That(counter, Is.EqualTo(4)); - Assert.That(wait.IsDone, Is.True); - } - - /// - /// 测试谓词闭包支持 - 捕获外部变量 - /// - [Test] - public void WaitUntil_Should_SupportClosureCapture() - { - var externalValue = 0; - var wait = new WaitUntil(() => externalValue > 10); - - externalValue = 5; - wait.Update(0.1f); - Assert.That(wait.IsDone, Is.False); - - externalValue = 15; - wait.Update(0.1f); - Assert.That(wait.IsDone, Is.True); - } - - /// - /// 测试谓词异常处理 - predicate 抛出异常时的行为 - /// - [Test] - public void WaitUntil_Should_HandleExceptionInPredicate() - { - var shouldThrow = false; - var wait = new WaitUntil(() => - { - if (shouldThrow) - throw new InvalidOperationException("Test exception"); - return false; - }); - - Assert.DoesNotThrow(() => wait.Update(0.1f)); - - shouldThrow = true; - Assert.Throws(() => wait.Update(0.1f)); - } - - // ==================== WaitWhile 测试 ==================== - - /// - /// 测试条件为假时完成 - predicate 返回 false 时 IsDone = true - /// - [Test] - public void WaitWhile_Should_CompleteWhenPredicateIsFalse() - { - var shouldWait = true; - var wait = new WaitWhile(() => shouldWait); - - Assert.That(wait.IsDone, Is.False); - - wait.Update(0.1f); - Assert.That(wait.IsDone, Is.False); - - shouldWait = false; - wait.Update(0.1f); - Assert.That(wait.IsDone, Is.True); - } - - /// - /// 测试条件为真时等待 - predicate 返回 true 时继续等待 - /// - [Test] - public void WaitWhile_Should_ContinueWaitingWhenPredicateIsTrue() - { - var shouldWait = true; - var wait = new WaitWhile(() => shouldWait); - - for (int i = 0; i < 100; i++) - { - wait.Update(0.1f); - } - - Assert.That(wait.IsDone, Is.False); - - shouldWait = false; - wait.Update(0.1f); - - Assert.That(wait.IsDone, Is.True); - } - - /// - /// 测试Update(deltaTime) 方法 - 每次更新检查条件 - /// - [Test] - public void WaitWhile_Update_Should_CheckPredicateEachTime() - { - var checkCount = 0; - var wait = new WaitWhile(() => checkCount++ < 5); - - for (int i = 0; i < 4; i++) - { - wait.Update(0.1f); - } - - Assert.That(checkCount, Is.EqualTo(4)); - Assert.That(wait.IsDone, Is.False); - - wait.Update(0.1f); - - Assert.That(checkCount, Is.EqualTo(5)); - Assert.That(wait.IsDone, Is.False); - - wait.Update(0.1f); - - Assert.That(checkCount, Is.EqualTo(6)); - Assert.That(wait.IsDone, Is.True); - } - - /// - /// 测试Reset() 方法 - 重置状态可复用 - /// - [Test] - public void WaitWhile_Reset_Should_AllowReuse() - { - var shouldWait = false; - var wait = new WaitWhile(() => shouldWait); - - wait.Update(0.1f); - Assert.That(wait.IsDone, Is.True); - - wait.Reset(); - Assert.That(wait.IsDone, Is.False); - - shouldWait = true; - wait.Update(0.1f); - Assert.That(wait.IsDone, Is.False); - - shouldWait = false; - wait.Update(0.1f); - Assert.That(wait.IsDone, Is.True); - } - - /// - /// 测试谓词参数传递 - predicate 正确调用 - /// - [Test] - public void WaitWhile_Should_PassParametersToPredicate() - { - var counter = 0; - var wait = new WaitWhile(() => counter++ < 3); - - Assert.That(wait.IsDone, Is.False); - - for (int i = 0; i < 3; i++) - { - wait.Update(0.1f); - } - - Assert.That(counter, Is.EqualTo(3)); - Assert.That(wait.IsDone, Is.False); - - wait.Update(0.1f); - Assert.That(counter, Is.EqualTo(4)); - Assert.That(wait.IsDone, Is.True); - } - - /// - /// 测试与 WaitUntil 对比 - 逻辑相反性验证 - /// - [Test] - public void WaitWhile_Should_BeOppositeOfWaitUntil() - { - var condition = true; - - var waitUntil = new WaitUntil(() => !condition); - var waitWhile = new WaitWhile(() => condition); - - waitUntil.Update(0.1f); - waitWhile.Update(0.1f); - - Assert.That(waitUntil.IsDone, Is.False); - Assert.That(waitWhile.IsDone, Is.False); - - condition = false; - - waitUntil.Update(0.1f); - waitWhile.Update(0.1f); - - Assert.That(waitUntil.IsDone, Is.True); - Assert.That(waitWhile.IsDone, Is.True); - } -} diff --git a/GFramework.Core/coroutine/CoroutineContext.cs b/GFramework.Core/coroutine/CoroutineContext.cs deleted file mode 100644 index 901df2c..0000000 --- a/GFramework.Core/coroutine/CoroutineContext.cs +++ /dev/null @@ -1,28 +0,0 @@ -using GFramework.Core.Abstractions.coroutine; - -namespace GFramework.Core.coroutine; - -/// -/// 协程上下文类,用于封装协程执行所需的环境信息 -/// -/// 协程作用域,定义协程的执行范围 -/// 协程调度器,负责协程的调度和执行管理 -/// 协程的所有者对象,可为空,默认为null -public class CoroutineContext(ICoroutineScope scope, CoroutineScheduler scheduler, object? owner = null) - : ICoroutineContext -{ - /// - /// 获取协程作用域 - /// - public ICoroutineScope Scope { get; } = scope; - - /// - /// 获取协程调度器 - /// - public ICoroutineScheduler Scheduler { get; } = scheduler; - - /// - /// 获取协程所有者对象 - /// - public object? Owner { get; } = owner; -} \ No newline at end of file diff --git a/GFramework.Core/coroutine/CoroutineHandle.cs b/GFramework.Core/coroutine/CoroutineHandle.cs deleted file mode 100644 index eec12d2..0000000 --- a/GFramework.Core/coroutine/CoroutineHandle.cs +++ /dev/null @@ -1,280 +0,0 @@ -using System.Collections; -using GFramework.Core.Abstractions.coroutine; - -namespace GFramework.Core.coroutine; - - /// - /// 协程句柄类,用于管理和控制协程的执行状态 - /// 实现了IYieldInstruction和ICoroutineHandle接口 - /// -public class CoroutineHandle : IYieldInstruction, ICoroutineHandle -{ - /// - /// 存储协程执行栈的堆栈结构 - /// - private readonly Stack _stack = new(); - - /// - /// 当前等待执行的指令 - /// - private IYieldInstruction? _waitingInstruction; - - /// - /// 是否由父协程控制(被yield) - /// - private bool _isManagedByParent; - - /// - /// 初始化一个新的协程句柄实例 - /// - /// 要执行的枚举器协程 - /// 协程上下文环境 - /// 初始等待的指令 - internal CoroutineHandle(IEnumerator routine, CoroutineContext context, IYieldInstruction? waitingInstruction) - { - _stack.Push(routine); - Context = context; - _waitingInstruction = waitingInstruction; - } - - /// - /// 标记协程由父协程管理 - /// - internal void MarkAsManagedByParent() - { - _isManagedByParent = true; - } - - /// - /// 检查协程是否由父协程管理 - /// - internal bool IsManagedByParent => _isManagedByParent; - - /// - /// 获取协程的上下文环境 - /// - public CoroutineContext Context { get; } - - /// - /// 获取协程句柄的上下文环境(接口实现) - /// - ICoroutineContext ICoroutineHandle.Context => Context; - - /// - /// 获取协程是否已被取消的标志 - /// - public bool IsCancelled { get; private set; } - - /// - /// 协程完成时触发的事件 - /// - public event Action? OnComplete; - - /// - /// 协程发生错误时触发的事件 - /// - public event Action? OnError; - - /// - /// 取消协程的执行 - /// - public void Cancel() - { - if (IsDone) return; - IsDone = true; - IsCancelled = true; - _stack.Clear(); - _waitingInstruction = null; - OnComplete?.Invoke(); - } - - /// - /// 获取协程是否已完成的标志 - /// - public bool IsDone { get; private set; } - - /// - /// 更新协程执行状态(接口实现) - /// - /// 时间增量 - void IYieldInstruction.Update(float deltaTime) - { - InternalUpdate(deltaTime); - } - - /// - /// 内部更新协程执行逻辑 - /// - /// 时间增量 - /// 如果协程仍在运行返回true,否则返回false - private bool InternalUpdate(float deltaTime) - { - if (IsDone) return false; - - // 如果有等待指令,先更新等待指令 - if (_waitingInstruction != null) - { - _waitingInstruction.Update(deltaTime); - - // 如果等待指令还未完成,继续等待 - if (!_waitingInstruction.IsDone) - { - return true; - } - - // 等待指令已完成,清除它 - _waitingInstruction = null; - } - - // 每帧只推进一步(执行一个 MoveNext) - if (_stack.Count > 0) - { - try - { - var current = _stack.Peek(); - bool hasNext = current.MoveNext(); - - if (!hasNext) - { - // 当前枚举器已完成,弹出栈 - _stack.Pop(); - - // 如果栈为空,协程完成 - if (_stack.Count == 0) - { - Complete(); - return false; - } - - // 否则继续执行下一个枚举器(在下一帧) - return true; - } - - // MoveNext() 返回 true,有下一个值 - var yielded = current.Current; - var needsWait = ProcessYieldValue(yielded); - - // 如果需要等待,则暂停执行 - if (needsWait) - { - return true; - } - - // 如果不需要等待(yield null 或嵌套协程),继续处理 - // 处理 yield null 的情况:yield null 后需要再调用一次 MoveNext 才能知道是否完成 - if (yielded == null) - { - // yield null 意味着等待一帧,但协程可能还没有完成 - // 需要再次检查是否还有更多步骤 - bool stillHasNext = current.MoveNext(); - if (!stillHasNext) - { - // 协程确实完成了 - _stack.Pop(); - if (_stack.Count == 0) - { - Complete(); - return false; - } - } - else - { - // 还有更多内容,yield 出来的值需要处理 - yielded = current.Current; - needsWait = ProcessYieldValue(yielded); - if (needsWait) - { - return true; - } - } - } - - return true; - } - catch (Exception ex) - { - HandleError(ex); - return false; - } - } - - // 栈为空,协程完成 - Complete(); - return false; - } - - /// - /// 处理协程中yield返回的值,根据类型决定如何处理 - /// - /// 协程yield返回的对象 - /// 如果需要等待返回true,否则返回false - private bool ProcessYieldValue(object yielded) - { - switch (yielded) - { - case CoroutineHandle otherHandle: - // 处理 yield return CoroutineHandle - if (otherHandle.IsDone) - { - // 子协程已完成,不需要等待 - return false; - } - - // 标记子协程由父协程管理 - otherHandle.MarkAsManagedByParent(); - _waitingInstruction = otherHandle; - return true; // 需要等待子协程完成 - - case IEnumerator nested: - // 处理 yield return IEnumerator(嵌套协程) - _stack.Push(nested); - return false; // 压入嵌套协程,在下一帧继续执行 - - case IYieldInstruction instruction: - // 处理 yield return IYieldInstruction - _waitingInstruction = instruction; - return true; // 需要等待指令完成 - - case null: - // 处理 yield return null(等待一帧) - return false; // null 立即继续,但会返回 true 让协程继续执行 - - default: - throw new InvalidOperationException($"Unsupported yield type: {yielded.GetType()}"); - } - } - - /// - /// 检查协程是否完成并进行相应处理 - /// - /// 如果协程已完成返回true,否则返回false - private bool CompleteCheck() - { - if (_stack.Count == 0) Complete(); - return IsDone; - } - - /// - /// 标记协程完成并清理相关资源 - /// - private void Complete() - { - if (IsDone) return; - IsDone = true; - _stack.Clear(); - _waitingInstruction = null; - OnComplete?.Invoke(); - } - - /// - /// 处理协程执行过程中发生的异常 - /// - /// 发生的异常 - private void HandleError(Exception ex) - { - IsDone = true; - _stack.Clear(); - _waitingInstruction = null; - OnError?.Invoke(ex); - } -} \ No newline at end of file diff --git a/GFramework.Core/coroutine/CoroutineScheduler.cs b/GFramework.Core/coroutine/CoroutineScheduler.cs deleted file mode 100644 index f33860d..0000000 --- a/GFramework.Core/coroutine/CoroutineScheduler.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System.Collections; -using GFramework.Core.Abstractions.coroutine; - -namespace GFramework.Core.coroutine; - -public class CoroutineScheduler : ICoroutineScheduler -{ - private readonly List _active = new(); - private readonly List _toAdd = new(); - private readonly HashSet _toRemove = new(); - private int? _ownerThreadId; - - public int ActiveCount => _active.Count + _toAdd.Count; - - public void Update(float deltaTime) - { - if (_ownerThreadId == null) - { - _ownerThreadId = Thread.CurrentThread.ManagedThreadId; - } - else if (Thread.CurrentThread.ManagedThreadId != _ownerThreadId) - { - throw new InvalidOperationException( - $"CoroutineScheduler must be updated on same thread. " + - $"Owner: {_ownerThreadId}, Current: {Thread.CurrentThread.ManagedThreadId}"); - } - - // 先将新协程添加到活动列表 - if (_toAdd.Count > 0) - { - _active.AddRange(_toAdd); - _toAdd.Clear(); - } - - // 遍历活动协程,每帧只推进一步 - for (var i = _active.Count - 1; i >= 0; i--) - { - var c = _active[i]; - - // 检查作用域是否仍然活跃 - if (!c.Context.Scope.IsActive) - { - c.Cancel(); - _toRemove.Add(c); - continue; - } - - // 跳过由父协程管理的协程 - if (c.IsManagedByParent) - continue; - - // 更新协程,每帧只推进一步 - ((IYieldInstruction)c).Update(deltaTime); - - // 如果协程完成,标记为待移除 - if (c.IsDone) - _toRemove.Add(c); - } - - // 移除已完成的协程 - if (_toRemove.Count > 0) - { - _active.RemoveAll(c => _toRemove.Contains(c)); - _toRemove.Clear(); - } - } - - internal CoroutineHandle StartCoroutine(IEnumerator routine, CoroutineContext context) - { - var handle = new CoroutineHandle(routine, context, null); - - // 添加到调度队列,协程将在下次 Update 时开始执行 - _toAdd.Add(handle); - - return handle; - } - - internal void RemoveCoroutine(CoroutineHandle handle) => _toRemove.Add(handle); -} \ No newline at end of file diff --git a/GFramework.Core/coroutine/CoroutineScope.cs b/GFramework.Core/coroutine/CoroutineScope.cs deleted file mode 100644 index 06294ae..0000000 --- a/GFramework.Core/coroutine/CoroutineScope.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System.Collections; -using GFramework.Core.Abstractions.coroutine; - -namespace GFramework.Core.coroutine; - -/// -/// 协程作用域管理器,用于管理和控制协程的生命周期 -/// -public sealed class CoroutineScope : ICoroutineScope, IDisposable -{ - private readonly List _children = new(); - private readonly HashSet _runningCoroutines = new(); - private readonly CoroutineScheduler _scheduler; - - private bool _isActive = true; - - /// - /// 初始化新的协程作用域实例 - /// - /// 协程调度器 - /// 作用域名称,如果为null则自动生成 - /// 父级作用域,如果为null则表示顶级作用域 - public CoroutineScope(CoroutineScheduler scheduler, string? name = null, CoroutineScope? parent = null) - { - _scheduler = scheduler ?? throw new ArgumentNullException(nameof(scheduler)); - parent?._children.Add(this); - Name = name ?? $"Scope_{GetHashCode()}"; - } - - /// - /// 获取作用域名称 - /// - public string Name { get; } - - /// - /// 获取当前作用域是否处于活动状态 - /// - public bool IsActive => _isActive; - - /// - /// 取消当前作用域及其所有子作用域中的所有运行中协程 - /// - public void Cancel() - { - if (!_isActive) return; - - _isActive = false; - - // 递归取消所有子作用域 - foreach (var child in _children) - child.Cancel(); - - // 取消当前作用域中所有运行中的协程 - foreach (var handle in _runningCoroutines) - handle.Cancel(); - - _runningCoroutines.Clear(); - } - - /// - /// 启动一个新的协程(接口实现) - /// - /// 要执行的协程枚举器 - /// 协程句柄 - ICoroutineHandle ICoroutineScope.Launch(IEnumerator routine) - { - return Launch(routine); - } - - /// - /// 释放资源并取消所有协程 - /// - public void Dispose() => Cancel(); - - /// - /// 启动一个新的协程 - /// - /// 要执行的协程枚举器 - /// 协程句柄 - public CoroutineHandle Launch(IEnumerator routine) - { - if (!_isActive) - throw new InvalidOperationException($"Scope '{Name}' is not active"); - - var context = new CoroutineContext(this, _scheduler, this); - var handle = _scheduler.StartCoroutine(routine, context); - - // 添加到运行中协程集合 - _runningCoroutines.Add(handle); - - // 注册完成事件以从集合中移除句柄 - handle.OnComplete += () => _runningCoroutines.Remove(handle); - - // 注册错误事件以从集合中移除句柄 - handle.OnError += (_) => _runningCoroutines.Remove(handle); - - return handle; - } -} \ No newline at end of file diff --git a/GFramework.Core/coroutine/CoroutineScopeExtensions.cs b/GFramework.Core/coroutine/CoroutineScopeExtensions.cs deleted file mode 100644 index 7c5293d..0000000 --- a/GFramework.Core/coroutine/CoroutineScopeExtensions.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System.Collections; -using GFramework.Core.Abstractions.coroutine; - -namespace GFramework.Core.coroutine; - -/// -/// 为ICoroutineScope提供扩展方法,支持延迟执行和重复执行协程功能 -/// -public static class CoroutineScopeExtensions -{ - /// - /// 在指定延迟时间后启动一个协程来执行给定的动作 - /// - /// 协程作用域 - /// 延迟时间(秒) - /// 要执行的动作 - /// 协程句柄,可用于控制或停止协程 - public static ICoroutineHandle LaunchDelayed(this ICoroutineScope scope, float delay, Action action) - { - return scope.Launch(DelayedRoutine(delay, action)); - } - - /// - /// 创建一个延迟执行的协程例程 - /// - /// 延迟时间(秒) - /// 要执行的动作,可为空 - /// 协程迭代器 - private static IEnumerator DelayedRoutine(float delay, Action? action) - { - yield return new WaitForSeconds(delay); - action?.Invoke(); - yield break; // 确保协程正确结束 - } - - /// - /// 启动一个重复执行的协程,按照指定间隔时间循环执行给定动作 - /// - /// 协程作用域 - /// 执行间隔时间(秒) - /// 要重复执行的动作 - /// 协程句柄,可用于控制或停止协程 - public static ICoroutineHandle LaunchRepeating(this ICoroutineScope scope, float interval, Action action) - { - return scope.Launch(RepeatingRoutine(interval, action)); - } - - /// - /// 创建一个重复执行的协程例程 - /// - /// 执行间隔时间(秒) - /// 要重复执行的动作 - /// 协程迭代器 - private static IEnumerator RepeatingRoutine(float interval, Action action) - { - // 持续循环等待指定间隔后执行动作 - while (true) - { - yield return new WaitForSeconds(interval); - action?.Invoke(); // 先等待,再执行 - } - } -} \ No newline at end of file diff --git a/GFramework.Core/coroutine/GlobalCoroutineScope.cs b/GFramework.Core/coroutine/GlobalCoroutineScope.cs deleted file mode 100644 index 6e6b913..0000000 --- a/GFramework.Core/coroutine/GlobalCoroutineScope.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System.Collections; -using GFramework.Core.Abstractions.coroutine; - -namespace GFramework.Core.coroutine; - -/// -/// 全局协程作用域管理器,提供全局唯一的协程执行环境 -/// -public static class GlobalCoroutineScope -{ - private static CoroutineScope? _instance; - - /// - /// 获取当前全局协程作用域是否已初始化 - /// - public static bool IsInitialized => _instance != null; - - /// - /// 尝试获取当前全局协程作用域实例 - /// - /// 输出参数,如果初始化则返回协程作用域实例,否则返回null - /// 如果全局协程作用域已初始化则返回true,否则返回false - public static bool TryGetScope(out ICoroutineScope? scope) - { - scope = _instance; - return _instance != null; - } - - /// - /// 初始化全局协程作用域 - /// - /// 用于执行协程的调度器 - public static void Initialize(CoroutineScheduler scheduler) - { - _instance = new CoroutineScope(scheduler, "GlobalScope"); - } - - /// - /// 在全局协程作用域中启动一个协程 - /// - /// 要执行的协程枚举器 - /// 协程句柄,用于控制和监控协程执行 - /// 当全局协程作用域未初始化时抛出 - public static ICoroutineHandle Launch(IEnumerator routine) - { - return _instance == null - ? throw new InvalidOperationException("GlobalCoroutineScope not initialized. Call Initialize() first.") - : _instance.Launch(routine); - } -} \ No newline at end of file diff --git a/GFramework.Core/coroutine/WaitForSeconds.cs b/GFramework.Core/coroutine/WaitForSeconds.cs deleted file mode 100644 index 447e2c3..0000000 --- a/GFramework.Core/coroutine/WaitForSeconds.cs +++ /dev/null @@ -1,37 +0,0 @@ -using GFramework.Core.Abstractions.coroutine; - -namespace GFramework.Core.coroutine; - -/// -/// 表示一个等待指定秒数的时间延迟指令 -/// -/// 需要等待的秒数 -public class WaitForSeconds(float seconds) : IYieldInstruction -{ - private float _elapsed; - - /// - /// 获取当前等待是否已完成 - /// - public bool IsDone { get; private set; } - - /// - /// 更新时间进度 - /// - /// 自上次更新以来经过的时间(秒) - public void Update(float deltaTime) - { - if (IsDone) return; - _elapsed += deltaTime; - if (_elapsed >= seconds) IsDone = true; - } - - /// - /// 重置等待状态到初始状态 - /// - public void Reset() - { - _elapsed = 0; - IsDone = false; - } -} \ No newline at end of file diff --git a/GFramework.Core/coroutine/WaitUntil.cs b/GFramework.Core/coroutine/WaitUntil.cs deleted file mode 100644 index 08151a8..0000000 --- a/GFramework.Core/coroutine/WaitUntil.cs +++ /dev/null @@ -1,33 +0,0 @@ -using GFramework.Core.Abstractions.coroutine; - -namespace GFramework.Core.coroutine; - -/// -/// 等待直到指定条件满足的协程等待指令 -/// -/// 用于判断等待是否完成的条件函数 -public class WaitUntil(Func predicate) : IYieldInstruction -{ - /// - /// 获取当前等待指令是否已完成 - /// - public bool IsDone { get; private set; } - - /// - /// 更新等待状态 - /// - /// 时间增量 - public void Update(float deltaTime) - { - // 每次更新都重新评估条件,但一旦完成就保持完成状态 - if (!IsDone) IsDone = predicate(); - } - - /// - /// 重置等待指令状态 - /// - public void Reset() - { - IsDone = false; - } -} \ No newline at end of file diff --git a/GFramework.Core/coroutine/WaitWhile.cs b/GFramework.Core/coroutine/WaitWhile.cs deleted file mode 100644 index 715e210..0000000 --- a/GFramework.Core/coroutine/WaitWhile.cs +++ /dev/null @@ -1,33 +0,0 @@ -using GFramework.Core.Abstractions.coroutine; - -namespace GFramework.Core.coroutine; - -/// -/// 等待条件为假时继续执行的协程等待指令 -/// -/// 用于判断是否继续等待的布尔函数委托 -public class WaitWhile(Func predicate) : IYieldInstruction -{ - /// - /// 获取当前等待指令是否已完成 - /// - public bool IsDone { get; private set; } - - /// - /// 更新等待状态 - /// - /// 时间增量 - public void Update(float deltaTime) - { - // 当前未完成时,检查谓词条件来更新完成状态 - if (!IsDone) IsDone = !predicate(); - } - - /// - /// 重置等待指令状态 - /// - public void Reset() - { - IsDone = false; - } -} \ No newline at end of file diff --git a/GFramework.Core/coroutine/协程系统改进计划.md b/GFramework.Core/coroutine/协程系统改进计划.md deleted file mode 100644 index f56c015..0000000 --- a/GFramework.Core/coroutine/协程系统改进计划.md +++ /dev/null @@ -1,494 +0,0 @@ -# 协程系统改进计划 - -## 文档信息 - -- 创建日期:2026-01-20 -- 版本:1.1 -- 负责模块:GFramework.Game.coroutines -- 状态:执行中 - ---- - -## 1. 问题清单与优先级 - -### 1.1 高优先级问题(P0) - -#### P0-1: 类型安全问题 - -**位置**:`CoroutineScopeExtensions.cs:19, 41` - -**问题描述**: - -- `LaunchDelayed` 和 `LaunchRepeating` 方法中强制转换 `(CoroutineScope)scope` -- 违反接口抽象原则,违反里氏替换原则 -- 如果传入其他 ICoroutineScope 实现会导致运行时异常 - -**影响**:破坏API设计,降低可维护性 - -**解决方案**: - -- 修改 `ICoroutineScope` 接口,添加核心启动方法 -- 或通过扩展方法模式,要求传入 `CoroutineScope` 具体类型 - ---- - -#### P0-2: 并发安全问题 - -**位置**:`CoroutineScheduler.cs:8-10`, `CoroutineHandle.cs` - -**问题描述**: - -- 所有集合(List、HashSet)无线程同步机制 -- 多线程环境下可能导致数据竞争 -- 可能导致索引越界、迭代器异常等 - -**影响**:在多线程环境下会导致崩溃和数据不一致 - -**解决方案**: - -- 方案A:明确协程系统为单线程设计,添加文档说明和断言 -- 方案B:添加线程安全机制(lock/ReaderWriterLockSlim/ConcurrentBag等) - ---- - -#### P0-3: 状态不一致风险 - -**位置**:`CoroutineHandle.cs:110-117` - -**问题描述**: - -- `Cancel()` 方法不触发 `OnComplete` 事件 -- 导致依赖 `OnComplete` 的逻辑无法正确清理资源 -- 可能造成内存泄漏 - -**影响**:资源泄漏,行为不符合预期 - -**解决方案**: - -- 取消时触发 `OnComplete` 事件,或添加 `OnCancelled` 专用事件 - ---- - -### 1.2 中优先级问题(P1) - -#### P1-1: API设计不完整 - -**位置**:`ICoroutineScheduler.cs`, `CoroutineScheduler.cs:44` - -**问题描述**: - -- `ICoroutineScheduler` 只有 `Update` 方法,缺少核心功能接口 -- `StartCoroutine` 是 internal,外部无法直接使用 -- `GlobalCoroutineScope` 抛异常控制未初始化状态 - -**影响**:API不友好,扩展性受限 - -**解决方案**: - -- 扩展接口,添加 `StartCoroutine`、`CancelCoroutine` 等方法 -- 使用 TryGet 模式替代异常控制流程 - ---- - -#### P1-2: YieldInstruction累积误差 - -**位置**:`WaitForSeconds.cs:20` - -**问题描述**: - -- `WaitForSeconds` 重复调用 `Update` 会导致 `_elapsed` 无限累加 -- 如果不重置状态,无法复用实例 - -**影响**:行为不符合预期,难以调试 - -**解决方案**: - -- 添加 `Reset()` 方法 -- 或标记为一次性使用,文档说明 - ---- - -#### P1-3: 性能优化空间 - -**位置**:`CoroutineScheduler.cs:39`, `CoroutineScope.cs:32,35` - -**问题描述**: - -- 每帧 `RemoveAll` 遍历整个列表,O(n)复杂度 -- `ToList()` 在循环中重复创建新列表 -- 存在潜在的GC压力 - -**影响**:高并发下性能下降 - -**解决方案**: - -- 使用倒序遍历+标记删除 -- 避免不必要的集合复制 - ---- - -### 1.3 低优先级问题(P2) - -#### P2-1: 缺少异步支持 - -**问题描述**: - -- 不支持 async/await 模式 -- 无法与现代异步生态集成 - -**解决方案**: - -- 提供 `GetAwaiter()` 方法 -- 实现 `INotifyCompletion` 接口 - ---- - -#### P2-2: 缺少协程池 - -**问题描述**: - -- 每次创建新协程实例 -- 高频使用场景下GC压力大 - -**解决方案**: - -- 实现协程池 -- 复用 `CoroutineHandle` 实例 - ---- - -#### P2-3: 缺少状态查询接口 - -**问题描述**: - -- 外部无法查询协程详细状态 -- 只能通过事件被动通知 - -**解决方案**: - -- 添加枚举状态:Running/Paused/Cancelled/Error/Completed - ---- - -#### P2-4: ProcessYieldValue case顺序优化 - -**位置**:`CoroutineHandle.cs:66-84` - -**问题描述**: - -- null 处理在第一个,应该移到最后 -- 虽然 CoroutineHandle 已在 IYieldInstruction 前,但可以更清晰 - -**解决方案**: - -- 调整 case 顺序:CoroutineHandle → IEnumerator → IYieldInstruction → null - ---- - -## 2. 改进计划 - -### 2.1 第一阶段:关键问题修复(预计2-3天) - -#### 任务1.1: 修复类型安全问题 - -- [x] 审计所有强制类型转换 -- [x] 扩展 `ICoroutineScope` 接口 -- [x] 重构 `CoroutineScopeExtensions` 移除类型转换 -- [ ] 编写单元测试验证 - -**验收标准**: - -- 无运行时类型转换 -- 所有实现都通过接口调用 -- 单元测试通过 - -**已完成**: - -- 创建新接口:`ICoroutineHandle`、`ICoroutineContext` -- 扩展 `ICoroutineScope` 添加 `Launch()` 方法 -- 重构 `CoroutineScopeExtensions` 使用接口方法 - ---- - -#### 任务1.2: 解决并发安全问题 - -- [x] 确定线程策略(单线程 vs 多线程) -- [x] 添加线程安全机制(如需要) -- [x] 添加线程安全断言/文档说明 -- [ ] 编写并发测试用例 - -**验收标准**: - -- 明确线程模型文档 -- 通过并发压力测试 -- 无数据竞争检测警告 - -**已完成**: - -- 确定采用单线程设计(游戏主线程) -- 添加线程ID检查,禁止跨线程调用 -- 在 `CoroutineScheduler.Update()` 中添加线程验证 - ---- - -#### 任务1.3: 统一状态变更事件 - -- [x] 设计取消/完成事件触发逻辑 -- [x] 实现 `OnCancelled` 专用事件 -- [x] 更新文档说明事件触发时机 -- [ ] 编写状态转换测试 - -**验收标准**: - -- 所有状态变更都有事件通知 -- 资源清理逻辑完整 -- 单元测试覆盖所有状态转换 - -**已完成**: - -- 修改 `CoroutineHandle.Cancel()` 触发 `OnComplete` 事件 -- 统一所有状态变更都通过事件通知 -- 取消和完成都会触发 `OnComplete` 事件 - ---- - -### 2.2 第二阶段:API完善与优化(预计2天) - -#### 任务2.1: 扩展核心接口 - -- [x] 扩展 `ICoroutineScheduler` 接口 -- [x] 添加 `ActiveCount` 属性 -- [x] 修改 `GlobalCoroutineScope` 使用 TryGet 模式 -- [ ] 更新接口文档 - -**验收标准**: - -- 接口完整性检查通过 -- 向后兼容 -- 示例代码可运行 - -**已完成**: - -- 扩展 `ICoroutineScheduler` 添加 `ActiveCount` 属性 -- 修改 `GlobalCoroutineScope` 添加 `IsInitialized` 和 `TryGetScope` -- 优化 `ActiveCount` 计算包含待添加的协程 - ---- - -#### 任务2.2: YieldInstruction 状态管理 - -- [x] 为所有 YieldInstruction 添加 `Reset()` 方法 -- [x] 更新 `WaitForSeconds` 状态管理 -- [ ] 文档说明可复用性 -- [ ] 编写状态重置测试 - -**验收标准**: - -- 所有 YieldInstruction 可正确重置 -- 重复使用无累积误差 -- 单元测试通过 - -**已完成**: - -- 为 `WaitForSeconds` 添加 `Reset()` 方法 -- 为 `WaitUntil` 添加 `Reset()` 方法 -- 为 `WaitWhile` 添加 `Reset()` 方法 -- 重置后可复用,无累积误差 - ---- - -#### 任务2.3: 性能优化 - -- [x] 优化 `CoroutineScheduler` 删除逻辑 -- [x] 减少集合拷贝操作 -- [ ] 性能基准测试 -- [ ] 对比优化前后性能指标 - -**验收标准**: - -- 删除性能提升 > 50% -- GC分配减少 > 30% -- 通过性能基准测试 - -**已完成**: - -- 移除 `CoroutineScope.Cancel()` 中不必要的 `ToList()` 调用 -- 减少GC分配 - ---- - -### 2.3 第三阶段:功能增强(预计3天) - -#### 任务3.1: 添加异步支持 - -- [ ] 为 `CoroutineHandle` 实现 `GetAwaiter()` -- [ ] 实现 `ICoroutineAwaiter` -- [ ] 编写 async/await 示例 -- [ ] 单元测试异步场景 - -**验收标准**: - -- 支持标准 async/await 语法 -- 异常正确传播 -- 单元测试覆盖 - ---- - -#### 任务3.2: 协程状态查询 - -- [ ] 定义协程状态枚举 -- [ ] 添加 `State` 属性到 `CoroutineHandle` -- [ ] 更新 `ICoroutineScope` 支持状态查询 -- [ ] 文档和示例更新 - -**验收标准**: - -- 可查询所有协程状态 -- 状态转换正确 -- 单元测试通过 - ---- - -#### 任务3.3: 协程池(可选) - -- [ ] 设计协程池接口 -- [ ] 实现 `CoroutineHandle` 对象池 -- [ ] 性能测试对比 -- [ ] 文档说明使用场景 - -**验收标准**: - -- 高频场景下性能提升明显 -- 内存占用减少 -- 单元测试通过 - ---- - -### 2.4 第四阶段:测试与文档(预计2天) - -#### 任务4.1: 完善单元测试 - -- [ ] 补充覆盖所有核心功能 -- [ ] 添加边界条件测试 -- [ ] 并发场景测试 -- [ ] 性能回归测试 - -**验收标准**: - -- 代码覆盖率 > 80% -- 所有关键路径有测试 -- 测试通过率 100% - ---- - -#### 任务4.2: 更新文档 - -- [ ] 更新API文档 -- [ ] 编写使用指南 -- [ ] 添加最佳实践 -- [ ] 更新架构图 - -**验收标准**: - -- 文档完整准确 -- 示例代码可运行 -- 架构图清晰 - ---- - -## 3. 风险评估 - -| 风险 | 可能性 | 影响 | 缓解措施 | -|--------|-----|----|---------------| -| 破坏向后兼容 | 中 | 高 | 保持接口稳定,仅扩展不修改 | -| 性能回退 | 低 | 中 | 每阶段做性能基准测试 | -| 引入新Bug | 中 | 中 | 充分单元测试,代码审查 | -| 延期交付 | 低 | 低 | 按优先级分阶段交付 | - ---- - -## 4. 验收标准 - -### 功能完整性 - -- [ ] 所有高优先级问题修复 -- [ ] 核心API完善 -- [ ] 支持异步模式(如选择实现) - -### 性能指标 - -- [ ] 协程启动延迟 < 1ms -- [ ] 调度器每帧处理时间 < 0.5ms(1000协程) -- [ ] GC分配减少 > 30% - -### 质量指标 - -- [ ] 单元测试覆盖率 > 80% -- [ ] 无编译警告 -- [ ] 代码审查通过 - -### 文档完整性 - -- [ ] API文档完整 -- [ ] 使用指南清晰 -- [ ] 示例代码可运行 - ---- - -## 5. 实施建议 - -1. **优先级驱动**:先解决高优先级问题,确保系统稳定性 -2. **增量交付**:按阶段交付,每个阶段都可独立使用 -3. **充分测试**:每阶段都进行充分测试,避免引入新问题 -4. **向后兼容**:尽量保持API向后兼容,减少迁移成本 -5. **性能基准**:建立性能基准,量化改进效果 - ---- - -## 6. 附录 - -### 6.1 文件清单 - -``` -GFramework.Game/coroutine/ -├── Abstractions/ -│ ├── ICoroutineScheduler.cs -│ ├── ICoroutineScope.cs -│ ├── ICoroutineSystem.cs -│ └── IYieldInstruction.cs -├── CoroutineContext.cs -├── CoroutineHandle.cs -├── CoroutineScheduler.cs -├── CoroutineScope.cs -├── CoroutineScopeExtensions.cs -├── CoroutineSystem.cs -├── GlobalCoroutineScope.cs -├── WaitForSeconds.cs -├── WaitUntil.cs -└── WaitWhile.cs -``` - -### 6.2 相关依赖 - -- GFramework.Core.Abstractions -- System.Collections -- .NET Standard 2.0+ - -### 6.3 参考资料 - -- Unity Coroutine Implementation -- Kotlin Coroutines -- C# Async/Await Best Practices - ---- - -## 7. 变更历史 - -| 版本 | 日期 | 变更内容 | 作者 | -|-----|------------|-------------------|----------| -| 1.0 | 2026-01-20 | 初始版本 | opencode | -| 1.1 | 2026-01-20 | 完成P0高优先级和P1中优先级任务 | opencode | - ---- - -**文档结束** diff --git a/GFramework.Game/coroutine/CoroutineSystem.cs b/GFramework.Game/coroutine/CoroutineSystem.cs deleted file mode 100644 index bf718e9..0000000 --- a/GFramework.Game/coroutine/CoroutineSystem.cs +++ /dev/null @@ -1,29 +0,0 @@ -using GFramework.Core.Abstractions.coroutine; -using GFramework.Core.coroutine; -using GFramework.Core.system; - -namespace GFramework.Game.coroutine; - -/// -/// 协程系统类,负责管理和更新协程调度器 -/// -/// 协程调度器实例 -public class CoroutineSystem(CoroutineScheduler scheduler) : AbstractSystem, ICoroutineSystem -{ - /// - /// 更新协程系统,驱动协程调度器执行协程逻辑 - /// - /// 时间间隔,表示自上一帧以来经过的时间(秒) - public void OnUpdate(float deltaTime) - { - // 更新协程调度器,处理等待中的协程 - scheduler.Update(deltaTime); - } - - /// - /// 初始化协程系统 - /// - protected override void OnInit() - { - } -} \ No newline at end of file