From 7e5036ae76dc24d4a2352af8ad36b24d0fc20cc6 Mon Sep 17 00:00:00 2001 From: GeWuYou <95328647+GeWuYou@users.noreply.github.com> Date: Mon, 26 Jan 2026 11:45:13 +0800 Subject: [PATCH] =?UTF-8?q?test(coroutine):=20=E6=B7=BB=E5=8A=A0=E5=8D=8F?= =?UTF-8?q?=E7=A8=8B=E6=89=A9=E5=B1=95=E6=96=B9=E6=B3=95=E5=92=8C=E7=AD=89?= =?UTF-8?q?=E5=BE=85=E6=89=80=E6=9C=89=E5=8D=8F=E7=A8=8B=E7=9A=84=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加 CoroutineExtensionsTests 测试类,覆盖 RepeatEvery、ExecuteAfter、Sequence、ParallelCoroutines 和 WaitForSecondsWithProgress 方法 - 添加 WaitForAllCoroutinesTests 测试类,验证 WaitForAllCoroutines 的初始化、IsDone 属性、空句柄处理等功能 - 实现协程调度器集成测试,验证多协程并发执行场景 - 添加边界条件测试,包括空数组、null 参数、负数参数等情况 - 实现异常处理和特殊状态测试,如暂停恢复、终止协程等场景 --- .../coroutine/CoroutineExtensionsTests.cs | 564 ++++++++++++++++++ .../coroutine/WaitForAllCoroutinesTests.cs | 521 ++++++++++++++++ 2 files changed, 1085 insertions(+) create mode 100644 GFramework.Core.Tests/coroutine/CoroutineExtensionsTests.cs create mode 100644 GFramework.Core.Tests/coroutine/WaitForAllCoroutinesTests.cs diff --git a/GFramework.Core.Tests/coroutine/CoroutineExtensionsTests.cs b/GFramework.Core.Tests/coroutine/CoroutineExtensionsTests.cs new file mode 100644 index 0000000..2d1f3e4 --- /dev/null +++ b/GFramework.Core.Tests/coroutine/CoroutineExtensionsTests.cs @@ -0,0 +1,564 @@ +using GFramework.Core.Abstractions.coroutine; +using GFramework.Core.coroutine; +using GFramework.Core.coroutine.extensions; +using GFramework.Core.coroutine.instructions; +using NUnit.Framework; + +namespace GFramework.Core.Tests.coroutine; + +/// +/// 协程扩展方法的单元测试类 +/// 测试内容包括: +/// - RepeatEvery方法 +/// - ExecuteAfter方法 +/// - Sequence方法 +/// - ParallelCoroutines方法 +/// - WaitForSecondsWithProgress方法 +/// +[TestFixture] +public class CoroutineExtensionsTests +{ + /// + /// 验证RepeatEvery应该返回有效的协程 + /// + [Test] + public void RepeatEvery_Should_Return_Valid_Coroutine() + { + var callCount = 0; + var coroutine = CoroutineExtensions.RepeatEvery(0.1, () => callCount++, 3); + + Assert.That(coroutine, Is.InstanceOf>()); + } + + /// + /// 验证RepeatEvery应该执行指定次数 + /// + [Test] + public void RepeatEvery_Should_Execute_Specified_Times() + { + var callCount = 0; + var coroutine = CoroutineExtensions.RepeatEvery(0.1, () => callCount++, 3); + + while (coroutine.MoveNext()) + { + coroutine.Current.Update(0.1); + } + + Assert.That(callCount, Is.EqualTo(3)); + } + + /// + /// 验证RepeatEvery应该无限执行当count为null + /// + [Test] + public void RepeatEvery_Should_Execute_Forever_When_Count_Is_Null() + { + var callCount = 0; + var coroutine = CoroutineExtensions.RepeatEvery(0.1, () => callCount++); + + for (var i = 0; i < 5; i++) + { + Assert.That(coroutine.MoveNext(), Is.True); + coroutine.Current.Update(0.1); + } + + Assert.That(callCount, Is.EqualTo(5)); + Assert.That(coroutine.MoveNext(), Is.True); + } + + /// + /// 验证RepeatEvery应该处理负数count + /// + [Test] + public void RepeatEvery_Should_Handle_Negative_Count() + { + var callCount = 0; + var coroutine = CoroutineExtensions.RepeatEvery(0.1, () => callCount++, -1); + + Assert.That(coroutine.MoveNext(), Is.False); + Assert.That(callCount, Is.EqualTo(0)); + } + + /// + /// 验证RepeatEvery应该处理零count + /// + [Test] + public void RepeatEvery_Should_Handle_Zero_Count() + { + var callCount = 0; + var coroutine = CoroutineExtensions.RepeatEvery(0.1, () => callCount++, 0); + + Assert.That(coroutine.MoveNext(), Is.False); + Assert.That(callCount, Is.EqualTo(0)); + } + + /// + /// 验证RepeatEvery应该处理null action + /// + [Test] + public void RepeatEvery_Should_Handle_Null_Action() + { + var coroutine = CoroutineExtensions.RepeatEvery(0.1, null, 3); + + Assert.DoesNotThrow(() => + { + while (coroutine.MoveNext()) + { + coroutine.Current.Update(0.1); + } + }); + } + + /// + /// 验证ExecuteAfter应该返回有效的协程 + /// + [Test] + public void ExecuteAfter_Should_Return_Valid_Coroutine() + { + var called = false; + var coroutine = CoroutineExtensions.ExecuteAfter(1.0, () => called = true); + + Assert.That(coroutine, Is.InstanceOf>()); + } + + /// + /// 验证ExecuteAfter应该在延迟后执行action + /// + [Test] + public void ExecuteAfter_Should_Execute_Action_After_Delay() + { + var called = false; + var coroutine = CoroutineExtensions.ExecuteAfter(1.0, () => called = true); + + Assert.That(called, Is.False); + + coroutine.MoveNext(); + coroutine.Current.Update(0.5); + Assert.That(called, Is.False); + + coroutine.Current.Update(0.5); + Assert.That(called, Is.False); + + Assert.That(coroutine.MoveNext(), Is.False); + Assert.That(called, Is.True); + } + + /// + /// 验证ExecuteAfter应该处理零延迟 + /// + [Test] + public void ExecuteAfter_Should_Handle_Zero_Delay() + { + var called = false; + var coroutine = CoroutineExtensions.ExecuteAfter(0, () => called = true); + + coroutine.MoveNext(); + + Assert.That(coroutine.MoveNext(), Is.False); + Assert.That(called, Is.True); + } + + /// + /// 验证ExecuteAfter应该处理负数延迟 + /// + [Test] + public void ExecuteAfter_Should_Handle_Negative_Delay() + { + var called = false; + var coroutine = CoroutineExtensions.ExecuteAfter(-1.0, () => called = true); + + Assert.That(coroutine.MoveNext(), Is.False); + Assert.That(called, Is.False); + } + + /// + /// 验证ExecuteAfter应该处理null action + /// + [Test] + public void ExecuteAfter_Should_Handle_Null_Action() + { + var coroutine = CoroutineExtensions.ExecuteAfter(1.0, null); + + Assert.DoesNotThrow(() => + { + coroutine.MoveNext(); + coroutine.Current.Update(1.0); + }); + } + + /// + /// 验证Sequence应该返回有效的协程 + /// + [Test] + public void Sequence_Should_Return_Valid_Coroutine() + { + var coroutine1 = CreateSimpleCoroutine(); + var coroutine2 = CreateSimpleCoroutine(); + var sequence = CoroutineExtensions.Sequence(coroutine1, coroutine2); + + Assert.That(sequence, Is.InstanceOf>()); + } + + /// + /// 验证Sequence应该按顺序执行多个协程 + /// + [Test] + public void Sequence_Should_Execute_Coroutines_In_Order() + { + var executionOrder = new List(); + var coroutine1 = CreateCoroutineWithCallback(1, () => executionOrder.Add(1)); + var coroutine2 = CreateCoroutineWithCallback(2, () => executionOrder.Add(2)); + var coroutine3 = CreateCoroutineWithCallback(3, () => executionOrder.Add(3)); + + var sequence = CoroutineExtensions.Sequence(coroutine1, coroutine2, coroutine3); + + while (sequence.MoveNext()) + { + sequence.Current.Update(0.1); + } + + Assert.That(executionOrder, Is.EqualTo(new List { 1, 2, 3 })); + } + + /// + /// 验证Sequence应该处理空协程数组 + /// + [Test] + public void Sequence_Should_Handle_Empty_Coroutines() + { + var sequence = CoroutineExtensions.Sequence(); + + Assert.That(sequence.MoveNext(), Is.False); + } + + /// + /// 验证Sequence应该处理单个协程 + /// + [Test] + public void Sequence_Should_Handle_Single_Coroutine() + { + var coroutine1 = CreateSimpleCoroutine(); + var sequence = CoroutineExtensions.Sequence(coroutine1); + + Assert.That(sequence.MoveNext(), Is.False); + } + + /// + /// 验证Sequence应该处理null协程 + /// + [Test] + public void Sequence_Should_Handle_Null_Coroutine() + { + var coroutine1 = CreateSimpleCoroutine(); + var sequence = CoroutineExtensions.Sequence(coroutine1, null!); + + Assert.Throws(() => + { + while (sequence.MoveNext()) + { + sequence.Current.Update(0.1); + } + }); + } + + /// + /// 验证ParallelCoroutines应该返回有效的协程 + /// + [Test] + public void ParallelCoroutines_Should_Return_Valid_Coroutine() + { + var timeSource = new TestTimeSource(); + var scheduler = new CoroutineScheduler(timeSource, instanceId: 1); + var coroutine1 = CreateSimpleCoroutine(); + var coroutine2 = CreateSimpleCoroutine(); + + var parallel = scheduler.ParallelCoroutines(coroutine1, coroutine2); + + Assert.That(parallel, Is.InstanceOf>()); + } + + /// + /// 验证ParallelCoroutines应该并行执行多个协程 + /// + [Test] + public void ParallelCoroutines_Should_Execute_Coroutines_In_Parallel() + { + var timeSource = new TestTimeSource(); + var scheduler = new CoroutineScheduler(timeSource, instanceId: 1); + + var executionCounts = new Dictionary { { 1, 0 }, { 2, 0 }, { 3, 0 } }; + var coroutine1 = CreateDelayedCoroutine(() => executionCounts[1]++, 0.5); + var coroutine2 = CreateDelayedCoroutine(() => executionCounts[2]++, 0.5); + var coroutine3 = CreateDelayedCoroutine(() => executionCounts[3]++, 0.5); + + var parallel = scheduler.ParallelCoroutines(coroutine1, coroutine2, coroutine3); + + parallel.MoveNext(); + + Assert.That(scheduler.ActiveCoroutineCount, Is.GreaterThan(0)); + + while (scheduler.ActiveCoroutineCount > 0) + { + scheduler.Update(); + } + + Assert.That(executionCounts[1], Is.EqualTo(1)); + Assert.That(executionCounts[2], Is.EqualTo(1)); + Assert.That(executionCounts[3], Is.EqualTo(1)); + } + + /// + /// 验证ParallelCoroutines应该处理空数组 + /// + [Test] + public void ParallelCoroutines_Should_Handle_Empty_Array() + { + var timeSource = new TestTimeSource(); + var scheduler = new CoroutineScheduler(timeSource, instanceId: 1); + + var parallel = scheduler.ParallelCoroutines(); + + Assert.That(parallel.MoveNext(), Is.False); + } + + /// + /// 验证ParallelCoroutines应该处理null数组 + /// + [Test] + public void ParallelCoroutines_Should_Handle_Null_Array() + { + var timeSource = new TestTimeSource(); + var scheduler = new CoroutineScheduler(timeSource, instanceId: 1); + + var parallel = scheduler.ParallelCoroutines(null); + + Assert.That(parallel.MoveNext(), Is.False); + } + + /// + /// 验证WaitForSecondsWithProgress应该返回有效的协程 + /// + [Test] + public void WaitForSecondsWithProgress_Should_Return_Valid_Coroutine() + { + var progressValues = new List(); + var coroutine = CoroutineExtensions.WaitForSecondsWithProgress(1.0, progressValues.Add); + + Assert.That(coroutine, Is.InstanceOf>()); + } + + /// + /// 验证WaitForSecondsWithProgress应该在指定时间后完成 + /// + [Test] + public void WaitForSecondsWithProgress_Should_Complete_After_Duration() + { + var coroutine = CoroutineExtensions.WaitForSecondsWithProgress(1.0, null); + + coroutine.MoveNext(); + coroutine.Current.Update(0.5); + Assert.That(coroutine.Current.IsDone, Is.False); + + coroutine.Current.Update(0.5); + Assert.That(coroutine.Current.IsDone, Is.True); + } + + /// + /// 验证WaitForSecondsWithProgress应该调用进度回调 + /// + [Test] + public void WaitForSecondsWithProgress_Should_Call_Progress_Callback() + { + var progressValues = new List(); + var coroutine = CoroutineExtensions.WaitForSecondsWithProgress(1.0, progressValues.Add); + + coroutine.MoveNext(); + + while (!coroutine.Current.IsDone) + { + coroutine.Current.Update(0.1); + } + + Assert.That(progressValues.Count, Is.GreaterThan(0)); + Assert.That(progressValues[0], Is.EqualTo(0.0f).Within(0.01f)); + Assert.That(progressValues[^1], Is.EqualTo(1.0f).Within(0.01f)); + } + + /// + /// 验证WaitForSecondsWithProgress应该处理零时间 + /// + [Test] + public void WaitForSecondsWithProgress_Should_Handle_Zero_Duration() + { + var progressValues = new List(); + var coroutine = CoroutineExtensions.WaitForSecondsWithProgress(0, progressValues.Add); + + Assert.That(coroutine.MoveNext(), Is.False); + Assert.That(progressValues, Is.EqualTo(new List { 1.0f })); + } + + /// + /// 验证WaitForSecondsWithProgress应该处理负数时间 + /// + [Test] + public void WaitForSecondsWithProgress_Should_Handle_Negative_Duration() + { + var progressValues = new List(); + var coroutine = CoroutineExtensions.WaitForSecondsWithProgress(-1.0, progressValues.Add); + + Assert.That(coroutine.MoveNext(), Is.False); + Assert.That(progressValues, Is.EqualTo(new List { 1.0f })); + } + + /// + /// 验证WaitForSecondsWithProgress应该处理null回调 + /// + [Test] + public void WaitForSecondsWithProgress_Should_Handle_Null_Callback() + { + var coroutine = CoroutineExtensions.WaitForSecondsWithProgress(1.0, null); + + coroutine.MoveNext(); + coroutine.Current.Update(0.5); + Assert.That(coroutine.Current.IsDone, Is.False); + + coroutine.Current.Update(0.5); + Assert.That(coroutine.Current.IsDone, Is.True); + } + + /// + /// 验证RepeatEvery应该使用Delay指令 + /// + [Test] + public void RepeatEvery_Should_Use_Delay_Instruction() + { + var coroutine = CoroutineExtensions.RepeatEvery(0.5, () => { }, 1); + + coroutine.MoveNext(); + Assert.That(coroutine.Current, Is.InstanceOf()); + } + + /// + /// 验证ExecuteAfter应该使用Delay指令 + /// + [Test] + public void ExecuteAfter_Should_Use_Delay_Instruction() + { + var coroutine = CoroutineExtensions.ExecuteAfter(1.0, () => { }); + + coroutine.MoveNext(); + Assert.That(coroutine.Current, Is.InstanceOf()); + } + + /// + /// 验证Sequence应该清理已完成的协程 + /// + [Test] + public void Sequence_Should_Dispose_Completed_Coroutines() + { + var coroutine1 = CreateSimpleCoroutine(); + var coroutine2 = CreateSimpleCoroutine(); + + var sequence = CoroutineExtensions.Sequence(coroutine1, coroutine2); + + while (sequence.MoveNext()) + { + sequence.Current.Update(0.1); + } + + Assert.That(sequence.MoveNext(), Is.False); + } + + /// + /// 验证RepeatEvery的间隔时间 + /// + [Test] + public void RepeatEvery_Should_Respect_Interval() + { + var callTimes = new List(); + var coroutine = CoroutineExtensions.RepeatEvery(0.5, () => callTimes.Add(0), 3); + + var currentTime = 0.0; + while (coroutine.MoveNext()) + { + coroutine.Current.Update(0.5); + currentTime += 0.5; + } + + Assert.That(callTimes.Count, Is.EqualTo(3)); + } + + /// + /// 验证ExecuteAfter的延迟时间 + /// + [Test] + public void ExecuteAfter_Should_Respect_Delay() + { + var executed = false; + var currentTime = 0.0; + var coroutine = CoroutineExtensions.ExecuteAfter(1.5, () => executed = true); + + coroutine.MoveNext(); + coroutine.Current.Update(0.5); + currentTime += 0.5; + Assert.That(executed, Is.False); + + coroutine.Current.Update(1.0); + currentTime += 1.0; + Assert.That(executed, Is.False); + + Assert.That(coroutine.MoveNext(), Is.False); + Assert.That(executed, Is.True); + Assert.That(currentTime, Is.EqualTo(1.5)); + } + + /// + /// 创建简单的立即完成协程 + /// + private IEnumerator CreateSimpleCoroutine() + { + yield break; + } + + /// + /// 创建带回调的协程 + /// + private IEnumerator CreateCoroutineWithCallback(int id, Action callback) + { + yield return new WaitOneFrame(); + callback(); + } + + /// + /// 创建计数协程 + /// + private IEnumerator CreateCountingCoroutine(int id, Action callback) + { + yield return new WaitOneFrame(); + callback(); + } + + /// + /// 创建延迟协程 + /// + private IEnumerator CreateDelayedCoroutine(Action callback, double delay) + { + yield return new Delay(delay); + callback(); + } + + /// + /// 测试用时间源类 + /// + private class TestTimeSource : ITimeSource + { + public double CurrentTime { get; private set; } + public double DeltaTime { get; private set; } + + public void Update() + { + DeltaTime = 0.016; + CurrentTime += DeltaTime; + } + } +} diff --git a/GFramework.Core.Tests/coroutine/WaitForAllCoroutinesTests.cs b/GFramework.Core.Tests/coroutine/WaitForAllCoroutinesTests.cs new file mode 100644 index 0000000..6b94f05 --- /dev/null +++ b/GFramework.Core.Tests/coroutine/WaitForAllCoroutinesTests.cs @@ -0,0 +1,521 @@ +using GFramework.Core.Abstractions.coroutine; +using GFramework.Core.coroutine; +using GFramework.Core.coroutine.extensions; +using GFramework.Core.coroutine.instructions; +using NUnit.Framework; + +namespace GFramework.Core.Tests.coroutine; + +/// +/// WaitForAllCoroutines的单元测试类 +/// 测试内容包括: +/// - 初始化和基本功能 +/// - IsDone属性行为 +/// - 空句柄集合处理 +/// - 单个协程处理 +/// - 多个协程处理 +/// - 与CoroutineScheduler集成 +/// +[TestFixture] +public class WaitForAllCoroutinesTests +{ + /// + /// 验证WaitForAllCoroutines初始状态为未完成 + /// + [Test] + public void WaitForAllCoroutines_Should_Not_Be_Done_Initially_With_Running_Coroutines() + { + var timeSource = new TestTimeSource(); + var scheduler = new CoroutineScheduler(timeSource, instanceId: 1); + var coroutine1 = CreateDelayedCoroutine(() => { }, 1.0); + var coroutine2 = CreateDelayedCoroutine(() => { }, 1.0); + + var handles = new List + { + scheduler.Run(coroutine1), + scheduler.Run(coroutine2) + }; + + var wait = new WaitForAllCoroutines(scheduler, handles); + + Assert.That(wait.IsDone, Is.False); + } + + /// + /// 验证WaitForAllCoroutines应该在所有协程完成后完成 + /// + [Test] + public void WaitForAllCoroutines_Should_Be_Done_When_All_Coroutines_Complete() + { + var timeSource = new TestTimeSource(); + var scheduler = new CoroutineScheduler(timeSource, instanceId: 1); + var coroutine1 = CreateSimpleCoroutine(); + var coroutine2 = CreateSimpleCoroutine(); + + var handles = new List + { + scheduler.Run(coroutine1), + scheduler.Run(coroutine2) + }; + + var wait = new WaitForAllCoroutines(scheduler, handles); + + scheduler.Update(); + scheduler.Update(); + + Assert.That(wait.IsDone, Is.True); + } + + /// + /// 验证WaitForAllCoroutines应该在所有协程完成后完成(使用Delay) + /// + [Test] + public void WaitForAllCoroutines_Should_Wait_For_All_Delayed_Coroutines() + { + var timeSource = new TestTimeSource(); + var scheduler = new CoroutineScheduler(timeSource, instanceId: 1); + + var executionCount = 0; + var coroutine1 = CreateDelayedCoroutine(() => executionCount++, 1.0); + var coroutine2 = CreateDelayedCoroutine(() => executionCount++, 1.0); + var coroutine3 = CreateDelayedCoroutine(() => executionCount++, 1.0); + + var handles = new List + { + scheduler.Run(coroutine1), + scheduler.Run(coroutine2), + scheduler.Run(coroutine3) + }; + + var wait = new WaitForAllCoroutines(scheduler, handles); + + Assert.That(wait.IsDone, Is.False); + Assert.That(executionCount, Is.EqualTo(0)); + + for (var i = 0; i < 12; i++) + { + scheduler.Update(); + } + + Assert.That(wait.IsDone, Is.True); + Assert.That(executionCount, Is.EqualTo(3)); + } + + /// + /// 验证WaitForAllCoroutines应该处理空句柄列表 + /// + [Test] + public void WaitForAllCoroutines_Should_Handle_Empty_Handles_List() + { + var timeSource = new TestTimeSource(); + var scheduler = new CoroutineScheduler(timeSource, instanceId: 1); + var handles = Array.Empty(); + + var wait = new WaitForAllCoroutines(scheduler, handles); + + Assert.That(wait.IsDone, Is.True); + } + + /// + /// 验证WaitForAllCoroutines应该抛出ArgumentNullException当handles为null + /// + [Test] + public void WaitForAllCoroutines_Should_Throw_ArgumentNullException_When_Handles_Is_Null() + { + var timeSource = new TestTimeSource(); + var scheduler = new CoroutineScheduler(timeSource, instanceId: 1); + + Assert.Throws(() => new WaitForAllCoroutines(scheduler, null!)); + } + + /// + /// 验证WaitForAllCoroutines应该抛出ArgumentNullException当scheduler为null + /// + [Test] + public void WaitForAllCoroutines_Should_Throw_ArgumentNullException_When_Scheduler_Is_Null() + { + var handles = Array.Empty(); + + Assert.Throws(() => new WaitForAllCoroutines(null!, handles)); + } + + /// + /// 验证WaitForAllCoroutines应该处理单个协程 + /// + [Test] + public void WaitForAllCoroutines_Should_Handle_Single_Coroutine() + { + var timeSource = new TestTimeSource(); + var scheduler = new CoroutineScheduler(timeSource, instanceId: 1); + var coroutine = CreateSimpleCoroutine(); + + var handles = new List { scheduler.Run(coroutine) }; + + var wait = new WaitForAllCoroutines(scheduler, handles); + + scheduler.Update(); + + Assert.That(wait.IsDone, Is.True); + } + + /// + /// 验证WaitForAllCoroutines应该在部分协程完成时未完成 + /// + [Test] + public void WaitForAllCoroutines_Should_Not_Be_Done_When_Some_Coroutines_Complete() + { + var timeSource = new TestTimeSource(); + var scheduler = new CoroutineScheduler(timeSource, instanceId: 1); + + var executionCount = 0; + var coroutine1 = CreateSimpleCoroutine(); + var coroutine2 = CreateDelayedCoroutine(() => executionCount++, 1.0); + var coroutine3 = CreateDelayedCoroutine(() => executionCount++, 1.0); + + var handles = new List + { + scheduler.Run(coroutine1), + scheduler.Run(coroutine2), + scheduler.Run(coroutine3) + }; + + var wait = new WaitForAllCoroutines(scheduler, handles); + + scheduler.Update(); + + Assert.That(wait.IsDone, Is.False); + Assert.That(executionCount, Is.EqualTo(0)); + } + + /// + /// 验证WaitForAllCoroutines应该处理被终止的协程 + /// + [Test] + public void WaitForAllCoroutines_Should_Handle_Killed_Coroutines() + { + var timeSource = new TestTimeSource(); + var scheduler = new CoroutineScheduler(timeSource, instanceId: 1); + + var coroutine1 = CreateDelayedCoroutine(() => { }, 1.0); + var coroutine2 = CreateDelayedCoroutine(() => { }, 1.0); + + var handles = new List + { + scheduler.Run(coroutine1), + scheduler.Run(coroutine2) + }; + + var wait = new WaitForAllCoroutines(scheduler, handles); + + Assert.That(wait.IsDone, Is.False); + + scheduler.Kill(handles[0]); + + Assert.That(wait.IsDone, Is.False); + + scheduler.Kill(handles[1]); + + Assert.That(wait.IsDone, Is.True); + } + + /// + /// 验证WaitForAllCoroutines应该处理被暂停和恢复的协程 + /// + [Test] + public void WaitForAllCoroutines_Should_Handle_Paused_And_Resumed_Coroutines() + { + var timeSource = new TestTimeSource(); + var scheduler = new CoroutineScheduler(timeSource, instanceId: 1); + + var executionCount = 0; + var coroutine1 = CreateDelayedCoroutine(() => executionCount++, 1.0); + var coroutine2 = CreateDelayedCoroutine(() => executionCount++, 1.0); + + var handles = new List + { + scheduler.Run(coroutine1), + scheduler.Run(coroutine2) + }; + + var wait = new WaitForAllCoroutines(scheduler, handles); + + scheduler.Pause(handles[0]); + + for (var i = 0; i < 12; i++) + { + scheduler.Update(); + } + + Assert.That(wait.IsDone, Is.False); + Assert.That(executionCount, Is.EqualTo(1)); + + scheduler.Resume(handles[0]); + + for (var i = 0; i < 12; i++) + { + scheduler.Update(); + } + + Assert.That(wait.IsDone, Is.True); + Assert.That(executionCount, Is.EqualTo(2)); + } + + /// + /// 验证WaitForAllCoroutines的Update方法不影响状态 + /// + [Test] + public void WaitForAllCoroutines_Update_Should_Not_Affect_State() + { + var timeSource = new TestTimeSource(); + var scheduler = new CoroutineScheduler(timeSource, instanceId: 1); + var coroutine = CreateDelayedCoroutine(() => { }, 1.0); + + var handles = new List { scheduler.Run(coroutine) }; + var wait = new WaitForAllCoroutines(scheduler, handles); + + wait.Update(0.1); + Assert.That(wait.IsDone, Is.False); + + wait.Update(1.0); + Assert.That(wait.IsDone, Is.False); + } + + /// + /// 验证WaitForAllCoroutines应该处理无效句柄 + /// + [Test] + public void WaitForAllCoroutines_Should_Handle_Invalid_Handles() + { + var timeSource = new TestTimeSource(); + var scheduler = new CoroutineScheduler(timeSource, instanceId: 1); + + var handles = new List { default }; + + var wait = new WaitForAllCoroutines(scheduler, handles); + + Assert.That(wait.IsDone, Is.True); + } + + /// + /// 验证WaitForAllCoroutines应该处理混合的有效和无效句柄 + /// + [Test] + public void WaitForAllCoroutines_Should_Handle_Mixed_Valid_And_Invalid_Handles() + { + var timeSource = new TestTimeSource(); + var scheduler = new CoroutineScheduler(timeSource, instanceId: 1); + var coroutine = CreateSimpleCoroutine(); + + var handles = new List + { + scheduler.Run(coroutine), + default + }; + + var wait = new WaitForAllCoroutines(scheduler, handles); + + scheduler.Update(); + + Assert.That(wait.IsDone, Is.True); + } + + /// + /// 验证WaitForAllCoroutines应该处理大量协程 + /// + [Test] + public void WaitForAllCoroutines_Should_Handle_Many_Coroutines() + { + var timeSource = new TestTimeSource(); + var scheduler = new CoroutineScheduler(timeSource, instanceId: 1); + var executionCount = 0; + + var handles = new List(); + for (var i = 0; i < 20; i++) + { + handles.Add(scheduler.Run(CreateDelayedCoroutine(() => executionCount++, 1.0))); + } + + var wait = new WaitForAllCoroutines(scheduler, handles); + + Assert.That(wait.IsDone, Is.False); + + for (var i = 0; i < 120; i++) + { + scheduler.Update(); + } + + Assert.That(wait.IsDone, Is.True); + Assert.That(executionCount, Is.EqualTo(20)); + } + + /// + /// 验证WaitForAllCoroutines应该处理抛出异常的协程 + /// + [Test] + public void WaitForAllCoroutines_Should_Handle_Coroutines_With_Exceptions() + { + var timeSource = new TestTimeSource(); + var scheduler = new CoroutineScheduler(timeSource, instanceId: 1); + + var coroutine1 = CreateSimpleCoroutine(); + var coroutine2 = CreateExceptionCoroutine(); + var coroutine3 = CreateSimpleCoroutine(); + + var handles = new List + { + scheduler.Run(coroutine1), + scheduler.Run(coroutine2), + scheduler.Run(coroutine3) + }; + + var wait = new WaitForAllCoroutines(scheduler, handles); + + scheduler.Update(); + + Assert.That(wait.IsDone, Is.True); + } + + /// + /// 验证WaitForAllCoroutines应该与ParallelCoroutines扩展方法一起工作 + /// + [Test] + public void WaitForAllCoroutines_Should_Work_With_ParallelCoroutines() + { + var timeSource = new TestTimeSource(); + var scheduler = new CoroutineScheduler(timeSource, instanceId: 1); + + var executionOrder = new List(); + var coroutine1 = CreateDelayedCoroutine(() => executionOrder.Add(1), 0.5); + var coroutine2 = CreateDelayedCoroutine(() => executionOrder.Add(2), 0.5); + var coroutine3 = CreateDelayedCoroutine(() => executionOrder.Add(3), 0.5); + + var parallel = scheduler.ParallelCoroutines(coroutine1, coroutine2, coroutine3); + + parallel.MoveNext(); + + while (scheduler.ActiveCoroutineCount > 0) + { + parallel.MoveNext(); + scheduler.Update(); + } + + Assert.That(executionOrder.Count, Is.EqualTo(3)); + Assert.That(executionOrder, Does.Contain(1)); + Assert.That(executionOrder, Does.Contain(2)); + Assert.That(executionOrder, Does.Contain(3)); + } + + /// + /// 验证WaitForAllCoroutines应该实现IYieldInstruction接口 + /// + [Test] + public void WaitForAllCoroutines_Should_Implement_IYieldInstruction() + { + var timeSource = new TestTimeSource(); + var scheduler = new CoroutineScheduler(timeSource, instanceId: 1); + var handles = Array.Empty(); + + var wait = new WaitForAllCoroutines(scheduler, handles); + + Assert.That(wait, Is.InstanceOf()); + } + + /// + /// 验证WaitForAllCoroutines应该在所有协程立即完成时立即完成 + /// + [Test] + public void WaitForAllCoroutines_Should_Be_Done_Immediately_When_All_Coroutines_Complete_Immediately() + { + var timeSource = new TestTimeSource(); + var scheduler = new CoroutineScheduler(timeSource, instanceId: 1); + + var coroutine1 = CreateSimpleCoroutine(); + var coroutine2 = CreateSimpleCoroutine(); + + var handles = new List + { + scheduler.Run(coroutine1), + scheduler.Run(coroutine2) + }; + + scheduler.Update(); + scheduler.Update(); + + var wait = new WaitForAllCoroutines(scheduler, handles); + + Assert.That(wait.IsDone, Is.True); + } + + /// + /// 验证WaitForAllCoroutines应该处理重复的句柄 + /// + [Test] + public void WaitForAllCoroutines_Should_Handle_Duplicate_Handles() + { + var timeSource = new TestTimeSource(); + var scheduler = new CoroutineScheduler(timeSource, instanceId: 1); + var coroutine = CreateDelayedCoroutine(() => { }, 1.0); + + var handle = scheduler.Run(coroutine); + var handles = new List { handle, handle }; + + var wait = new WaitForAllCoroutines(scheduler, handles); + + for (var i = 0; i < 12; i++) + { + scheduler.Update(); + } + + Assert.That(wait.IsDone, Is.True); + } + + /// + /// 创建简单的立即完成协程 + /// + private IEnumerator CreateSimpleCoroutine() + { + yield break; + } + + /// + /// 创建带回调的协程 + /// + private IEnumerator CreateCoroutineWithCallback(int id, Action callback) + { + yield return new WaitOneFrame(); + callback(); + } + + /// + /// 创建延迟协程 + /// + private IEnumerator CreateDelayedCoroutine(Action callback, double delay) + { + yield return new Delay(delay); + callback(); + } + + /// + /// 创建抛出异常的协程 + /// + private IEnumerator CreateExceptionCoroutine() + { + yield return new WaitOneFrame(); + throw new InvalidOperationException("Test exception"); + } + + /// + /// 测试用时间源类 + /// + private class TestTimeSource : ITimeSource + { + public double CurrentTime { get; private set; } + public double DeltaTime { get; private set; } + + public void Update() + { + DeltaTime = 0.1; + CurrentTime += DeltaTime; + } + } +}