ApplicationTests.cs 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086
  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 Rectangle (0, 0, 80, 25), Application.Top.Frame);
  35. ((FakeDriver)Application.Driver).SetBufferSize (5, 5);
  36. Assert.Equal (new Rectangle (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. var top = Application.Top;
  51. #if DEBUG_IDISPOSABLE
  52. var 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 View ();
  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 View ();
  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 Dialog ();
  622. d.DrawContent += (s, a) => count++;
  623. Application.Run (d);
  624. }
  625. else if (iteration < 3)
  626. {
  627. Application.OnMouseEvent (
  628. new MouseEventEventArgs (
  629. new MouseEvent
  630. { X = 0, Y = 0, Flags = MouseFlags.ReportMousePosition }
  631. )
  632. );
  633. Assert.False (top.NeedsDisplay);
  634. Assert.False (top.SubViewNeedsDisplay);
  635. Assert.False (top.LayoutNeeded);
  636. Assert.False (d.NeedsDisplay);
  637. Assert.False (d.SubViewNeedsDisplay);
  638. Assert.False (d.LayoutNeeded);
  639. }
  640. else
  641. {
  642. Application.RequestStop ();
  643. }
  644. };
  645. Application.Run (top);
  646. top.Dispose ();
  647. Application.Shutdown ();
  648. // 1 - First top load, 1 - Dialog load, 1 - Dialog unload, Total - 3.
  649. Assert.Equal (3, count);
  650. }
  651. // TODO: All Toplevel layout tests should be moved to ToplevelTests.cs
  652. [Fact]
  653. public void Run_A_Modal_Toplevel_Refresh_Background_On_Moving ()
  654. {
  655. Init ();
  656. // Don't use Dialog here as it has more layout logic. Use Window instead.
  657. var w = new Window { Width = 5, Height = 5 };
  658. ((FakeDriver)Application.Driver).SetBufferSize (10, 10);
  659. RunState rs = Application.Begin (w);
  660. TestHelpers.AssertDriverContentsWithFrameAre (
  661. @"
  662. ┌───┐
  663. │ │
  664. │ │
  665. │ │
  666. └───┘",
  667. _output
  668. );
  669. Attribute [] attributes =
  670. {
  671. // 0
  672. new (ColorName.White, ColorName.Black),
  673. // 1
  674. Colors.ColorSchemes ["Base"].Normal
  675. };
  676. TestHelpers.AssertDriverAttributesAre (
  677. @"
  678. 1111100000
  679. 1111100000
  680. 1111100000
  681. 1111100000
  682. 1111100000
  683. ",
  684. null,
  685. attributes
  686. );
  687. // TODO: In PR #2920 this breaks because the mouse is not grabbed anymore.
  688. // TODO: Move the mouse grap/drag mode from Toplevel to Border.
  689. Application.OnMouseEvent (
  690. new MouseEventEventArgs (
  691. new MouseEvent { X = 0, Y = 0, Flags = MouseFlags.Button1Pressed }
  692. )
  693. );
  694. Assert.Equal (w.Border, Application.MouseGrabView);
  695. // Move down and to the right.
  696. Application.OnMouseEvent (
  697. new MouseEventEventArgs (
  698. new MouseEvent
  699. {
  700. X = 1,
  701. Y = 1,
  702. Flags = MouseFlags.Button1Pressed
  703. | MouseFlags.ReportMousePosition
  704. }
  705. )
  706. );
  707. Application.Refresh ();
  708. TestHelpers.AssertDriverContentsWithFrameAre (
  709. @"
  710. ┌───┐
  711. │ │
  712. │ │
  713. │ │
  714. └───┘",
  715. _output
  716. );
  717. attributes = new []
  718. {
  719. // 0
  720. new (ColorName.White, ColorName.Black),
  721. // 1
  722. Colors.ColorSchemes ["Base"].Normal
  723. };
  724. TestHelpers.AssertDriverAttributesAre (
  725. @"
  726. 0000000000
  727. 0111110000
  728. 0111110000
  729. 0111110000
  730. 0111110000
  731. 0111110000
  732. ",
  733. null,
  734. attributes
  735. );
  736. Application.End (rs);
  737. w.Dispose ();
  738. Application.Shutdown ();
  739. }
  740. [Fact]
  741. public void End_Does_Not_Dispose ()
  742. {
  743. Init ();
  744. var top = new Toplevel ();
  745. Window w = new ();
  746. w.Ready += (s, e) => Application.RequestStop (); // Causes `End` to be called
  747. Application.Run(w);
  748. #if DEBUG_IDISPOSABLE
  749. Assert.False (w.WasDisposed);
  750. #endif
  751. Assert.NotNull (w);
  752. Assert.Equal (string.Empty, w.Title); // Valid - w has not been disposed. The user may want to run it again
  753. Assert.NotNull (Application.Top);
  754. Assert.Equal(w, Application.Top);
  755. Assert.NotEqual(top, Application.Top);
  756. Assert.Null (Application.Current);
  757. Application.Run(w); // Valid - w has not been disposed.
  758. #if DEBUG_IDISPOSABLE
  759. Assert.False (w.WasDisposed);
  760. var exception = Record.Exception (() => Application.Shutdown()); // Invalid - w has not been disposed.
  761. Assert.NotNull (exception);
  762. w.Dispose ();
  763. Assert.True (w.WasDisposed);
  764. exception = Record.Exception (() => Application.Run (w)); // Invalid - w has been disposed. Run it in debug mode will throw, otherwise the user may want to run it again
  765. Assert.NotNull (exception);
  766. exception = Record.Exception (() => Assert.Equal (string.Empty, w.Title)); // Invalid - w has been disposed and cannot be accessed
  767. Assert.NotNull (exception);
  768. exception = Record.Exception (() => w.Title = "NewTitle"); // Invalid - w has been disposed and cannot be accessed
  769. Assert.NotNull (exception);
  770. #endif
  771. Application.Shutdown ();
  772. Assert.NotNull (w);
  773. Assert.Null (Application.Current);
  774. Assert.NotNull (top);
  775. Assert.Null (Application.Top);
  776. }
  777. [Fact]
  778. public void Run_Creates_Top_Without_Init ()
  779. {
  780. var driver = new FakeDriver ();
  781. Assert.Null (Application.Top);
  782. Application.Iteration += (s, e) =>
  783. {
  784. Assert.NotNull (Application.Top);
  785. Application.RequestStop ();
  786. };
  787. Application.Run (null, driver);
  788. #if DEBUG_IDISPOSABLE
  789. Assert.False (Application.Top.WasDisposed);
  790. var exception = Record.Exception (() => Application.Shutdown ());
  791. Assert.NotNull (exception);
  792. Assert.False (Application.Top.WasDisposed);
  793. // It's up to caller to dispose it
  794. Application.Top.Dispose ();
  795. Assert.True (Application.Top.WasDisposed);
  796. #endif
  797. Assert.NotNull (Application.Top);
  798. Application.Shutdown ();
  799. Assert.Null (Application.Top);
  800. }
  801. [Fact]
  802. public void Run_T_Creates_Top_Without_Init ()
  803. {
  804. var driver = new FakeDriver ();
  805. Assert.Null (Application.Top);
  806. Application.Iteration += (s, e) =>
  807. {
  808. Assert.NotNull (Application.Top);
  809. Application.RequestStop ();
  810. };
  811. Application.Run<Toplevel> (null, driver);
  812. #if DEBUG_IDISPOSABLE
  813. Assert.False (Application.Top.WasDisposed);
  814. var exception = Record.Exception (() => Application.Shutdown ());
  815. Assert.NotNull (exception);
  816. Assert.False (Application.Top.WasDisposed);
  817. // It's up to caller to dispose it
  818. Application.Top.Dispose ();
  819. Assert.True (Application.Top.WasDisposed);
  820. #endif
  821. Assert.NotNull (Application.Top);
  822. Application.Shutdown ();
  823. Assert.Null (Application.Top);
  824. }
  825. [Fact]
  826. public void Run_t_Creates_Top_Without_Init ()
  827. {
  828. var driver = new FakeDriver ();
  829. Assert.Null (Application.Top);
  830. Application.Iteration += (s, e) =>
  831. {
  832. Assert.NotNull (Application.Top);
  833. Application.RequestStop ();
  834. };
  835. Application.Run (new (), null, driver);
  836. #if DEBUG_IDISPOSABLE
  837. Assert.False (Application.Top.WasDisposed);
  838. var exception = Record.Exception (() => Application.Shutdown ());
  839. Assert.NotNull (exception);
  840. Assert.False (Application.Top.WasDisposed);
  841. // It's up to caller to dispose it
  842. Application.Top.Dispose ();
  843. Assert.True (Application.Top.WasDisposed);
  844. #endif
  845. Assert.NotNull (Application.Top);
  846. Application.Shutdown ();
  847. Assert.Null (Application.Top);
  848. }
  849. // TODO: Add tests for Run that test errorHandler
  850. #endregion
  851. #region ShutdownTests
  852. [Fact]
  853. public async void Shutdown_Allows_Async ()
  854. {
  855. var isCompletedSuccessfully = false;
  856. async Task TaskWithAsyncContinuation ()
  857. {
  858. await Task.Yield ();
  859. await Task.Yield ();
  860. isCompletedSuccessfully = true;
  861. }
  862. Init ();
  863. Application.Shutdown ();
  864. Assert.False (isCompletedSuccessfully);
  865. await TaskWithAsyncContinuation ();
  866. Thread.Sleep (100);
  867. Assert.True (isCompletedSuccessfully);
  868. }
  869. [Fact]
  870. public void Shutdown_Resets_SyncContext ()
  871. {
  872. Init ();
  873. Application.Shutdown ();
  874. Assert.Null (SynchronizationContext.Current);
  875. }
  876. #endregion
  877. }