diff --git a/GFramework.Core.Tests/coroutine/WaitForMultipleEventsTests.cs b/GFramework.Core.Tests/coroutine/WaitForMultipleEventsTests.cs new file mode 100644 index 0000000..e059db2 --- /dev/null +++ b/GFramework.Core.Tests/coroutine/WaitForMultipleEventsTests.cs @@ -0,0 +1,148 @@ +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 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); + return Task.CompletedTask; + } + + [Test] + public 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); + return Task.CompletedTask; + } + + [Test] + public 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); + // Second event should win because it fired first and set _done = true + Assert.That(waitForMultipleEvents.TriggeredBy, + Is.EqualTo(2)); // Second event actually won since it fired first + Assert.That(waitForMultipleEvents.SecondEventData?.Data, Is.EqualTo("second_event")); + Assert.That(waitForMultipleEvents.FirstEventData, Is.Null); + return Task.CompletedTask; + } + + [Test] + public 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 + return Task.CompletedTask; + } + + [Test] + public 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 + + return Task.CompletedTask; + } + + // Test event classes + private class TestEvent1 + { + public string Data { get; init; } = string.Empty; + } + + private class TestEvent2 + { + public string Data { get; init; } = string.Empty; + } + } +} \ No newline at end of file diff --git a/GFramework.Core/coroutine/instructions/WaitForMultipleEvents.cs b/GFramework.Core/coroutine/instructions/WaitForMultipleEvents.cs index e06a7b6..3b5a7c7 100644 --- a/GFramework.Core/coroutine/instructions/WaitForMultipleEvents.cs +++ b/GFramework.Core/coroutine/instructions/WaitForMultipleEvents.cs @@ -82,9 +82,18 @@ public sealed class WaitForMultipleEvents : IYieldInstruction, /// private void OnFirstEvent(TEvent1 eventData) { + // 如果已经完成或者被释放,则直接返回 + if (_done || _disposed) return; + FirstEventData = eventData; TriggeredBy = 1; _done = true; + + // 立即注销事件监听器 + _unRegister1?.UnRegister(); + _unRegister2?.UnRegister(); + _unRegister1 = null; + _unRegister2 = null; } /// @@ -92,8 +101,17 @@ public sealed class WaitForMultipleEvents : IYieldInstruction, /// 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