RunnableEdgeCasesTests.cs 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. using Xunit.Abstractions;
  2. namespace ApplicationTests.RunnableTests;
  3. /// <summary>
  4. /// Tests for edge cases and error conditions in IRunnable implementation.
  5. /// </summary>
  6. public class RunnableEdgeCasesTests (ITestOutputHelper output)
  7. {
  8. private readonly ITestOutputHelper _output = output;
  9. [Fact]
  10. public void Runnable_MultipleEventSubscribers_AllInvoked ()
  11. {
  12. // Arrange
  13. Runnable<int> runnable = new ();
  14. var subscriber1Called = false;
  15. var subscriber2Called = false;
  16. var subscriber3Called = false;
  17. runnable.IsRunningChanging += (s, e) => subscriber1Called = true;
  18. runnable.IsRunningChanging += (s, e) => subscriber2Called = true;
  19. runnable.IsRunningChanging += (s, e) => subscriber3Called = true;
  20. // Act
  21. runnable.RaiseIsRunningChanging (false, true);
  22. // Assert
  23. Assert.True (subscriber1Called);
  24. Assert.True (subscriber2Called);
  25. Assert.True (subscriber3Called);
  26. }
  27. [Fact]
  28. public void Runnable_EventSubscriber_CanCancelAfterOthers ()
  29. {
  30. // Arrange
  31. Runnable<int> runnable = new ();
  32. var subscriber1Called = false;
  33. var subscriber2Called = false;
  34. runnable.IsRunningChanging += (s, e) => subscriber1Called = true;
  35. runnable.IsRunningChanging += (s, e) =>
  36. {
  37. subscriber2Called = true;
  38. e.Cancel = true; // Second subscriber cancels
  39. };
  40. // Act
  41. bool canceled = runnable.RaiseIsRunningChanging (false, true);
  42. // Assert
  43. Assert.True (subscriber1Called);
  44. Assert.True (subscriber2Called);
  45. Assert.True (canceled);
  46. }
  47. [Fact]
  48. public void Runnable_Result_CanBeSetMultipleTimes ()
  49. {
  50. // Arrange
  51. Runnable<int> runnable = new ();
  52. // Act
  53. runnable.Result = 1;
  54. runnable.Result = 2;
  55. runnable.Result = 3;
  56. // Assert
  57. Assert.Equal (3, runnable.Result);
  58. }
  59. [Fact]
  60. public void Runnable_Result_ClearedOnMultipleStarts ()
  61. {
  62. // Arrange
  63. Runnable<int> runnable = new () { Result = 42 };
  64. // Act & Assert - First start
  65. runnable.RaiseIsRunningChanging (false, true);
  66. Assert.Equal (0, runnable.Result);
  67. // Set result again
  68. runnable.Result = 99;
  69. Assert.Equal (99, runnable.Result);
  70. // Second start should clear again
  71. runnable.RaiseIsRunningChanging (false, true);
  72. Assert.Equal (0, runnable.Result);
  73. }
  74. [Fact]
  75. public void Runnable_NullableResult_DefaultsToNull ()
  76. {
  77. // Arrange & Act
  78. Runnable<string> runnable = new ();
  79. // Assert
  80. Assert.Null (runnable.Result);
  81. }
  82. [Fact]
  83. public void Runnable_NullableResult_CanBeExplicitlyNull ()
  84. {
  85. // Arrange
  86. Runnable<string> runnable = new () { Result = "test" };
  87. // Act
  88. runnable.Result = null;
  89. // Assert
  90. Assert.Null (runnable.Result);
  91. }
  92. [Fact]
  93. public void Runnable_ComplexType_Result ()
  94. {
  95. // Arrange
  96. Runnable<ComplexResult> runnable = new ();
  97. ComplexResult result = new () { Value = 42, Text = "test" };
  98. // Act
  99. runnable.Result = result;
  100. // Assert
  101. Assert.NotNull (runnable.Result);
  102. Assert.Equal (42, runnable.Result.Value);
  103. Assert.Equal ("test", runnable.Result.Text);
  104. }
  105. [Fact]
  106. public void Runnable_IsRunning_WithNoApp ()
  107. {
  108. // Arrange
  109. Runnable<int> runnable = new ();
  110. // Don't set App property
  111. // Act & Assert
  112. Assert.False (runnable.IsRunning);
  113. }
  114. [Fact]
  115. public void Runnable_IsModal_WithNoApp ()
  116. {
  117. // Arrange
  118. Runnable<int> runnable = new ();
  119. // Don't set App property
  120. // Act & Assert
  121. Assert.False (runnable.IsModal);
  122. }
  123. [Fact]
  124. public void Runnable_VirtualMethods_CanBeOverridden ()
  125. {
  126. // Arrange
  127. OverriddenRunnable runnable = new ();
  128. // Act
  129. bool canceledRunning = runnable.RaiseIsRunningChanging (false, true);
  130. runnable.RaiseIsRunningChangedEvent (true);
  131. runnable.RaiseIsModalChangedEvent (true);
  132. // Assert
  133. Assert.True (runnable.OnIsRunningChangingCalled);
  134. Assert.True (runnable.OnIsRunningChangedCalled);
  135. Assert.True (runnable.OnIsModalChangedCalled);
  136. }
  137. [Fact]
  138. public void Runnable_RequestStop_WithNoApp ()
  139. {
  140. // Arrange
  141. Runnable<int> runnable = new ();
  142. // Don't set App property
  143. // Act & Assert - Should not throw
  144. runnable.RequestStop ();
  145. }
  146. [Fact]
  147. public void RunnableSessionToken_Constructor_RequiresRunnable ()
  148. {
  149. // This is implicitly tested by the constructor signature,
  150. // but let's verify it creates with non-null runnable
  151. // Arrange
  152. Runnable<int> runnable = new ();
  153. // Act
  154. SessionToken token = new (runnable);
  155. // Assert
  156. Assert.NotNull (token.Runnable);
  157. }
  158. [Fact]
  159. public void Runnable_EventArgs_PreservesValues ()
  160. {
  161. // Arrange
  162. Runnable<int> runnable = new ();
  163. bool? capturedOldValue = null;
  164. bool? capturedNewValue = null;
  165. runnable.IsRunningChanging += (s, e) =>
  166. {
  167. capturedOldValue = e.CurrentValue;
  168. capturedNewValue = e.NewValue;
  169. };
  170. // Act
  171. runnable.RaiseIsRunningChanging (false, true);
  172. // Assert
  173. Assert.NotNull (capturedOldValue);
  174. Assert.NotNull (capturedNewValue);
  175. Assert.False (capturedOldValue.Value);
  176. Assert.True (capturedNewValue.Value);
  177. }
  178. [Fact]
  179. public void Runnable_IsModalChanged_EventArgs_PreservesValue ()
  180. {
  181. // Arrange
  182. Runnable<int> runnable = new ();
  183. bool? capturedValue = null;
  184. runnable.IsModalChanged += (s, e) => { capturedValue = e.Value; };
  185. // Act
  186. runnable.RaiseIsModalChangedEvent (true);
  187. // Assert
  188. Assert.NotNull (capturedValue);
  189. Assert.True (capturedValue.Value);
  190. }
  191. [Fact]
  192. public void Runnable_DifferentGenericTypes_Independent ()
  193. {
  194. // Arrange & Act
  195. Runnable<int> intRunnable = new () { Result = 42 };
  196. Runnable<string> stringRunnable = new () { Result = "test" };
  197. Runnable<bool> boolRunnable = new () { Result = true };
  198. // Assert
  199. Assert.Equal (42, intRunnable.Result);
  200. Assert.Equal ("test", stringRunnable.Result);
  201. Assert.True (boolRunnable.Result);
  202. }
  203. /// <summary>
  204. /// Complex result type for testing.
  205. /// </summary>
  206. private class ComplexResult
  207. {
  208. public int Value { get; set; }
  209. public string? Text { get; set; }
  210. }
  211. /// <summary>
  212. /// Runnable that tracks virtual method calls.
  213. /// </summary>
  214. private class OverriddenRunnable : Runnable<int>
  215. {
  216. public bool OnIsRunningChangingCalled { get; private set; }
  217. public bool OnIsRunningChangedCalled { get; private set; }
  218. public bool OnIsModalChangedCalled { get; private set; }
  219. protected override bool OnIsRunningChanging (bool oldIsRunning, bool newIsRunning)
  220. {
  221. OnIsRunningChangingCalled = true;
  222. return base.OnIsRunningChanging (oldIsRunning, newIsRunning);
  223. }
  224. protected override void OnIsRunningChanged (bool newIsRunning)
  225. {
  226. OnIsRunningChangedCalled = true;
  227. base.OnIsRunningChanged (newIsRunning);
  228. }
  229. protected override void OnIsModalChanged (bool newIsModal)
  230. {
  231. OnIsModalChangedCalled = true;
  232. base.OnIsModalChanged (newIsModal);
  233. }
  234. }
  235. }