using Xunit.Abstractions; namespace UnitTests_Parallelizable.ApplicationTests.RunnableTests; /// /// Tests for IRunnable lifecycle behavior. /// public class RunnableLifecycleTests (ITestOutputHelper output) { private readonly ITestOutputHelper _output = output; [Fact] public void Runnable_OnIsRunningChanging_CanExtractResult () { // Arrange ResultExtractingRunnable runnable = new (); runnable.TestValue = "extracted"; // Act bool canceled = runnable.RaiseIsRunningChanging (true, false); // Stopping // Assert Assert.False (canceled); Assert.Equal ("extracted", runnable.Result); } [Fact] public void Runnable_OnIsRunningChanging_ClearsResultWhenStarting () { // Arrange ResultExtractingRunnable runnable = new () { Result = "previous" }; // Act bool canceled = runnable.RaiseIsRunningChanging (false, true); // Starting // Assert Assert.False (canceled); Assert.Null (runnable.Result); // Result should be cleared } [Fact] public void Runnable_CanCancelStoppingWithUnsavedChanges () { // Arrange UnsavedChangesRunnable runnable = new () { HasUnsavedChanges = true }; // Act bool canceled = runnable.RaiseIsRunningChanging (true, false); // Stopping // Assert Assert.True (canceled); // Should be canceled } [Fact] public void Runnable_AllowsStoppingWithoutUnsavedChanges () { // Arrange UnsavedChangesRunnable runnable = new () { HasUnsavedChanges = false }; // Act bool canceled = runnable.RaiseIsRunningChanging (true, false); // Stopping // Assert Assert.False (canceled); // Should not be canceled } [Fact] public void Runnable_OnIsRunningChanged_CalledAfterStateChange () { // Arrange TrackedRunnable runnable = new (); // Act runnable.RaiseIsRunningChangedEvent (true); // Assert Assert.True (runnable.OnIsRunningChangedCalled); Assert.True (runnable.LastIsRunningValue); } [Fact] public void Runnable_OnIsModalChanged_CalledAfterStateChange () { // Arrange TrackedRunnable runnable = new (); // Act runnable.RaiseIsModalChangedEvent (true); // Assert Assert.True (runnable.OnIsModalChangedCalled); Assert.True (runnable.LastIsModalValue); } /// /// Test runnable that extracts result in OnIsRunningChanging. /// private class ResultExtractingRunnable : Runnable { public string? TestValue { get; set; } protected override bool OnIsRunningChanging (bool oldIsRunning, bool newIsRunning) { if (!newIsRunning) // Stopping { // Extract result before removal from stack Result = TestValue; } return base.OnIsRunningChanging (oldIsRunning, newIsRunning); } } /// /// Test runnable that can prevent stopping with unsaved changes. /// private class UnsavedChangesRunnable : Runnable { public bool HasUnsavedChanges { get; set; } protected override bool OnIsRunningChanging (bool oldIsRunning, bool newIsRunning) { if (!newIsRunning && HasUnsavedChanges) // Stopping with unsaved changes { return true; // Cancel stopping } return base.OnIsRunningChanging (oldIsRunning, newIsRunning); } } /// /// Test runnable that tracks lifecycle method calls. /// private class TrackedRunnable : Runnable { public bool OnIsRunningChangedCalled { get; private set; } public bool LastIsRunningValue { get; private set; } public bool OnIsModalChangedCalled { get; private set; } public bool LastIsModalValue { get; private set; } protected override void OnIsRunningChanged (bool newIsRunning) { OnIsRunningChangedCalled = true; LastIsRunningValue = newIsRunning; base.OnIsRunningChanged (newIsRunning); } protected override void OnIsModalChanged (bool newIsModal) { OnIsModalChangedCalled = true; LastIsModalValue = newIsModal; base.OnIsModalChanged (newIsModal); } } }