RunnableEdgeCasesTests.cs 9.0 KB

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