test(coroutine): 添加协程扩展和助手类的单元测试

- 为 WaitForSecondsWithProgress 方法添加 null 回调参数的安全性测试
- 为 WaitForProgress 方法添加完整的功能和边界条件测试
- 为 RepeatCallForever 方法添加 shouldContinue 参数相关测试
- 为 RepeatCallForever 方法添加 CancellationToken 相关测试
- 移除未使用的 CreateCountingCoroutine 私有方法
- 使用 Assert.DoesNotThrow 确保 null 回调不会引发异常
This commit is contained in:
GeWuYou 2026-02-01 12:48:02 +08:00
parent c50797dbe2
commit 085370aa27
2 changed files with 191 additions and 19 deletions

View File

@ -199,9 +199,9 @@ public class CoroutineExtensionsTests
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 coroutine1 = CreateCoroutineWithCallback(() => executionOrder.Add(1));
var coroutine2 = CreateCoroutineWithCallback(() => executionOrder.Add(2));
var coroutine3 = CreateCoroutineWithCallback(() => executionOrder.Add(3));
var sequence = CoroutineExtensions.Sequence(coroutine1, coroutine2, coroutine3);
@ -397,14 +397,22 @@ public class CoroutineExtensionsTests
[Test]
public void WaitForSecondsWithProgress_Should_Handle_Null_Callback()
{
// 测试传入null回调参数时不会抛出异常
var coroutine = CoroutineExtensions.WaitForSecondsWithProgress(1.0, null);
coroutine.MoveNext();
coroutine.Current.Update(0.5);
Assert.That(coroutine.Current.IsDone, Is.False);
// 验证协程可以正常启动和执行
Assert.That(coroutine, Is.Not.Null);
coroutine.Current.Update(0.5);
Assert.That(coroutine.Current.IsDone, Is.True);
// 执行协程并验证不会因为null回调而抛出异常
Assert.DoesNotThrow(() =>
{
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>
@ -493,7 +501,7 @@ public class CoroutineExtensionsTests
/// <summary>
/// 创建简单的立即完成协程
/// </summary>
private IEnumerator<IYieldInstruction> CreateSimpleCoroutine()
private static IEnumerator<IYieldInstruction> CreateSimpleCoroutine()
{
yield break;
}
@ -501,25 +509,17 @@ public class CoroutineExtensionsTests
/// <summary>
/// 创建带回调的协程
/// </summary>
private IEnumerator<IYieldInstruction> CreateCoroutineWithCallback(int id, Action callback)
private static IEnumerator<IYieldInstruction> CreateCoroutineWithCallback(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)
private static IEnumerator<IYieldInstruction> CreateDelayedCoroutine(Action callback, double delay)
{
yield return new Delay(delay);
callback();

View File

@ -335,4 +335,176 @@ public class CoroutineHelperTests
{
Assert.Throws<ArgumentNullException>(() => CoroutineHelper.WaitWhile(null!));
}
/// <summary>
/// 验证WaitForProgress应该返回WaitForProgress实例
/// </summary>
[Test]
public void WaitForProgress_Should_Return_WaitForProgress_Instance()
{
var wait = CoroutineHelper.WaitForProgress(1.0, _ => { });
Assert.That(wait, Is.InstanceOf<WaitForProgress>());
}
/// <summary>
/// 验证WaitForProgress应该调用进度回调
/// </summary>
[Test]
public void WaitForProgress_Should_Call_Progress_Callback()
{
var progressValues = new List<float>();
var wait = CoroutineHelper.WaitForProgress(1.0, progress => progressValues.Add(progress));
// 更新直到完成
while (!wait.IsDone) wait.Update(0.1);
Assert.That(progressValues.Count, Is.GreaterThan(0));
// 进度值应该递增
for (var i = 1; i < progressValues.Count; i++)
Assert.That(progressValues[i], Is.GreaterThanOrEqualTo(progressValues[i - 1]));
Assert.That(progressValues[^1], Is.EqualTo(1.0f).Within(0.01f));
}
/// <summary>
/// 验证WaitForProgress应该在指定时间后完成
/// </summary>
[Test]
public void WaitForProgress_Should_Complete_After_Duration()
{
var wait = CoroutineHelper.WaitForProgress(1.0, _ => { });
wait.Update(0.5);
Assert.That(wait.IsDone, Is.False);
wait.Update(0.5);
Assert.That(wait.IsDone, Is.True);
}
/// <summary>
/// 验证WaitForProgress应该在零持续时间时抛出ArgumentException
/// </summary>
[Test]
public void WaitForProgress_Should_Throw_ArgumentException_When_Zero_Duration()
{
Assert.Throws<ArgumentException>(() => CoroutineHelper.WaitForProgress(0, _ => { }));
}
/// <summary>
/// 验证WaitForProgress应该在负数持续时间时抛出ArgumentException
/// </summary>
[Test]
public void WaitForProgress_Should_Throw_ArgumentException_When_Negative_Duration()
{
Assert.Throws<ArgumentException>(() => CoroutineHelper.WaitForProgress(-1.0, _ => { }));
}
/// <summary>
/// 验证WaitForProgress应该在null回调时抛出ArgumentNullException
/// </summary>
[Test]
public void WaitForProgress_Should_Throw_ArgumentNullException_When_Null_Callback()
{
Assert.Throws<ArgumentNullException>(() => CoroutineHelper.WaitForProgress(1.0, null!));
}
/// <summary>
/// 验证RepeatCallForever应该在shouldContinue返回false时停止
/// </summary>
[Test]
public void RepeatCallForever_Should_Stop_When_ShouldContinue_Returns_False()
{
var callCount = 0;
var maxCalls = 3;
var coroutine = CoroutineHelper.RepeatCallForever(0.1, () => callCount++, () => callCount < maxCalls);
while (coroutine.MoveNext()) coroutine.Current.Update(0.1);
Assert.That(callCount, Is.EqualTo(maxCalls));
}
/// <summary>
/// 验证RepeatCallForever应该在shouldContinue初始返回false时不执行
/// </summary>
[Test]
public void RepeatCallForever_Should_Not_Execute_When_ShouldContinue_Initially_False()
{
var callCount = 0;
var coroutine = CoroutineHelper.RepeatCallForever(0.1, () => callCount++, () => false);
Assert.That(coroutine.MoveNext(), Is.False);
Assert.That(callCount, Is.EqualTo(0));
}
/// <summary>
/// 验证RepeatCallForever应该处理null shouldContinue无限执行
/// </summary>
[Test]
public void RepeatCallForever_Should_Execute_Forever_When_ShouldContinue_Is_Null()
{
var callCount = 0;
var coroutine = CoroutineHelper.RepeatCallForever(0.1, () => callCount++, (Func<bool>?)null);
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>
/// 验证RepeatCallForever应该在CancellationToken取消时停止
/// </summary>
[Test]
public void RepeatCallForever_Should_Stop_When_CancellationToken_Cancelled()
{
var callCount = 0;
using var cts = new CancellationTokenSource();
var coroutine = CoroutineHelper.RepeatCallForever(0.1, () =>
{
callCount++;
if (callCount >= 3) cts.Cancel();
}, cts.Token);
while (coroutine.MoveNext()) coroutine.Current.Update(0.1);
Assert.That(callCount, Is.EqualTo(3));
}
/// <summary>
/// 验证RepeatCallForever应该在CancellationToken已取消时不执行
/// </summary>
[Test]
public void RepeatCallForever_Should_Not_Execute_When_CancellationToken_Already_Cancelled()
{
var callCount = 0;
using var cts = new CancellationTokenSource();
cts.Cancel();
var coroutine = CoroutineHelper.RepeatCallForever(0.1, () => callCount++, cts.Token);
Assert.That(coroutine.MoveNext(), Is.False);
Assert.That(callCount, Is.EqualTo(0));
}
/// <summary>
/// 验证RepeatCallForever应该正常执行当CancellationToken未取消时
/// </summary>
[Test]
public void RepeatCallForever_Should_Execute_When_CancellationToken_Not_Cancelled()
{
var callCount = 0;
using var cts = new CancellationTokenSource();
var coroutine = CoroutineHelper.RepeatCallForever(0.1, () => callCount++, cts.Token);
for (var i = 0; i < 5; i++)
{
Assert.That(coroutine.MoveNext(), Is.True);
coroutine.Current.Update(0.1);
}
Assert.That(callCount, Is.EqualTo(5));
}
}