mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-24 20:34:29 +08:00
feat(coroutine): 扩展协程功能并重构指令结构
- 将协程等待指令移动到instructions命名空间下 - 添加WaitForProgress指令支持带进度回调的时间等待 - 添加WaitForAllCoroutines指令用于等待多个协程完成 - 添加AsyncOperation类用于桥接协程与async/await模型 - 添加协程扩展方法包括RepeatEvery、ExecuteAfter、Sequence等功能 - 添加Task与协程的转换扩展方法AsCoroutineInstruction - 添加协程调度器的ParallelCoroutines扩展方法 - 添加IsCoroutineAlive方法检查协程状态 - 更新相关测试文件以匹配新的命名空间结构
This commit is contained in:
parent
27bc481577
commit
01d9fefa1d
342
GFramework.Core.Tests/coroutine/AsyncOperationTests.cs
Normal file
342
GFramework.Core.Tests/coroutine/AsyncOperationTests.cs
Normal file
@ -0,0 +1,342 @@
|
|||||||
|
using GFramework.Core.Abstractions.coroutine;
|
||||||
|
using GFramework.Core.coroutine.instructions;
|
||||||
|
using NUnit.Framework;
|
||||||
|
|
||||||
|
namespace GFramework.Core.Tests.coroutine;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// AsyncOperation的单元测试类
|
||||||
|
/// 测试内容包括:
|
||||||
|
/// - 初始化状态
|
||||||
|
/// - 完成和状态检查
|
||||||
|
/// - 异常处理
|
||||||
|
/// - 延续操作
|
||||||
|
/// - GetAwaiter
|
||||||
|
/// - IsCompleted属性
|
||||||
|
/// </summary>
|
||||||
|
[TestFixture]
|
||||||
|
public class AsyncOperationTests
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 验证AsyncOperation初始状态为未完成
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void AsyncOperation_Should_Not_Be_Done_Initially()
|
||||||
|
{
|
||||||
|
var op = new AsyncOperation();
|
||||||
|
|
||||||
|
Assert.That(op.IsDone, Is.False);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证AsyncOperation初始状态IsCompleted为false
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void AsyncOperation_Should_Not_Be_Completed_Initially()
|
||||||
|
{
|
||||||
|
var op = new AsyncOperation();
|
||||||
|
|
||||||
|
Assert.That(op.IsCompleted, Is.False);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证SetCompleted后IsDone应该为true
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void SetCompleted_Should_Set_IsDone_To_True()
|
||||||
|
{
|
||||||
|
var op = new AsyncOperation();
|
||||||
|
|
||||||
|
op.SetCompleted();
|
||||||
|
|
||||||
|
Assert.That(op.IsDone, Is.True);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证SetCompleted后IsCompleted应该为true
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void SetCompleted_Should_Set_IsCompleted_To_True()
|
||||||
|
{
|
||||||
|
var op = new AsyncOperation();
|
||||||
|
|
||||||
|
op.SetCompleted();
|
||||||
|
|
||||||
|
Assert.That(op.IsCompleted, Is.True);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证SetCompleted只能被调用一次
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void SetCompleted_Should_Be_Idempotent()
|
||||||
|
{
|
||||||
|
var op = new AsyncOperation();
|
||||||
|
|
||||||
|
op.SetCompleted();
|
||||||
|
op.SetCompleted();
|
||||||
|
op.SetCompleted();
|
||||||
|
|
||||||
|
Assert.That(op.IsDone, Is.True);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证SetException后IsDone应该为true
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void SetException_Should_Set_IsDone_To_True()
|
||||||
|
{
|
||||||
|
var op = new AsyncOperation();
|
||||||
|
|
||||||
|
op.SetException(new InvalidOperationException("Test exception"));
|
||||||
|
|
||||||
|
Assert.That(op.IsDone, Is.True);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证SetException后Task应该包含异常
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void SetException_Should_Set_Exception_On_Task()
|
||||||
|
{
|
||||||
|
var op = new AsyncOperation();
|
||||||
|
var expectedException = new InvalidOperationException("Test exception");
|
||||||
|
|
||||||
|
op.SetException(expectedException);
|
||||||
|
|
||||||
|
Assert.That(async () => await op.Task, Throws.InstanceOf<InvalidOperationException>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证OnCompleted应该在已完成时立即执行延续
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void OnCompleted_Should_Execute_Immediately_When_Already_Completed()
|
||||||
|
{
|
||||||
|
var op = new AsyncOperation();
|
||||||
|
var continuationCalled = false;
|
||||||
|
|
||||||
|
op.SetCompleted();
|
||||||
|
op.OnCompleted(() => continuationCalled = true);
|
||||||
|
|
||||||
|
Assert.That(continuationCalled, Is.True);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证OnCompleted应该在未完成时不立即执行延续
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void OnCompleted_Should_Not_Execute_Immediately_When_Not_Completed()
|
||||||
|
{
|
||||||
|
var op = new AsyncOperation();
|
||||||
|
var continuationCalled = false;
|
||||||
|
|
||||||
|
op.OnCompleted(() => continuationCalled = true);
|
||||||
|
|
||||||
|
Assert.That(continuationCalled, Is.False);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证延续应该在SetCompleted后被调用
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void Continuation_Should_Be_Called_After_SetCompleted()
|
||||||
|
{
|
||||||
|
var op = new AsyncOperation();
|
||||||
|
var continuationCalled = false;
|
||||||
|
|
||||||
|
op.OnCompleted(() => continuationCalled = true);
|
||||||
|
op.SetCompleted();
|
||||||
|
|
||||||
|
Assert.That(continuationCalled, Is.True);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证多个延续应该都能被调用
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void Multiple_Continuations_Should_All_Be_Called()
|
||||||
|
{
|
||||||
|
var op = new AsyncOperation();
|
||||||
|
var callCount = 0;
|
||||||
|
|
||||||
|
op.OnCompleted(() => callCount++);
|
||||||
|
op.OnCompleted(() => callCount++);
|
||||||
|
op.OnCompleted(() => callCount++);
|
||||||
|
op.SetCompleted();
|
||||||
|
|
||||||
|
Assert.That(callCount, Is.EqualTo(3));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证延续应该在SetException后被调用
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void Continuation_Should_Be_Called_After_SetException()
|
||||||
|
{
|
||||||
|
var op = new AsyncOperation();
|
||||||
|
var continuationCalled = false;
|
||||||
|
|
||||||
|
op.OnCompleted(() => continuationCalled = true);
|
||||||
|
op.SetException(new InvalidOperationException("Test"));
|
||||||
|
|
||||||
|
Assert.That(continuationCalled, Is.True);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证SetCompleted后设置的延续也应该被调用
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void Continuation_Registered_After_Completed_Should_Be_Called()
|
||||||
|
{
|
||||||
|
var op = new AsyncOperation();
|
||||||
|
var firstCalled = false;
|
||||||
|
var secondCalled = false;
|
||||||
|
|
||||||
|
op.OnCompleted(() => firstCalled = true);
|
||||||
|
op.SetCompleted();
|
||||||
|
op.OnCompleted(() => secondCalled = true);
|
||||||
|
|
||||||
|
Assert.That(firstCalled, Is.True);
|
||||||
|
Assert.That(secondCalled, Is.True);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证GetAwaiter应该返回自身
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void GetAwaiter_Should_Return_Self()
|
||||||
|
{
|
||||||
|
var op = new AsyncOperation();
|
||||||
|
|
||||||
|
var awaiter = op.GetAwaiter();
|
||||||
|
|
||||||
|
Assert.That(awaiter, Is.SameAs(op));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证Update方法不应该改变状态
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void Update_Should_Not_Change_State()
|
||||||
|
{
|
||||||
|
var op = new AsyncOperation();
|
||||||
|
|
||||||
|
op.Update(0.1);
|
||||||
|
|
||||||
|
Assert.That(op.IsDone, Is.False);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证AsyncOperation实现IYieldInstruction接口
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void AsyncOperation_Should_Implement_IYieldInstruction()
|
||||||
|
{
|
||||||
|
var op = new AsyncOperation();
|
||||||
|
|
||||||
|
Assert.That(op, Is.InstanceOf<IYieldInstruction>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证Task属性应该返回有效的Task
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void Task_Property_Should_Return_Valid_Task()
|
||||||
|
{
|
||||||
|
var op = new AsyncOperation();
|
||||||
|
|
||||||
|
Assert.That(op.Task, Is.Not.Null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证SetCompleted后Task应该完成
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public async Task Task_Should_Complete_After_SetCompleted()
|
||||||
|
{
|
||||||
|
var op = new AsyncOperation();
|
||||||
|
|
||||||
|
op.SetCompleted();
|
||||||
|
|
||||||
|
await op.Task;
|
||||||
|
|
||||||
|
Assert.That(op.Task.IsCompleted, Is.True);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证SetException后Task应该失败
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void Task_Should_Fault_After_SetException()
|
||||||
|
{
|
||||||
|
var op = new AsyncOperation();
|
||||||
|
|
||||||
|
op.SetException(new InvalidOperationException("Test"));
|
||||||
|
|
||||||
|
Assert.That(op.Task.IsFaulted, Is.True);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证SetCompleted只能设置一次
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void SetCompleted_Should_Only_Set_Once()
|
||||||
|
{
|
||||||
|
var op = new AsyncOperation();
|
||||||
|
var firstCallCompleted = false;
|
||||||
|
var secondCallCompleted = false;
|
||||||
|
|
||||||
|
op.OnCompleted(() => firstCallCompleted = true);
|
||||||
|
op.SetCompleted();
|
||||||
|
|
||||||
|
op.OnCompleted(() => secondCallCompleted = true);
|
||||||
|
op.SetCompleted();
|
||||||
|
|
||||||
|
Assert.That(firstCallCompleted, Is.True);
|
||||||
|
Assert.That(secondCallCompleted, Is.True);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证SetException只能在未完成时设置
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void SetException_Should_Not_Work_After_SetCompleted()
|
||||||
|
{
|
||||||
|
var op = new AsyncOperation();
|
||||||
|
|
||||||
|
op.SetCompleted();
|
||||||
|
op.SetException(new InvalidOperationException("Test"));
|
||||||
|
|
||||||
|
Assert.That(op.Task.IsCompletedSuccessfully, Is.True);
|
||||||
|
Assert.That(op.Task.IsFaulted, Is.False);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证SetCompleted不能在SetException后设置
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void SetCompleted_Should_Not_Work_After_SetException()
|
||||||
|
{
|
||||||
|
var op = new AsyncOperation();
|
||||||
|
|
||||||
|
op.SetException(new InvalidOperationException("Test"));
|
||||||
|
op.SetCompleted();
|
||||||
|
|
||||||
|
Assert.That(op.Task.IsFaulted, Is.True);
|
||||||
|
Assert.That(op.Task.IsCompletedSuccessfully, Is.False);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证延续抛出的异常应该被捕获
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void Continuation_Exception_Should_Be_Caught()
|
||||||
|
{
|
||||||
|
var op = new AsyncOperation();
|
||||||
|
|
||||||
|
op.OnCompleted(() => throw new InvalidOperationException("Test exception"));
|
||||||
|
|
||||||
|
Assert.DoesNotThrow(() => op.SetCompleted());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
using GFramework.Core.Abstractions.coroutine;
|
using GFramework.Core.Abstractions.coroutine;
|
||||||
using GFramework.Core.coroutine;
|
using GFramework.Core.coroutine;
|
||||||
|
using GFramework.Core.coroutine.instructions;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
|
||||||
namespace GFramework.Core.Tests.coroutine;
|
namespace GFramework.Core.Tests.coroutine;
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
using GFramework.Core.Abstractions.coroutine;
|
using GFramework.Core.Abstractions.coroutine;
|
||||||
using GFramework.Core.coroutine;
|
using GFramework.Core.coroutine;
|
||||||
|
using GFramework.Core.coroutine.instructions;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
|
||||||
namespace GFramework.Core.Tests.coroutine;
|
namespace GFramework.Core.Tests.coroutine;
|
||||||
|
|||||||
295
GFramework.Core.Tests/coroutine/TaskCoroutineExtensionsTests.cs
Normal file
295
GFramework.Core.Tests/coroutine/TaskCoroutineExtensionsTests.cs
Normal file
@ -0,0 +1,295 @@
|
|||||||
|
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>
|
||||||
|
/// TaskCoroutineExtensions的单元测试类
|
||||||
|
/// 测试内容包括:
|
||||||
|
/// - AsCoroutineInstruction方法
|
||||||
|
/// - StartTaskAsCoroutine方法
|
||||||
|
/// </summary>
|
||||||
|
[TestFixture]
|
||||||
|
public class TaskCoroutineExtensionsTests
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 验证AsCoroutineInstruction应该返回WaitForTask
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void AsCoroutineInstruction_Should_Return_WaitForTask()
|
||||||
|
{
|
||||||
|
var task = Task.CompletedTask;
|
||||||
|
var instruction = task.AsCoroutineInstruction();
|
||||||
|
|
||||||
|
Assert.That(instruction, Is.InstanceOf<WaitForTask>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证AsCoroutineInstruction<T>应该返回WaitForTask<T>
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void AsCoroutineInstructionOfT_Should_Return_WaitForTaskOfT()
|
||||||
|
{
|
||||||
|
var task = Task.FromResult(42);
|
||||||
|
var instruction = task.AsCoroutineInstruction<int>();
|
||||||
|
|
||||||
|
Assert.That(instruction, Is.InstanceOf<WaitForTask<int>>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证AsCoroutineInstruction可以处理已完成的Task
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void AsCoroutineInstruction_Should_Handle_Completed_Task()
|
||||||
|
{
|
||||||
|
var task = Task.CompletedTask;
|
||||||
|
var instruction = task.AsCoroutineInstruction();
|
||||||
|
|
||||||
|
Assert.That(instruction, Is.InstanceOf<WaitForTask>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证AsCoroutineInstruction<T>可以处理已完成的Task
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void AsCoroutineInstructionOfT_Should_Handle_Completed_Task()
|
||||||
|
{
|
||||||
|
var task = Task.FromResult(42);
|
||||||
|
var instruction = task.AsCoroutineInstruction<int>();
|
||||||
|
|
||||||
|
Assert.That(instruction, Is.InstanceOf<WaitForTask<int>>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证AsCoroutineInstruction<T>应该能够访问Task结果
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void AsCoroutineInstructionOfT_Should_Access_Task_Result()
|
||||||
|
{
|
||||||
|
var task = Task.FromResult(42);
|
||||||
|
var instruction = task.AsCoroutineInstruction<int>();
|
||||||
|
|
||||||
|
task.Wait();
|
||||||
|
|
||||||
|
Assert.That(instruction.Result, Is.EqualTo(42));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证AsCoroutineInstruction应该处理null Task(抛出异常)
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void AsCoroutineInstruction_Should_Handle_Null_Task()
|
||||||
|
{
|
||||||
|
Task task = null!;
|
||||||
|
|
||||||
|
Assert.Throws<ArgumentNullException>(() => task.AsCoroutineInstruction());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证AsCoroutineInstruction<T>应该处理null Task(抛出异常)
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void AsCoroutineInstructionOfT_Should_Handle_Null_Task()
|
||||||
|
{
|
||||||
|
Task<int> task = null!;
|
||||||
|
|
||||||
|
Assert.Throws<ArgumentNullException>(() => task.AsCoroutineInstruction<int>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证AsCoroutineInstruction应该处理失败的Task
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void AsCoroutineInstruction_Should_Handle_Faulted_Task()
|
||||||
|
{
|
||||||
|
var task = Task.FromException(new InvalidOperationException("Test exception"));
|
||||||
|
var instruction = task.AsCoroutineInstruction();
|
||||||
|
|
||||||
|
Assert.That(instruction, Is.InstanceOf<WaitForTask>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证AsCoroutineInstruction<T>应该处理失败的Task
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void AsCoroutineInstructionOfT_Should_Handle_Faulted_Task()
|
||||||
|
{
|
||||||
|
var task = Task.FromException<int>(new InvalidOperationException("Test exception"));
|
||||||
|
var instruction = task.AsCoroutineInstruction<int>();
|
||||||
|
|
||||||
|
Assert.That(instruction, Is.InstanceOf<WaitForTask<int>>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证StartTaskAsCoroutine应该返回有效的协程句柄
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void StartTaskAsCoroutine_Should_Return_Valid_Handle()
|
||||||
|
{
|
||||||
|
var timeSource = new TestTimeSource();
|
||||||
|
var scheduler = new CoroutineScheduler(timeSource, instanceId: 1);
|
||||||
|
var task = Task.CompletedTask;
|
||||||
|
|
||||||
|
var handle = scheduler.StartTaskAsCoroutine(task);
|
||||||
|
|
||||||
|
Assert.That(handle.IsValid, Is.True);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证StartTaskAsCoroutine<T>应该返回有效的协程句柄
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void StartTaskAsCoroutineOfT_Should_Return_Valid_Handle()
|
||||||
|
{
|
||||||
|
var timeSource = new TestTimeSource();
|
||||||
|
var scheduler = new CoroutineScheduler(timeSource, instanceId: 1);
|
||||||
|
var task = Task.FromResult(42);
|
||||||
|
|
||||||
|
var handle = scheduler.StartTaskAsCoroutine(task);
|
||||||
|
|
||||||
|
Assert.That(handle.IsValid, Is.True);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证StartTaskAsCoroutine应该等待Task完成
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void StartTaskAsCoroutine_Should_Wait_For_Task_Completion()
|
||||||
|
{
|
||||||
|
var timeSource = new TestTimeSource();
|
||||||
|
var scheduler = new CoroutineScheduler(timeSource, instanceId: 1);
|
||||||
|
|
||||||
|
var completed = false;
|
||||||
|
var tcs = new TaskCompletionSource<object?>();
|
||||||
|
|
||||||
|
var handle = scheduler.StartTaskAsCoroutine(tcs.Task);
|
||||||
|
|
||||||
|
Assert.That(scheduler.ActiveCoroutineCount, Is.EqualTo(1));
|
||||||
|
Assert.That(completed, Is.False);
|
||||||
|
|
||||||
|
tcs.SetResult(null);
|
||||||
|
Task.Delay(50).Wait();
|
||||||
|
|
||||||
|
scheduler.Update();
|
||||||
|
scheduler.Update();
|
||||||
|
|
||||||
|
Assert.That(scheduler.ActiveCoroutineCount, Is.EqualTo(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证StartTaskAsCoroutine<T>应该等待Task完成
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void StartTaskAsCoroutineOfT_Should_Wait_For_Task_Completion()
|
||||||
|
{
|
||||||
|
var timeSource = new TestTimeSource();
|
||||||
|
var scheduler = new CoroutineScheduler(timeSource, instanceId: 1);
|
||||||
|
|
||||||
|
var tcs = new TaskCompletionSource<int>();
|
||||||
|
|
||||||
|
var handle = scheduler.StartTaskAsCoroutine(tcs.Task);
|
||||||
|
|
||||||
|
Assert.That(scheduler.ActiveCoroutineCount, Is.EqualTo(1));
|
||||||
|
|
||||||
|
tcs.SetResult(42);
|
||||||
|
Task.Delay(50).Wait();
|
||||||
|
|
||||||
|
scheduler.Update();
|
||||||
|
scheduler.Update();
|
||||||
|
|
||||||
|
Assert.That(scheduler.ActiveCoroutineCount, Is.EqualTo(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证StartTaskAsCoroutine应该处理已完成的Task
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void StartTaskAsCoroutine_Should_Handle_Completed_Task()
|
||||||
|
{
|
||||||
|
var timeSource = new TestTimeSource();
|
||||||
|
var scheduler = new CoroutineScheduler(timeSource, instanceId: 1);
|
||||||
|
var task = Task.CompletedTask;
|
||||||
|
|
||||||
|
var handle = scheduler.StartTaskAsCoroutine(task);
|
||||||
|
|
||||||
|
scheduler.Update();
|
||||||
|
|
||||||
|
Assert.That(scheduler.ActiveCoroutineCount, Is.EqualTo(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证StartTaskAsCoroutine应该处理失败的Task
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void StartTaskAsCoroutine_Should_Handle_Faulted_Task()
|
||||||
|
{
|
||||||
|
var timeSource = new TestTimeSource();
|
||||||
|
var scheduler = new CoroutineScheduler(timeSource, instanceId: 1);
|
||||||
|
var task = Task.FromException(new InvalidOperationException("Test"));
|
||||||
|
|
||||||
|
var handle = scheduler.StartTaskAsCoroutine(task);
|
||||||
|
|
||||||
|
Assert.DoesNotThrow(() => scheduler.Update());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证StartTaskAsCoroutine<T>应该处理失败的Task
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void StartTaskAsCoroutineOfT_Should_Handle_Faulted_Task()
|
||||||
|
{
|
||||||
|
var timeSource = new TestTimeSource();
|
||||||
|
var scheduler = new CoroutineScheduler(timeSource, instanceId: 1);
|
||||||
|
var task = Task.FromException<int>(new InvalidOperationException("Test"));
|
||||||
|
|
||||||
|
scheduler.StartTaskAsCoroutine(task);
|
||||||
|
|
||||||
|
Assert.DoesNotThrow(() => scheduler.Update());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证StartTaskAsCoroutine应该与调度器正常协作
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void StartTaskAsCoroutine_Should_Work_With_Scheduler()
|
||||||
|
{
|
||||||
|
var timeSource = new TestTimeSource();
|
||||||
|
var scheduler = new CoroutineScheduler(timeSource, instanceId: 1);
|
||||||
|
|
||||||
|
var tcs = new TaskCompletionSource<object?>();
|
||||||
|
scheduler.StartTaskAsCoroutine(tcs.Task);
|
||||||
|
|
||||||
|
var tcs2 = new TaskCompletionSource<int>();
|
||||||
|
scheduler.StartTaskAsCoroutine(tcs2.Task);
|
||||||
|
|
||||||
|
Assert.That(scheduler.ActiveCoroutineCount, Is.EqualTo(2));
|
||||||
|
|
||||||
|
tcs.SetResult(null);
|
||||||
|
tcs2.SetResult(42);
|
||||||
|
|
||||||
|
Task.Delay(50).Wait();
|
||||||
|
scheduler.Update();
|
||||||
|
scheduler.Update();
|
||||||
|
|
||||||
|
Assert.That(scheduler.ActiveCoroutineCount, Is.EqualTo(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
294
GFramework.Core.Tests/coroutine/WaitForProgressTests.cs
Normal file
294
GFramework.Core.Tests/coroutine/WaitForProgressTests.cs
Normal file
@ -0,0 +1,294 @@
|
|||||||
|
using GFramework.Core.Abstractions.coroutine;
|
||||||
|
using GFramework.Core.coroutine.instructions;
|
||||||
|
using NUnit.Framework;
|
||||||
|
|
||||||
|
namespace GFramework.Core.Tests.coroutine;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// WaitForProgress的单元测试类
|
||||||
|
/// 测试内容包括:
|
||||||
|
/// - 初始化和基本功能
|
||||||
|
/// - 进度回调
|
||||||
|
/// - 边界条件
|
||||||
|
/// - 异常处理
|
||||||
|
/// </summary>
|
||||||
|
[TestFixture]
|
||||||
|
public class WaitForProgressTests
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 验证WaitForProgress初始状态为未完成
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void WaitForProgress_Should_Not_Be_Done_Initially()
|
||||||
|
{
|
||||||
|
var progressValues = new List<float>();
|
||||||
|
var wait = new WaitForProgress(1.0, progressValues.Add);
|
||||||
|
|
||||||
|
Assert.That(wait.IsDone, Is.False);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证WaitForProgress应该在指定时间后完成
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void WaitForProgress_Should_Be_Done_After_Duration()
|
||||||
|
{
|
||||||
|
var progressValues = new List<float>();
|
||||||
|
var wait = new WaitForProgress(1.0, progressValues.Add);
|
||||||
|
|
||||||
|
wait.Update(0.5);
|
||||||
|
Assert.That(wait.IsDone, Is.False);
|
||||||
|
|
||||||
|
wait.Update(0.5);
|
||||||
|
Assert.That(wait.IsDone, Is.True);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证WaitForProgress应该在Update时调用进度回调
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void WaitForProgress_Should_Call_Progress_Callback_On_Update()
|
||||||
|
{
|
||||||
|
var progressValues = new List<float>();
|
||||||
|
var wait = new WaitForProgress(1.0, progressValues.Add);
|
||||||
|
|
||||||
|
wait.Update(0.25);
|
||||||
|
|
||||||
|
Assert.That(progressValues.Count, Is.EqualTo(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证进度值应该在0到1之间
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void WaitForProgress_Should_Have_Progress_Between_0_And_1()
|
||||||
|
{
|
||||||
|
var progressValues = new List<float>();
|
||||||
|
var wait = new WaitForProgress(1.0, progressValues.Add);
|
||||||
|
|
||||||
|
while (!wait.IsDone)
|
||||||
|
{
|
||||||
|
wait.Update(0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var progress in progressValues)
|
||||||
|
{
|
||||||
|
Assert.That(progress, Is.GreaterThanOrEqualTo(0.0f));
|
||||||
|
Assert.That(progress, Is.LessThanOrEqualTo(1.0f));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证进度值应该随着时间增加
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void WaitForProgress_Should_Increase_Progress_Over_Time()
|
||||||
|
{
|
||||||
|
var progressValues = new List<float>();
|
||||||
|
var wait = new WaitForProgress(1.0, progressValues.Add);
|
||||||
|
|
||||||
|
wait.Update(0.25);
|
||||||
|
Assert.That(progressValues[0], Is.EqualTo(0.25f).Within(0.01f));
|
||||||
|
|
||||||
|
wait.Update(0.25);
|
||||||
|
Assert.That(progressValues[1], Is.EqualTo(0.5f).Within(0.01f));
|
||||||
|
|
||||||
|
wait.Update(0.25);
|
||||||
|
Assert.That(progressValues[2], Is.EqualTo(0.75f).Within(0.01f));
|
||||||
|
|
||||||
|
wait.Update(0.25);
|
||||||
|
Assert.That(progressValues[^1], Is.EqualTo(1.0f).Within(0.01f));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证WaitForProgress应该抛出ArgumentNullException当回调为null
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void WaitForProgress_Should_Throw_ArgumentNullException_When_Callback_Is_Null()
|
||||||
|
{
|
||||||
|
Assert.Throws<ArgumentNullException>(() => new WaitForProgress(1.0, null!));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证WaitForProgress应该抛出ArgumentException当duration为0
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void WaitForProgress_Should_Throw_ArgumentException_When_Duration_Is_Zero()
|
||||||
|
{
|
||||||
|
Assert.Throws<ArgumentException>(() => new WaitForProgress(0, _ => { }));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证WaitForProgress应该抛出ArgumentException当duration为负数
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void WaitForProgress_Should_Throw_ArgumentException_When_Duration_Is_Negative()
|
||||||
|
{
|
||||||
|
Assert.Throws<ArgumentException>(() => new WaitForProgress(-1.0, _ => { }));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证WaitForProgress应该处理超过duration的更新
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void WaitForProgress_Should_Clamp_Progress_To_1_When_Exceeding_Duration()
|
||||||
|
{
|
||||||
|
var progressValues = new List<float>();
|
||||||
|
var wait = new WaitForProgress(1.0, progressValues.Add);
|
||||||
|
|
||||||
|
wait.Update(0.5);
|
||||||
|
wait.Update(0.5);
|
||||||
|
wait.Update(0.5);
|
||||||
|
|
||||||
|
Assert.That(progressValues[^1], Is.EqualTo(1.0f).Within(0.01f));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证WaitForProgress可以处理精确的duration
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void WaitForProgress_Should_Handle_Exact_Duration()
|
||||||
|
{
|
||||||
|
var progressValues = new List<float>();
|
||||||
|
var duration = 1.0;
|
||||||
|
var wait = new WaitForProgress(duration, progressValues.Add);
|
||||||
|
|
||||||
|
wait.Update(duration);
|
||||||
|
|
||||||
|
Assert.That(wait.IsDone, Is.True);
|
||||||
|
Assert.That(progressValues[^1], Is.EqualTo(1.0f).Within(0.01f));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证WaitForProgress可以处理不同的delta time
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void WaitForProgress_Should_Handle_Variable_Delta_Time()
|
||||||
|
{
|
||||||
|
var progressValues = new List<float>();
|
||||||
|
var wait = new WaitForProgress(1.0, progressValues.Add);
|
||||||
|
|
||||||
|
wait.Update(0.1);
|
||||||
|
wait.Update(0.05);
|
||||||
|
wait.Update(0.15);
|
||||||
|
wait.Update(0.2);
|
||||||
|
wait.Update(0.5);
|
||||||
|
|
||||||
|
Assert.That(wait.IsDone, Is.True);
|
||||||
|
Assert.That(progressValues[^1], Is.EqualTo(1.0f).Within(0.01f));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证WaitForProgress可以处理多次Update
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void WaitForProgress_Should_Handle_Multiple_Updates()
|
||||||
|
{
|
||||||
|
var progressValues = new List<float>();
|
||||||
|
var wait = new WaitForProgress(1.0, progressValues.Add);
|
||||||
|
|
||||||
|
var updateCount = 0;
|
||||||
|
while (!wait.IsDone && updateCount < 100)
|
||||||
|
{
|
||||||
|
wait.Update(0.01);
|
||||||
|
updateCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.That(wait.IsDone, Is.True);
|
||||||
|
Assert.That(progressValues.Count, Is.GreaterThan(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证WaitForProgress应该确保最后一个进度为1.0
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void WaitForProgress_Should_Ensure_Final_Progress_Is_1()
|
||||||
|
{
|
||||||
|
var progressValues = new List<float>();
|
||||||
|
var wait = new WaitForProgress(1.0, progressValues.Add);
|
||||||
|
|
||||||
|
while (!wait.IsDone)
|
||||||
|
{
|
||||||
|
wait.Update(0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.That(progressValues[^1], Is.EqualTo(1.0f).Within(0.01f));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证WaitForProgress实现IYieldInstruction接口
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void WaitForProgress_Should_Implement_IYieldInstruction()
|
||||||
|
{
|
||||||
|
var progressValues = new List<float>();
|
||||||
|
var wait = new WaitForProgress(1.0, progressValues.Add);
|
||||||
|
|
||||||
|
Assert.That(wait, Is.InstanceOf<IYieldInstruction>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证WaitForProgress可以处理很短的duration
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void WaitForProgress_Should_Handle_Short_Duration()
|
||||||
|
{
|
||||||
|
var progressValues = new List<float>();
|
||||||
|
var wait = new WaitForProgress(0.001, progressValues.Add);
|
||||||
|
|
||||||
|
wait.Update(0.001);
|
||||||
|
|
||||||
|
Assert.That(wait.IsDone, Is.True);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证WaitForProgress可以处理很长的duration
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void WaitForProgress_Should_Handle_Long_Duration()
|
||||||
|
{
|
||||||
|
var progressValues = new List<float>();
|
||||||
|
var wait = new WaitForProgress(100.0, progressValues.Add);
|
||||||
|
|
||||||
|
wait.Update(50.0);
|
||||||
|
Assert.That(wait.IsDone, Is.False);
|
||||||
|
|
||||||
|
wait.Update(50.0);
|
||||||
|
Assert.That(wait.IsDone, Is.True);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证WaitForProgress在完成前不会超过1.0
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void WaitForProgress_Should_Not_Exceed_1_Before_Completion()
|
||||||
|
{
|
||||||
|
var progressValues = new List<float>();
|
||||||
|
var wait = new WaitForProgress(1.0, progressValues.Add);
|
||||||
|
|
||||||
|
wait.Update(0.9);
|
||||||
|
Assert.That(progressValues[^1], Is.LessThan(1.0f));
|
||||||
|
|
||||||
|
wait.Update(0.05);
|
||||||
|
Assert.That(progressValues[^1], Is.LessThanOrEqualTo(1.0f));
|
||||||
|
|
||||||
|
wait.Update(0.05);
|
||||||
|
Assert.That(progressValues[^1], Is.EqualTo(1.0f).Within(0.01f));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证WaitForProgress的Update方法不影响未完成状态
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void WaitForProgress_Update_Should_Not_Affect_Before_Completion()
|
||||||
|
{
|
||||||
|
var progressValues = new List<float>();
|
||||||
|
var wait = new WaitForProgress(1.0, progressValues.Add);
|
||||||
|
|
||||||
|
wait.Update(0.1);
|
||||||
|
Assert.That(wait.IsDone, Is.False);
|
||||||
|
|
||||||
|
wait.Update(0.1);
|
||||||
|
Assert.That(wait.IsDone, Is.False);
|
||||||
|
}
|
||||||
|
}
|
||||||
311
GFramework.Core.Tests/coroutine/WaitForTaskTests.cs
Normal file
311
GFramework.Core.Tests/coroutine/WaitForTaskTests.cs
Normal file
@ -0,0 +1,311 @@
|
|||||||
|
using GFramework.Core.Abstractions.coroutine;
|
||||||
|
using GFramework.Core.coroutine.instructions;
|
||||||
|
using NUnit.Framework;
|
||||||
|
|
||||||
|
namespace GFramework.Core.Tests.coroutine;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// WaitForTask的单元测试类
|
||||||
|
/// 测试内容包括:
|
||||||
|
/// - WaitForTask初始化和等待
|
||||||
|
/// - WaitForTask<T>初始化、等待和结果获取
|
||||||
|
/// - 异常处理
|
||||||
|
/// - 边界条件
|
||||||
|
/// </summary>
|
||||||
|
[TestFixture]
|
||||||
|
public class WaitForTaskTests
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 验证WaitForTask初始状态为未完成
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void WaitForTask_Should_Not_Be_Done_Initially()
|
||||||
|
{
|
||||||
|
var task = Task.Run(() => { });
|
||||||
|
var wait = new WaitForTask(task);
|
||||||
|
|
||||||
|
Assert.That(wait.IsDone, Is.False);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证WaitForTask应该在Task完成后完成
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void WaitForTask_Should_Be_Done_After_Task_Completes()
|
||||||
|
{
|
||||||
|
var tcs = new TaskCompletionSource<object?>();
|
||||||
|
var wait = new WaitForTask(tcs.Task);
|
||||||
|
|
||||||
|
Assert.That(wait.IsDone, Is.False);
|
||||||
|
|
||||||
|
tcs.SetResult(null);
|
||||||
|
|
||||||
|
Task.Delay(100).Wait();
|
||||||
|
|
||||||
|
Assert.That(wait.IsDone, Is.True);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证WaitForTask应该处理已完成的Task
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void WaitForTask_Should_Handle_Already_Completed_Task()
|
||||||
|
{
|
||||||
|
var task = Task.CompletedTask;
|
||||||
|
|
||||||
|
Task.Delay(100).Wait();
|
||||||
|
|
||||||
|
var wait = new WaitForTask(task);
|
||||||
|
|
||||||
|
Task.Delay(100).Wait();
|
||||||
|
|
||||||
|
Assert.That(wait.IsDone, Is.True);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证WaitForTask应该处理失败的Task
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void WaitForTask_Should_Handle_Faulted_Task()
|
||||||
|
{
|
||||||
|
var tcs = new TaskCompletionSource<object?>();
|
||||||
|
var wait = new WaitForTask(tcs.Task);
|
||||||
|
|
||||||
|
tcs.SetException(new InvalidOperationException("Test exception"));
|
||||||
|
|
||||||
|
Task.Delay(100).Wait();
|
||||||
|
|
||||||
|
Assert.That(wait.IsDone, Is.True);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证WaitForTask应该处理取消的Task
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void WaitForTask_Should_Handle_Cancelled_Task()
|
||||||
|
{
|
||||||
|
var cts = new CancellationTokenSource();
|
||||||
|
cts.Cancel();
|
||||||
|
var task = Task.FromCanceled(cts.Token);
|
||||||
|
|
||||||
|
Task.Delay(100).Wait();
|
||||||
|
|
||||||
|
var wait = new WaitForTask(task);
|
||||||
|
|
||||||
|
Task.Delay(100).Wait();
|
||||||
|
|
||||||
|
Assert.That(wait.IsDone, Is.True);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证WaitForTask应该抛出ArgumentNullException当task为null
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void WaitForTask_Should_Throw_ArgumentNullException_When_Task_Is_Null()
|
||||||
|
{
|
||||||
|
Assert.Throws<ArgumentNullException>(() => new WaitForTask(null!));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证WaitForTask<T>初始状态为未完成
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void WaitForTaskOfT_Should_Not_Be_Done_Initially()
|
||||||
|
{
|
||||||
|
var tcs = new TaskCompletionSource<int>();
|
||||||
|
var wait = new WaitForTask<int>(tcs.Task);
|
||||||
|
|
||||||
|
Assert.That(wait.IsDone, Is.False);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证WaitForTask<T>应该在Task完成后完成
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void WaitForTaskOfT_Should_Be_Done_After_Task_Completes()
|
||||||
|
{
|
||||||
|
var tcs = new TaskCompletionSource<int>();
|
||||||
|
var wait = new WaitForTask<int>(tcs.Task);
|
||||||
|
|
||||||
|
Assert.That(wait.IsDone, Is.False);
|
||||||
|
|
||||||
|
tcs.SetResult(42);
|
||||||
|
|
||||||
|
Task.Delay(100).Wait();
|
||||||
|
|
||||||
|
Assert.That(wait.IsDone, Is.True);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证WaitForTask<T>应该返回Task的结果
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void WaitForTaskOfT_Should_Return_Task_Result()
|
||||||
|
{
|
||||||
|
var tcs = new TaskCompletionSource<int>();
|
||||||
|
var wait = new WaitForTask<int>(tcs.Task);
|
||||||
|
|
||||||
|
tcs.SetResult(42);
|
||||||
|
|
||||||
|
Task.Delay(100).Wait();
|
||||||
|
|
||||||
|
Assert.That(wait.Result, Is.EqualTo(42));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证WaitForTask<T>应该处理已完成的Task
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void WaitForTaskOfT_Should_Handle_Already_Completed_Task()
|
||||||
|
{
|
||||||
|
var task = Task.FromResult(42);
|
||||||
|
|
||||||
|
Task.Delay(100).Wait();
|
||||||
|
|
||||||
|
var wait = new WaitForTask<int>(task);
|
||||||
|
|
||||||
|
Task.Delay(100).Wait();
|
||||||
|
|
||||||
|
Assert.That(wait.IsDone, Is.True);
|
||||||
|
Assert.That(wait.Result, Is.EqualTo(42));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证WaitForTask<T>应该处理失败的Task
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void WaitForTaskOfT_Should_Handle_Faulted_Task()
|
||||||
|
{
|
||||||
|
var tcs = new TaskCompletionSource<int>();
|
||||||
|
var wait = new WaitForTask<int>(tcs.Task);
|
||||||
|
|
||||||
|
tcs.SetException(new InvalidOperationException("Test exception"));
|
||||||
|
|
||||||
|
Task.Delay(100).Wait();
|
||||||
|
|
||||||
|
Assert.That(wait.IsDone, Is.True);
|
||||||
|
|
||||||
|
Assert.Throws<InvalidOperationException>(() => { var _ = wait.Result; });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证WaitForTask<T>应该抛出ArgumentNullException当task为null
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void WaitForTaskOfT_Should_Throw_ArgumentNullException_When_Task_Is_Null()
|
||||||
|
{
|
||||||
|
Assert.Throws<ArgumentNullException>(() => new WaitForTask<int>(null!));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证WaitForTask实现IYieldInstruction接口
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void WaitForTask_Should_Implement_IYieldInstruction()
|
||||||
|
{
|
||||||
|
var task = Task.CompletedTask;
|
||||||
|
var wait = new WaitForTask(task);
|
||||||
|
|
||||||
|
Assert.That(wait, Is.InstanceOf<IYieldInstruction>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证WaitForTask<T>实现IYieldInstruction接口
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void WaitForTaskOfT_Should_Implement_IYieldInstruction()
|
||||||
|
{
|
||||||
|
var task = Task.FromResult(42);
|
||||||
|
var wait = new WaitForTask<int>(task);
|
||||||
|
|
||||||
|
Assert.That(wait, Is.InstanceOf<IYieldInstruction>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证WaitForTask的Update方法不影响状态
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void WaitForTask_Update_Should_Not_Affect_State()
|
||||||
|
{
|
||||||
|
var tcs = new TaskCompletionSource<object?>();
|
||||||
|
var wait = new WaitForTask(tcs.Task);
|
||||||
|
|
||||||
|
wait.Update(0.1);
|
||||||
|
Assert.That(wait.IsDone, Is.False);
|
||||||
|
|
||||||
|
wait.Update(1.0);
|
||||||
|
Assert.That(wait.IsDone, Is.False);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证WaitForTask<T>的Update方法不影响状态
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void WaitForTaskOfT_Update_Should_Not_Affect_State()
|
||||||
|
{
|
||||||
|
var tcs = new TaskCompletionSource<int>();
|
||||||
|
var wait = new WaitForTask<int>(tcs.Task);
|
||||||
|
|
||||||
|
wait.Update(0.1);
|
||||||
|
Assert.That(wait.IsDone, Is.False);
|
||||||
|
|
||||||
|
wait.Update(1.0);
|
||||||
|
Assert.That(wait.IsDone, Is.False);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证WaitForTask在延迟完成后能够正确等待
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void WaitForTask_Should_Wait_For_Delayed_Task()
|
||||||
|
{
|
||||||
|
var delayMs = 100;
|
||||||
|
var task = Task.Delay(delayMs);
|
||||||
|
var wait = new WaitForTask(task);
|
||||||
|
|
||||||
|
Assert.That(wait.IsDone, Is.False);
|
||||||
|
|
||||||
|
Task.Delay(delayMs + 50).Wait();
|
||||||
|
|
||||||
|
Assert.That(wait.IsDone, Is.True);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证WaitForTask<T>在异步操作完成后能够正确获取结果
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public async Task WaitForTaskOfT_Should_Get_Result_From_Async_Operation()
|
||||||
|
{
|
||||||
|
var expectedValue = 123;
|
||||||
|
var task = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await Task.Delay(50);
|
||||||
|
return expectedValue;
|
||||||
|
});
|
||||||
|
|
||||||
|
var wait = new WaitForTask<int>(task);
|
||||||
|
|
||||||
|
await task;
|
||||||
|
|
||||||
|
Task.Delay(100).Wait();
|
||||||
|
|
||||||
|
Assert.That(wait.IsDone, Is.True);
|
||||||
|
Assert.That(wait.Result, Is.EqualTo(expectedValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证WaitForTask应该处理长时间运行的Task
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void WaitForTask_Should_Handle_Long_Running_Task()
|
||||||
|
{
|
||||||
|
var task = Task.Delay(200);
|
||||||
|
var wait = new WaitForTask(task);
|
||||||
|
|
||||||
|
Assert.That(wait.IsDone, Is.False);
|
||||||
|
|
||||||
|
Task.Delay(250).Wait();
|
||||||
|
|
||||||
|
Assert.That(wait.IsDone, Is.True);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,5 @@
|
|||||||
using GFramework.Core.Abstractions.coroutine;
|
using GFramework.Core.Abstractions.coroutine;
|
||||||
using GFramework.Core.coroutine;
|
using GFramework.Core.coroutine.instructions;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
|
||||||
namespace GFramework.Core.Tests.coroutine;
|
namespace GFramework.Core.Tests.coroutine;
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading;
|
|
||||||
using GFramework.Core.Abstractions.coroutine;
|
using GFramework.Core.Abstractions.coroutine;
|
||||||
|
using GFramework.Core.coroutine.instructions;
|
||||||
|
|
||||||
namespace GFramework.Core.coroutine;
|
namespace GFramework.Core.coroutine;
|
||||||
|
|
||||||
@ -59,6 +57,17 @@ public static class CoroutineHelper
|
|||||||
return new WaitWhile(predicate);
|
return new WaitWhile(predicate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 等待指定时间并提供进度回调
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="duration">等待的持续时间(秒)</param>
|
||||||
|
/// <param name="onProgress">进度回调函数,接收0-1之间的进度值</param>
|
||||||
|
/// <returns>等待进度指令</returns>
|
||||||
|
public static WaitForProgress WaitForProgress(double duration, Action<float> onProgress)
|
||||||
|
{
|
||||||
|
return new WaitForProgress(duration, onProgress);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 延迟调用指定的委托
|
/// 延迟调用指定的委托
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@ -368,4 +368,8 @@ public sealed class CoroutineScheduler(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
public bool IsCoroutineAlive(CoroutineHandle handle)
|
||||||
|
{
|
||||||
|
return _metadata.ContainsKey(handle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
131
GFramework.Core/coroutine/extensions/CoroutineExtensions.cs
Normal file
131
GFramework.Core/coroutine/extensions/CoroutineExtensions.cs
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
using GFramework.Core.Abstractions.coroutine;
|
||||||
|
using GFramework.Core.coroutine.instructions;
|
||||||
|
|
||||||
|
namespace GFramework.Core.coroutine.extensions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 协程相关的扩展方法
|
||||||
|
/// </summary>
|
||||||
|
public static class CoroutineExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 在指定时间间隔内重复执行动作的协程
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="interval">执行间隔时间(秒)</param>
|
||||||
|
/// <param name="action">要重复执行的动作</param>
|
||||||
|
/// <param name="count">重复次数,如果为null则无限重复</param>
|
||||||
|
/// <returns>协程枚举器</returns>
|
||||||
|
public static IEnumerator<IYieldInstruction> RepeatEvery(
|
||||||
|
double interval,
|
||||||
|
Action action,
|
||||||
|
int? count = null)
|
||||||
|
{
|
||||||
|
if (count is < 0)
|
||||||
|
{
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var executedCount = 0;
|
||||||
|
while (count == null || executedCount < count)
|
||||||
|
{
|
||||||
|
action?.Invoke();
|
||||||
|
yield return new Delay(interval);
|
||||||
|
executedCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 在指定延迟后执行动作的协程
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="delay">延迟时间(秒)</param>
|
||||||
|
/// <param name="action">要执行的动作</param>
|
||||||
|
/// <returns>协程枚举器</returns>
|
||||||
|
public static IEnumerator<IYieldInstruction> ExecuteAfter(
|
||||||
|
double delay,
|
||||||
|
Action? action)
|
||||||
|
{
|
||||||
|
if (delay < 0)
|
||||||
|
{
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
yield return new Delay(delay);
|
||||||
|
action?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 顺序执行多个协程
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="coroutines">要顺序执行的协程集合</param>
|
||||||
|
/// <returns>协程枚举器</returns>
|
||||||
|
public static IEnumerator<IYieldInstruction> Sequence(
|
||||||
|
params IEnumerator<IYieldInstruction>[] coroutines)
|
||||||
|
{
|
||||||
|
foreach (var coroutine in coroutines)
|
||||||
|
{
|
||||||
|
while (coroutine.MoveNext())
|
||||||
|
{
|
||||||
|
yield return coroutine.Current;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理协程
|
||||||
|
coroutine.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 并行执行多个协程(等待所有协程完成)
|
||||||
|
/// 注意:这需要协程调度器的支持,这里提供一个包装器返回多个句柄
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="scheduler">协程调度器</param>
|
||||||
|
/// <param name="coroutines">要并行执行的协程集合</param>
|
||||||
|
/// <returns>等待所有协程完成的协程</returns>
|
||||||
|
public static IEnumerator<IYieldInstruction> ParallelCoroutines(
|
||||||
|
this CoroutineScheduler scheduler,
|
||||||
|
params IEnumerator<IYieldInstruction>[]? coroutines)
|
||||||
|
{
|
||||||
|
if (coroutines == null || coroutines.Length == 0)
|
||||||
|
{
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动所有协程并收集句柄
|
||||||
|
var handles = new List<CoroutineHandle>();
|
||||||
|
foreach (var coroutine in coroutines)
|
||||||
|
{
|
||||||
|
var handle = scheduler.Run(coroutine);
|
||||||
|
handles.Add(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待所有协程完成
|
||||||
|
yield return new WaitForAllCoroutines(scheduler,handles);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 带进度回调的等待协程
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="totalTime">总等待时间(秒)</param>
|
||||||
|
/// <param name="onProgress">进度回调,参数为0-1之间的进度值</param>
|
||||||
|
/// <returns>协程枚举器</returns>
|
||||||
|
public static IEnumerator<IYieldInstruction> WaitForSecondsWithProgress(
|
||||||
|
double totalTime,
|
||||||
|
Action<float>? onProgress)
|
||||||
|
{
|
||||||
|
if (totalTime <= 0)
|
||||||
|
{
|
||||||
|
onProgress?.Invoke(1.0f);
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
|
||||||
|
onProgress?.Invoke(0.0f);
|
||||||
|
|
||||||
|
if (onProgress != null)
|
||||||
|
{
|
||||||
|
yield return new WaitForProgress(totalTime, onProgress);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
yield return new Delay(totalTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,70 @@
|
|||||||
|
using GFramework.Core.Abstractions.coroutine;
|
||||||
|
using GFramework.Core.coroutine.instructions;
|
||||||
|
|
||||||
|
namespace GFramework.Core.coroutine.extensions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Task与协程之间的扩展方法
|
||||||
|
/// </summary>
|
||||||
|
public static class TaskCoroutineExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 将Task转换为协程等待指令
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="task">要等待的Task</param>
|
||||||
|
/// <returns>等待Task的协程指令</returns>
|
||||||
|
public static WaitForTask AsCoroutineInstruction(this Task task)
|
||||||
|
{
|
||||||
|
return new WaitForTask(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将泛型Task转换为协程等待指令
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Task返回值的类型</typeparam>
|
||||||
|
/// <param name="task">要等待的Task</param>
|
||||||
|
/// <returns>等待Task的协程指令</returns>
|
||||||
|
public static WaitForTask<T> AsCoroutineInstruction<T>(this Task<T> task)
|
||||||
|
{
|
||||||
|
return new WaitForTask<T>(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 在调度器中启动一个Task并等待其完成
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="scheduler">协程调度器</param>
|
||||||
|
/// <param name="task">要等待的Task</param>
|
||||||
|
/// <returns>协程句柄</returns>
|
||||||
|
public static CoroutineHandle StartTaskAsCoroutine(this CoroutineScheduler scheduler, Task task)
|
||||||
|
{
|
||||||
|
return scheduler.Run(CreateTaskCoroutine(task));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 在调度器中启动一个泛型Task并等待其完成
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Task返回值的类型</typeparam>
|
||||||
|
/// <param name="scheduler">协程调度器</param>
|
||||||
|
/// <param name="task">要等待的Task</param>
|
||||||
|
/// <returns>协程句柄</returns>
|
||||||
|
public static CoroutineHandle StartTaskAsCoroutine<T>(this CoroutineScheduler scheduler, Task<T> task)
|
||||||
|
{
|
||||||
|
return scheduler.Run(CreateTaskCoroutine(task));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建等待Task的协程
|
||||||
|
/// </summary>
|
||||||
|
private static IEnumerator<IYieldInstruction> CreateTaskCoroutine(Task task)
|
||||||
|
{
|
||||||
|
yield return task.AsCoroutineInstruction();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建等待泛型Task的协程
|
||||||
|
/// </summary>
|
||||||
|
private static IEnumerator<IYieldInstruction> CreateTaskCoroutine<T>(Task<T> task)
|
||||||
|
{
|
||||||
|
yield return task.AsCoroutineInstruction();
|
||||||
|
}
|
||||||
|
}
|
||||||
141
GFramework.Core/coroutine/instructions/AsyncOperation.cs
Normal file
141
GFramework.Core/coroutine/instructions/AsyncOperation.cs
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using GFramework.Core.Abstractions.coroutine;
|
||||||
|
|
||||||
|
namespace GFramework.Core.coroutine.instructions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 异步操作包装器,用于桥接协程系统和async/await异步编程模型
|
||||||
|
/// </summary>
|
||||||
|
public class AsyncOperation : IYieldInstruction, INotifyCompletion
|
||||||
|
{
|
||||||
|
private readonly TaskCompletionSource<object?> _tcs = new();
|
||||||
|
private volatile bool _completed;
|
||||||
|
private volatile Action? _continuation;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取异步操作是否已完成
|
||||||
|
/// </summary>
|
||||||
|
public bool IsDone => _completed;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取异步操作的任务
|
||||||
|
/// </summary>
|
||||||
|
public Task Task => _tcs.Task;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 更新方法,用于处理时间更新逻辑
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="deltaTime">时间增量</param>
|
||||||
|
public void Update(double deltaTime)
|
||||||
|
{
|
||||||
|
// 由外部调用SetCompleted来更新状态
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 标记异步操作已完成
|
||||||
|
/// </summary>
|
||||||
|
public void SetCompleted()
|
||||||
|
{
|
||||||
|
if (_completed) return;
|
||||||
|
|
||||||
|
_completed = true;
|
||||||
|
_tcs.SetResult(null);
|
||||||
|
|
||||||
|
var continuation = Interlocked.Exchange(ref _continuation, null);
|
||||||
|
if (continuation != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
continuation.Invoke();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// 忽略延续中的异常
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 标记异步操作因异常而失败
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="exception">导致失败的异常</param>
|
||||||
|
public void SetException(Exception exception)
|
||||||
|
{
|
||||||
|
if (_completed) return;
|
||||||
|
|
||||||
|
_completed = true;
|
||||||
|
_tcs.SetException(exception);
|
||||||
|
|
||||||
|
var continuation = Interlocked.Exchange(ref _continuation, null);
|
||||||
|
if (continuation != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
continuation.Invoke();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// 忽略延续中的异常
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置延续操作
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="continuation">要执行的延续操作</param>
|
||||||
|
public void OnCompleted(Action continuation)
|
||||||
|
{
|
||||||
|
// 尝试添加延续
|
||||||
|
var current = _continuation;
|
||||||
|
var newContinuation = current == null ? continuation : current + continuation;
|
||||||
|
|
||||||
|
if (Interlocked.CompareExchange(ref _continuation, newContinuation, current) != current)
|
||||||
|
{
|
||||||
|
// 如果CAS失败,说明可能已经完成,直接执行
|
||||||
|
if (_completed)
|
||||||
|
{
|
||||||
|
continuation();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 重试
|
||||||
|
OnCompleted(continuation);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 双重检查:如果在设置延续后发现已完成,需要执行延续
|
||||||
|
if (_completed)
|
||||||
|
{
|
||||||
|
var cont = Interlocked.Exchange(ref _continuation, null);
|
||||||
|
if (cont != null)
|
||||||
|
{
|
||||||
|
cont();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取异步操作结果
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>操作结果</returns>
|
||||||
|
public object? GetResult()
|
||||||
|
{
|
||||||
|
return _tcs.Task.GetAwaiter().GetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取awaiter对象
|
||||||
|
/// </summary>
|
||||||
|
public AsyncOperation GetAwaiter()
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检查是否已完成
|
||||||
|
/// </summary>
|
||||||
|
public bool IsCompleted => _completed;
|
||||||
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
using GFramework.Core.Abstractions.coroutine;
|
using GFramework.Core.Abstractions.coroutine;
|
||||||
|
|
||||||
namespace GFramework.Core.coroutine;
|
namespace GFramework.Core.coroutine.instructions;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 延迟等待指令,实现IYieldInstruction接口,用于协程中的时间延迟
|
/// 延迟等待指令,实现IYieldInstruction接口,用于协程中的时间延迟
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
|
||||||
|
using GFramework.Core.Abstractions.coroutine;
|
||||||
|
|
||||||
|
namespace GFramework.Core.coroutine.instructions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 等待所有协程完成的等待指令
|
||||||
|
/// </summary>
|
||||||
|
public sealed class WaitForAllCoroutines(
|
||||||
|
CoroutineScheduler scheduler,
|
||||||
|
IReadOnlyList<CoroutineHandle> handles)
|
||||||
|
: IYieldInstruction
|
||||||
|
{
|
||||||
|
private readonly CoroutineScheduler _scheduler = scheduler ?? throw new ArgumentNullException(nameof(scheduler));
|
||||||
|
private readonly IReadOnlyList<CoroutineHandle> _handles = handles ?? throw new ArgumentNullException(nameof(handles));
|
||||||
|
|
||||||
|
public void Update(double deltaTime)
|
||||||
|
{
|
||||||
|
// 不需要做任何事
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsDone
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _handles.All(handle => !_scheduler.IsCoroutineAlive(handle));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
using GFramework.Core.Abstractions.coroutine;
|
using GFramework.Core.Abstractions.coroutine;
|
||||||
|
|
||||||
namespace GFramework.Core.coroutine;
|
namespace GFramework.Core.coroutine.instructions;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 等待协程完成的指令类,实现IYieldInstruction接口
|
/// 等待协程完成的指令类,实现IYieldInstruction接口
|
||||||
@ -1,6 +1,6 @@
|
|||||||
using GFramework.Core.Abstractions.coroutine;
|
using GFramework.Core.Abstractions.coroutine;
|
||||||
|
|
||||||
namespace GFramework.Core.coroutine;
|
namespace GFramework.Core.coroutine.instructions;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 等待指定帧数的等待指令类
|
/// 等待指定帧数的等待指令类
|
||||||
60
GFramework.Core/coroutine/instructions/WaitForProgress.cs
Normal file
60
GFramework.Core/coroutine/instructions/WaitForProgress.cs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
using GFramework.Core.Abstractions.coroutine;
|
||||||
|
|
||||||
|
namespace GFramework.Core.coroutine.instructions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 带进度回调的等待指令
|
||||||
|
/// </summary>
|
||||||
|
public class WaitForProgress : IYieldInstruction
|
||||||
|
{
|
||||||
|
private readonly double _duration;
|
||||||
|
private readonly Action<float> _onProgress;
|
||||||
|
private double _elapsed;
|
||||||
|
private bool _progressCompleted;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化等待进度指令
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="duration">总持续时间(秒)</param>
|
||||||
|
/// <param name="onProgress">进度回调,参数为0-1之间的进度值</param>
|
||||||
|
public WaitForProgress(double duration, Action<float> onProgress)
|
||||||
|
{
|
||||||
|
if (duration <= 0)
|
||||||
|
throw new ArgumentException("Duration must be positive", nameof(duration));
|
||||||
|
|
||||||
|
_duration = duration;
|
||||||
|
_onProgress = onProgress ?? throw new ArgumentNullException(nameof(onProgress));
|
||||||
|
_elapsed = 0;
|
||||||
|
_progressCompleted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 更新方法
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="deltaTime">时间增量</param>
|
||||||
|
public void Update(double deltaTime)
|
||||||
|
{
|
||||||
|
if (_progressCompleted)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_elapsed += deltaTime;
|
||||||
|
|
||||||
|
// 计算进度并回调
|
||||||
|
if (_elapsed >= _duration)
|
||||||
|
{
|
||||||
|
_elapsed = _duration;
|
||||||
|
_progressCompleted = true;
|
||||||
|
_onProgress(1.0f);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var progress = (float)(_elapsed / _duration);
|
||||||
|
_onProgress(progress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取等待是否已完成
|
||||||
|
/// </summary>
|
||||||
|
public bool IsDone => _progressCompleted;
|
||||||
|
}
|
||||||
60
GFramework.Core/coroutine/instructions/WaitForTask.T.cs
Normal file
60
GFramework.Core/coroutine/instructions/WaitForTask.T.cs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
using GFramework.Core.Abstractions.coroutine;
|
||||||
|
|
||||||
|
namespace GFramework.Core.coroutine.instructions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 等待泛型Task完成的等待指令
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Task返回值的类型</typeparam>
|
||||||
|
public sealed class WaitForTask<T> : IYieldInstruction
|
||||||
|
{
|
||||||
|
private readonly Task<T> _task;
|
||||||
|
private volatile bool _done;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化等待泛型Task的指令
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="task">要等待完成的Task</param>
|
||||||
|
public WaitForTask(Task<T> task)
|
||||||
|
{
|
||||||
|
_task = task ?? throw new ArgumentNullException(nameof(task));
|
||||||
|
|
||||||
|
// 检查Task是否已经完成
|
||||||
|
if (_task.IsCompleted)
|
||||||
|
{
|
||||||
|
_done = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 注册完成回调
|
||||||
|
_task.ContinueWith(_ =>
|
||||||
|
{
|
||||||
|
_done = true;
|
||||||
|
}, TaskContinuationOptions.ExecuteSynchronously);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 更新方法,用于处理时间更新逻辑
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="deltaTime">时间增量</param>
|
||||||
|
public void Update(double deltaTime)
|
||||||
|
{
|
||||||
|
// Task的完成由ContinueWith回调设置
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取等待是否已完成
|
||||||
|
/// </summary>
|
||||||
|
public bool IsDone => _done;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取Task的结果值
|
||||||
|
/// </summary>
|
||||||
|
public T Result => _task.GetAwaiter().GetResult();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取Task的异常(如果有)
|
||||||
|
/// </summary>
|
||||||
|
public Exception? Exception => _task.Exception;
|
||||||
|
}
|
||||||
57
GFramework.Core/coroutine/instructions/WaitForTask.cs
Normal file
57
GFramework.Core/coroutine/instructions/WaitForTask.cs
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
using GFramework.Core.Abstractions.coroutine;
|
||||||
|
|
||||||
|
namespace GFramework.Core.coroutine.instructions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 等待Task完成的等待指令
|
||||||
|
/// </summary>
|
||||||
|
/// <summary>
|
||||||
|
/// 等待Task完成的等待指令
|
||||||
|
/// </summary>
|
||||||
|
public sealed class WaitForTask : IYieldInstruction
|
||||||
|
{
|
||||||
|
private readonly Task _task;
|
||||||
|
private volatile bool _done;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化等待Task的指令
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="task">要等待完成的Task</param>
|
||||||
|
public WaitForTask(Task task)
|
||||||
|
{
|
||||||
|
_task = task ?? throw new ArgumentNullException(nameof(task));
|
||||||
|
|
||||||
|
// 检查Task是否已经完成
|
||||||
|
if (_task.IsCompleted)
|
||||||
|
{
|
||||||
|
_done = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 注册完成回调
|
||||||
|
_task.ContinueWith(_ =>
|
||||||
|
{
|
||||||
|
_done = true;
|
||||||
|
}, TaskContinuationOptions.ExecuteSynchronously);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 更新方法,用于处理时间更新逻辑
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="deltaTime">时间增量</param>
|
||||||
|
public void Update(double deltaTime)
|
||||||
|
{
|
||||||
|
// Task的完成由ContinueWith回调设置
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取等待是否已完成
|
||||||
|
/// </summary>
|
||||||
|
public bool IsDone => _done;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取Task的异常(如果有)
|
||||||
|
/// </summary>
|
||||||
|
public Exception? Exception => _task.Exception;
|
||||||
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
using GFramework.Core.Abstractions.coroutine;
|
using GFramework.Core.Abstractions.coroutine;
|
||||||
|
|
||||||
namespace GFramework.Core.coroutine;
|
namespace GFramework.Core.coroutine.instructions;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 表示等待一帧的等待指令实现
|
/// 表示等待一帧的等待指令实现
|
||||||
@ -1,6 +1,6 @@
|
|||||||
using GFramework.Core.Abstractions.coroutine;
|
using GFramework.Core.Abstractions.coroutine;
|
||||||
|
|
||||||
namespace GFramework.Core.coroutine;
|
namespace GFramework.Core.coroutine.instructions;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 表示一个等待直到指定条件满足的协程指令
|
/// 表示一个等待直到指定条件满足的协程指令
|
||||||
@ -1,6 +1,6 @@
|
|||||||
using GFramework.Core.Abstractions.coroutine;
|
using GFramework.Core.Abstractions.coroutine;
|
||||||
|
|
||||||
namespace GFramework.Core.coroutine;
|
namespace GFramework.Core.coroutine.instructions;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 表示一个等待条件为假时才完成的协程指令
|
/// 表示一个等待条件为假时才完成的协程指令
|
||||||
@ -1,6 +1,7 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using GFramework.Core.Abstractions.coroutine;
|
using GFramework.Core.Abstractions.coroutine;
|
||||||
using GFramework.Core.coroutine;
|
using GFramework.Core.coroutine;
|
||||||
|
using GFramework.Core.coroutine.instructions;
|
||||||
using Godot;
|
using Godot;
|
||||||
|
|
||||||
namespace GFramework.Godot.coroutine;
|
namespace GFramework.Godot.coroutine;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user