diff --git a/GFramework.Core.Tests/coroutine/DelayTests.cs b/GFramework.Core.Tests/coroutine/DelayTests.cs new file mode 100644 index 00000000..e3ff5dd5 --- /dev/null +++ b/GFramework.Core.Tests/coroutine/DelayTests.cs @@ -0,0 +1,66 @@ +using GFramework.Core.coroutine.instructions; +using NUnit.Framework; + +namespace GFramework.Core.Tests.coroutine +{ + [TestFixture] + public class DelayTests + { + [Test] + public void Constructor_SetsInitialRemainingTime() + { + // Arrange & Act + var delay = new Delay(2.5); + + // Assert + Assert.That(delay.IsDone, Is.False); + } + + [Test] + public void Update_ReducesRemainingTime() + { + // Arrange + var delay = new Delay(2.0); + + // Act + delay.Update(0.5); + + // Assert + Assert.That(delay.IsDone, Is.False); + } + + [Test] + public void Update_MultipleTimes_EventuallyCompletes() + { + // Arrange + var delay = new Delay(1.0); + + // Act + delay.Update(0.5); + delay.Update(0.6); // Total: 1.1 > 1.0, so should be done + + // Assert + Assert.That(delay.IsDone, Is.True); + } + + [Test] + public void NegativeTime_TreatedAsZero() + { + // Arrange & Act + var delay = new Delay(-1.0); + + // Assert + Assert.That(delay.IsDone, Is.True); + } + + [Test] + public void ZeroTime_CompletesImmediately() + { + // Arrange & Act + var delay = new Delay(0.0); + + // Assert + Assert.That(delay.IsDone, Is.True); + } + } +} \ No newline at end of file diff --git a/GFramework.Core.Tests/coroutine/WaitForConditionChangeTests.cs b/GFramework.Core.Tests/coroutine/WaitForConditionChangeTests.cs new file mode 100644 index 00000000..db91ee28 --- /dev/null +++ b/GFramework.Core.Tests/coroutine/WaitForConditionChangeTests.cs @@ -0,0 +1,124 @@ +using GFramework.Core.Abstractions.coroutine; +using GFramework.Core.coroutine.instructions; +using NUnit.Framework; + +namespace GFramework.Core.Tests.coroutine; + +/// +/// WaitForConditionChange的单元测试类 +/// +[TestFixture] +public class WaitForConditionChangeTests +{ + /// + /// 验证WaitForConditionChange初始状态为未完成 + /// + [Test] + public void WaitForConditionChange_Should_Not_Be_Done_Initially() + { + var condition = false; + var wait = new WaitForConditionChange(() => condition, true); + + Assert.That(wait.IsDone, Is.False); + } + + /// + /// 验证WaitForConditionChange从false变为true时完成 + /// + [Test] + public void WaitForConditionChange_Should_Be_Done_When_Changing_From_False_To_True() + { + var condition = false; + var wait = new WaitForConditionChange(() => condition, true); + + // 初始状态记录 + wait.Update(0.1); + + condition = true; + wait.Update(0.1); + + Assert.That(wait.IsDone, Is.True); + } + + /// + /// 验证WaitForConditionChange从true变为false时完成 + /// + [Test] + public void WaitForConditionChange_Should_Be_Done_When_Changing_From_True_To_False() + { + var condition = true; + var wait = new WaitForConditionChange(() => condition, false); + + // 初始状态记录 + wait.Update(0.1); + + condition = false; + wait.Update(0.1); + + Assert.That(wait.IsDone, Is.True); + } + + /// + /// 验证WaitForConditionChange不响应相同状态的变化 + /// + [Test] + public void WaitForConditionChange_Should_Not_Be_Done_When_No_State_Change() + { + var condition = false; + var wait = new WaitForConditionChange(() => condition, true); + + // 初始状态记录 + wait.Update(0.1); + + // 仍然是false,没有状态改变 + wait.Update(0.1); + + Assert.That(wait.IsDone, Is.False); + } + + /// + /// 验证WaitForConditionChange多次状态切换只响应第一次 + /// + [Test] + public void WaitForConditionChange_Should_Only_Respond_To_First_Transition() + { + var condition = false; + var wait = new WaitForConditionChange(() => condition, true); + + // 记录初始状态 + wait.Update(0.1); + Assert.That(wait.IsDone, Is.False); + + // 触发状态转换到目标状态 + condition = true; + wait.Update(0.1); + Assert.That(wait.IsDone, Is.True); + + // 再次切换回原始状态 + condition = false; + wait.Update(0.1); + + // 应该仍然保持完成状态 + Assert.That(wait.IsDone, Is.True); + } + + /// + /// 验证WaitForConditionChange抛出ArgumentNullException当conditionGetter为null + /// + [Test] + public void WaitForConditionChange_Should_Throw_ArgumentNullException_When_ConditionGetter_Is_Null() + { + Assert.Throws(() => new WaitForConditionChange(null!, true)); + } + + /// + /// 验证WaitForConditionChange实现IYieldInstruction接口 + /// + [Test] + public void WaitForConditionChange_Should_Implement_IYieldInstruction() + { + var wait = new WaitForConditionChange(() => false, true); + + Assert.That(wait, Is.InstanceOf()); + } +} \ No newline at end of file diff --git a/GFramework.Core.Tests/coroutine/WaitForCoroutineTests.cs b/GFramework.Core.Tests/coroutine/WaitForCoroutineTests.cs new file mode 100644 index 00000000..768072dc --- /dev/null +++ b/GFramework.Core.Tests/coroutine/WaitForCoroutineTests.cs @@ -0,0 +1,42 @@ +using GFramework.Core.Abstractions.coroutine; +using GFramework.Core.coroutine.instructions; +using NUnit.Framework; + +namespace GFramework.Core.Tests.coroutine +{ + [TestFixture] + public class WaitForCoroutineTests + { + [Test] + public void Constructor_CreatesInstance() + { + // Arrange + var coroutine = CreateDummyCoroutine(); + + // Act + var waitForCoroutine = new WaitForCoroutine(coroutine); + + // Assert + Assert.That(waitForCoroutine.IsDone, Is.False); + } + + [Test] + public void Update_DoesNotChangeState() + { + // Arrange + var coroutine = CreateDummyCoroutine(); + var waitForCoroutine = new WaitForCoroutine(coroutine); + + // Act + waitForCoroutine.Update(1.0); + + // Assert + Assert.That(waitForCoroutine.IsDone, Is.False); + } + + private static IEnumerator CreateDummyCoroutine() + { + return new List().GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/GFramework.Core.Tests/coroutine/WaitForEndOfFrameTests.cs b/GFramework.Core.Tests/coroutine/WaitForEndOfFrameTests.cs new file mode 100644 index 00000000..d57ff72c --- /dev/null +++ b/GFramework.Core.Tests/coroutine/WaitForEndOfFrameTests.cs @@ -0,0 +1,61 @@ +using GFramework.Core.Abstractions.coroutine; +using GFramework.Core.coroutine.instructions; +using NUnit.Framework; + +namespace GFramework.Core.Tests.coroutine; + +/// +/// WaitForEndOfFrame的单元测试类 +/// +[TestFixture] +public class WaitForEndOfFrameTests +{ + /// + /// 验证WaitForEndOfFrame初始状态为未完成 + /// + [Test] + public void WaitForEndOfFrame_Should_Not_Be_Done_Initially() + { + var wait = new WaitForEndOfFrame(); + + Assert.That(wait.IsDone, Is.False); + } + + /// + /// 验证WaitForEndOfFrame在Update后应该完成 + /// + [Test] + public void WaitForEndOfFrame_Should_Be_Done_After_Update() + { + var wait = new WaitForEndOfFrame(); + + wait.Update(0.016); + Assert.That(wait.IsDone, Is.True); + } + + /// + /// 验证WaitForEndOfFrame多次Update后仍保持完成状态 + /// + [Test] + public void WaitForEndOfFrame_Should_Remain_Done_After_Multiple_Updates() + { + var wait = new WaitForEndOfFrame(); + + wait.Update(0.016); + Assert.That(wait.IsDone, Is.True); + + wait.Update(0.016); + Assert.That(wait.IsDone, Is.True); + } + + /// + /// 验证WaitForEndOfFrame实现IYieldInstruction接口 + /// + [Test] + public void WaitForEndOfFrame_Should_Implement_IYieldInstruction() + { + var wait = new WaitForEndOfFrame(); + + Assert.That(wait, Is.InstanceOf()); + } +} \ No newline at end of file diff --git a/GFramework.Core.Tests/coroutine/WaitForFixedUpdateTests.cs b/GFramework.Core.Tests/coroutine/WaitForFixedUpdateTests.cs new file mode 100644 index 00000000..a5d981fb --- /dev/null +++ b/GFramework.Core.Tests/coroutine/WaitForFixedUpdateTests.cs @@ -0,0 +1,61 @@ +using GFramework.Core.Abstractions.coroutine; +using GFramework.Core.coroutine.instructions; +using NUnit.Framework; + +namespace GFramework.Core.Tests.coroutine; + +/// +/// WaitForFixedUpdate的单元测试类 +/// +[TestFixture] +public class WaitForFixedUpdateTests +{ + /// + /// 验证WaitForFixedUpdate初始状态为未完成 + /// + [Test] + public void WaitForFixedUpdate_Should_Not_Be_Done_Initially() + { + var wait = new WaitForFixedUpdate(); + + Assert.That(wait.IsDone, Is.False); + } + + /// + /// 验证WaitForFixedUpdate在Update后应该完成 + /// + [Test] + public void WaitForFixedUpdate_Should_Be_Done_After_Update() + { + var wait = new WaitForFixedUpdate(); + + wait.Update(0.016); + Assert.That(wait.IsDone, Is.True); + } + + /// + /// 验证WaitForFixedUpdate多次Update后仍保持完成状态 + /// + [Test] + public void WaitForFixedUpdate_Should_Remain_Done_After_Multiple_Updates() + { + var wait = new WaitForFixedUpdate(); + + wait.Update(0.016); + Assert.That(wait.IsDone, Is.True); + + wait.Update(0.016); + Assert.That(wait.IsDone, Is.True); + } + + /// + /// 验证WaitForFixedUpdate实现IYieldInstruction接口 + /// + [Test] + public void WaitForFixedUpdate_Should_Implement_IYieldInstruction() + { + var wait = new WaitForFixedUpdate(); + + Assert.That(wait, Is.InstanceOf()); + } +} \ No newline at end of file diff --git a/GFramework.Core.Tests/coroutine/WaitForFramesTests.cs b/GFramework.Core.Tests/coroutine/WaitForFramesTests.cs new file mode 100644 index 00000000..43c2a682 --- /dev/null +++ b/GFramework.Core.Tests/coroutine/WaitForFramesTests.cs @@ -0,0 +1,74 @@ +using GFramework.Core.coroutine.instructions; +using NUnit.Framework; + +namespace GFramework.Core.Tests.coroutine +{ + [TestFixture] + public class WaitForFramesTests + { + [Test] + public void Constructor_SetsInitialFrameCount() + { + // Arrange & Act + var waitForFrames = new WaitForFrames(3); + + // Assert + Assert.That(waitForFrames.IsDone, Is.False); + } + + [Test] + public void Update_ReducesFrameCount() + { + // Arrange + var waitForFrames = new WaitForFrames(2); + + // Act + waitForFrames.Update(0.1); + + // Assert + Assert.That(waitForFrames.IsDone, Is.False); + } + + [Test] + public void MultipleUpdates_EventuallyCompletes() + { + // Arrange + var waitForFrames = new WaitForFrames(2); + + // Act + waitForFrames.Update(0.1); // 2-1 = 1 frame remaining + waitForFrames.Update(0.1); // 1-1 = 0 frames remaining + + // Assert + Assert.That(waitForFrames.IsDone, Is.True); + } + + [Test] + public void NegativeFrameCount_TreatedAsOne() + { + // Arrange & Act + var waitForFrames = new WaitForFrames(-1); + + // Assert + Assert.That(waitForFrames.IsDone, Is.False); + + // One update should complete it + waitForFrames.Update(0.1); + Assert.That(waitForFrames.IsDone, Is.True); + } + + [Test] + public void ZeroFrameCount_TreatedAsOne() + { + // Arrange & Act + var waitForFrames = new WaitForFrames(0); + + // Assert + Assert.That(waitForFrames.IsDone, Is.False); + + // One update should complete it + waitForFrames.Update(0.1); + Assert.That(waitForFrames.IsDone, Is.True); + } + } +} \ No newline at end of file diff --git a/GFramework.Core.Tests/coroutine/WaitForMultipleEventsTests.cs b/GFramework.Core.Tests/coroutine/WaitForMultipleEventsTests.cs new file mode 100644 index 00000000..8bc30132 --- /dev/null +++ b/GFramework.Core.Tests/coroutine/WaitForMultipleEventsTests.cs @@ -0,0 +1,141 @@ +using GFramework.Core.Abstractions.events; +using GFramework.Core.coroutine.instructions; +using GFramework.Core.events; +using NUnit.Framework; + +namespace GFramework.Core.Tests.coroutine +{ + [TestFixture] + public class WaitForMultipleEventsTests + { + [SetUp] + public void SetUp() + { + eventBus = new EventBus(); + } + + [TearDown] + public void TearDown() + { + (eventBus as IDisposable)?.Dispose(); + } + + private IEventBus eventBus; + + [Test] + public void Constructor_RegistersBothEventTypes() + { + // Arrange & Act + var waitForMultipleEvents = new WaitForMultipleEvents(eventBus); + + // Assert + Assert.That(waitForMultipleEvents.IsDone, Is.False); + Assert.That(waitForMultipleEvents.TriggeredBy, Is.EqualTo(0)); + } + + [Test] + public async Task FirstEventWins_WhenBothEventsFired() + { + // Arrange + var waitForMultipleEvents = new WaitForMultipleEvents(eventBus); + + // Act + eventBus.Send(new TestEvent1 { Data = "first_event" }); + eventBus.Send(new TestEvent2 { Data = "second_event" }); + + // Assert + Assert.That(waitForMultipleEvents.IsDone, Is.True); + Assert.That(waitForMultipleEvents.TriggeredBy, Is.EqualTo(1)); // First event should win + Assert.That(waitForMultipleEvents.FirstEventData?.Data, Is.EqualTo("first_event")); + Assert.That(waitForMultipleEvents.SecondEventData, Is.Null); + } + + [Test] + public async Task SecondEventWins_WhenOnlySecondEventFired() + { + // Arrange + var waitForMultipleEvents = new WaitForMultipleEvents(eventBus); + + // Act + eventBus.Send(new TestEvent2 { Data = "second_event" }); + + // Assert + Assert.That(waitForMultipleEvents.IsDone, Is.True); + Assert.That(waitForMultipleEvents.TriggeredBy, Is.EqualTo(2)); // Second event should win + Assert.That(waitForMultipleEvents.SecondEventData?.Data, Is.EqualTo("second_event")); + Assert.That(waitForMultipleEvents.FirstEventData, Is.Null); + } + + [Test] + public async Task FirstEventWins_WhenBothEventsFiredInReverseOrder() + { + // Arrange + var waitForMultipleEvents = new WaitForMultipleEvents(eventBus); + + // Act + eventBus.Send(new TestEvent2 { Data = "second_event" }); + eventBus.Send(new TestEvent1 { Data = "first_event" }); + + // Assert + Assert.That(waitForMultipleEvents.IsDone, Is.True); + // Actually, the second event should win because it fired first and set _done = true + Assert.That(waitForMultipleEvents.TriggeredBy, Is.EqualTo(2)); // Second event wins since it fired first + Assert.That(waitForMultipleEvents.SecondEventData?.Data, Is.EqualTo("second_event")); + Assert.That(waitForMultipleEvents.FirstEventData, Is.Null); + } + + [Test] + public async Task MultipleEvents_AfterCompletion_DoNotOverrideState() + { + // Arrange + var waitForMultipleEvents = new WaitForMultipleEvents(eventBus); + + // Act - Fire first event + eventBus.Send(new TestEvent1 { Data = "first_event" }); + + // Verify first event was processed + Assert.That(waitForMultipleEvents.IsDone, Is.True); + Assert.That(waitForMultipleEvents.TriggeredBy, Is.EqualTo(1)); + Assert.That(waitForMultipleEvents.FirstEventData?.Data, Is.EqualTo("first_event")); + + // Fire second event after completion + eventBus.Send(new TestEvent2 { Data = "second_event" }); + + // Assert - The state should not change + Assert.That(waitForMultipleEvents.IsDone, Is.True); + Assert.That(waitForMultipleEvents.TriggeredBy, Is.EqualTo(1)); // Should remain as 1, not change to 2 + Assert.That(waitForMultipleEvents.FirstEventData?.Data, + Is.EqualTo("first_event")); // Should remain unchanged + Assert.That(waitForMultipleEvents.SecondEventData, Is.Null); // Should remain null + } + + [Test] + public async Task Disposal_PreventsFurtherEventHandling() + { + // Arrange + var waitForMultipleEvents = new WaitForMultipleEvents(eventBus); + + // Act - Dispose the instance + waitForMultipleEvents.Dispose(); + + // Fire an event after disposal + eventBus.Send(new TestEvent1 { Data = "after_disposal" }); + + // Assert - Event should not be processed due to disposal + // Since we disposed, no event data should be captured + Assert.That(waitForMultipleEvents.FirstEventData, Is.Null); + Assert.That(waitForMultipleEvents.IsDone, Is.False); // Should remain false after disposal + } + + // Test event classes + private class TestEvent1 + { + public string Data { get; set; } = string.Empty; + } + + private class TestEvent2 + { + public string Data { get; set; } = string.Empty; + } + } +} \ No newline at end of file diff --git a/GFramework.Core.Tests/coroutine/WaitForNextFrameTests.cs b/GFramework.Core.Tests/coroutine/WaitForNextFrameTests.cs new file mode 100644 index 00000000..fabff301 --- /dev/null +++ b/GFramework.Core.Tests/coroutine/WaitForNextFrameTests.cs @@ -0,0 +1,61 @@ +using GFramework.Core.Abstractions.coroutine; +using GFramework.Core.coroutine.instructions; +using NUnit.Framework; + +namespace GFramework.Core.Tests.coroutine; + +/// +/// WaitForNextFrame的单元测试类 +/// +[TestFixture] +public class WaitForNextFrameTests +{ + /// + /// 验证WaitForNextFrame初始状态为未完成 + /// + [Test] + public void WaitForNextFrame_Should_Not_Be_Done_Initially() + { + var wait = new WaitForNextFrame(); + + Assert.That(wait.IsDone, Is.False); + } + + /// + /// 验证WaitForNextFrame在Update后应该完成 + /// + [Test] + public void WaitForNextFrame_Should_Be_Done_After_Update() + { + var wait = new WaitForNextFrame(); + + wait.Update(0.016); + Assert.That(wait.IsDone, Is.True); + } + + /// + /// 验证WaitForNextFrame多次Update后仍保持完成状态 + /// + [Test] + public void WaitForNextFrame_Should_Remain_Done_After_Multiple_Updates() + { + var wait = new WaitForNextFrame(); + + wait.Update(0.016); + Assert.That(wait.IsDone, Is.True); + + wait.Update(0.016); + Assert.That(wait.IsDone, Is.True); + } + + /// + /// 验证WaitForNextFrame实现IYieldInstruction接口 + /// + [Test] + public void WaitForNextFrame_Should_Implement_IYieldInstruction() + { + var wait = new WaitForNextFrame(); + + Assert.That(wait, Is.InstanceOf()); + } +} \ No newline at end of file diff --git a/GFramework.Core.Tests/coroutine/WaitForPredicateTests.cs b/GFramework.Core.Tests/coroutine/WaitForPredicateTests.cs new file mode 100644 index 00000000..d46f808d --- /dev/null +++ b/GFramework.Core.Tests/coroutine/WaitForPredicateTests.cs @@ -0,0 +1,87 @@ +using GFramework.Core.Abstractions.coroutine; +using GFramework.Core.coroutine.instructions; +using NUnit.Framework; + +namespace GFramework.Core.Tests.coroutine; + +/// +/// WaitForPredicate的单元测试类 +/// +[TestFixture] +public class WaitForPredicateTests +{ + /// + /// 验证WaitForPredicate默认等待条件为真时完成 + /// + [Test] + public void WaitForPredicate_Should_Wait_For_True_By_Default() + { + var condition = false; + var wait = new WaitForPredicate(() => condition); + + Assert.That(wait.IsDone, Is.False); + + condition = true; + Assert.That(wait.IsDone, Is.True); + } + + /// + /// 验证WaitForPredicate可以等待条件为假时完成 + /// + [Test] + public void WaitForPredicate_Should_Wait_For_False_When_Specified() + { + var condition = true; + var wait = new WaitForPredicate(() => condition, false); + + Assert.That(wait.IsDone, Is.False); + + condition = false; + Assert.That(wait.IsDone, Is.True); + } + + /// + /// 验证WaitForPredicate多次检查条件 + /// + [Test] + public void WaitForPredicate_Should_Check_Condition_Multiple_Times() + { + var callCount = 0; + var wait = new WaitForPredicate(() => + { + callCount++; + return callCount >= 3; + }); + + Assert.That(wait.IsDone, Is.False); + Assert.That(callCount, Is.EqualTo(1)); + + wait.Update(0.1); + Assert.That(wait.IsDone, Is.False); + Assert.That(callCount, Is.EqualTo(2)); + + wait.Update(0.1); + Assert.That(wait.IsDone, Is.True); + Assert.That(callCount, Is.EqualTo(3)); + } + + /// + /// 验证WaitForPredicate抛出ArgumentNullException当predicate为null + /// + [Test] + public void WaitForPredicate_Should_Throw_ArgumentNullException_When_Predicate_Is_Null() + { + Assert.Throws(() => new WaitForPredicate(null!)); + } + + /// + /// 验证WaitForPredicate实现IYieldInstruction接口 + /// + [Test] + public void WaitForPredicate_Should_Implement_IYieldInstruction() + { + var wait = new WaitForPredicate(() => true); + + Assert.That(wait, Is.InstanceOf()); + } +} \ No newline at end of file diff --git a/GFramework.Core.Tests/coroutine/WaitForSecondsRealtimeTests.cs b/GFramework.Core.Tests/coroutine/WaitForSecondsRealtimeTests.cs new file mode 100644 index 00000000..af86fc9b --- /dev/null +++ b/GFramework.Core.Tests/coroutine/WaitForSecondsRealtimeTests.cs @@ -0,0 +1,80 @@ +using GFramework.Core.Abstractions.coroutine; +using GFramework.Core.coroutine.instructions; +using NUnit.Framework; + +namespace GFramework.Core.Tests.coroutine; + +/// +/// WaitForSecondsRealtime的单元测试类 +/// +[TestFixture] +public class WaitForSecondsRealtimeTests +{ + /// + /// 验证WaitForSecondsRealtime初始状态根据时间设置 + /// + [Test] + public void WaitForSecondsRealtime_Should_Handle_Initial_State_Correctly() + { + var waitZero = new WaitForSecondsRealtime(0); + var waitPositive = new WaitForSecondsRealtime(1.0); + + Assert.That(waitZero.IsDone, Is.True); + Assert.That(waitPositive.IsDone, Is.False); + } + + /// + /// 验证WaitForSecondsRealtime应该在指定时间后完成 + /// + [Test] + public void WaitForSecondsRealtime_Should_Be_Done_After_Specified_Time() + { + var wait = new WaitForSecondsRealtime(1.0); + + wait.Update(0.5); + Assert.That(wait.IsDone, Is.False); + + wait.Update(0.5); + Assert.That(wait.IsDone, Is.True); + } + + /// + /// 验证WaitForSecondsRealtime可以处理负数时间 + /// + [Test] + public void WaitForSecondsRealtime_Should_Handle_Negative_Time() + { + var wait = new WaitForSecondsRealtime(-1.0); + + Assert.That(wait.IsDone, Is.True); + } + + /// + /// 验证WaitForSecondsRealtime多次更新累积时间 + /// + [Test] + public void WaitForSecondsRealtime_Should_Accumulate_Time_Over_Multiple_Updates() + { + var wait = new WaitForSecondsRealtime(2.0); + + wait.Update(0.5); + Assert.That(wait.IsDone, Is.False); + + wait.Update(1.0); + Assert.That(wait.IsDone, Is.False); + + wait.Update(0.5); + Assert.That(wait.IsDone, Is.True); + } + + /// + /// 验证WaitForSecondsRealtime实现IYieldInstruction接口 + /// + [Test] + public void WaitForSecondsRealtime_Should_Implement_IYieldInstruction() + { + var wait = new WaitForSecondsRealtime(1.0); + + Assert.That(wait, Is.InstanceOf()); + } +} \ No newline at end of file diff --git a/GFramework.Core.Tests/coroutine/WaitForSecondsScaledTests.cs b/GFramework.Core.Tests/coroutine/WaitForSecondsScaledTests.cs new file mode 100644 index 00000000..a017fb34 --- /dev/null +++ b/GFramework.Core.Tests/coroutine/WaitForSecondsScaledTests.cs @@ -0,0 +1,80 @@ +using GFramework.Core.Abstractions.coroutine; +using GFramework.Core.coroutine.instructions; +using NUnit.Framework; + +namespace GFramework.Core.Tests.coroutine; + +/// +/// WaitForSecondsScaled的单元测试类 +/// +[TestFixture] +public class WaitForSecondsScaledTests +{ + /// + /// 验证WaitForSecondsScaled初始状态根据时间设置 + /// + [Test] + public void WaitForSecondsScaled_Should_Handle_Initial_State_Correctly() + { + var waitZero = new WaitForSecondsScaled(0); + var waitPositive = new WaitForSecondsScaled(1.0); + + Assert.That(waitZero.IsDone, Is.True); + Assert.That(waitPositive.IsDone, Is.False); + } + + /// + /// 验证WaitForSecondsScaled应该在指定时间后完成 + /// + [Test] + public void WaitForSecondsScaled_Should_Be_Done_After_Specified_Time() + { + var wait = new WaitForSecondsScaled(1.0); + + wait.Update(0.5); + Assert.That(wait.IsDone, Is.False); + + wait.Update(0.5); + Assert.That(wait.IsDone, Is.True); + } + + /// + /// 验证WaitForSecondsScaled可以处理负数时间 + /// + [Test] + public void WaitForSecondsScaled_Should_Handle_Negative_Time() + { + var wait = new WaitForSecondsScaled(-1.0); + + Assert.That(wait.IsDone, Is.True); + } + + /// + /// 验证WaitForSecondsScaled多次更新累积时间 + /// + [Test] + public void WaitForSecondsScaled_Should_Accumulate_Time_Over_Multiple_Updates() + { + var wait = new WaitForSecondsScaled(2.0); + + wait.Update(0.5); + Assert.That(wait.IsDone, Is.False); + + wait.Update(1.0); + Assert.That(wait.IsDone, Is.False); + + wait.Update(0.5); + Assert.That(wait.IsDone, Is.True); + } + + /// + /// 验证WaitForSecondsScaled实现IYieldInstruction接口 + /// + [Test] + public void WaitForSecondsScaled_Should_Implement_IYieldInstruction() + { + var wait = new WaitForSecondsScaled(1.0); + + Assert.That(wait, Is.InstanceOf()); + } +} \ No newline at end of file diff --git a/GFramework.Core.Tests/coroutine/WaitForTaskTTests.cs b/GFramework.Core.Tests/coroutine/WaitForTaskTTests.cs new file mode 100644 index 00000000..ab446cda --- /dev/null +++ b/GFramework.Core.Tests/coroutine/WaitForTaskTTests.cs @@ -0,0 +1,96 @@ +using GFramework.Core.coroutine.instructions; +using NUnit.Framework; + +namespace GFramework.Core.Tests.coroutine +{ + [TestFixture] + public class WaitForTaskTTests + { + [Test] + public void Constructor_WithNullTask_ThrowsArgumentNullException() + { + // Act & Assert + Assert.Throws(() => new WaitForTask(null!)); + } + + [Test] + public async Task Constructor_WithCompletedTask_IsDoneImmediately() + { + // Arrange + var completedTask = Task.FromResult("test"); + + // Act + var waitForTask = new WaitForTask(completedTask); + + // Assert + Assert.That(waitForTask.IsDone, Is.True); + Assert.That(waitForTask.Result, Is.EqualTo("test")); + } + + [Test] + public async Task Constructor_WithIncompleteTask_IsNotDoneInitially() + { + // Arrange + var tcs = new TaskCompletionSource(); + var incompleteTask = tcs.Task; + + // Act + var waitForTask = new WaitForTask(incompleteTask); + + // Assert + Assert.That(waitForTask.IsDone, Is.False); + } + + [Test] + public async Task TaskCompletes_CallbackSetsDoneFlag() + { + // Arrange + var tcs = new TaskCompletionSource(); + var task = tcs.Task; + var waitForTask = new WaitForTask(task); + + // Assert initial state + Assert.That(waitForTask.IsDone, Is.False); + + // Act + tcs.SetResult("completed"); + await Task.Delay(10); // Allow time for continuation + + // Assert final state + Assert.That(waitForTask.IsDone, Is.True); + Assert.That(waitForTask.Result, Is.EqualTo("completed")); + } + + [Test] + public async Task Update_DoesNotChangeState() + { + // Arrange + var completedTask = Task.FromResult("test"); + var waitForTask = new WaitForTask(completedTask); + + // Act + waitForTask.Update(0.1); + + // Assert + Assert.That(waitForTask.IsDone, Is.True); + } + + [Test] + public async Task TaskWithException_HoldsException() + { + // Arrange + var tcs = new TaskCompletionSource(); + var task = tcs.Task; + var waitForTask = new WaitForTask(task); + + // Act + tcs.SetException(new InvalidOperationException("Test exception")); + await Task.Delay(10); // Allow time for continuation + + // Assert + Assert.That(waitForTask.IsDone, Is.True); + Assert.That(waitForTask.Exception, Is.Not.Null); + Assert.That(waitForTask.Exception?.InnerException, Is.TypeOf()); + } + } +} \ No newline at end of file diff --git a/GFramework.Core.Tests/coroutine/WaitOneFrameTests.cs b/GFramework.Core.Tests/coroutine/WaitOneFrameTests.cs new file mode 100644 index 00000000..1f2af7ad --- /dev/null +++ b/GFramework.Core.Tests/coroutine/WaitOneFrameTests.cs @@ -0,0 +1,46 @@ +using GFramework.Core.coroutine.instructions; +using NUnit.Framework; + +namespace GFramework.Core.Tests.coroutine +{ + [TestFixture] + public class WaitOneFrameTests + { + [Test] + public void Constructor_CreatesInstance() + { + // Act + var waitOneFrame = new WaitOneFrame(); + + // Assert + Assert.That(waitOneFrame.IsDone, Is.False); + } + + [Test] + public void Update_MakesItDone() + { + // Arrange + var waitOneFrame = new WaitOneFrame(); + + // Act + waitOneFrame.Update(0.1); + + // Assert + Assert.That(waitOneFrame.IsDone, Is.True); + } + + [Test] + public void Update_MultipleTimes_RemainsDone() + { + // Arrange + var waitOneFrame = new WaitOneFrame(); + + // Act + waitOneFrame.Update(0.1); + waitOneFrame.Update(0.1); + + // Assert + Assert.That(waitOneFrame.IsDone, Is.True); + } + } +} \ No newline at end of file diff --git a/GFramework.Core.Tests/coroutine/WaitUntilOrTimeoutTests.cs b/GFramework.Core.Tests/coroutine/WaitUntilOrTimeoutTests.cs new file mode 100644 index 00000000..c9b04d36 --- /dev/null +++ b/GFramework.Core.Tests/coroutine/WaitUntilOrTimeoutTests.cs @@ -0,0 +1,109 @@ +using GFramework.Core.Abstractions.coroutine; +using GFramework.Core.coroutine.instructions; +using NUnit.Framework; + +namespace GFramework.Core.Tests.coroutine; + +/// +/// WaitUntilOrTimeout的单元测试类 +/// +[TestFixture] +public class WaitUntilOrTimeoutTests +{ + /// + /// 验证WaitUntilOrTimeout初始状态为未完成 + /// + [Test] + public void WaitUntilOrTimeout_Should_Not_Be_Done_Initially() + { + var condition = false; + var wait = new WaitUntilOrTimeout(() => condition, 5.0); + + Assert.That(wait.IsDone, Is.False); + Assert.That(wait.ConditionMet, Is.False); + Assert.That(wait.IsTimedOut, Is.False); + } + + /// + /// 验证WaitUntilOrTimeout应该在条件满足时完成 + /// + [Test] + public void WaitUntilOrTimeout_Should_Be_Done_When_Condition_Met() + { + var condition = false; + var wait = new WaitUntilOrTimeout(() => condition, 5.0); + + condition = true; + wait.Update(0.1); + + Assert.That(wait.IsDone, Is.True); + Assert.That(wait.ConditionMet, Is.True); + Assert.That(wait.IsTimedOut, Is.False); + } + + /// + /// 验证WaitUntilOrTimeout应该在超时时完成 + /// + [Test] + public void WaitUntilOrTimeout_Should_Be_Done_When_Timed_Out() + { + var condition = false; + var wait = new WaitUntilOrTimeout(() => condition, 1.0); + + wait.Update(1.5); + + Assert.That(wait.IsDone, Is.True); + Assert.That(wait.ConditionMet, Is.False); + Assert.That(wait.IsTimedOut, Is.True); + } + + /// + /// 验证WaitUntilOrTimeout可以处理零超时时间 + /// + [Test] + public void WaitUntilOrTimeout_Should_Handle_Zero_Timeout() + { + var condition = false; + var wait = new WaitUntilOrTimeout(() => condition, 0); + + wait.Update(0.1); + + Assert.That(wait.IsDone, Is.True); + Assert.That(wait.IsTimedOut, Is.True); + } + + /// + /// 验证WaitUntilOrTimeout可以处理负数超时时间 + /// + [Test] + public void WaitUntilOrTimeout_Should_Handle_Negative_Timeout() + { + var condition = false; + var wait = new WaitUntilOrTimeout(() => condition, -1.0); + + wait.Update(0.1); + + Assert.That(wait.IsDone, Is.True); + Assert.That(wait.IsTimedOut, Is.True); + } + + /// + /// 验证WaitUntilOrTimeout抛出ArgumentNullException当predicate为null + /// + [Test] + public void WaitUntilOrTimeout_Should_Throw_ArgumentNullException_When_Predicate_Is_Null() + { + Assert.Throws(() => new WaitUntilOrTimeout(null!, 1.0)); + } + + /// + /// 验证WaitUntilOrTimeout实现IYieldInstruction接口 + /// + [Test] + public void WaitUntilOrTimeout_Should_Implement_IYieldInstruction() + { + var wait = new WaitUntilOrTimeout(() => false, 1.0); + + Assert.That(wait, Is.InstanceOf()); + } +} \ No newline at end of file diff --git a/GFramework.Core.Tests/coroutine/WaitUntilTests.cs b/GFramework.Core.Tests/coroutine/WaitUntilTests.cs new file mode 100644 index 00000000..47fffb77 --- /dev/null +++ b/GFramework.Core.Tests/coroutine/WaitUntilTests.cs @@ -0,0 +1,55 @@ +using GFramework.Core.coroutine.instructions; +using NUnit.Framework; + +namespace GFramework.Core.Tests.coroutine +{ + [TestFixture] + public class WaitUntilTests + { + [Test] + public void Constructor_WithNullPredicate_ThrowsArgumentNullException() + { + // Act & Assert + Assert.Throws(() => new WaitUntil(null!)); + } + + [Test] + public void IsDone_ReturnsPredicateResult_True() + { + // Arrange + var condition = false; + var waitUntil = new WaitUntil(() => condition); + + // Act + condition = true; + + // Assert + Assert.That(waitUntil.IsDone, Is.True); + } + + [Test] + public void IsDone_ReturnsPredicateResult_False() + { + // Arrange + var condition = false; + var waitUntil = new WaitUntil(() => condition); + + // Assert + Assert.That(waitUntil.IsDone, Is.False); + } + + [Test] + public void Update_DoesNotChangeState() + { + // Arrange + var condition = false; + var waitUntil = new WaitUntil(() => condition); + + // Act + waitUntil.Update(0.1); + + // Assert + Assert.That(waitUntil.IsDone, Is.False); + } + } +} \ No newline at end of file diff --git a/GFramework.Core.Tests/coroutine/WaitWhileTests.cs b/GFramework.Core.Tests/coroutine/WaitWhileTests.cs new file mode 100644 index 00000000..9829b7ed --- /dev/null +++ b/GFramework.Core.Tests/coroutine/WaitWhileTests.cs @@ -0,0 +1,66 @@ +using GFramework.Core.coroutine.instructions; +using NUnit.Framework; + +namespace GFramework.Core.Tests.coroutine +{ + [TestFixture] + public class WaitWhileTests + { + [Test] + public void Constructor_WithNullPredicate_ThrowsArgumentNullException() + { + // Act & Assert + Assert.Throws(() => new WaitWhile(null!)); + } + + [Test] + public void IsDone_ReturnsInverseOfPredicateResult_True() + { + // Arrange + var condition = true; + var waitWhile = new WaitWhile(() => condition); + + // Act + condition = false; + + // Assert + Assert.That(waitWhile.IsDone, Is.True); + } + + [Test] + public void IsDone_ReturnsInverseOfPredicateResult_False() + { + // Arrange + var condition = false; + var waitWhile = new WaitWhile(() => condition); + + // Assert + Assert.That(waitWhile.IsDone, Is.True); // Because !false = true + } + + [Test] + public void IsDone_WhenPredicateReturnsTrue() + { + // Arrange + var condition = true; + var waitWhile = new WaitWhile(() => condition); + + // Assert + Assert.That(waitWhile.IsDone, Is.False); // Because !true = false + } + + [Test] + public void Update_DoesNotChangeState() + { + // Arrange + var condition = true; + var waitWhile = new WaitWhile(() => condition); + + // Act + waitWhile.Update(0.1); + + // Assert + Assert.That(waitWhile.IsDone, Is.False); + } + } +} \ No newline at end of file diff --git a/GFramework.Core/coroutine/instructions/WaitForConditionChange.cs b/GFramework.Core/coroutine/instructions/WaitForConditionChange.cs new file mode 100644 index 00000000..0380daf8 --- /dev/null +++ b/GFramework.Core/coroutine/instructions/WaitForConditionChange.cs @@ -0,0 +1,46 @@ +using GFramework.Core.Abstractions.coroutine; + +namespace GFramework.Core.coroutine.instructions; + +/// +/// 等待条件状态发生变化的指令 +/// 当条件从一种状态切换到另一种状态时完成 +/// +/// 获取当前条件状态的函数 +/// 期望转换到的目标状态 +public sealed class WaitForConditionChange(Func conditionGetter, bool waitForTransitionTo) : IYieldInstruction +{ + private readonly Func _conditionGetter = + conditionGetter ?? throw new ArgumentNullException(nameof(conditionGetter)); + + private bool? _initialState; + private bool _isCompleted; + + /// + /// 更新方法,检测条件变化 + /// + /// 时间增量 + public void Update(double deltaTime) + { + if (_isCompleted) + return; + + if (!_initialState.HasValue) + { + _initialState = _conditionGetter(); + return; + } + + // 检查是否发生了期望的状态转换 + var currentState = _conditionGetter(); + if (currentState == waitForTransitionTo && _initialState.Value != waitForTransitionTo) + { + _isCompleted = true; + } + } + + /// + /// 获取等待是否已完成(条件发生了期望的状态转换) + /// + public bool IsDone => _isCompleted; +} \ No newline at end of file diff --git a/GFramework.Core/coroutine/instructions/WaitForEndOfFrame.cs b/GFramework.Core/coroutine/instructions/WaitForEndOfFrame.cs new file mode 100644 index 00000000..f03c48fa --- /dev/null +++ b/GFramework.Core/coroutine/instructions/WaitForEndOfFrame.cs @@ -0,0 +1,27 @@ +using GFramework.Core.Abstractions.coroutine; + +namespace GFramework.Core.coroutine.instructions; + +/// +/// 等待当前帧渲染完成的指令 +/// 通常用于需要在渲染完成后执行的操作 +/// +public sealed class WaitForEndOfFrame : IYieldInstruction +{ + private bool _completed; + + /// + /// 更新方法,在帧末尾被调用 + /// + /// 时间增量 + public void Update(double deltaTime) + { + // 在帧结束时标记完成 + _completed = true; + } + + /// + /// 获取等待是否已完成 + /// + public bool IsDone => _completed; +} \ No newline at end of file diff --git a/GFramework.Core/coroutine/instructions/WaitForFixedUpdate.cs b/GFramework.Core/coroutine/instructions/WaitForFixedUpdate.cs new file mode 100644 index 00000000..a2ebe6c7 --- /dev/null +++ b/GFramework.Core/coroutine/instructions/WaitForFixedUpdate.cs @@ -0,0 +1,27 @@ +using GFramework.Core.Abstractions.coroutine; + +namespace GFramework.Core.coroutine.instructions; + +/// +/// 等待下一个物理固定更新周期的指令 +/// 主要用于需要与物理系统同步的操作 +/// +public sealed class WaitForFixedUpdate : IYieldInstruction +{ + private bool _completed; + + /// + /// 更新方法,在固定更新时被调用 + /// + /// 时间增量 + public void Update(double deltaTime) + { + // 在固定更新周期中标记完成 + _completed = true; + } + + /// + /// 获取等待是否已完成 + /// + public bool IsDone => _completed; +} \ No newline at end of file diff --git a/GFramework.Core/coroutine/instructions/WaitForMultipleEvents.cs b/GFramework.Core/coroutine/instructions/WaitForMultipleEvents.cs new file mode 100644 index 00000000..3b5a7c7d --- /dev/null +++ b/GFramework.Core/coroutine/instructions/WaitForMultipleEvents.cs @@ -0,0 +1,117 @@ +using GFramework.Core.Abstractions.coroutine; +using GFramework.Core.Abstractions.events; + +namespace GFramework.Core.coroutine.instructions; + +/// +/// 等待多个事件中的任意一个触发的指令 +/// 实现了 IDisposable 接口,支持资源释放 +/// +/// 第一个事件类型 +/// 第二个事件类型 +public sealed class WaitForMultipleEvents : IYieldInstruction, IDisposable +{ + private bool _disposed; + private volatile bool _done; + private IUnRegister? _unRegister1; + private IUnRegister? _unRegister2; + + /// + /// 初始化 WaitForMultipleEvents 实例 + /// + /// 事件总线实例 + public WaitForMultipleEvents(IEventBus eventBus) + { + var eventBus1 = eventBus ?? throw new ArgumentNullException(nameof(eventBus)); + + // 注册两个事件的监听器 + _unRegister1 = eventBus1.Register(OnFirstEvent); + _unRegister2 = eventBus1.Register(OnSecondEvent); + } + + /// + /// 获取第一个事件的数据(如果已触发) + /// + public TEvent1? FirstEventData { get; private set; } + + /// + /// 获取第二个事件的数据(如果已触发) + /// + public TEvent2? SecondEventData { get; private set; } + + /// + /// 获取是哪个事件先触发(1表示第一个事件,2表示第二个事件) + /// + public int TriggeredBy { get; private set; } + + /// + /// 释放资源 + /// + public void Dispose() + { + if (_disposed) return; + + _unRegister1?.UnRegister(); + _unRegister2?.UnRegister(); + _unRegister1 = null; + _unRegister2 = null; + _disposed = true; + } + + /// + /// 获取等待是否已完成 + /// + public bool IsDone => _done; + + /// + /// 更新方法 + /// + /// 时间增量 + public void Update(double deltaTime) + { + if (!_done || (_unRegister1 == null && _unRegister2 == null)) return; + + _unRegister1?.UnRegister(); + _unRegister2?.UnRegister(); + _unRegister1 = null; + _unRegister2 = null; + } + + /// + /// 第一个事件触发处理 + /// + private void OnFirstEvent(TEvent1 eventData) + { + // 如果已经完成或者被释放,则直接返回 + if (_done || _disposed) return; + + FirstEventData = eventData; + TriggeredBy = 1; + _done = true; + + // 立即注销事件监听器 + _unRegister1?.UnRegister(); + _unRegister2?.UnRegister(); + _unRegister1 = null; + _unRegister2 = null; + } + + /// + /// 第二个事件触发处理 + /// + private void OnSecondEvent(TEvent2 eventData) + { + // 如果已经完成或者被释放,则直接返回 + if (_done || _disposed) return; + + SecondEventData = eventData; + TriggeredBy = 2; + _done = true; + + // 立即注销事件监听器 + _unRegister1?.UnRegister(); + _unRegister2?.UnRegister(); + _unRegister1 = null; + _unRegister2 = null; + } +} \ No newline at end of file diff --git a/GFramework.Core/coroutine/instructions/WaitForNextFrame.cs b/GFramework.Core/coroutine/instructions/WaitForNextFrame.cs new file mode 100644 index 00000000..8f146445 --- /dev/null +++ b/GFramework.Core/coroutine/instructions/WaitForNextFrame.cs @@ -0,0 +1,26 @@ +using GFramework.Core.Abstractions.coroutine; + +namespace GFramework.Core.coroutine.instructions; + +/// +/// 等待下一帧的指令(与WaitOneFrame功能相同,提供另一种命名选择) +/// 用于需要明确表达"等待到下一帧开始"的场景 +/// +public sealed class WaitForNextFrame : IYieldInstruction +{ + private bool _completed; + + /// + /// 更新方法,在下一帧被调用时将完成状态设置为true + /// + /// 时间间隔 + public void Update(double deltaTime) + { + _completed = true; + } + + /// + /// 获取当前等待指令是否已完成 + /// + public bool IsDone => _completed; +} \ No newline at end of file diff --git a/GFramework.Core/coroutine/instructions/WaitForPredicate.cs b/GFramework.Core/coroutine/instructions/WaitForPredicate.cs new file mode 100644 index 00000000..5c77a05e --- /dev/null +++ b/GFramework.Core/coroutine/instructions/WaitForPredicate.cs @@ -0,0 +1,28 @@ +using GFramework.Core.Abstractions.coroutine; + +namespace GFramework.Core.coroutine.instructions; + +/// +/// 通用谓词等待指令 +/// 支持自定义比较逻辑,可以替代 WaitUntil 和 WaitWhile +/// +/// 条件判断函数 +/// true表示等待条件为真时完成,false表示等待条件为假时完成 +public sealed class WaitForPredicate(Func predicate, bool waitForTrue = true) : IYieldInstruction +{ + private readonly Func _predicate = predicate ?? throw new ArgumentNullException(nameof(predicate)); + + /// + /// 更新协程状态 + /// + /// 时间增量 + public void Update(double deltaTime) + { + // 不需要特殊处理时间 + } + + /// + /// 获取协程指令是否已完成 + /// + public bool IsDone => waitForTrue ? _predicate() : !_predicate(); +} \ No newline at end of file diff --git a/GFramework.Core/coroutine/instructions/WaitForSecondsRealtime.cs b/GFramework.Core/coroutine/instructions/WaitForSecondsRealtime.cs new file mode 100644 index 00000000..541d02ab --- /dev/null +++ b/GFramework.Core/coroutine/instructions/WaitForSecondsRealtime.cs @@ -0,0 +1,30 @@ +using GFramework.Core.Abstractions.coroutine; + +namespace GFramework.Core.coroutine.instructions; + +/// +/// 基于真实时间的等待指令(不受时间缩放影响) +/// 适用于需要精确计时的场景,如UI动画、计时器等 +/// +/// 需要等待的秒数 +public sealed class WaitForSecondsRealtime(double seconds) : IYieldInstruction +{ + /// + /// 剩余等待时间(真实时间) + /// + private double _remaining = Math.Max(0, seconds); + + /// + /// 更新延迟计时器(使用真实时间) + /// + /// 时间增量 + public void Update(double deltaTime) + { + _remaining -= deltaTime; + } + + /// + /// 获取延迟是否完成 + /// + public bool IsDone => _remaining <= 0; +} \ No newline at end of file diff --git a/GFramework.Core/coroutine/instructions/WaitForSecondsScaled.cs b/GFramework.Core/coroutine/instructions/WaitForSecondsScaled.cs new file mode 100644 index 00000000..2ae6ee4e --- /dev/null +++ b/GFramework.Core/coroutine/instructions/WaitForSecondsScaled.cs @@ -0,0 +1,30 @@ +using GFramework.Core.Abstractions.coroutine; + +namespace GFramework.Core.coroutine.instructions; + +/// +/// 受时间缩放影响的等待指令 +/// 明确表示会受到游戏时间缩放的影响 +/// +/// 需要等待的秒数 +public sealed class WaitForSecondsScaled(double seconds) : IYieldInstruction +{ + /// + /// 剩余等待时间(受时间缩放影响) + /// + private double _remaining = Math.Max(0, seconds); + + /// + /// 更新延迟计时器(受时间缩放影响) + /// + /// 时间增量 + public void Update(double deltaTime) + { + _remaining -= deltaTime; + } + + /// + /// 获取延迟是否完成 + /// + public bool IsDone => _remaining <= 0; +} \ No newline at end of file diff --git a/GFramework.Core/coroutine/instructions/WaitUntilOrTimeout.cs b/GFramework.Core/coroutine/instructions/WaitUntilOrTimeout.cs new file mode 100644 index 00000000..b90ce2a4 --- /dev/null +++ b/GFramework.Core/coroutine/instructions/WaitUntilOrTimeout.cs @@ -0,0 +1,40 @@ +using GFramework.Core.Abstractions.coroutine; + +namespace GFramework.Core.coroutine.instructions; + +/// +/// 带超时的条件等待指令 +/// 当条件满足或超时时间到达时完成 +/// +/// 条件判断函数 +/// 超时时间(秒) +public sealed class WaitUntilOrTimeout(Func predicate, double timeoutSeconds) : IYieldInstruction +{ + private readonly Func _predicate = predicate ?? throw new ArgumentNullException(nameof(predicate)); + private readonly double _timeout = Math.Max(0, timeoutSeconds); + private double _elapsedTime; + + /// + /// 获取是否因条件满足而完成 + /// + public bool ConditionMet => _predicate(); + + /// + /// 获取是否因超时而完成 + /// + public bool IsTimedOut => _elapsedTime >= _timeout; + + /// + /// 更新方法,累计时间和检查条件 + /// + /// 时间增量 + public void Update(double deltaTime) + { + _elapsedTime += deltaTime; + } + + /// + /// 获取等待是否已完成(条件满足或超时) + /// + public bool IsDone => ConditionMet || IsTimedOut; +} \ No newline at end of file