MainLoopTests.cs 19 KB

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