using Xunit.Abstractions; namespace ApplicationTests.RunnableTests; /// /// Tests for edge cases and error conditions in IRunnable implementation. /// public class RunnableEdgeCasesTests (ITestOutputHelper output) { private readonly ITestOutputHelper _output = output; [Fact] public void Runnable_MultipleEventSubscribers_AllInvoked () { // Arrange Runnable runnable = new (); var subscriber1Called = false; var subscriber2Called = false; var subscriber3Called = false; runnable.IsRunningChanging += (s, e) => subscriber1Called = true; runnable.IsRunningChanging += (s, e) => subscriber2Called = true; runnable.IsRunningChanging += (s, e) => subscriber3Called = true; // Act runnable.RaiseIsRunningChanging (false, true); // Assert Assert.True (subscriber1Called); Assert.True (subscriber2Called); Assert.True (subscriber3Called); } [Fact] public void Runnable_EventSubscriber_CanCancelAfterOthers () { // Arrange Runnable runnable = new (); var subscriber1Called = false; var subscriber2Called = false; runnable.IsRunningChanging += (s, e) => subscriber1Called = true; runnable.IsRunningChanging += (s, e) => { subscriber2Called = true; e.Cancel = true; // Second subscriber cancels }; // Act bool canceled = runnable.RaiseIsRunningChanging (false, true); // Assert Assert.True (subscriber1Called); Assert.True (subscriber2Called); Assert.True (canceled); } [Fact] public void Runnable_Result_CanBeSetMultipleTimes () { // Arrange Runnable runnable = new (); // Act runnable.Result = 1; runnable.Result = 2; runnable.Result = 3; // Assert Assert.Equal (3, runnable.Result); } [Fact] public void Runnable_Result_ClearedOnMultipleStarts () { // Arrange Runnable runnable = new () { Result = 42 }; // Act & Assert - First start runnable.RaiseIsRunningChanging (false, true); Assert.Equal (0, runnable.Result); // Set result again runnable.Result = 99; Assert.Equal (99, runnable.Result); // Second start should clear again runnable.RaiseIsRunningChanging (false, true); Assert.Equal (0, runnable.Result); } [Fact] public void Runnable_NullableResult_DefaultsToNull () { // Arrange & Act Runnable runnable = new (); // Assert Assert.Null (runnable.Result); } [Fact] public void Runnable_NullableResult_CanBeExplicitlyNull () { // Arrange Runnable runnable = new () { Result = "test" }; // Act runnable.Result = null; // Assert Assert.Null (runnable.Result); } [Fact] public void Runnable_ComplexType_Result () { // Arrange Runnable runnable = new (); ComplexResult result = new () { Value = 42, Text = "test" }; // Act runnable.Result = result; // Assert Assert.NotNull (runnable.Result); Assert.Equal (42, runnable.Result.Value); Assert.Equal ("test", runnable.Result.Text); } [Fact] public void Runnable_IsRunning_WithNoApp () { // Arrange Runnable runnable = new (); // Don't set App property // Act & Assert Assert.False (runnable.IsRunning); } [Fact] public void Runnable_IsModal_WithNoApp () { // Arrange Runnable runnable = new (); // Don't set App property // Act & Assert Assert.False (runnable.IsModal); } [Fact] public void Runnable_VirtualMethods_CanBeOverridden () { // Arrange OverriddenRunnable runnable = new (); // Act bool canceledRunning = runnable.RaiseIsRunningChanging (false, true); runnable.RaiseIsRunningChangedEvent (true); runnable.RaiseIsModalChangedEvent (true); // Assert Assert.True (runnable.OnIsRunningChangingCalled); Assert.True (runnable.OnIsRunningChangedCalled); Assert.True (runnable.OnIsModalChangedCalled); } [Fact] public void Runnable_RequestStop_WithNoApp () { // Arrange Runnable runnable = new (); // Don't set App property // Act & Assert - Should not throw runnable.RequestStop (); } [Fact] public void RunnableSessionToken_Constructor_RequiresRunnable () { // This is implicitly tested by the constructor signature, // but let's verify it creates with non-null runnable // Arrange Runnable runnable = new (); // Act SessionToken token = new (runnable); // Assert Assert.NotNull (token.Runnable); } [Fact] public void Runnable_EventArgs_PreservesValues () { // Arrange Runnable runnable = new (); bool? capturedOldValue = null; bool? capturedNewValue = null; runnable.IsRunningChanging += (s, e) => { capturedOldValue = e.CurrentValue; capturedNewValue = e.NewValue; }; // Act runnable.RaiseIsRunningChanging (false, true); // Assert Assert.NotNull (capturedOldValue); Assert.NotNull (capturedNewValue); Assert.False (capturedOldValue.Value); Assert.True (capturedNewValue.Value); } [Fact] public void Runnable_IsModalChanged_EventArgs_PreservesValue () { // Arrange Runnable runnable = new (); bool? capturedValue = null; runnable.IsModalChanged += (s, e) => { capturedValue = e.Value; }; // Act runnable.RaiseIsModalChangedEvent (true); // Assert Assert.NotNull (capturedValue); Assert.True (capturedValue.Value); } [Fact] public void Runnable_DifferentGenericTypes_Independent () { // Arrange & Act Runnable intRunnable = new () { Result = 42 }; Runnable stringRunnable = new () { Result = "test" }; Runnable boolRunnable = new () { Result = true }; // Assert Assert.Equal (42, intRunnable.Result); Assert.Equal ("test", stringRunnable.Result); Assert.True (boolRunnable.Result); } /// /// Complex result type for testing. /// private class ComplexResult { public int Value { get; set; } public string? Text { get; set; } } /// /// Runnable that tracks virtual method calls. /// private class OverriddenRunnable : Runnable { public bool OnIsRunningChangingCalled { get; private set; } public bool OnIsRunningChangedCalled { get; private set; } public bool OnIsModalChangedCalled { get; private set; } protected override bool OnIsRunningChanging (bool oldIsRunning, bool newIsRunning) { OnIsRunningChangingCalled = true; return base.OnIsRunningChanging (oldIsRunning, newIsRunning); } protected override void OnIsRunningChanged (bool newIsRunning) { OnIsRunningChangedCalled = true; base.OnIsRunningChanged (newIsRunning); } protected override void OnIsModalChanged (bool newIsModal) { OnIsModalChangedCalled = true; base.OnIsModalChanged (newIsModal); } } }