ApplicationTests.cs 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069
  1. using Xunit.Abstractions;
  2. // Alias Console to MockConsole so we don't accidentally use Console
  3. namespace Terminal.Gui.ApplicationTests;
  4. public class ApplicationTests
  5. {
  6. private readonly ITestOutputHelper _output;
  7. public ApplicationTests (ITestOutputHelper output)
  8. {
  9. _output = output;
  10. ConsoleDriver.RunningUnitTests = true;
  11. #if DEBUG_IDISPOSABLE
  12. Responder.Instances.Clear ();
  13. RunState.Instances.Clear ();
  14. #endif
  15. }
  16. [Fact]
  17. public void Begin_Null_Toplevel_Throws ()
  18. {
  19. // Setup Mock driver
  20. Init ();
  21. // Test null Toplevel
  22. Assert.Throws<ArgumentNullException> (() => Application.Begin (null));
  23. Shutdown ();
  24. Assert.Null (Application.Top);
  25. Assert.Null (Application.MainLoop);
  26. Assert.Null (Application.Driver);
  27. }
  28. [Fact]
  29. [AutoInitShutdown]
  30. public void Begin_Sets_Application_Top_To_Console_Size ()
  31. {
  32. Assert.Null (Application.Top);
  33. Application.Begin (new ());
  34. Assert.Equal (new (0, 0, 80, 25), Application.Top.Frame);
  35. ((FakeDriver)Application.Driver).SetBufferSize (5, 5);
  36. Assert.Equal (new (0, 0, 5, 5), Application.Top.Frame);
  37. }
  38. [Fact]
  39. public void End_And_Shutdown_Should_Not_Dispose_ApplicationTop ()
  40. {
  41. Init ();
  42. RunState rs = Application.Begin (new ());
  43. Assert.Equal (rs.Toplevel, Application.Top);
  44. Application.End (rs);
  45. #if DEBUG_IDISPOSABLE
  46. Assert.True (rs.WasDisposed);
  47. Assert.False (Application.Top.WasDisposed); // Is true because the rs.Toplevel is the same as Application.Top
  48. #endif
  49. Assert.Null (rs.Toplevel);
  50. Toplevel top = Application.Top;
  51. #if DEBUG_IDISPOSABLE
  52. Exception exception = Record.Exception (() => Shutdown ());
  53. Assert.NotNull (exception);
  54. Assert.False (top.WasDisposed);
  55. top.Dispose ();
  56. Assert.True (top.WasDisposed);
  57. #endif
  58. Shutdown ();
  59. Assert.Null (Application.Top);
  60. }
  61. [Fact]
  62. public void Init_Begin_End_Cleans_Up ()
  63. {
  64. Init ();
  65. // Begin will cause Run() to be called, which will call Begin(). Thus will block the tests
  66. // if we don't stop
  67. Application.Iteration += (s, a) => { Application.RequestStop (); };
  68. RunState runstate = null;
  69. EventHandler<RunStateEventArgs> NewRunStateFn = (s, e) =>
  70. {
  71. Assert.NotNull (e.State);
  72. runstate = e.State;
  73. };
  74. Application.NotifyNewRunState += NewRunStateFn;
  75. var topLevel = new Toplevel ();
  76. RunState rs = Application.Begin (topLevel);
  77. Assert.NotNull (rs);
  78. Assert.NotNull (runstate);
  79. Assert.Equal (rs, runstate);
  80. Assert.Equal (topLevel, Application.Top);
  81. Assert.Equal (topLevel, Application.Current);
  82. Application.NotifyNewRunState -= NewRunStateFn;
  83. Application.End (runstate);
  84. Assert.Null (Application.Current);
  85. Assert.NotNull (Application.Top);
  86. Assert.NotNull (Application.MainLoop);
  87. Assert.NotNull (Application.Driver);
  88. topLevel.Dispose ();
  89. Shutdown ();
  90. Assert.Null (Application.Top);
  91. Assert.Null (Application.MainLoop);
  92. Assert.Null (Application.Driver);
  93. }
  94. [Theory]
  95. [InlineData (typeof (FakeDriver))]
  96. [InlineData (typeof (NetDriver))]
  97. //[InlineData (typeof (ANSIDriver))]
  98. [InlineData (typeof (WindowsDriver))]
  99. [InlineData (typeof (CursesDriver))]
  100. public void Init_DriverName_Should_Pick_Correct_Driver (Type driverType)
  101. {
  102. var driver = (ConsoleDriver)Activator.CreateInstance (driverType);
  103. Application.Init (driverName: driverType.Name);
  104. Assert.NotNull (Application.Driver);
  105. Assert.NotEqual (driver, Application.Driver);
  106. Assert.Equal (driverType, Application.Driver.GetType ());
  107. Shutdown ();
  108. }
  109. [Fact]
  110. public void Init_Null_Driver_Should_Pick_A_Driver ()
  111. {
  112. Application.Init ();
  113. Assert.NotNull (Application.Driver);
  114. Shutdown ();
  115. }
  116. [Fact]
  117. public void Init_ResetState_Resets_Properties ()
  118. {
  119. ConfigurationManager.ThrowOnJsonErrors = true;
  120. // For all the fields/properties of Application, check that they are reset to their default values
  121. // Set some values
  122. Application.Init ();
  123. Application._initialized = true;
  124. // Reset
  125. Application.ResetState ();
  126. void CheckReset ()
  127. {
  128. // Check that all fields and properties are set to their default values
  129. // Public Properties
  130. Assert.Null (Application.Top);
  131. Assert.Null (Application.Current);
  132. Assert.Null (Application.MouseGrabView);
  133. Assert.Null (Application.WantContinuousButtonPressedView);
  134. // Don't check Application.ForceDriver
  135. // Assert.Empty (Application.ForceDriver);
  136. Assert.False (Application.Force16Colors);
  137. Assert.Null (Application.Driver);
  138. Assert.Null (Application.MainLoop);
  139. Assert.False (Application.EndAfterFirstIteration);
  140. Assert.Equal (Key.Empty, Application.AlternateBackwardKey);
  141. Assert.Equal (Key.Empty, Application.AlternateForwardKey);
  142. Assert.Equal (Key.Empty, Application.QuitKey);
  143. Assert.Null (Application.OverlappedChildren);
  144. Assert.Null (Application.OverlappedTop);
  145. // Internal properties
  146. Assert.False (Application._initialized);
  147. Assert.Equal (Application.GetSupportedCultures (), Application.SupportedCultures);
  148. Assert.False (Application._forceFakeConsole);
  149. Assert.Equal (-1, Application._mainThreadId);
  150. Assert.Empty (Application._topLevels);
  151. Assert.Null (Application._mouseEnteredView);
  152. // Events - Can't check
  153. //Assert.Null (Application.NotifyNewRunState);
  154. //Assert.Null (Application.NotifyNewRunState);
  155. //Assert.Null (Application.Iteration);
  156. //Assert.Null (Application.SizeChanging);
  157. //Assert.Null (Application.GrabbedMouse);
  158. //Assert.Null (Application.UnGrabbingMouse);
  159. //Assert.Null (Application.GrabbedMouse);
  160. //Assert.Null (Application.UnGrabbedMouse);
  161. //Assert.Null (Application.MouseEvent);
  162. //Assert.Null (Application.KeyDown);
  163. //Assert.Null (Application.KeyUp);
  164. }
  165. CheckReset ();
  166. // Set the values that can be set
  167. Application._initialized = true;
  168. Application._forceFakeConsole = true;
  169. Application._mainThreadId = 1;
  170. //Application._topLevels = new List<Toplevel> ();
  171. Application._mouseEnteredView = new ();
  172. //Application.SupportedCultures = new List<CultureInfo> ();
  173. Application.Force16Colors = true;
  174. //Application.ForceDriver = "driver";
  175. Application.EndAfterFirstIteration = true;
  176. Application.AlternateBackwardKey = Key.A;
  177. Application.AlternateForwardKey = Key.B;
  178. Application.QuitKey = Key.C;
  179. //Application.OverlappedChildren = new List<View> ();
  180. //Application.OverlappedTop =
  181. Application._mouseEnteredView = new ();
  182. //Application.WantContinuousButtonPressedView = new View ();
  183. Application.ResetState ();
  184. CheckReset ();
  185. ConfigurationManager.ThrowOnJsonErrors = false;
  186. }
  187. [Fact]
  188. public void Init_Shutdown_Cleans_Up ()
  189. {
  190. // Verify initial state is per spec
  191. //Pre_Init_State ();
  192. Application.Init (new FakeDriver ());
  193. // Verify post-Init state is correct
  194. //Post_Init_State ();
  195. Application.Shutdown ();
  196. // Verify state is back to initial
  197. //Pre_Init_State ();
  198. #if DEBUG_IDISPOSABLE
  199. // Validate there are no outstanding Responder-based instances
  200. // after a scenario was selected to run. This proves the main UI Catalog
  201. // 'app' closed cleanly.
  202. Assert.Empty (Responder.Instances);
  203. #endif
  204. }
  205. [Fact]
  206. public void Init_Unbalanced_Throws ()
  207. {
  208. Application.Init (new FakeDriver ());
  209. Toplevel topLevel = null;
  210. Assert.Throws<InvalidOperationException> (
  211. () =>
  212. Application.InternalInit (
  213. new FakeDriver ()
  214. )
  215. );
  216. Shutdown ();
  217. Assert.Null (Application.Top);
  218. Assert.Null (Application.MainLoop);
  219. Assert.Null (Application.Driver);
  220. // Now try the other way
  221. topLevel = null;
  222. Application.InternalInit (new FakeDriver ());
  223. Assert.Throws<InvalidOperationException> (() => Application.Init (new FakeDriver ()));
  224. Shutdown ();
  225. Assert.Null (Application.Top);
  226. Assert.Null (Application.MainLoop);
  227. Assert.Null (Application.Driver);
  228. }
  229. [Fact]
  230. public void InitWithoutTopLevelFactory_Begin_End_Cleans_Up ()
  231. {
  232. // Begin will cause Run() to be called, which will call Begin(). Thus will block the tests
  233. // if we don't stop
  234. Application.Iteration += (s, a) => { Application.RequestStop (); };
  235. // NOTE: Run<T>, when called after Init has been called behaves differently than
  236. // when called if Init has not been called.
  237. Toplevel topLevel = new ();
  238. Application.InternalInit (new FakeDriver ());
  239. RunState runstate = null;
  240. EventHandler<RunStateEventArgs> NewRunStateFn = (s, e) =>
  241. {
  242. Assert.NotNull (e.State);
  243. runstate = e.State;
  244. };
  245. Application.NotifyNewRunState += NewRunStateFn;
  246. RunState rs = Application.Begin (topLevel);
  247. Assert.NotNull (rs);
  248. Assert.NotNull (runstate);
  249. Assert.Equal (rs, runstate);
  250. Assert.Equal (topLevel, Application.Top);
  251. Assert.Equal (topLevel, Application.Current);
  252. Application.NotifyNewRunState -= NewRunStateFn;
  253. Application.End (runstate);
  254. Assert.Null (Application.Current);
  255. Assert.NotNull (Application.Top);
  256. Assert.NotNull (Application.MainLoop);
  257. Assert.NotNull (Application.Driver);
  258. topLevel.Dispose ();
  259. Shutdown ();
  260. Assert.Null (Application.Top);
  261. Assert.Null (Application.MainLoop);
  262. Assert.Null (Application.Driver);
  263. }
  264. [Fact]
  265. [AutoInitShutdown]
  266. public void Internal_Properties_Correct ()
  267. {
  268. Assert.True (Application._initialized);
  269. Assert.Null (Application.Top);
  270. RunState rs = Application.Begin (new ());
  271. Assert.Equal (Application.Top, rs.Toplevel);
  272. Assert.Null (Application.MouseGrabView); // public
  273. Assert.Null (Application.WantContinuousButtonPressedView); // public
  274. Assert.False (Application.MoveToOverlappedChild (Application.Top));
  275. }
  276. // Invoke Tests
  277. // TODO: Test with threading scenarios
  278. [Fact]
  279. public void Invoke_Adds_Idle ()
  280. {
  281. Application.Init (new FakeDriver ());
  282. var top = new Toplevel ();
  283. RunState rs = Application.Begin (top);
  284. var firstIteration = false;
  285. var actionCalled = 0;
  286. Application.Invoke (() => { actionCalled++; });
  287. Application.MainLoop.Running = true;
  288. Application.RunIteration (ref rs, ref firstIteration);
  289. Assert.Equal (1, actionCalled);
  290. top.Dispose ();
  291. Application.Shutdown ();
  292. }
  293. [Fact]
  294. [AutoInitShutdown]
  295. public void SetCurrentAsTop_Run_A_Not_Modal_Toplevel_Make_It_The_Current_Application_Top ()
  296. {
  297. var top = new Toplevel ();
  298. var t1 = new Toplevel ();
  299. var t2 = new Toplevel ();
  300. var t3 = new Toplevel ();
  301. // Don't use Dialog here as it has more layout logic. Use Window instead.
  302. var d = new Dialog ();
  303. var t4 = new Toplevel ();
  304. // t1, t2, t3, d, t4
  305. var iterations = 5;
  306. t1.Ready += (s, e) =>
  307. {
  308. Assert.Equal (t1, Application.Top);
  309. Application.Run (t2);
  310. };
  311. t2.Ready += (s, e) =>
  312. {
  313. Assert.Equal (t2, Application.Top);
  314. Application.Run (t3);
  315. };
  316. t3.Ready += (s, e) =>
  317. {
  318. Assert.Equal (t3, Application.Top);
  319. Application.Run (d);
  320. };
  321. d.Ready += (s, e) =>
  322. {
  323. Assert.Equal (t3, Application.Top);
  324. Application.Run (t4);
  325. };
  326. t4.Ready += (s, e) =>
  327. {
  328. Assert.Equal (t4, Application.Top);
  329. t4.RequestStop ();
  330. d.RequestStop ();
  331. t3.RequestStop ();
  332. t2.RequestStop ();
  333. };
  334. // Now this will close the OverlappedContainer when all OverlappedChildren was closed
  335. t2.Closed += (s, _) => { t1.RequestStop (); };
  336. Application.Iteration += (s, a) =>
  337. {
  338. if (iterations == 5)
  339. {
  340. // The Current still is t4 because Current.Running is false.
  341. Assert.Equal (t4, Application.Current);
  342. Assert.False (Application.Current.Running);
  343. Assert.Equal (t4, Application.Top);
  344. }
  345. else if (iterations == 4)
  346. {
  347. // The Current is d and Current.Running is false.
  348. Assert.Equal (d, Application.Current);
  349. Assert.False (Application.Current.Running);
  350. Assert.Equal (t4, Application.Top);
  351. }
  352. else if (iterations == 3)
  353. {
  354. // The Current is t3 and Current.Running is false.
  355. Assert.Equal (t3, Application.Current);
  356. Assert.False (Application.Current.Running);
  357. Assert.Equal (t3, Application.Top);
  358. }
  359. else if (iterations == 2)
  360. {
  361. // The Current is t2 and Current.Running is false.
  362. Assert.Equal (t2, Application.Current);
  363. Assert.False (Application.Current.Running);
  364. Assert.Equal (t2, Application.Top);
  365. }
  366. else
  367. {
  368. // The Current is t1.
  369. Assert.Equal (t1, Application.Current);
  370. Assert.False (Application.Current.Running);
  371. Assert.Equal (t1, Application.Top);
  372. }
  373. iterations--;
  374. };
  375. Application.Run (t1);
  376. Assert.Equal (t1, Application.Top);
  377. // top wasn't run and so never was added to toplevel's stack
  378. Assert.NotEqual (top, Application.Top);
  379. #if DEBUG_IDISPOSABLE
  380. Assert.False (Application.Top.WasDisposed);
  381. t1.Dispose ();
  382. Assert.True (Application.Top.WasDisposed);
  383. #endif
  384. }
  385. private void Init ()
  386. {
  387. Application.Init (new FakeDriver ());
  388. Assert.NotNull (Application.Driver);
  389. Assert.NotNull (Application.MainLoop);
  390. Assert.NotNull (SynchronizationContext.Current);
  391. }
  392. private void Post_Init_State ()
  393. {
  394. Assert.NotNull (Application.Driver);
  395. Assert.NotNull (Application.Top);
  396. Assert.NotNull (Application.Current);
  397. Assert.NotNull (Application.MainLoop);
  398. // FakeDriver is always 80x25
  399. Assert.Equal (80, Application.Driver.Cols);
  400. Assert.Equal (25, Application.Driver.Rows);
  401. }
  402. private void Pre_Init_State ()
  403. {
  404. Assert.Null (Application.Driver);
  405. Assert.Null (Application.Top);
  406. Assert.Null (Application.Current);
  407. Assert.Null (Application.MainLoop);
  408. }
  409. private void Shutdown () { Application.Shutdown (); }
  410. private class TestToplevel : Toplevel
  411. {
  412. public TestToplevel () { IsOverlappedContainer = false; }
  413. }
  414. #region RunTests
  415. [Fact]
  416. public void Run_T_After_InitWithDriver_with_TopLevel_Does_Not_Throws ()
  417. {
  418. // Setup Mock driver
  419. Init ();
  420. Application.Iteration += (s, e) => Application.RequestStop ();
  421. // Run<Toplevel> when already initialized or not with a Driver will not throw (because Window is derived from Toplevel)
  422. // Using another type not derived from Toplevel will throws at compile time
  423. Application.Run<Window> ();
  424. Assert.True (Application.Top is Window);
  425. Application.Top.Dispose ();
  426. Shutdown ();
  427. Assert.Null (Application.Top);
  428. Assert.Null (Application.MainLoop);
  429. Assert.Null (Application.Driver);
  430. }
  431. [Fact]
  432. public void Run_T_After_InitWithDriver_with_TopLevel_and_Driver_Does_Not_Throws ()
  433. {
  434. // Setup Mock driver
  435. Init ();
  436. Application.Iteration += (s, e) => Application.RequestStop ();
  437. // Run<Toplevel> when already initialized or not with a Driver will not throw (because Window is derived from Toplevel)
  438. // Using another type not derived from Toplevel will throws at compile time
  439. Application.Run<Window> (null, new FakeDriver ());
  440. Assert.True (Application.Top is Window);
  441. Application.Top.Dispose ();
  442. // Run<Toplevel> when already initialized or not with a Driver will not throw (because Dialog is derived from Toplevel)
  443. Application.Run<Dialog> (null, new FakeDriver ());
  444. Assert.True (Application.Top is Dialog);
  445. Application.Top.Dispose ();
  446. Shutdown ();
  447. Assert.Null (Application.Top);
  448. Assert.Null (Application.MainLoop);
  449. Assert.Null (Application.Driver);
  450. }
  451. [Fact]
  452. [TestRespondersDisposed]
  453. public void Run_T_After_Init_Does_Not_Disposes_Application_Top ()
  454. {
  455. Init ();
  456. // Init doesn't create a Toplevel and assigned it to Application.Top
  457. // but Begin does
  458. var initTop = new Toplevel ();
  459. Application.Iteration += (s, a) =>
  460. {
  461. Assert.NotEqual (initTop, Application.Top);
  462. #if DEBUG_IDISPOSABLE
  463. Assert.False (initTop.WasDisposed);
  464. #endif
  465. Application.RequestStop ();
  466. };
  467. Application.Run<TestToplevel> ();
  468. #if DEBUG_IDISPOSABLE
  469. Assert.False (initTop.WasDisposed);
  470. initTop.Dispose ();
  471. Assert.True (initTop.WasDisposed);
  472. #endif
  473. Application.Top.Dispose ();
  474. Shutdown ();
  475. Assert.Null (Application.Top);
  476. Assert.Null (Application.MainLoop);
  477. Assert.Null (Application.Driver);
  478. }
  479. [Fact]
  480. [TestRespondersDisposed]
  481. public void Run_T_After_InitWithDriver_with_TestTopLevel_DoesNotThrow ()
  482. {
  483. // Setup Mock driver
  484. Init ();
  485. Application.Iteration += (s, a) => { Application.RequestStop (); };
  486. // Init has been called and we're passing no driver to Run<TestTopLevel>. This is ok.
  487. Application.Run<TestToplevel> ();
  488. Application.Top.Dispose ();
  489. Shutdown ();
  490. Assert.Null (Application.Top);
  491. Assert.Null (Application.MainLoop);
  492. Assert.Null (Application.Driver);
  493. }
  494. [Fact]
  495. [TestRespondersDisposed]
  496. public void Run_T_After_InitNullDriver_with_TestTopLevel_DoesNotThrow ()
  497. {
  498. Application.ForceDriver = "FakeDriver";
  499. Application.Init ();
  500. Assert.Equal (typeof (FakeDriver), Application.Driver.GetType ());
  501. Application.Iteration += (s, a) => { Application.RequestStop (); };
  502. // Init has been called, selecting FakeDriver; we're passing no driver to Run<TestTopLevel>. Should be fine.
  503. Application.Run<TestToplevel> ();
  504. Application.Top.Dispose ();
  505. Shutdown ();
  506. Assert.Null (Application.Top);
  507. Assert.Null (Application.MainLoop);
  508. Assert.Null (Application.Driver);
  509. }
  510. [Fact]
  511. [TestRespondersDisposed]
  512. public void Run_T_Init_Driver_Cleared_with_TestTopLevel_Throws ()
  513. {
  514. Init ();
  515. Application.Driver = null;
  516. // Init has been called, but Driver has been set to null. Bad.
  517. Assert.Throws<InvalidOperationException> (() => Application.Run<TestToplevel> ());
  518. Shutdown ();
  519. Assert.Null (Application.Top);
  520. Assert.Null (Application.MainLoop);
  521. Assert.Null (Application.Driver);
  522. }
  523. [Fact]
  524. [TestRespondersDisposed]
  525. public void Run_T_NoInit_DoesNotThrow ()
  526. {
  527. Application.ForceDriver = "FakeDriver";
  528. Application.Iteration += (s, a) => { Application.RequestStop (); };
  529. Application.Run<TestToplevel> ();
  530. Assert.Equal (typeof (FakeDriver), Application.Driver.GetType ());
  531. Application.Top.Dispose ();
  532. Shutdown ();
  533. Assert.Null (Application.Top);
  534. Assert.Null (Application.MainLoop);
  535. Assert.Null (Application.Driver);
  536. }
  537. [Fact]
  538. [TestRespondersDisposed]
  539. public void Run_T_NoInit_WithDriver_DoesNotThrow ()
  540. {
  541. Application.Iteration += (s, a) => { Application.RequestStop (); };
  542. // Init has NOT been called and we're passing a valid driver to Run<TestTopLevel>. This is ok.
  543. Application.Run<TestToplevel> (null, new FakeDriver ());
  544. Application.Top.Dispose ();
  545. Shutdown ();
  546. Assert.Null (Application.Top);
  547. Assert.Null (Application.MainLoop);
  548. Assert.Null (Application.Driver);
  549. }
  550. [Fact]
  551. [TestRespondersDisposed]
  552. public void Run_RequestStop_Stops ()
  553. {
  554. // Setup Mock driver
  555. Init ();
  556. var top = new Toplevel ();
  557. RunState rs = Application.Begin (top);
  558. Assert.NotNull (rs);
  559. Assert.Equal (top, Application.Current);
  560. Application.Iteration += (s, a) => { Application.RequestStop (); };
  561. Application.Run (top);
  562. top.Dispose ();
  563. Application.Shutdown ();
  564. Assert.Null (Application.Current);
  565. Assert.Null (Application.Top);
  566. Assert.Null (Application.MainLoop);
  567. Assert.Null (Application.Driver);
  568. }
  569. [Fact]
  570. [TestRespondersDisposed]
  571. public void Run_RunningFalse_Stops ()
  572. {
  573. // Setup Mock driver
  574. Init ();
  575. var top = new Toplevel ();
  576. RunState rs = Application.Begin (top);
  577. Assert.NotNull (rs);
  578. Assert.Equal (top, Application.Current);
  579. Application.Iteration += (s, a) => { top.Running = false; };
  580. Application.Run (top);
  581. top.Dispose ();
  582. Application.Shutdown ();
  583. Assert.Null (Application.Current);
  584. Assert.Null (Application.Top);
  585. Assert.Null (Application.MainLoop);
  586. Assert.Null (Application.Driver);
  587. }
  588. [Fact]
  589. [TestRespondersDisposed]
  590. public void Run_Loaded_Ready_Unlodaded_Events ()
  591. {
  592. Init ();
  593. Toplevel top = new ();
  594. var count = 0;
  595. top.Loaded += (s, e) => count++;
  596. top.Ready += (s, e) => count++;
  597. top.Unloaded += (s, e) => count++;
  598. Application.Iteration += (s, a) => Application.RequestStop ();
  599. Application.Run (top);
  600. top.Dispose ();
  601. Application.Shutdown ();
  602. Assert.Equal (3, count);
  603. }
  604. // TODO: All Toplevel layout tests should be moved to ToplevelTests.cs
  605. [Fact]
  606. public void Run_Toplevel_With_Modal_View_Does_Not_Refresh_If_Not_Dirty ()
  607. {
  608. Init ();
  609. var count = 0;
  610. // Don't use Dialog here as it has more layout logic. Use Window instead.
  611. Dialog d = null;
  612. Toplevel top = new ();
  613. top.DrawContent += (s, a) => count++;
  614. int iteration = -1;
  615. Application.Iteration += (s, a) =>
  616. {
  617. iteration++;
  618. if (iteration == 0)
  619. {
  620. // TODO: Don't use Dialog here as it has more layout logic. Use Window instead.
  621. d = new ();
  622. d.DrawContent += (s, a) => count++;
  623. Application.Run (d);
  624. }
  625. else if (iteration < 3)
  626. {
  627. Application.OnMouseEvent (new () { X = 0, Y = 0, Flags = MouseFlags.ReportMousePosition });
  628. Assert.False (top.NeedsDisplay);
  629. Assert.False (top.SubViewNeedsDisplay);
  630. Assert.False (top.LayoutNeeded);
  631. Assert.False (d.NeedsDisplay);
  632. Assert.False (d.SubViewNeedsDisplay);
  633. Assert.False (d.LayoutNeeded);
  634. }
  635. else
  636. {
  637. Application.RequestStop ();
  638. }
  639. };
  640. Application.Run (top);
  641. top.Dispose ();
  642. Application.Shutdown ();
  643. // 1 - First top load, 1 - Dialog load, 1 - Dialog unload, Total - 3.
  644. Assert.Equal (3, count);
  645. }
  646. // TODO: All Toplevel layout tests should be moved to ToplevelTests.cs
  647. [Fact]
  648. public void Run_A_Modal_Toplevel_Refresh_Background_On_Moving ()
  649. {
  650. Init ();
  651. // Don't use Dialog here as it has more layout logic. Use Window instead.
  652. var w = new Window { Width = 5, Height = 5 };
  653. ((FakeDriver)Application.Driver).SetBufferSize (10, 10);
  654. RunState rs = Application.Begin (w);
  655. TestHelpers.AssertDriverContentsWithFrameAre (
  656. @"
  657. ┌───┐
  658. │ │
  659. │ │
  660. │ │
  661. └───┘",
  662. _output
  663. );
  664. Attribute [] attributes =
  665. {
  666. // 0
  667. new (ColorName.White, ColorName.Black),
  668. // 1
  669. Colors.ColorSchemes ["Base"].Normal
  670. };
  671. TestHelpers.AssertDriverAttributesAre (
  672. @"
  673. 1111100000
  674. 1111100000
  675. 1111100000
  676. 1111100000
  677. 1111100000
  678. ",
  679. null,
  680. attributes
  681. );
  682. // TODO: In PR #2920 this breaks because the mouse is not grabbed anymore.
  683. // TODO: Move the mouse grap/drag mode from Toplevel to Border.
  684. Application.OnMouseEvent (new () { X = 0, Y = 0, Flags = MouseFlags.Button1Pressed });
  685. Assert.Equal (w.Border, Application.MouseGrabView);
  686. // Move down and to the right.
  687. Application.OnMouseEvent (new () { X = 1, Y = 1, Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition });
  688. Application.Refresh ();
  689. TestHelpers.AssertDriverContentsWithFrameAre (
  690. @"
  691. ┌───┐
  692. │ │
  693. │ │
  694. │ │
  695. └───┘",
  696. _output
  697. );
  698. attributes = new []
  699. {
  700. // 0
  701. new (ColorName.White, ColorName.Black),
  702. // 1
  703. Colors.ColorSchemes ["Base"].Normal
  704. };
  705. TestHelpers.AssertDriverAttributesAre (
  706. @"
  707. 0000000000
  708. 0111110000
  709. 0111110000
  710. 0111110000
  711. 0111110000
  712. 0111110000
  713. ",
  714. null,
  715. attributes
  716. );
  717. Application.End (rs);
  718. w.Dispose ();
  719. Application.Shutdown ();
  720. }
  721. [Fact]
  722. public void End_Does_Not_Dispose ()
  723. {
  724. Init ();
  725. var top = new Toplevel ();
  726. Window w = new ();
  727. w.Ready += (s, e) => Application.RequestStop (); // Causes `End` to be called
  728. Application.Run (w);
  729. #if DEBUG_IDISPOSABLE
  730. Assert.False (w.WasDisposed);
  731. #endif
  732. Assert.NotNull (w);
  733. Assert.Equal (string.Empty, w.Title); // Valid - w has not been disposed. The user may want to run it again
  734. Assert.NotNull (Application.Top);
  735. Assert.Equal (w, Application.Top);
  736. Assert.NotEqual (top, Application.Top);
  737. Assert.Null (Application.Current);
  738. Application.Run (w); // Valid - w has not been disposed.
  739. #if DEBUG_IDISPOSABLE
  740. Assert.False (w.WasDisposed);
  741. Exception exception = Record.Exception (() => Application.Shutdown ()); // Invalid - w has not been disposed.
  742. Assert.NotNull (exception);
  743. w.Dispose ();
  744. Assert.True (w.WasDisposed);
  745. exception = Record.Exception (
  746. () => Application.Run (
  747. w)); // Invalid - w has been disposed. Run it in debug mode will throw, otherwise the user may want to run it again
  748. Assert.NotNull (exception);
  749. exception = Record.Exception (() => Assert.Equal (string.Empty, w.Title)); // Invalid - w has been disposed and cannot be accessed
  750. Assert.NotNull (exception);
  751. exception = Record.Exception (() => w.Title = "NewTitle"); // Invalid - w has been disposed and cannot be accessed
  752. Assert.NotNull (exception);
  753. #endif
  754. Application.Shutdown ();
  755. Assert.NotNull (w);
  756. Assert.Null (Application.Current);
  757. Assert.NotNull (top);
  758. Assert.Null (Application.Top);
  759. }
  760. [Fact]
  761. public void Run_Creates_Top_Without_Init ()
  762. {
  763. var driver = new FakeDriver ();
  764. Assert.Null (Application.Top);
  765. Application.Iteration += (s, e) =>
  766. {
  767. Assert.NotNull (Application.Top);
  768. Application.RequestStop ();
  769. };
  770. Toplevel top = Application.Run (null, driver);
  771. #if DEBUG_IDISPOSABLE
  772. Assert.Equal (top, Application.Top);
  773. Assert.False (top.WasDisposed);
  774. Exception exception = Record.Exception (() => Application.Shutdown ());
  775. Assert.NotNull (exception);
  776. Assert.False (top.WasDisposed);
  777. #endif
  778. // It's up to caller to dispose it
  779. top.Dispose ();
  780. #if DEBUG_IDISPOSABLE
  781. Assert.True (top.WasDisposed);
  782. #endif
  783. Assert.NotNull (Application.Top);
  784. Application.Shutdown ();
  785. Assert.Null (Application.Top);
  786. }
  787. [Fact]
  788. public void Run_T_Creates_Top_Without_Init ()
  789. {
  790. var driver = new FakeDriver ();
  791. Assert.Null (Application.Top);
  792. Application.Iteration += (s, e) =>
  793. {
  794. Assert.NotNull (Application.Top);
  795. Application.RequestStop ();
  796. };
  797. Application.Run<Toplevel> (null, driver);
  798. #if DEBUG_IDISPOSABLE
  799. Assert.False (Application.Top.WasDisposed);
  800. Exception exception = Record.Exception (() => Application.Shutdown ());
  801. Assert.NotNull (exception);
  802. Assert.False (Application.Top.WasDisposed);
  803. // It's up to caller to dispose it
  804. Application.Top.Dispose ();
  805. Assert.True (Application.Top.WasDisposed);
  806. #endif
  807. Assert.NotNull (Application.Top);
  808. Application.Shutdown ();
  809. Assert.Null (Application.Top);
  810. }
  811. [Fact]
  812. public void Run_t_Creates_Top_Without_Init ()
  813. {
  814. var driver = new FakeDriver ();
  815. Assert.Null (Application.Top);
  816. Application.Iteration += (s, e) =>
  817. {
  818. Assert.NotNull (Application.Top);
  819. Application.RequestStop ();
  820. };
  821. Application.Run (new (), null, driver);
  822. #if DEBUG_IDISPOSABLE
  823. Assert.False (Application.Top.WasDisposed);
  824. Exception exception = Record.Exception (() => Application.Shutdown ());
  825. Assert.NotNull (exception);
  826. Assert.False (Application.Top.WasDisposed);
  827. // It's up to caller to dispose it
  828. Application.Top.Dispose ();
  829. Assert.True (Application.Top.WasDisposed);
  830. #endif
  831. Assert.NotNull (Application.Top);
  832. Application.Shutdown ();
  833. Assert.Null (Application.Top);
  834. }
  835. // TODO: Add tests for Run that test errorHandler
  836. #endregion
  837. #region ShutdownTests
  838. [Fact]
  839. public async void Shutdown_Allows_Async ()
  840. {
  841. var isCompletedSuccessfully = false;
  842. async Task TaskWithAsyncContinuation ()
  843. {
  844. await Task.Yield ();
  845. await Task.Yield ();
  846. isCompletedSuccessfully = true;
  847. }
  848. Init ();
  849. Application.Shutdown ();
  850. Assert.False (isCompletedSuccessfully);
  851. await TaskWithAsyncContinuation ();
  852. Thread.Sleep (100);
  853. Assert.True (isCompletedSuccessfully);
  854. }
  855. [Fact]
  856. public void Shutdown_Resets_SyncContext ()
  857. {
  858. Init ();
  859. Application.Shutdown ();
  860. Assert.Null (SynchronizationContext.Current);
  861. }
  862. #endregion
  863. }