test(coroutine): 添加协程扩展方法和等待所有协程的单元测试

- 添加 CoroutineExtensionsTests 测试类,覆盖 RepeatEvery、ExecuteAfter、Sequence、ParallelCoroutines 和 WaitForSecondsWithProgress 方法
- 添加 WaitForAllCoroutinesTests 测试类,验证 WaitForAllCoroutines 的初始化、IsDone 属性、空句柄处理等功能
- 实现协程调度器集成测试,验证多协程并发执行场景
- 添加边界条件测试,包括空数组、null 参数、负数参数等情况
- 实现异常处理和特殊状态测试,如暂停恢复、终止协程等场景
This commit is contained in:
GeWuYou 2026-01-26 11:45:13 +08:00
parent 01d9fefa1d
commit 7e5036ae76
2 changed files with 1085 additions and 0 deletions

View File

@ -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;
/// <summary>
/// 协程扩展方法的单元测试类
/// 测试内容包括:
/// - RepeatEvery方法
/// - ExecuteAfter方法
/// - Sequence方法
/// - ParallelCoroutines方法
/// - WaitForSecondsWithProgress方法
/// </summary>
[TestFixture]
public class CoroutineExtensionsTests
{
/// <summary>
/// 验证RepeatEvery应该返回有效的协程
/// </summary>
[Test]
public void RepeatEvery_Should_Return_Valid_Coroutine()
{
var callCount = 0;
var coroutine = CoroutineExtensions.RepeatEvery(0.1, () => callCount++, 3);
Assert.That(coroutine, Is.InstanceOf<IEnumerator<IYieldInstruction>>());
}
/// <summary>
/// 验证RepeatEvery应该执行指定次数
/// </summary>
[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));
}
/// <summary>
/// 验证RepeatEvery应该无限执行当count为null
/// </summary>
[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);
}
/// <summary>
/// 验证RepeatEvery应该处理负数count
/// </summary>
[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));
}
/// <summary>
/// 验证RepeatEvery应该处理零count
/// </summary>
[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));
}
/// <summary>
/// 验证RepeatEvery应该处理null action
/// </summary>
[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);
}
});
}
/// <summary>
/// 验证ExecuteAfter应该返回有效的协程
/// </summary>
[Test]
public void ExecuteAfter_Should_Return_Valid_Coroutine()
{
var called = false;
var coroutine = CoroutineExtensions.ExecuteAfter(1.0, () => called = true);
Assert.That(coroutine, Is.InstanceOf<IEnumerator<IYieldInstruction>>());
}
/// <summary>
/// 验证ExecuteAfter应该在延迟后执行action
/// </summary>
[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);
}
/// <summary>
/// 验证ExecuteAfter应该处理零延迟
/// </summary>
[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);
}
/// <summary>
/// 验证ExecuteAfter应该处理负数延迟
/// </summary>
[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);
}
/// <summary>
/// 验证ExecuteAfter应该处理null action
/// </summary>
[Test]
public void ExecuteAfter_Should_Handle_Null_Action()
{
var coroutine = CoroutineExtensions.ExecuteAfter(1.0, null);
Assert.DoesNotThrow(() =>
{
coroutine.MoveNext();
coroutine.Current.Update(1.0);
});
}
/// <summary>
/// 验证Sequence应该返回有效的协程
/// </summary>
[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<IEnumerator<IYieldInstruction>>());
}
/// <summary>
/// 验证Sequence应该按顺序执行多个协程
/// </summary>
[Test]
public void Sequence_Should_Execute_Coroutines_In_Order()
{
var executionOrder = new List<int>();
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<int> { 1, 2, 3 }));
}
/// <summary>
/// 验证Sequence应该处理空协程数组
/// </summary>
[Test]
public void Sequence_Should_Handle_Empty_Coroutines()
{
var sequence = CoroutineExtensions.Sequence();
Assert.That(sequence.MoveNext(), Is.False);
}
/// <summary>
/// 验证Sequence应该处理单个协程
/// </summary>
[Test]
public void Sequence_Should_Handle_Single_Coroutine()
{
var coroutine1 = CreateSimpleCoroutine();
var sequence = CoroutineExtensions.Sequence(coroutine1);
Assert.That(sequence.MoveNext(), Is.False);
}
/// <summary>
/// 验证Sequence应该处理null协程
/// </summary>
[Test]
public void Sequence_Should_Handle_Null_Coroutine()
{
var coroutine1 = CreateSimpleCoroutine();
var sequence = CoroutineExtensions.Sequence(coroutine1, null!);
Assert.Throws<NullReferenceException>(() =>
{
while (sequence.MoveNext())
{
sequence.Current.Update(0.1);
}
});
}
/// <summary>
/// 验证ParallelCoroutines应该返回有效的协程
/// </summary>
[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<IEnumerator<IYieldInstruction>>());
}
/// <summary>
/// 验证ParallelCoroutines应该并行执行多个协程
/// </summary>
[Test]
public void ParallelCoroutines_Should_Execute_Coroutines_In_Parallel()
{
var timeSource = new TestTimeSource();
var scheduler = new CoroutineScheduler(timeSource, instanceId: 1);
var executionCounts = new Dictionary<int, int> { { 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));
}
/// <summary>
/// 验证ParallelCoroutines应该处理空数组
/// </summary>
[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);
}
/// <summary>
/// 验证ParallelCoroutines应该处理null数组
/// </summary>
[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);
}
/// <summary>
/// 验证WaitForSecondsWithProgress应该返回有效的协程
/// </summary>
[Test]
public void WaitForSecondsWithProgress_Should_Return_Valid_Coroutine()
{
var progressValues = new List<float>();
var coroutine = CoroutineExtensions.WaitForSecondsWithProgress(1.0, progressValues.Add);
Assert.That(coroutine, Is.InstanceOf<IEnumerator<IYieldInstruction>>());
}
/// <summary>
/// 验证WaitForSecondsWithProgress应该在指定时间后完成
/// </summary>
[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);
}
/// <summary>
/// 验证WaitForSecondsWithProgress应该调用进度回调
/// </summary>
[Test]
public void WaitForSecondsWithProgress_Should_Call_Progress_Callback()
{
var progressValues = new List<float>();
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));
}
/// <summary>
/// 验证WaitForSecondsWithProgress应该处理零时间
/// </summary>
[Test]
public void WaitForSecondsWithProgress_Should_Handle_Zero_Duration()
{
var progressValues = new List<float>();
var coroutine = CoroutineExtensions.WaitForSecondsWithProgress(0, progressValues.Add);
Assert.That(coroutine.MoveNext(), Is.False);
Assert.That(progressValues, Is.EqualTo(new List<float> { 1.0f }));
}
/// <summary>
/// 验证WaitForSecondsWithProgress应该处理负数时间
/// </summary>
[Test]
public void WaitForSecondsWithProgress_Should_Handle_Negative_Duration()
{
var progressValues = new List<float>();
var coroutine = CoroutineExtensions.WaitForSecondsWithProgress(-1.0, progressValues.Add);
Assert.That(coroutine.MoveNext(), Is.False);
Assert.That(progressValues, Is.EqualTo(new List<float> { 1.0f }));
}
/// <summary>
/// 验证WaitForSecondsWithProgress应该处理null回调
/// </summary>
[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);
}
/// <summary>
/// 验证RepeatEvery应该使用Delay指令
/// </summary>
[Test]
public void RepeatEvery_Should_Use_Delay_Instruction()
{
var coroutine = CoroutineExtensions.RepeatEvery(0.5, () => { }, 1);
coroutine.MoveNext();
Assert.That(coroutine.Current, Is.InstanceOf<Delay>());
}
/// <summary>
/// 验证ExecuteAfter应该使用Delay指令
/// </summary>
[Test]
public void ExecuteAfter_Should_Use_Delay_Instruction()
{
var coroutine = CoroutineExtensions.ExecuteAfter(1.0, () => { });
coroutine.MoveNext();
Assert.That(coroutine.Current, Is.InstanceOf<Delay>());
}
/// <summary>
/// 验证Sequence应该清理已完成的协程
/// </summary>
[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);
}
/// <summary>
/// 验证RepeatEvery的间隔时间
/// </summary>
[Test]
public void RepeatEvery_Should_Respect_Interval()
{
var callTimes = new List<double>();
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));
}
/// <summary>
/// 验证ExecuteAfter的延迟时间
/// </summary>
[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));
}
/// <summary>
/// 创建简单的立即完成协程
/// </summary>
private IEnumerator<IYieldInstruction> CreateSimpleCoroutine()
{
yield break;
}
/// <summary>
/// 创建带回调的协程
/// </summary>
private IEnumerator<IYieldInstruction> CreateCoroutineWithCallback(int id, Action callback)
{
yield return new WaitOneFrame();
callback();
}
/// <summary>
/// 创建计数协程
/// </summary>
private IEnumerator<IYieldInstruction> CreateCountingCoroutine(int id, Action callback)
{
yield return new WaitOneFrame();
callback();
}
/// <summary>
/// 创建延迟协程
/// </summary>
private IEnumerator<IYieldInstruction> CreateDelayedCoroutine(Action callback, double delay)
{
yield return new Delay(delay);
callback();
}
/// <summary>
/// 测试用时间源类
/// </summary>
private class TestTimeSource : ITimeSource
{
public double CurrentTime { get; private set; }
public double DeltaTime { get; private set; }
public void Update()
{
DeltaTime = 0.016;
CurrentTime += DeltaTime;
}
}
}

