mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-22 19:03:29 +08:00
test(coroutine): 添加协程扩展方法和等待所有协程的单元测试
- 添加 CoroutineExtensionsTests 测试类,覆盖 RepeatEvery、ExecuteAfter、Sequence、ParallelCoroutines 和 WaitForSecondsWithProgress 方法 - 添加 WaitForAllCoroutinesTests 测试类,验证 WaitForAllCoroutines 的初始化、IsDone 属性、空句柄处理等功能 - 实现协程调度器集成测试,验证多协程并发执行场景 - 添加边界条件测试,包括空数组、null 参数、负数参数等情况 - 实现异常处理和特殊状态测试,如暂停恢复、终止协程等场景
This commit is contained in:
parent
01d9fefa1d
commit
7e5036ae76
564
GFramework.Core.Tests/coroutine/CoroutineExtensionsTests.cs
Normal file
564
GFramework.Core.Tests/coroutine/CoroutineExtensionsTests.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
521
GFramework.Core.Tests/coroutine/WaitForAllCoroutinesTests.cs
Normal file
521
GFramework.Core.Tests/coroutine/WaitForAllCoroutinesTests.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user