diff --git a/GFramework.Core.Tests/coroutine/AsyncOperationTests.cs b/GFramework.Core.Tests/coroutine/AsyncOperationTests.cs new file mode 100644 index 0000000..fb41180 --- /dev/null +++ b/GFramework.Core.Tests/coroutine/AsyncOperationTests.cs @@ -0,0 +1,342 @@ +using GFramework.Core.Abstractions.coroutine; +using GFramework.Core.coroutine.instructions; +using NUnit.Framework; + +namespace GFramework.Core.Tests.coroutine; + +/// +/// AsyncOperation的单元测试类 +/// 测试内容包括: +/// - 初始化状态 +/// - 完成和状态检查 +/// - 异常处理 +/// - 延续操作 +/// - GetAwaiter +/// - IsCompleted属性 +/// +[TestFixture] +public class AsyncOperationTests +{ + /// + /// 验证AsyncOperation初始状态为未完成 + /// + [Test] + public void AsyncOperation_Should_Not_Be_Done_Initially() + { + var op = new AsyncOperation(); + + Assert.That(op.IsDone, Is.False); + } + + /// + /// 验证AsyncOperation初始状态IsCompleted为false + /// + [Test] + public void AsyncOperation_Should_Not_Be_Completed_Initially() + { + var op = new AsyncOperation(); + + Assert.That(op.IsCompleted, Is.False); + } + + /// + /// 验证SetCompleted后IsDone应该为true + /// + [Test] + public void SetCompleted_Should_Set_IsDone_To_True() + { + var op = new AsyncOperation(); + + op.SetCompleted(); + + Assert.That(op.IsDone, Is.True); + } + + /// + /// 验证SetCompleted后IsCompleted应该为true + /// + [Test] + public void SetCompleted_Should_Set_IsCompleted_To_True() + { + var op = new AsyncOperation(); + + op.SetCompleted(); + + Assert.That(op.IsCompleted, Is.True); + } + + /// + /// 验证SetCompleted只能被调用一次 + /// + [Test] + public void SetCompleted_Should_Be_Idempotent() + { + var op = new AsyncOperation(); + + op.SetCompleted(); + op.SetCompleted(); + op.SetCompleted(); + + Assert.That(op.IsDone, Is.True); + } + + /// + /// 验证SetException后IsDone应该为true + /// + [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); + } + + /// + /// 验证SetException后Task应该包含异常 + /// + [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()); + } + + /// + /// 验证OnCompleted应该在已完成时立即执行延续 + /// + [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); + } + + /// + /// 验证OnCompleted应该在未完成时不立即执行延续 + /// + [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); + } + + /// + /// 验证延续应该在SetCompleted后被调用 + /// + [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); + } + + /// + /// 验证多个延续应该都能被调用 + /// + [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)); + } + + /// + /// 验证延续应该在SetException后被调用 + /// + [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); + } + + /// + /// 验证SetCompleted后设置的延续也应该被调用 + /// + [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); + } + + /// + /// 验证GetAwaiter应该返回自身 + /// + [Test] + public void GetAwaiter_Should_Return_Self() + { + var op = new AsyncOperation(); + + var awaiter = op.GetAwaiter(); + + Assert.That(awaiter, Is.SameAs(op)); + } + + /// + /// 验证Update方法不应该改变状态 + /// + [Test] + public void Update_Should_Not_Change_State() + { + var op = new AsyncOperation(); + + op.Update(0.1); + + Assert.That(op.IsDone, Is.False); + } + + /// + /// 验证AsyncOperation实现IYieldInstruction接口 + /// + [Test] + public void AsyncOperation_Should_Implement_IYieldInstruction() + { + var op = new AsyncOperation(); + + Assert.That(op, Is.InstanceOf()); + } + + /// + /// 验证Task属性应该返回有效的Task + /// + [Test] + public void Task_Property_Should_Return_Valid_Task() + { + var op = new AsyncOperation(); + + Assert.That(op.Task, Is.Not.Null); + } + + /// + /// 验证SetCompleted后Task应该完成 + /// + [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); + } + + /// + /// 验证SetException后Task应该失败 + /// + [Test] + public void Task_Should_Fault_After_SetException() + { + var op = new AsyncOperation(); + + op.SetException(new InvalidOperationException("Test")); + + Assert.That(op.Task.IsFaulted, Is.True); + } + + /// + /// 验证SetCompleted只能设置一次 + /// + [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); + } + + /// + /// 验证SetException只能在未完成时设置 + /// + [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); + } + + /// + /// 验证SetCompleted不能在SetException后设置 + /// + [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); + } + + /// + /// 验证延续抛出的异常应该被捕获 + /// + [Test] + public void Continuation_Exception_Should_Be_Caught() + { + var op = new AsyncOperation(); + + op.OnCompleted(() => throw new InvalidOperationException("Test exception")); + + Assert.DoesNotThrow(() => op.SetCompleted()); + } +} diff --git a/GFramework.Core.Tests/coroutine/CoroutineHelperTests.cs b/GFramework.Core.Tests/coroutine/CoroutineHelperTests.cs index 1e89e24..965df34 100644 --- a/GFramework.Core.Tests/coroutine/CoroutineHelperTests.cs +++ b/GFramework.Core.Tests/coroutine/CoroutineHelperTests.cs @@ -1,5 +1,6 @@ using GFramework.Core.Abstractions.coroutine; using GFramework.Core.coroutine; +using GFramework.Core.coroutine.instructions; using NUnit.Framework; namespace GFramework.Core.Tests.coroutine; diff --git a/GFramework.Core.Tests/coroutine/CoroutineSchedulerTests.cs b/GFramework.Core.Tests/coroutine/CoroutineSchedulerTests.cs index 0a30adb..9e8e43d 100644 --- a/GFramework.Core.Tests/coroutine/CoroutineSchedulerTests.cs +++ b/GFramework.Core.Tests/coroutine/CoroutineSchedulerTests.cs @@ -1,5 +1,6 @@ using GFramework.Core.Abstractions.coroutine; using GFramework.Core.coroutine; +using GFramework.Core.coroutine.instructions; using NUnit.Framework; namespace GFramework.Core.Tests.coroutine; diff --git a/GFramework.Core.Tests/coroutine/TaskCoroutineExtensionsTests.cs b/GFramework.Core.Tests/coroutine/TaskCoroutineExtensionsTests.cs new file mode 100644 index 0000000..780d207 --- /dev/null +++ b/GFramework.Core.Tests/coroutine/TaskCoroutineExtensionsTests.cs @@ -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; + +/// +/// TaskCoroutineExtensions的单元测试类 +/// 测试内容包括: +/// - AsCoroutineInstruction方法 +/// - StartTaskAsCoroutine方法 +/// +[TestFixture] +public class TaskCoroutineExtensionsTests +{ + /// + /// 验证AsCoroutineInstruction应该返回WaitForTask + /// + [Test] + public void AsCoroutineInstruction_Should_Return_WaitForTask() + { + var task = Task.CompletedTask; + var instruction = task.AsCoroutineInstruction(); + + Assert.That(instruction, Is.InstanceOf()); + } + + /// + /// 验证AsCoroutineInstruction应该返回WaitForTask + /// + [Test] + public void AsCoroutineInstructionOfT_Should_Return_WaitForTaskOfT() + { + var task = Task.FromResult(42); + var instruction = task.AsCoroutineInstruction(); + + Assert.That(instruction, Is.InstanceOf>()); + } + + /// + /// 验证AsCoroutineInstruction可以处理已完成的Task + /// + [Test] + public void AsCoroutineInstruction_Should_Handle_Completed_Task() + { + var task = Task.CompletedTask; + var instruction = task.AsCoroutineInstruction(); + + Assert.That(instruction, Is.InstanceOf()); + } + + /// + /// 验证AsCoroutineInstruction可以处理已完成的Task + /// + [Test] + public void AsCoroutineInstructionOfT_Should_Handle_Completed_Task() + { + var task = Task.FromResult(42); + var instruction = task.AsCoroutineInstruction(); + + Assert.That(instruction, Is.InstanceOf>()); + } + + /// + /// 验证AsCoroutineInstruction应该能够访问Task结果 + /// + [Test] + public void AsCoroutineInstructionOfT_Should_Access_Task_Result() + { + var task = Task.FromResult(42); + var instruction = task.AsCoroutineInstruction(); + + task.Wait(); + + Assert.That(instruction.Result, Is.EqualTo(42)); + } + + /// + /// 验证AsCoroutineInstruction应该处理null Task(抛出异常) + /// + [Test] + public void AsCoroutineInstruction_Should_Handle_Null_Task() + { + Task task = null!; + + Assert.Throws(() => task.AsCoroutineInstruction()); + } + + /// + /// 验证AsCoroutineInstruction应该处理null Task(抛出异常) + /// + [Test] + public void AsCoroutineInstructionOfT_Should_Handle_Null_Task() + { + Task task = null!; + + Assert.Throws(() => task.AsCoroutineInstruction()); + } + + /// + /// 验证AsCoroutineInstruction应该处理失败的Task + /// + [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()); + } + + /// + /// 验证AsCoroutineInstruction应该处理失败的Task + /// + [Test] + public void AsCoroutineInstructionOfT_Should_Handle_Faulted_Task() + { + var task = Task.FromException(new InvalidOperationException("Test exception")); + var instruction = task.AsCoroutineInstruction(); + + Assert.That(instruction, Is.InstanceOf>()); + } + + /// + /// 验证StartTaskAsCoroutine应该返回有效的协程句柄 + /// + [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); + } + + /// + /// 验证StartTaskAsCoroutine应该返回有效的协程句柄 + /// + [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); + } + + /// + /// 验证StartTaskAsCoroutine应该等待Task完成 + /// + [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(); + + 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)); + } + + /// + /// 验证StartTaskAsCoroutine应该等待Task完成 + /// + [Test] + public void StartTaskAsCoroutineOfT_Should_Wait_For_Task_Completion() + { + var timeSource = new TestTimeSource(); + var scheduler = new CoroutineScheduler(timeSource, instanceId: 1); + + var tcs = new TaskCompletionSource(); + + 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)); + } + + /// + /// 验证StartTaskAsCoroutine应该处理已完成的Task + /// + [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)); + } + + /// + /// 验证StartTaskAsCoroutine应该处理失败的Task + /// + [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()); + } + + /// + /// 验证StartTaskAsCoroutine应该处理失败的Task + /// + [Test] + public void StartTaskAsCoroutineOfT_Should_Handle_Faulted_Task() + { + var timeSource = new TestTimeSource(); + var scheduler = new CoroutineScheduler(timeSource, instanceId: 1); + var task = Task.FromException(new InvalidOperationException("Test")); + + scheduler.StartTaskAsCoroutine(task); + + Assert.DoesNotThrow(() => scheduler.Update()); + } + + + /// + /// 验证StartTaskAsCoroutine应该与调度器正常协作 + /// + [Test] + public void StartTaskAsCoroutine_Should_Work_With_Scheduler() + { + var timeSource = new TestTimeSource(); + var scheduler = new CoroutineScheduler(timeSource, instanceId: 1); + + var tcs = new TaskCompletionSource(); + scheduler.StartTaskAsCoroutine(tcs.Task); + + var tcs2 = new TaskCompletionSource(); + 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)); + } + + /// + /// 测试用时间源类 + /// + private class TestTimeSource : ITimeSource + { + public double CurrentTime { get; private set; } + public double DeltaTime { get; private set; } + + public void Update() + { + DeltaTime = 0.1; + CurrentTime += DeltaTime; + } + } +} diff --git a/GFramework.Core.Tests/coroutine/WaitForProgressTests.cs b/GFramework.Core.Tests/coroutine/WaitForProgressTests.cs new file mode 100644 index 0000000..6bc1b99 --- /dev/null +++ b/GFramework.Core.Tests/coroutine/WaitForProgressTests.cs @@ -0,0 +1,294 @@ +using GFramework.Core.Abstractions.coroutine; +using GFramework.Core.coroutine.instructions; +using NUnit.Framework; + +namespace GFramework.Core.Tests.coroutine; + +/// +/// WaitForProgress的单元测试类 +/// 测试内容包括: +/// - 初始化和基本功能 +/// - 进度回调 +/// - 边界条件 +/// - 异常处理 +/// +[TestFixture] +public class WaitForProgressTests +{ + /// + /// 验证WaitForProgress初始状态为未完成 + /// + [Test] + public void WaitForProgress_Should_Not_Be_Done_Initially() + { + var progressValues = new List(); + var wait = new WaitForProgress(1.0, progressValues.Add); + + Assert.That(wait.IsDone, Is.False); + } + + /// + /// 验证WaitForProgress应该在指定时间后完成 + /// + [Test] + public void WaitForProgress_Should_Be_Done_After_Duration() + { + var progressValues = new List(); + 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); + } + + /// + /// 验证WaitForProgress应该在Update时调用进度回调 + /// + [Test] + public void WaitForProgress_Should_Call_Progress_Callback_On_Update() + { + var progressValues = new List(); + var wait = new WaitForProgress(1.0, progressValues.Add); + + wait.Update(0.25); + + Assert.That(progressValues.Count, Is.EqualTo(1)); + } + + /// + /// 验证进度值应该在0到1之间 + /// + [Test] + public void WaitForProgress_Should_Have_Progress_Between_0_And_1() + { + var progressValues = new List(); + 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)); + } + } + + /// + /// 验证进度值应该随着时间增加 + /// + [Test] + public void WaitForProgress_Should_Increase_Progress_Over_Time() + { + var progressValues = new List(); + 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)); + } + + /// + /// 验证WaitForProgress应该抛出ArgumentNullException当回调为null + /// + [Test] + public void WaitForProgress_Should_Throw_ArgumentNullException_When_Callback_Is_Null() + { + Assert.Throws(() => new WaitForProgress(1.0, null!)); + } + + /// + /// 验证WaitForProgress应该抛出ArgumentException当duration为0 + /// + [Test] + public void WaitForProgress_Should_Throw_ArgumentException_When_Duration_Is_Zero() + { + Assert.Throws(() => new WaitForProgress(0, _ => { })); + } + + /// + /// 验证WaitForProgress应该抛出ArgumentException当duration为负数 + /// + [Test] + public void WaitForProgress_Should_Throw_ArgumentException_When_Duration_Is_Negative() + { + Assert.Throws(() => new WaitForProgress(-1.0, _ => { })); + } + + /// + /// 验证WaitForProgress应该处理超过duration的更新 + /// + [Test] + public void WaitForProgress_Should_Clamp_Progress_To_1_When_Exceeding_Duration() + { + var progressValues = new List(); + 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)); + } + + /// + /// 验证WaitForProgress可以处理精确的duration + /// + [Test] + public void WaitForProgress_Should_Handle_Exact_Duration() + { + var progressValues = new List(); + 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)); + } + + /// + /// 验证WaitForProgress可以处理不同的delta time + /// + [Test] + public void WaitForProgress_Should_Handle_Variable_Delta_Time() + { + var progressValues = new List(); + 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)); + } + + /// + /// 验证WaitForProgress可以处理多次Update + /// + [Test] + public void WaitForProgress_Should_Handle_Multiple_Updates() + { + var progressValues = new List(); + 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)); + } + + /// + /// 验证WaitForProgress应该确保最后一个进度为1.0 + /// + [Test] + public void WaitForProgress_Should_Ensure_Final_Progress_Is_1() + { + var progressValues = new List(); + 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)); + } + + /// + /// 验证WaitForProgress实现IYieldInstruction接口 + /// + [Test] + public void WaitForProgress_Should_Implement_IYieldInstruction() + { + var progressValues = new List(); + var wait = new WaitForProgress(1.0, progressValues.Add); + + Assert.That(wait, Is.InstanceOf()); + } + + /// + /// 验证WaitForProgress可以处理很短的duration + /// + [Test] + public void WaitForProgress_Should_Handle_Short_Duration() + { + var progressValues = new List(); + var wait = new WaitForProgress(0.001, progressValues.Add); + + wait.Update(0.001); + + Assert.That(wait.IsDone, Is.True); + } + + /// + /// 验证WaitForProgress可以处理很长的duration + /// + [Test] + public void WaitForProgress_Should_Handle_Long_Duration() + { + var progressValues = new List(); + 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); + } + + /// + /// 验证WaitForProgress在完成前不会超过1.0 + /// + [Test] + public void WaitForProgress_Should_Not_Exceed_1_Before_Completion() + { + var progressValues = new List(); + 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)); + } + + /// + /// 验证WaitForProgress的Update方法不影响未完成状态 + /// + [Test] + public void WaitForProgress_Update_Should_Not_Affect_Before_Completion() + { + var progressValues = new List(); + 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); + } +} diff --git a/GFramework.Core.Tests/coroutine/WaitForTaskTests.cs b/GFramework.Core.Tests/coroutine/WaitForTaskTests.cs new file mode 100644 index 0000000..449cdc2 --- /dev/null +++ b/GFramework.Core.Tests/coroutine/WaitForTaskTests.cs @@ -0,0 +1,311 @@ +using GFramework.Core.Abstractions.coroutine; +using GFramework.Core.coroutine.instructions; +using NUnit.Framework; + +namespace GFramework.Core.Tests.coroutine; + +/// +/// WaitForTask的单元测试类 +/// 测试内容包括: +/// - WaitForTask初始化和等待 +/// - WaitForTask初始化、等待和结果获取 +/// - 异常处理 +/// - 边界条件 +/// +[TestFixture] +public class WaitForTaskTests +{ + /// + /// 验证WaitForTask初始状态为未完成 + /// + [Test] + public void WaitForTask_Should_Not_Be_Done_Initially() + { + var task = Task.Run(() => { }); + var wait = new WaitForTask(task); + + Assert.That(wait.IsDone, Is.False); + } + + /// + /// 验证WaitForTask应该在Task完成后完成 + /// + [Test] + public void WaitForTask_Should_Be_Done_After_Task_Completes() + { + var tcs = new TaskCompletionSource(); + 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); + } + + /// + /// 验证WaitForTask应该处理已完成的Task + /// + [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); + } + + /// + /// 验证WaitForTask应该处理失败的Task + /// + [Test] + public void WaitForTask_Should_Handle_Faulted_Task() + { + var tcs = new TaskCompletionSource(); + var wait = new WaitForTask(tcs.Task); + + tcs.SetException(new InvalidOperationException("Test exception")); + + Task.Delay(100).Wait(); + + Assert.That(wait.IsDone, Is.True); + } + + /// + /// 验证WaitForTask应该处理取消的Task + /// + [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); + } + + /// + /// 验证WaitForTask应该抛出ArgumentNullException当task为null + /// + [Test] + public void WaitForTask_Should_Throw_ArgumentNullException_When_Task_Is_Null() + { + Assert.Throws(() => new WaitForTask(null!)); + } + + /// + /// 验证WaitForTask初始状态为未完成 + /// + [Test] + public void WaitForTaskOfT_Should_Not_Be_Done_Initially() + { + var tcs = new TaskCompletionSource(); + var wait = new WaitForTask(tcs.Task); + + Assert.That(wait.IsDone, Is.False); + } + + /// + /// 验证WaitForTask应该在Task完成后完成 + /// + [Test] + public void WaitForTaskOfT_Should_Be_Done_After_Task_Completes() + { + var tcs = new TaskCompletionSource(); + var wait = new WaitForTask(tcs.Task); + + Assert.That(wait.IsDone, Is.False); + + tcs.SetResult(42); + + Task.Delay(100).Wait(); + + Assert.That(wait.IsDone, Is.True); + } + + /// + /// 验证WaitForTask应该返回Task的结果 + /// + [Test] + public void WaitForTaskOfT_Should_Return_Task_Result() + { + var tcs = new TaskCompletionSource(); + var wait = new WaitForTask(tcs.Task); + + tcs.SetResult(42); + + Task.Delay(100).Wait(); + + Assert.That(wait.Result, Is.EqualTo(42)); + } + + /// + /// 验证WaitForTask应该处理已完成的Task + /// + [Test] + public void WaitForTaskOfT_Should_Handle_Already_Completed_Task() + { + var task = Task.FromResult(42); + + Task.Delay(100).Wait(); + + var wait = new WaitForTask(task); + + Task.Delay(100).Wait(); + + Assert.That(wait.IsDone, Is.True); + Assert.That(wait.Result, Is.EqualTo(42)); + } + + /// + /// 验证WaitForTask应该处理失败的Task + /// + [Test] + public void WaitForTaskOfT_Should_Handle_Faulted_Task() + { + var tcs = new TaskCompletionSource(); + var wait = new WaitForTask(tcs.Task); + + tcs.SetException(new InvalidOperationException("Test exception")); + + Task.Delay(100).Wait(); + + Assert.That(wait.IsDone, Is.True); + + Assert.Throws(() => { var _ = wait.Result; }); + } + + /// + /// 验证WaitForTask应该抛出ArgumentNullException当task为null + /// + [Test] + public void WaitForTaskOfT_Should_Throw_ArgumentNullException_When_Task_Is_Null() + { + Assert.Throws(() => new WaitForTask(null!)); + } + + /// + /// 验证WaitForTask实现IYieldInstruction接口 + /// + [Test] + public void WaitForTask_Should_Implement_IYieldInstruction() + { + var task = Task.CompletedTask; + var wait = new WaitForTask(task); + + Assert.That(wait, Is.InstanceOf()); + } + + /// + /// 验证WaitForTask实现IYieldInstruction接口 + /// + [Test] + public void WaitForTaskOfT_Should_Implement_IYieldInstruction() + { + var task = Task.FromResult(42); + var wait = new WaitForTask(task); + + Assert.That(wait, Is.InstanceOf()); + } + + /// + /// 验证WaitForTask的Update方法不影响状态 + /// + [Test] + public void WaitForTask_Update_Should_Not_Affect_State() + { + var tcs = new TaskCompletionSource(); + 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); + } + + /// + /// 验证WaitForTask的Update方法不影响状态 + /// + [Test] + public void WaitForTaskOfT_Update_Should_Not_Affect_State() + { + var tcs = new TaskCompletionSource(); + 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); + } + + /// + /// 验证WaitForTask在延迟完成后能够正确等待 + /// + [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); + } + + /// + /// 验证WaitForTask在异步操作完成后能够正确获取结果 + /// + [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(task); + + await task; + + Task.Delay(100).Wait(); + + Assert.That(wait.IsDone, Is.True); + Assert.That(wait.Result, Is.EqualTo(expectedValue)); + } + + /// + /// 验证WaitForTask应该处理长时间运行的Task + /// + [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); + } +} diff --git a/GFramework.Core.Tests/coroutine/YieldInstructionTests.cs b/GFramework.Core.Tests/coroutine/YieldInstructionTests.cs index 7f6faf9..3509345 100644 --- a/GFramework.Core.Tests/coroutine/YieldInstructionTests.cs +++ b/GFramework.Core.Tests/coroutine/YieldInstructionTests.cs @@ -1,5 +1,5 @@ using GFramework.Core.Abstractions.coroutine; -using GFramework.Core.coroutine; +using GFramework.Core.coroutine.instructions; using NUnit.Framework; namespace GFramework.Core.Tests.coroutine; diff --git a/GFramework.Core/coroutine/CoroutineHelper.cs b/GFramework.Core/coroutine/CoroutineHelper.cs index 59bfcd6..4417784 100644 --- a/GFramework.Core/coroutine/CoroutineHelper.cs +++ b/GFramework.Core/coroutine/CoroutineHelper.cs @@ -1,7 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Threading; using GFramework.Core.Abstractions.coroutine; +using GFramework.Core.coroutine.instructions; namespace GFramework.Core.coroutine; @@ -59,6 +57,17 @@ public static class CoroutineHelper return new WaitWhile(predicate); } + /// + /// 等待指定时间并提供进度回调 + /// + /// 等待的持续时间(秒) + /// 进度回调函数,接收0-1之间的进度值 + /// 等待进度指令 + public static WaitForProgress WaitForProgress(double duration, Action onProgress) + { + return new WaitForProgress(duration, onProgress); + } + /// /// 延迟调用指定的委托 /// diff --git a/GFramework.Core/coroutine/CoroutineScheduler.cs b/GFramework.Core/coroutine/CoroutineScheduler.cs index 2ccf9a3..f91be10 100644 --- a/GFramework.Core/coroutine/CoroutineScheduler.cs +++ b/GFramework.Core/coroutine/CoroutineScheduler.cs @@ -368,4 +368,8 @@ public sealed class CoroutineScheduler( } #endregion + public bool IsCoroutineAlive(CoroutineHandle handle) + { + return _metadata.ContainsKey(handle); + } } \ No newline at end of file diff --git a/GFramework.Core/coroutine/extensions/CoroutineExtensions.cs b/GFramework.Core/coroutine/extensions/CoroutineExtensions.cs new file mode 100644 index 0000000..b18eb8d --- /dev/null +++ b/GFramework.Core/coroutine/extensions/CoroutineExtensions.cs @@ -0,0 +1,131 @@ +using GFramework.Core.Abstractions.coroutine; +using GFramework.Core.coroutine.instructions; + +namespace GFramework.Core.coroutine.extensions; + +/// +/// 协程相关的扩展方法 +/// +public static class CoroutineExtensions +{ + /// + /// 在指定时间间隔内重复执行动作的协程 + /// + /// 执行间隔时间(秒) + /// 要重复执行的动作 + /// 重复次数,如果为null则无限重复 + /// 协程枚举器 + public static IEnumerator 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++; + } + } + + /// + /// 在指定延迟后执行动作的协程 + /// + /// 延迟时间(秒) + /// 要执行的动作 + /// 协程枚举器 + public static IEnumerator ExecuteAfter( + double delay, + Action? action) + { + if (delay < 0) + { + yield break; + } + + yield return new Delay(delay); + action?.Invoke(); + } + + /// + /// 顺序执行多个协程 + /// + /// 要顺序执行的协程集合 + /// 协程枚举器 + public static IEnumerator Sequence( + params IEnumerator[] coroutines) + { + foreach (var coroutine in coroutines) + { + while (coroutine.MoveNext()) + { + yield return coroutine.Current; + } + + // 清理协程 + coroutine.Dispose(); + } + } + + /// + /// 并行执行多个协程(等待所有协程完成) + /// 注意:这需要协程调度器的支持,这里提供一个包装器返回多个句柄 + /// + /// 协程调度器 + /// 要并行执行的协程集合 + /// 等待所有协程完成的协程 + public static IEnumerator ParallelCoroutines( + this CoroutineScheduler scheduler, + params IEnumerator[]? coroutines) + { + if (coroutines == null || coroutines.Length == 0) + { + yield break; + } + + // 启动所有协程并收集句柄 + var handles = new List(); + foreach (var coroutine in coroutines) + { + var handle = scheduler.Run(coroutine); + handles.Add(handle); + } + + // 等待所有协程完成 + yield return new WaitForAllCoroutines(scheduler,handles); + } + + /// + /// 带进度回调的等待协程 + /// + /// 总等待时间(秒) + /// 进度回调,参数为0-1之间的进度值 + /// 协程枚举器 + public static IEnumerator WaitForSecondsWithProgress( + double totalTime, + Action? 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); + } + } +} diff --git a/GFramework.Core/coroutine/extensions/TaskCoroutineExtensions.cs b/GFramework.Core/coroutine/extensions/TaskCoroutineExtensions.cs new file mode 100644 index 0000000..2678f2f --- /dev/null +++ b/GFramework.Core/coroutine/extensions/TaskCoroutineExtensions.cs @@ -0,0 +1,70 @@ +using GFramework.Core.Abstractions.coroutine; +using GFramework.Core.coroutine.instructions; + +namespace GFramework.Core.coroutine.extensions; + +/// +/// Task与协程之间的扩展方法 +/// +public static class TaskCoroutineExtensions +{ + /// + /// 将Task转换为协程等待指令 + /// + /// 要等待的Task + /// 等待Task的协程指令 + public static WaitForTask AsCoroutineInstruction(this Task task) + { + return new WaitForTask(task); + } + + /// + /// 将泛型Task转换为协程等待指令 + /// + /// Task返回值的类型 + /// 要等待的Task + /// 等待Task的协程指令 + public static WaitForTask AsCoroutineInstruction(this Task task) + { + return new WaitForTask(task); + } + + /// + /// 在调度器中启动一个Task并等待其完成 + /// + /// 协程调度器 + /// 要等待的Task + /// 协程句柄 + public static CoroutineHandle StartTaskAsCoroutine(this CoroutineScheduler scheduler, Task task) + { + return scheduler.Run(CreateTaskCoroutine(task)); + } + + /// + /// 在调度器中启动一个泛型Task并等待其完成 + /// + /// Task返回值的类型 + /// 协程调度器 + /// 要等待的Task + /// 协程句柄 + public static CoroutineHandle StartTaskAsCoroutine(this CoroutineScheduler scheduler, Task task) + { + return scheduler.Run(CreateTaskCoroutine(task)); + } + + /// + /// 创建等待Task的协程 + /// + private static IEnumerator CreateTaskCoroutine(Task task) + { + yield return task.AsCoroutineInstruction(); + } + + /// + /// 创建等待泛型Task的协程 + /// + private static IEnumerator CreateTaskCoroutine(Task task) + { + yield return task.AsCoroutineInstruction(); + } +} \ No newline at end of file diff --git a/GFramework.Core/coroutine/instructions/AsyncOperation.cs b/GFramework.Core/coroutine/instructions/AsyncOperation.cs new file mode 100644 index 0000000..73f7fb5 --- /dev/null +++ b/GFramework.Core/coroutine/instructions/AsyncOperation.cs @@ -0,0 +1,141 @@ +using System.Runtime.CompilerServices; +using GFramework.Core.Abstractions.coroutine; + +namespace GFramework.Core.coroutine.instructions; + +/// +/// 异步操作包装器,用于桥接协程系统和async/await异步编程模型 +/// +public class AsyncOperation : IYieldInstruction, INotifyCompletion +{ + private readonly TaskCompletionSource _tcs = new(); + private volatile bool _completed; + private volatile Action? _continuation; + + /// + /// 获取异步操作是否已完成 + /// + public bool IsDone => _completed; + + /// + /// 获取异步操作的任务 + /// + public Task Task => _tcs.Task; + + /// + /// 更新方法,用于处理时间更新逻辑 + /// + /// 时间增量 + public void Update(double deltaTime) + { + // 由外部调用SetCompleted来更新状态 + } + + /// + /// 标记异步操作已完成 + /// + 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 + { + // 忽略延续中的异常 + } + } + } + + /// + /// 标记异步操作因异常而失败 + /// + /// 导致失败的异常 + 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 + { + // 忽略延续中的异常 + } + } + } + + /// + /// 设置延续操作 + /// + /// 要执行的延续操作 + 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(); + } + } + } + + /// + /// 获取异步操作结果 + /// + /// 操作结果 + public object? GetResult() + { + return _tcs.Task.GetAwaiter().GetResult(); + } + + /// + /// 获取awaiter对象 + /// + public AsyncOperation GetAwaiter() + { + return this; + } + + /// + /// 检查是否已完成 + /// + public bool IsCompleted => _completed; +} \ No newline at end of file diff --git a/GFramework.Core/coroutine/Delay.cs b/GFramework.Core/coroutine/instructions/Delay.cs similarity index 93% rename from GFramework.Core/coroutine/Delay.cs rename to GFramework.Core/coroutine/instructions/Delay.cs index 15a3fa0..6fbdea9 100644 --- a/GFramework.Core/coroutine/Delay.cs +++ b/GFramework.Core/coroutine/instructions/Delay.cs @@ -1,6 +1,6 @@ using GFramework.Core.Abstractions.coroutine; -namespace GFramework.Core.coroutine; +namespace GFramework.Core.coroutine.instructions; /// /// 延迟等待指令,实现IYieldInstruction接口,用于协程中的时间延迟 diff --git a/GFramework.Core/coroutine/instructions/WaitForAllCoroutines.cs b/GFramework.Core/coroutine/instructions/WaitForAllCoroutines.cs new file mode 100644 index 0000000..0a5b007 --- /dev/null +++ b/GFramework.Core/coroutine/instructions/WaitForAllCoroutines.cs @@ -0,0 +1,29 @@ + +using GFramework.Core.Abstractions.coroutine; + +namespace GFramework.Core.coroutine.instructions; + +/// +/// 等待所有协程完成的等待指令 +/// +public sealed class WaitForAllCoroutines( + CoroutineScheduler scheduler, + IReadOnlyList handles) + : IYieldInstruction +{ + private readonly CoroutineScheduler _scheduler = scheduler ?? throw new ArgumentNullException(nameof(scheduler)); + private readonly IReadOnlyList _handles = handles ?? throw new ArgumentNullException(nameof(handles)); + + public void Update(double deltaTime) + { + // 不需要做任何事 + } + + public bool IsDone + { + get + { + return _handles.All(handle => !_scheduler.IsCoroutineAlive(handle)); + } + } +} diff --git a/GFramework.Core/coroutine/WaitForCoroutine.cs b/GFramework.Core/coroutine/instructions/WaitForCoroutine.cs similarity index 93% rename from GFramework.Core/coroutine/WaitForCoroutine.cs rename to GFramework.Core/coroutine/instructions/WaitForCoroutine.cs index d2fb8da..7cfe9c4 100644 --- a/GFramework.Core/coroutine/WaitForCoroutine.cs +++ b/GFramework.Core/coroutine/instructions/WaitForCoroutine.cs @@ -1,6 +1,6 @@ using GFramework.Core.Abstractions.coroutine; -namespace GFramework.Core.coroutine; +namespace GFramework.Core.coroutine.instructions; /// /// 等待协程完成的指令类,实现IYieldInstruction接口 diff --git a/GFramework.Core/coroutine/WaitForFrames.cs b/GFramework.Core/coroutine/instructions/WaitForFrames.cs similarity index 93% rename from GFramework.Core/coroutine/WaitForFrames.cs rename to GFramework.Core/coroutine/instructions/WaitForFrames.cs index 6441463..10745ba 100644 --- a/GFramework.Core/coroutine/WaitForFrames.cs +++ b/GFramework.Core/coroutine/instructions/WaitForFrames.cs @@ -1,6 +1,6 @@ using GFramework.Core.Abstractions.coroutine; -namespace GFramework.Core.coroutine; +namespace GFramework.Core.coroutine.instructions; /// /// 等待指定帧数的等待指令类 diff --git a/GFramework.Core/coroutine/instructions/WaitForProgress.cs b/GFramework.Core/coroutine/instructions/WaitForProgress.cs new file mode 100644 index 0000000..c688de5 --- /dev/null +++ b/GFramework.Core/coroutine/instructions/WaitForProgress.cs @@ -0,0 +1,60 @@ +using GFramework.Core.Abstractions.coroutine; + +namespace GFramework.Core.coroutine.instructions; + +/// +/// 带进度回调的等待指令 +/// +public class WaitForProgress : IYieldInstruction +{ + private readonly double _duration; + private readonly Action _onProgress; + private double _elapsed; + private bool _progressCompleted; + + /// + /// 初始化等待进度指令 + /// + /// 总持续时间(秒) + /// 进度回调,参数为0-1之间的进度值 + public WaitForProgress(double duration, Action 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; + } + + /// + /// 更新方法 + /// + /// 时间增量 + 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); + } + } + + /// + /// 获取等待是否已完成 + /// + public bool IsDone => _progressCompleted; +} \ No newline at end of file diff --git a/GFramework.Core/coroutine/instructions/WaitForTask.T.cs b/GFramework.Core/coroutine/instructions/WaitForTask.T.cs new file mode 100644 index 0000000..8891799 --- /dev/null +++ b/GFramework.Core/coroutine/instructions/WaitForTask.T.cs @@ -0,0 +1,60 @@ +using GFramework.Core.Abstractions.coroutine; + +namespace GFramework.Core.coroutine.instructions; + +/// +/// 等待泛型Task完成的等待指令 +/// +/// Task返回值的类型 +public sealed class WaitForTask : IYieldInstruction +{ + private readonly Task _task; + private volatile bool _done; + + /// + /// 初始化等待泛型Task的指令 + /// + /// 要等待完成的Task + public WaitForTask(Task task) + { + _task = task ?? throw new ArgumentNullException(nameof(task)); + + // 检查Task是否已经完成 + if (_task.IsCompleted) + { + _done = true; + } + else + { + // 注册完成回调 + _task.ContinueWith(_ => + { + _done = true; + }, TaskContinuationOptions.ExecuteSynchronously); + } + } + + /// + /// 更新方法,用于处理时间更新逻辑 + /// + /// 时间增量 + public void Update(double deltaTime) + { + // Task的完成由ContinueWith回调设置 + } + + /// + /// 获取等待是否已完成 + /// + public bool IsDone => _done; + + /// + /// 获取Task的结果值 + /// + public T Result => _task.GetAwaiter().GetResult(); + + /// + /// 获取Task的异常(如果有) + /// + public Exception? Exception => _task.Exception; +} \ No newline at end of file diff --git a/GFramework.Core/coroutine/instructions/WaitForTask.cs b/GFramework.Core/coroutine/instructions/WaitForTask.cs new file mode 100644 index 0000000..1c4403e --- /dev/null +++ b/GFramework.Core/coroutine/instructions/WaitForTask.cs @@ -0,0 +1,57 @@ +using GFramework.Core.Abstractions.coroutine; + +namespace GFramework.Core.coroutine.instructions; + +/// +/// 等待Task完成的等待指令 +/// +/// +/// 等待Task完成的等待指令 +/// +public sealed class WaitForTask : IYieldInstruction +{ + private readonly Task _task; + private volatile bool _done; + + /// + /// 初始化等待Task的指令 + /// + /// 要等待完成的Task + public WaitForTask(Task task) + { + _task = task ?? throw new ArgumentNullException(nameof(task)); + + // 检查Task是否已经完成 + if (_task.IsCompleted) + { + _done = true; + } + else + { + // 注册完成回调 + _task.ContinueWith(_ => + { + _done = true; + }, TaskContinuationOptions.ExecuteSynchronously); + } + } + + /// + /// 更新方法,用于处理时间更新逻辑 + /// + /// 时间增量 + public void Update(double deltaTime) + { + // Task的完成由ContinueWith回调设置 + } + + /// + /// 获取等待是否已完成 + /// + public bool IsDone => _done; + + /// + /// 获取Task的异常(如果有) + /// + public Exception? Exception => _task.Exception; +} \ No newline at end of file diff --git a/GFramework.Core/coroutine/WaitOneFrame.cs b/GFramework.Core/coroutine/instructions/WaitOneFrame.cs similarity index 93% rename from GFramework.Core/coroutine/WaitOneFrame.cs rename to GFramework.Core/coroutine/instructions/WaitOneFrame.cs index 7fdeb19..9d03b92 100644 --- a/GFramework.Core/coroutine/WaitOneFrame.cs +++ b/GFramework.Core/coroutine/instructions/WaitOneFrame.cs @@ -1,6 +1,6 @@ using GFramework.Core.Abstractions.coroutine; -namespace GFramework.Core.coroutine; +namespace GFramework.Core.coroutine.instructions; /// /// 表示等待一帧的等待指令实现 diff --git a/GFramework.Core/coroutine/WaitUntil.cs b/GFramework.Core/coroutine/instructions/WaitUntil.cs similarity index 93% rename from GFramework.Core/coroutine/WaitUntil.cs rename to GFramework.Core/coroutine/instructions/WaitUntil.cs index d0e3f94..bf9cf8b 100644 --- a/GFramework.Core/coroutine/WaitUntil.cs +++ b/GFramework.Core/coroutine/instructions/WaitUntil.cs @@ -1,6 +1,6 @@ using GFramework.Core.Abstractions.coroutine; -namespace GFramework.Core.coroutine; +namespace GFramework.Core.coroutine.instructions; /// /// 表示一个等待直到指定条件满足的协程指令 diff --git a/GFramework.Core/coroutine/WaitWhile.cs b/GFramework.Core/coroutine/instructions/WaitWhile.cs similarity index 94% rename from GFramework.Core/coroutine/WaitWhile.cs rename to GFramework.Core/coroutine/instructions/WaitWhile.cs index 430fe45..c37674b 100644 --- a/GFramework.Core/coroutine/WaitWhile.cs +++ b/GFramework.Core/coroutine/instructions/WaitWhile.cs @@ -1,6 +1,6 @@ using GFramework.Core.Abstractions.coroutine; -namespace GFramework.Core.coroutine; +namespace GFramework.Core.coroutine.instructions; /// /// 表示一个等待条件为假时才完成的协程指令 diff --git a/GFramework.Godot/coroutine/Timing.cs b/GFramework.Godot/coroutine/Timing.cs index e7bdc8f..fd889a1 100644 --- a/GFramework.Godot/coroutine/Timing.cs +++ b/GFramework.Godot/coroutine/Timing.cs @@ -1,6 +1,7 @@ using System.Reflection; using GFramework.Core.Abstractions.coroutine; using GFramework.Core.coroutine; +using GFramework.Core.coroutine.instructions; using Godot; namespace GFramework.Godot.coroutine;