View File

@ -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;
/// <summary>
/// WaitForAllCoroutines的单元测试类
/// 测试内容包括:
/// - 初始化和基本功能
/// - IsDone属性行为
/// - 空句柄集合处理
/// - 单个协程处理
/// - 多个协程处理
/// - 与CoroutineScheduler集成
/// </summary>
[TestFixture]
public class WaitForAllCoroutinesTests
{
/// <summary>
/// 验证WaitForAllCoroutines初始状态为未完成
/// </summary>
[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<CoroutineHandle>
{
scheduler.Run(coroutine1),
scheduler.Run(coroutine2)
};
var wait = new WaitForAllCoroutines(scheduler, handles);
Assert.That(wait.IsDone, Is.False);
}
/// <summary>
/// 验证WaitForAllCoroutines应该在所有协程完成后完成
/// </summary>
[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<CoroutineHandle>
{
scheduler.Run(coroutine1),
scheduler.Run(coroutine2)
};
var wait = new WaitForAllCoroutines(scheduler, handles);
scheduler.Update();
scheduler.Update();
Assert.That(wait.IsDone, Is.True);
}
/// <summary>
/// 验证WaitForAllCoroutines应该在所有协程完成后完成使用Delay
/// </summary>
[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<CoroutineHandle>
{
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));
}
/// <summary>
/// 验证WaitForAllCoroutines应该处理空句柄列表
/// </summary>
[Test]
public void WaitForAllCoroutines_Should_Handle_Empty_Handles_List()
{
var timeSource = new TestTimeSource();
var scheduler = new CoroutineScheduler(timeSource, instanceId: 1);
var handles = Array.Empty<CoroutineHandle>();
var wait = new WaitForAllCoroutines(scheduler, handles);
Assert.That(wait.IsDone, Is.True);
}
/// <summary>
/// 验证WaitForAllCoroutines应该抛出ArgumentNullException当handles为null
/// </summary>
[Test]
public void WaitForAllCoroutines_Should_Throw_ArgumentNullException_When_Handles_Is_Null()
{
var timeSource = new TestTimeSource();
var scheduler = new CoroutineScheduler(timeSource, instanceId: 1);
Assert.Throws<ArgumentNullException>(() => new WaitForAllCoroutines(scheduler, null!));
}
/// <summary>
/// 验证WaitForAllCoroutines应该抛出ArgumentNullException当scheduler为null
/// </summary>
[Test]
public void WaitForAllCoroutines_Should_Throw_ArgumentNullException_When_Scheduler_Is_Null()
{
var handles = Array.Empty<CoroutineHandle>();
Assert.Throws<ArgumentNullException>(() => new WaitForAllCoroutines(null!, handles));
}
/// <summary>
/// 验证WaitForAllCoroutines应该处理单个协程
/// </summary>
[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<CoroutineHandle> { scheduler.Run(coroutine) };
var wait = new WaitForAllCoroutines(scheduler, handles);
scheduler.Update();
Assert.That(wait.IsDone, Is.True);
}
/// <summary>
/// 验证WaitForAllCoroutines应该在部分协程完成时未完成
/// </summary>
[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<CoroutineHandle>
{
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));
}
/// <summary>
/// 验证WaitForAllCoroutines应该处理被终止的协程
/// </summary>
[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<CoroutineHandle>
{
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);
}
/// <summary>
/// 验证WaitForAllCoroutines应该处理被暂停和恢复的协程
/// </summary>
[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<CoroutineHandle>
{
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));
}
/// <summary>
/// 验证WaitForAllCoroutines的Update方法不影响状态
/// </summary>
[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<CoroutineHandle> { 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);
}
/// <summary>
/// 验证WaitForAllCoroutines应该处理无效句柄
/// </summary>
[Test]
public void WaitForAllCoroutines_Should_Handle_Invalid_Handles()
{
var timeSource = new TestTimeSource();
var scheduler = new CoroutineScheduler(timeSource, instanceId: 1);
var handles = new List<CoroutineHandle> { default };
var wait = new WaitForAllCoroutines(scheduler, handles);
Assert.That(wait.IsDone, Is.True);
}
/// <summary>
/// 验证WaitForAllCoroutines应该处理混合的有效和无效句柄
/// </summary>
[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<CoroutineHandle>
{
scheduler.Run(coroutine),
default
};
var wait = new WaitForAllCoroutines(scheduler, handles);
scheduler.Update();
Assert.That(wait.IsDone, Is.True);
}
/// <summary>
/// 验证WaitForAllCoroutines应该处理大量协程
/// </summary>
[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<CoroutineHandle>();
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));
}
/// <summary>
/// 验证WaitForAllCoroutines应该处理抛出异常的协程
/// </summary>
[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<CoroutineHandle>
{
scheduler.Run(coroutine1),
scheduler.Run(coroutine2),
scheduler.Run(coroutine3)
};
var wait = new WaitForAllCoroutines(scheduler, handles);
scheduler.Update();
Assert.That(wait.IsDone, Is.True);
}
/// <summary>
/// 验证WaitForAllCoroutines应该与ParallelCoroutines扩展方法一起工作
/// </summary>
[Test]
public void WaitForAllCoroutines_Should_Work_With_ParallelCoroutines()
{
var timeSource = new TestTimeSource();
var scheduler = new CoroutineScheduler(timeSource, instanceId: 1);
var executionOrder = new List<int>();
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));
}
/// <summary>
/// 验证WaitForAllCoroutines应该实现IYieldInstruction接口
/// </summary>
[Test]
public void WaitForAllCoroutines_Should_Implement_IYieldInstruction()
{
var timeSource = new TestTimeSource();
var scheduler = new CoroutineScheduler(timeSource, instanceId: 1);
var handles = Array.Empty<CoroutineHandle>();
var wait = new WaitForAllCoroutines(scheduler, handles);
Assert.That(wait, Is.InstanceOf<IYieldInstruction>());
}
/// <summary>
/// 验证WaitForAllCoroutines应该在所有协程立即完成时立即完成
/// </summary>
[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<CoroutineHandle>
{
scheduler.Run(coroutine1),
scheduler.Run(coroutine2)
};
scheduler.Update();
scheduler.Update();
var wait = new WaitForAllCoroutines(scheduler, handles);
Assert.That(wait.IsDone, Is.True);
}
/// <summary>
/// 验证WaitForAllCoroutines应该处理重复的句柄
/// </summary>
[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<CoroutineHandle> { handle, handle };
var wait = new WaitForAllCoroutines(scheduler, handles);
for (var i = 0; i < 12; i++)
{
scheduler.Update();
}
Assert.That(wait.IsDone, Is.True);
}
/// <summary>
/// 创建简单的立即完成协程
/// </summary>
private IEnumerator<IYieldInstruction> CreateSimpleCoroutine()
{
yield break;
}
/// <summary>
/// 创建带回调的协程
/// </summary>
private IEnumerator<IYieldInstruction> CreateCoroutineWithCallback(int id, Action callback)
{
yield return new WaitOneFrame();
callback();
}
/// <summary>
/// 创建延迟协程
/// </summary>
private IEnumerator<IYieldInstruction> CreateDelayedCoroutine(Action callback, double delay)
{
yield return new Delay(delay);
callback();
}
/// <summary>
/// 创建抛出异常的协程
/// </summary>
private IEnumerator<IYieldInstruction> CreateExceptionCoroutine()
{
yield return new WaitOneFrame();
throw new InvalidOperationException("Test exception");
}
/// <summary>
/// 测试用时间源类
/// </summary>
private class TestTimeSource : ITimeSource
{
public double CurrentTime { get; private set; }
public double DeltaTime { get; private set; }
public void Update()
{
DeltaTime = 0.1;
CurrentTime += DeltaTime;
}
}
}