RunnableEdgeCasesTests.cs 9.0 KB

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