MainLoopTests.cs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863
  1. using System.Diagnostics;
  2. // Alias Console to MockConsole so we don't accidentally use Console
  3. namespace Terminal.Gui.ApplicationTests;
  4. /// <summary>Tests MainLoop using the FakeMainLoop.</summary>
  5. public class MainLoopTests
  6. {
  7. private static Button btn;
  8. private static string cancel;
  9. private static string clickMe;
  10. private static int four;
  11. private static int one;
  12. private static string pewPew;
  13. private static bool taskCompleted;
  14. // TODO: EventsPending tests
  15. // - wait = true
  16. // - wait = false
  17. // TODO: Add IMainLoop tests
  18. private static int three;
  19. private static int total;
  20. private static int two;
  21. private static int zero;
  22. // See Also ConsoleDRivers/MainLoopDriverTests.cs for tests of the MainLoopDriver
  23. // Idle Handler tests
  24. [Fact]
  25. public void AddIdle_Adds_And_Removes ()
  26. {
  27. var ml = new MainLoop (new FakeMainLoop ());
  28. Func<bool> fnTrue = () => true;
  29. Func<bool> fnFalse = () => false;
  30. ml.AddIdle (fnTrue);
  31. ml.AddIdle (fnFalse);
  32. Assert.Equal (2, ml.TimedEvents.IdleHandlers.Count);
  33. Assert.Equal (fnTrue, ml.TimedEvents.IdleHandlers [0]);
  34. Assert.NotEqual (fnFalse, ml.TimedEvents.IdleHandlers [0]);
  35. Assert.True (ml.TimedEvents.RemoveIdle (fnTrue));
  36. Assert.Single (ml.TimedEvents.IdleHandlers);
  37. // BUGBUG: This doesn't throw or indicate an error. Ideally RemoveIdle would either
  38. // throw an exception in this case, or return an error.
  39. // No. Only need to return a boolean.
  40. Assert.False (ml.TimedEvents.RemoveIdle (fnTrue));
  41. Assert.True (ml.TimedEvents.RemoveIdle (fnFalse));
  42. // BUGBUG: This doesn't throw an exception or indicate an error. Ideally RemoveIdle would either
  43. // throw an exception in this case, or return an error.
  44. // No. Only need to return a boolean.
  45. Assert.False (ml.TimedEvents.RemoveIdle (fnFalse));
  46. // Add again, but with dupe
  47. ml.AddIdle (fnTrue);
  48. ml.AddIdle (fnTrue);
  49. Assert.Equal (2, ml.TimedEvents.IdleHandlers.Count);
  50. Assert.Equal (fnTrue, ml.TimedEvents.IdleHandlers [0]);
  51. Assert.True (ml.TimedEvents.IdleHandlers [0] ());
  52. Assert.Equal (fnTrue, ml.TimedEvents.IdleHandlers [1]);
  53. Assert.True (ml.TimedEvents.IdleHandlers [1] ());
  54. Assert.True (ml.TimedEvents.RemoveIdle (fnTrue));
  55. Assert.Single (ml.TimedEvents.IdleHandlers);
  56. Assert.Equal (fnTrue, ml.TimedEvents.IdleHandlers [0]);
  57. Assert.NotEqual (fnFalse, ml.TimedEvents.IdleHandlers [0]);
  58. Assert.True (ml.TimedEvents.RemoveIdle (fnTrue));
  59. Assert.Empty (ml.TimedEvents.IdleHandlers);
  60. // BUGBUG: This doesn't throw an exception or indicate an error. Ideally RemoveIdle would either
  61. // throw an exception in this case, or return an error.
  62. // No. Only need to return a boolean.
  63. Assert.False (ml.TimedEvents.RemoveIdle (fnTrue));
  64. }
  65. [Fact]
  66. public void AddIdle_Function_GetsCalled_OnIteration ()
  67. {
  68. var ml = new MainLoop (new FakeMainLoop ());
  69. var functionCalled = 0;
  70. Func<bool> fn = () =>
  71. {
  72. functionCalled++;
  73. return true;
  74. };
  75. ml.AddIdle (fn);
  76. ml.RunIteration ();
  77. Assert.Equal (1, functionCalled);
  78. }
  79. [Fact]
  80. public void AddIdle_Twice_Returns_False_Called_Twice ()
  81. {
  82. var ml = new MainLoop (new FakeMainLoop ());
  83. var functionCalled = 0;
  84. Func<bool> fn1 = () =>
  85. {
  86. functionCalled++;
  87. return false;
  88. };
  89. // Force stop if 10 iterations
  90. var stopCount = 0;
  91. Func<bool> fnStop = () =>
  92. {
  93. stopCount++;
  94. if (stopCount == 10)
  95. {
  96. ml.Stop ();
  97. }
  98. return true;
  99. };
  100. ml.AddIdle (fnStop);
  101. ml.AddIdle (fn1);
  102. ml.AddIdle (fn1);
  103. ml.Run ();
  104. Assert.True (ml.TimedEvents.RemoveIdle (fnStop));
  105. Assert.False (ml.TimedEvents.RemoveIdle (fn1));
  106. Assert.False (ml.TimedEvents.RemoveIdle (fn1));
  107. Assert.Equal (2, functionCalled);
  108. }
  109. [Fact]
  110. public void AddIdleTwice_Function_CalledTwice ()
  111. {
  112. var ml = new MainLoop (new FakeMainLoop ());
  113. var functionCalled = 0;
  114. Func<bool> fn = () =>
  115. {
  116. functionCalled++;
  117. return true;
  118. };
  119. ml.AddIdle (fn);
  120. ml.AddIdle (fn);
  121. ml.RunIteration ();
  122. Assert.Equal (2, functionCalled);
  123. Assert.Equal (2, ml.TimedEvents.IdleHandlers.Count);
  124. functionCalled = 0;
  125. Assert.True (ml.TimedEvents.RemoveIdle (fn));
  126. Assert.Single (ml.TimedEvents.IdleHandlers);
  127. ml.RunIteration ();
  128. Assert.Equal (1, functionCalled);
  129. functionCalled = 0;
  130. Assert.True (ml.TimedEvents.RemoveIdle (fn));
  131. Assert.Empty (ml.TimedEvents.IdleHandlers);
  132. ml.RunIteration ();
  133. Assert.Equal (0, functionCalled);
  134. Assert.False (ml.TimedEvents.RemoveIdle (fn));
  135. }
  136. [Fact]
  137. public void AddThenRemoveIdle_Function_NotCalled ()
  138. {
  139. var ml = new MainLoop (new FakeMainLoop ());
  140. var functionCalled = 0;
  141. Func<bool> fn = () =>
  142. {
  143. functionCalled++;
  144. return true;
  145. };
  146. ml.AddIdle (fn);
  147. Assert.True (ml.TimedEvents.RemoveIdle (fn));
  148. ml.RunIteration ();
  149. Assert.Equal (0, functionCalled);
  150. }
  151. // Timeout Handler Tests
  152. [Fact]
  153. public void AddTimer_Adds_Removes_NoFaults ()
  154. {
  155. var ml = new MainLoop (new FakeMainLoop ());
  156. var ms = 100;
  157. var callbackCount = 0;
  158. Func<bool> callback = () =>
  159. {
  160. callbackCount++;
  161. return true;
  162. };
  163. object token = ml.TimedEvents.AddTimeout (TimeSpan.FromMilliseconds (ms), callback);
  164. Assert.True (ml.TimedEvents.RemoveTimeout (token));
  165. // BUGBUG: This should probably fault?
  166. // Must return a boolean.
  167. Assert.False (ml.TimedEvents.RemoveTimeout (token));
  168. }
  169. [Fact]
  170. public async Task AddTimer_Duplicate_Keys_Not_Allowed ()
  171. {
  172. var ml = new MainLoop (new FakeMainLoop ());
  173. const int ms = 100;
  174. object token1 = null, token2 = null;
  175. var callbackCount = 0;
  176. Func<bool> callback = () =>
  177. {
  178. callbackCount++;
  179. if (callbackCount == 2)
  180. {
  181. ml.Stop ();
  182. }
  183. return true;
  184. };
  185. var task1 = new Task (() => token1 = ml.TimedEvents.AddTimeout (TimeSpan.FromMilliseconds (ms), callback));
  186. var task2 = new Task (() => token2 = ml.TimedEvents.AddTimeout (TimeSpan.FromMilliseconds (ms), callback));
  187. Assert.Null (token1);
  188. Assert.Null (token2);
  189. task1.Start ();
  190. task2.Start ();
  191. ml.Run ();
  192. Assert.NotNull (token1);
  193. Assert.NotNull (token2);
  194. await Task.WhenAll (task1, task2);
  195. Assert.True (ml.TimedEvents.RemoveTimeout (token1));
  196. Assert.True (ml.TimedEvents.RemoveTimeout (token2));
  197. Assert.Equal (2, callbackCount);
  198. }
  199. // Timeout Handler Tests
  200. [Fact]
  201. public void AddTimer_EventFired ()
  202. {
  203. var ml = new MainLoop (new FakeMainLoop ());
  204. var ms = 100;
  205. long originTicks = DateTime.UtcNow.Ticks;
  206. var callbackCount = 0;
  207. Func<bool> callback = () =>
  208. {
  209. callbackCount++;
  210. return true;
  211. };
  212. object sender = null;
  213. TimeoutEventArgs args = null;
  214. ml.TimedEvents.TimeoutAdded += (s, e) =>
  215. {
  216. sender = s;
  217. args = e;
  218. };
  219. object token = ml.TimedEvents.AddTimeout (TimeSpan.FromMilliseconds (ms), callback);
  220. Assert.Same (ml.TimedEvents, sender);
  221. Assert.NotNull (args.Timeout);
  222. Assert.True (args.Ticks - originTicks >= 100 * TimeSpan.TicksPerMillisecond);
  223. }
  224. [Fact]
  225. public void AddTimer_In_Parallel_Wont_Throw ()
  226. {
  227. var ml = new MainLoop (new FakeMainLoop ());
  228. const int ms = 100;
  229. object token1 = null, token2 = null;
  230. var callbackCount = 0;
  231. Func<bool> callback = () =>
  232. {
  233. callbackCount++;
  234. if (callbackCount == 2)
  235. {
  236. ml.Stop ();
  237. }
  238. return true;
  239. };
  240. Parallel.Invoke (
  241. () => token1 = ml.TimedEvents.AddTimeout (TimeSpan.FromMilliseconds (ms), callback),
  242. () => token2 = ml.TimedEvents.AddTimeout (TimeSpan.FromMilliseconds (ms), callback)
  243. );
  244. ml.Run ();
  245. Assert.NotNull (token1);
  246. Assert.NotNull (token2);
  247. Assert.True (ml.TimedEvents.RemoveTimeout (token1));
  248. Assert.True (ml.TimedEvents.RemoveTimeout (token2));
  249. Assert.Equal (2, callbackCount);
  250. }
  251. [Fact]
  252. public void AddTimer_Remove_NotCalled ()
  253. {
  254. var ml = new MainLoop (new FakeMainLoop ());
  255. TimeSpan ms = TimeSpan.FromMilliseconds (50);
  256. // Force stop if 10 iterations
  257. var stopCount = 0;
  258. Func<bool> fnStop = () =>
  259. {
  260. stopCount++;
  261. if (stopCount == 10)
  262. {
  263. ml.Stop ();
  264. }
  265. return true;
  266. };
  267. ml.AddIdle (fnStop);
  268. var callbackCount = 0;
  269. Func<bool> callback = () =>
  270. {
  271. callbackCount++;
  272. return true;
  273. };
  274. object token = ml.TimedEvents.AddTimeout (ms, callback);
  275. Assert.True (ml.TimedEvents.RemoveTimeout (token));
  276. ml.Run ();
  277. Assert.Equal (0, callbackCount);
  278. }
  279. [Fact]
  280. public void AddTimer_ReturnFalse_StopsBeingCalled ()
  281. {
  282. var ml = new MainLoop (new FakeMainLoop ());
  283. TimeSpan ms = TimeSpan.FromMilliseconds (50);
  284. // Force stop if 10 iterations
  285. var stopCount = 0;
  286. Func<bool> fnStop = () =>
  287. {
  288. Thread.Sleep (10); // Sleep to enable timer to fire
  289. stopCount++;
  290. if (stopCount == 10)
  291. {
  292. ml.Stop ();
  293. }
  294. return true;
  295. };
  296. ml.AddIdle (fnStop);
  297. var callbackCount = 0;
  298. Func<bool> callback = () =>
  299. {
  300. callbackCount++;
  301. return false;
  302. };
  303. object token = ml.TimedEvents.AddTimeout (ms, callback);
  304. ml.Run ();
  305. Assert.Equal (1, callbackCount);
  306. Assert.Equal (10, stopCount);
  307. Assert.False (ml.TimedEvents.RemoveTimeout (token));
  308. }
  309. [Fact]
  310. public void AddTimer_Run_Called ()
  311. {
  312. var ml = new MainLoop (new FakeMainLoop ());
  313. var ms = 100;
  314. var callbackCount = 0;
  315. Func<bool> callback = () =>
  316. {
  317. callbackCount++;
  318. ml.Stop ();
  319. return true;
  320. };
  321. object token = ml.TimedEvents.AddTimeout (TimeSpan.FromMilliseconds (ms), callback);
  322. ml.Run ();
  323. Assert.True (ml.TimedEvents.RemoveTimeout (token));
  324. Assert.Equal (1, callbackCount);
  325. }
  326. [Fact]
  327. public void AddTimer_Run_CalledAtApproximatelyRightTime ()
  328. {
  329. var ml = new MainLoop (new FakeMainLoop ());
  330. TimeSpan ms = TimeSpan.FromMilliseconds (50);
  331. var watch = new Stopwatch ();
  332. var callbackCount = 0;
  333. Func<bool> callback = () =>
  334. {
  335. watch.Stop ();
  336. callbackCount++;
  337. ml.Stop ();
  338. return true;
  339. };
  340. object token = ml.TimedEvents.AddTimeout (ms, callback);
  341. watch.Start ();
  342. ml.Run ();
  343. // +/- 100ms should be good enuf
  344. // https://github.com/xunit/assert.xunit/pull/25
  345. Assert.Equal (ms * callbackCount, watch.Elapsed, new MillisecondTolerance (100));
  346. Assert.True (ml.TimedEvents.RemoveTimeout (token));
  347. Assert.Equal (1, callbackCount);
  348. }
  349. [Fact]
  350. public void AddTimer_Run_CalledTwiceApproximatelyRightTime ()
  351. {
  352. var ml = new MainLoop (new FakeMainLoop ());
  353. TimeSpan ms = TimeSpan.FromMilliseconds (50);
  354. var watch = new Stopwatch ();
  355. var callbackCount = 0;
  356. Func<bool> callback = () =>
  357. {
  358. callbackCount++;
  359. if (callbackCount == 2)
  360. {
  361. watch.Stop ();
  362. ml.Stop ();
  363. }
  364. return true;
  365. };
  366. object token = ml.TimedEvents.AddTimeout (ms, callback);
  367. watch.Start ();
  368. ml.Run ();
  369. // +/- 100ms should be good enuf
  370. // https://github.com/xunit/assert.xunit/pull/25
  371. Assert.Equal (ms * callbackCount, watch.Elapsed, new MillisecondTolerance (100));
  372. Assert.True (ml.TimedEvents.RemoveTimeout (token));
  373. Assert.Equal (2, callbackCount);
  374. }
  375. [Fact]
  376. public void CheckTimersAndIdleHandlers_NoTimers_Returns_False ()
  377. {
  378. var ml = new MainLoop (new FakeMainLoop ());
  379. bool retVal = ml.TimedEvents.CheckTimersAndIdleHandlers (out int waitTimeOut);
  380. Assert.False (retVal);
  381. Assert.Equal (-1, waitTimeOut);
  382. }
  383. [Fact]
  384. public void CheckTimersAndIdleHandlers_NoTimers_WithIdle_Returns_True ()
  385. {
  386. var ml = new MainLoop (new FakeMainLoop ());
  387. Func<bool> fnTrue = () => true;
  388. ml.AddIdle (fnTrue);
  389. bool retVal = ml.TimedEvents.CheckTimersAndIdleHandlers (out int waitTimeOut);
  390. Assert.True (retVal);
  391. Assert.Equal (-1, waitTimeOut);
  392. }
  393. [Fact]
  394. public void CheckTimersAndIdleHandlers_With1Timer_Returns_Timer ()
  395. {
  396. var ml = new MainLoop (new FakeMainLoop ());
  397. TimeSpan ms = TimeSpan.FromMilliseconds (50);
  398. static bool Callback () { return false; }
  399. _ = ml.TimedEvents.AddTimeout (ms, Callback);
  400. bool retVal = ml.TimedEvents.CheckTimersAndIdleHandlers (out int waitTimeOut);
  401. Assert.True (retVal);
  402. // It should take < 10ms to execute to here
  403. Assert.True (ms.TotalMilliseconds <= waitTimeOut + 10);
  404. }
  405. [Fact]
  406. public void CheckTimersAndIdleHandlers_With2Timers_Returns_Timer ()
  407. {
  408. var ml = new MainLoop (new FakeMainLoop ());
  409. TimeSpan ms = TimeSpan.FromMilliseconds (50);
  410. static bool Callback () { return false; }
  411. _ = ml.TimedEvents.AddTimeout (ms, Callback);
  412. _ = ml.TimedEvents.AddTimeout (ms, Callback);
  413. bool retVal = ml.TimedEvents.CheckTimersAndIdleHandlers (out int waitTimeOut);
  414. Assert.True (retVal);
  415. // It should take < 10ms to execute to here
  416. Assert.True (ms.TotalMilliseconds <= waitTimeOut + 10);
  417. }
  418. [Fact]
  419. public void False_Idle_Stops_It_Being_Called_Again ()
  420. {
  421. var ml = new MainLoop (new FakeMainLoop ());
  422. var functionCalled = 0;
  423. Func<bool> fn1 = () =>
  424. {
  425. functionCalled++;
  426. if (functionCalled == 10)
  427. {
  428. return false;
  429. }
  430. return true;
  431. };
  432. // Force stop if 20 iterations
  433. var stopCount = 0;
  434. Func<bool> fnStop = () =>
  435. {
  436. stopCount++;
  437. if (stopCount == 20)
  438. {
  439. ml.Stop ();
  440. }
  441. return true;
  442. };
  443. ml.AddIdle (fnStop);
  444. ml.AddIdle (fn1);
  445. ml.Run ();
  446. Assert.True (ml.TimedEvents.RemoveIdle (fnStop));
  447. Assert.False (ml.TimedEvents.RemoveIdle (fn1));
  448. Assert.Equal (10, functionCalled);
  449. Assert.Equal (20, stopCount);
  450. }
  451. [Fact]
  452. public void Internal_Tests ()
  453. {
  454. var testMainloop = new TestMainloop ();
  455. var mainloop = new MainLoop (testMainloop);
  456. Assert.Empty (mainloop.TimedEvents.Timeouts);
  457. Assert.Empty (mainloop.TimedEvents.IdleHandlers);
  458. Assert.NotNull (
  459. new Timeout { Span = new (), Callback = () => true }
  460. );
  461. }
  462. [Theory]
  463. [MemberData (nameof (TestAddIdle))]
  464. public void Mainloop_Invoke_Or_AddIdle_Can_Be_Used_For_Events_Or_Actions (
  465. Action action,
  466. string pclickMe,
  467. string pcancel,
  468. string ppewPew,
  469. int pzero,
  470. int pone,
  471. int ptwo,
  472. int pthree,
  473. int pfour
  474. )
  475. {
  476. // TODO: Expand this test to test all drivers
  477. Application.Init (new FakeDriver ());
  478. total = 0;
  479. btn = null;
  480. clickMe = pclickMe;
  481. cancel = pcancel;
  482. pewPew = ppewPew;
  483. zero = pzero;
  484. one = pone;
  485. two = ptwo;
  486. three = pthree;
  487. four = pfour;
  488. taskCompleted = false;
  489. var btnLaunch = new Button { Text = "Open Window" };
  490. btnLaunch.Accepting += (s, e) => action ();
  491. var top = new Toplevel ();
  492. top.Add (btnLaunch);
  493. int iterations = -1;
  494. Application.Iteration += (s, a) =>
  495. {
  496. iterations++;
  497. if (iterations == 0)
  498. {
  499. Assert.Null (btn);
  500. Assert.Equal (zero, total);
  501. Assert.False (btnLaunch.NewKeyDownEvent (Key.Space));
  502. if (btn == null)
  503. {
  504. Assert.Null (btn);
  505. Assert.Equal (zero, total);
  506. }
  507. else
  508. {
  509. Assert.Equal (clickMe, btn.Text);
  510. Assert.Equal (four, total);
  511. }
  512. }
  513. else if (iterations == 1)
  514. {
  515. Assert.Equal (clickMe, btn.Text);
  516. Assert.Equal (zero, total);
  517. Assert.False (btn.NewKeyDownEvent (Key.Space));
  518. Assert.Equal (cancel, btn.Text);
  519. Assert.Equal (one, total);
  520. }
  521. else if (taskCompleted)
  522. {
  523. Application.RequestStop ();
  524. }
  525. };
  526. Application.Run (top);
  527. top.Dispose ();
  528. Assert.True (taskCompleted);
  529. Assert.Equal (clickMe, btn.Text);
  530. Assert.Equal (four, total);
  531. Application.Shutdown ();
  532. }
  533. [Fact]
  534. public void RemoveIdle_Function_NotCalled ()
  535. {
  536. var ml = new MainLoop (new FakeMainLoop ());
  537. var functionCalled = 0;
  538. Func<bool> fn = () =>
  539. {
  540. functionCalled++;
  541. return true;
  542. };
  543. Assert.False (ml.TimedEvents.RemoveIdle (fn));
  544. ml.RunIteration ();
  545. Assert.Equal (0, functionCalled);
  546. }
  547. [Fact]
  548. public void Run_Runs_Idle_Stop_Stops_Idle ()
  549. {
  550. var ml = new MainLoop (new FakeMainLoop ());
  551. var functionCalled = 0;
  552. Func<bool> fn = () =>
  553. {
  554. functionCalled++;
  555. if (functionCalled == 10)
  556. {
  557. ml.Stop ();
  558. }
  559. return true;
  560. };
  561. ml.AddIdle (fn);
  562. ml.Run ();
  563. Assert.True (ml.TimedEvents.RemoveIdle (fn));
  564. Assert.Equal (10, functionCalled);
  565. }
  566. public static IEnumerable<object []> TestAddIdle
  567. {
  568. get
  569. {
  570. // Goes fine
  571. Action a1 = StartWindow;
  572. yield return new object [] { a1, "Click Me", "Cancel", "Pew Pew", 0, 1, 2, 3, 4 };
  573. // Also goes fine
  574. Action a2 = () => Application.Invoke (StartWindow);
  575. yield return new object [] { a2, "Click Me", "Cancel", "Pew Pew", 0, 1, 2, 3, 4 };
  576. }
  577. }
  578. private static async void RunAsyncTest (object sender, EventArgs e)
  579. {
  580. Assert.Equal (clickMe, btn.Text);
  581. Assert.Equal (zero, total);
  582. btn.Text = "Cancel";
  583. Interlocked.Increment (ref total);
  584. btn.SetNeedsDraw ();
  585. await Task.Run (
  586. () =>
  587. {
  588. try
  589. {
  590. Assert.Equal (cancel, btn.Text);
  591. Assert.Equal (one, total);
  592. RunSql ();
  593. }
  594. finally
  595. {
  596. SetReadyToRun ();
  597. }
  598. }
  599. )
  600. .ContinueWith (
  601. async (s, e) =>
  602. {
  603. await Task.Delay (1000);
  604. Assert.Equal (clickMe, btn.Text);
  605. Assert.Equal (three, total);
  606. Interlocked.Increment (ref total);
  607. Assert.Equal (clickMe, btn.Text);
  608. Assert.Equal (four, total);
  609. taskCompleted = true;
  610. },
  611. TaskScheduler.FromCurrentSynchronizationContext ()
  612. );
  613. }
  614. private static void RunSql ()
  615. {
  616. Thread.Sleep (100);
  617. Assert.Equal (cancel, btn.Text);
  618. Assert.Equal (one, total);
  619. Application.Invoke (
  620. () =>
  621. {
  622. btn.Text = "Pew Pew";
  623. Interlocked.Increment (ref total);
  624. btn.SetNeedsDraw ();
  625. }
  626. );
  627. }
  628. private static void SetReadyToRun ()
  629. {
  630. Thread.Sleep (100);
  631. Assert.Equal (pewPew, btn.Text);
  632. Assert.Equal (two, total);
  633. Application.Invoke (
  634. () =>
  635. {
  636. btn.Text = "Click Me";
  637. Interlocked.Increment (ref total);
  638. btn.SetNeedsDraw ();
  639. }
  640. );
  641. }
  642. private static void StartWindow ()
  643. {
  644. var startWindow = new Window { Modal = true };
  645. btn = new() { Text = "Click Me" };
  646. btn.Accepting += RunAsyncTest;
  647. var totalbtn = new Button { X = Pos.Right (btn), Text = "total" };
  648. totalbtn.Accepting += (s, e) => { MessageBox.Query ("Count", $"Count is {total}", "Ok"); };
  649. startWindow.Add (btn);
  650. startWindow.Add (totalbtn);
  651. Application.Run (startWindow);
  652. Assert.Equal (clickMe, btn.Text);
  653. Assert.Equal (four, total);
  654. Application.RequestStop ();
  655. }
  656. private class MillisecondTolerance : IEqualityComparer<TimeSpan>
  657. {
  658. public MillisecondTolerance (int tolerance) { _tolerance = tolerance; }
  659. private readonly int _tolerance;
  660. public bool Equals (TimeSpan x, TimeSpan y) { return Math.Abs (x.Milliseconds - y.Milliseconds) <= _tolerance; }
  661. public int GetHashCode (TimeSpan obj) { return obj.GetHashCode (); }
  662. }
  663. private class TestMainloop : IMainLoopDriver
  664. {
  665. private MainLoop mainLoop;
  666. public bool EventsPending () { throw new NotImplementedException (); }
  667. public void Iteration () { throw new NotImplementedException (); }
  668. public void TearDown () { throw new NotImplementedException (); }
  669. public void Setup (MainLoop mainLoop) { this.mainLoop = mainLoop; }
  670. public void Wakeup () { throw new NotImplementedException (); }
  671. }
  672. }