ApplicationTests.cs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937
  1. #nullable enable
  2. using System.Diagnostics;
  3. using Xunit.Abstractions;
  4. using static Terminal.Gui.Configuration.ConfigurationManager;
  5. // Alias Console to MockConsole so we don't accidentally use Console
  6. namespace UnitTests.ApplicationTests;
  7. public class ApplicationTests
  8. {
  9. public ApplicationTests (ITestOutputHelper output)
  10. {
  11. _output = output;
  12. #if DEBUG_IDISPOSABLE
  13. View.EnableDebugIDisposableAsserts = true;
  14. View.Instances.Clear ();
  15. SessionToken.Instances.Clear ();
  16. #endif
  17. }
  18. private readonly ITestOutputHelper _output;
  19. [Fact]
  20. public void AddTimeout_Fires ()
  21. {
  22. IApplication app = ApplicationImpl.Instance; // Force legacy
  23. app.Init ("fake");
  24. uint timeoutTime = 100;
  25. var timeoutFired = false;
  26. // Setup a timeout that will fire
  27. app.AddTimeout (
  28. TimeSpan.FromMilliseconds (timeoutTime),
  29. () =>
  30. {
  31. timeoutFired = true;
  32. // Return false so the timer does not repeat
  33. return false;
  34. }
  35. );
  36. // The timeout has not fired yet
  37. Assert.False (timeoutFired);
  38. // Block the thread to prove the timeout does not fire on a background thread
  39. Thread.Sleep ((int)timeoutTime * 2);
  40. Assert.False (timeoutFired);
  41. app.StopAfterFirstIteration = true;
  42. app.Run<Toplevel> ().Dispose ();
  43. // The timeout should have fired
  44. Assert.True (timeoutFired);
  45. app.Shutdown ();
  46. }
  47. [Fact]
  48. [SetupFakeApplication]
  49. public void Begin_Null_Toplevel_Throws ()
  50. {
  51. // Test null Toplevel
  52. Assert.Throws<ArgumentNullException> (() => Application.Begin (null!));
  53. }
  54. [Fact]
  55. [SetupFakeApplication]
  56. public void Begin_Sets_Application_Top_To_Console_Size ()
  57. {
  58. Assert.Null (Application.TopRunnable);
  59. Application.Driver!.SetScreenSize (80, 25);
  60. Toplevel top = new ();
  61. Application.Begin (top);
  62. Assert.Equal (new (0, 0, 80, 25), Application.TopRunnable!.Frame);
  63. Application.Driver!.SetScreenSize (5, 5);
  64. Assert.Equal (new (0, 0, 5, 5), Application.TopRunnable!.Frame);
  65. top.Dispose ();
  66. }
  67. [Fact]
  68. [SetupFakeApplication]
  69. public void End_And_Shutdown_Should_Not_Dispose_ApplicationTop ()
  70. {
  71. Assert.Null (Application.TopRunnable);
  72. SessionToken rs = Application.Begin (new ());
  73. Application.TopRunnable!.Title = "End_And_Shutdown_Should_Not_Dispose_ApplicationTop";
  74. Assert.Equal (rs.Toplevel, Application.TopRunnable);
  75. Application.End (rs);
  76. #if DEBUG_IDISPOSABLE
  77. Assert.True (rs.WasDisposed);
  78. Assert.False (Application.TopRunnable!.WasDisposed); // Is true because the rs.Toplevel is the same as Application.TopRunnable
  79. #endif
  80. Assert.Null (rs.Toplevel);
  81. Toplevel top = Application.TopRunnable;
  82. #if DEBUG_IDISPOSABLE
  83. Exception exception = Record.Exception (Application.Shutdown);
  84. Assert.NotNull (exception);
  85. Assert.False (top.WasDisposed);
  86. top.Dispose ();
  87. Assert.True (top.WasDisposed);
  88. #endif
  89. }
  90. [Fact]
  91. [SetupFakeApplication]
  92. public void Init_Begin_End_Cleans_Up ()
  93. {
  94. // Start stopwatch
  95. var stopwatch = new Stopwatch ();
  96. stopwatch.Start ();
  97. SessionToken? sessionToken = null;
  98. EventHandler<SessionTokenEventArgs> newSessionTokenFn = (s, e) =>
  99. {
  100. Assert.NotNull (e.State);
  101. sessionToken = e.State;
  102. };
  103. Application.SessionBegun += newSessionTokenFn;
  104. var topLevel = new Toplevel ();
  105. SessionToken rs = Application.Begin (topLevel);
  106. Assert.NotNull (rs);
  107. Assert.NotNull (sessionToken);
  108. Assert.Equal (rs, sessionToken);
  109. Assert.Equal (topLevel, Application.TopRunnable);
  110. Application.SessionBegun -= newSessionTokenFn;
  111. Application.End (sessionToken);
  112. Assert.NotNull (Application.TopRunnable);
  113. Assert.NotNull (Application.Driver);
  114. topLevel.Dispose ();
  115. // Stop stopwatch
  116. stopwatch.Stop ();
  117. _output.WriteLine ($"Load took {stopwatch.ElapsedMilliseconds} ms");
  118. }
  119. [Fact]
  120. public void Init_KeyBindings_Are_Not_Reset ()
  121. {
  122. Debug.Assert (!IsEnabled);
  123. try
  124. {
  125. // arrange
  126. ThrowOnJsonErrors = true;
  127. Application.QuitKey = Key.Q;
  128. Assert.Equal (Key.Q, Application.QuitKey);
  129. Application.Init ("fake");
  130. Assert.Equal (Key.Q, Application.QuitKey);
  131. }
  132. finally
  133. {
  134. Application.ResetState ();
  135. }
  136. }
  137. [Fact]
  138. public void Init_NoParam_ForceDriver_Works ()
  139. {
  140. Application.ForceDriver = "Fake";
  141. Application.Init ();
  142. Assert.Equal ("fake", Application.Driver!.GetName ());
  143. Application.ResetState ();
  144. }
  145. [Fact]
  146. public void Init_Null_Driver_Should_Pick_A_Driver ()
  147. {
  148. Application.Init ();
  149. Assert.NotNull (Application.Driver);
  150. Application.Shutdown ();
  151. }
  152. [Fact]
  153. public void Init_ResetState_Resets_Properties ()
  154. {
  155. ThrowOnJsonErrors = true;
  156. // For all the fields/properties of Application, check that they are reset to their default values
  157. // Set some values
  158. Application.Init (driverName: "fake");
  159. // Application.IsInitialized = true;
  160. // Reset
  161. Application.ResetState ();
  162. CheckReset ();
  163. // Set the values that can be set
  164. Application.Initialized = true;
  165. Application.MainThreadId = 1;
  166. //Application._topLevels = new List<Toplevel> ();
  167. Application.CachedViewsUnderMouse.Clear ();
  168. //Application.SupportedCultures = new List<CultureInfo> ();
  169. Application.Force16Colors = true;
  170. //Application.ForceDriver = "driver";
  171. Application.StopAfterFirstIteration = true;
  172. Application.PrevTabGroupKey = Key.A;
  173. Application.NextTabGroupKey = Key.B;
  174. Application.QuitKey = Key.C;
  175. Application.KeyBindings.Add (Key.D, Command.Cancel);
  176. Application.CachedViewsUnderMouse.Clear ();
  177. //Application.WantContinuousButtonPressedView = new View ();
  178. // Mouse
  179. Application.LastMousePosition = new Point (1, 1);
  180. Application.ResetState ();
  181. CheckReset ();
  182. ThrowOnJsonErrors = false;
  183. return;
  184. void CheckReset ()
  185. {
  186. // Check that all fields and properties are set to their default values
  187. // Public Properties
  188. Assert.Null (Application.TopRunnable);
  189. Assert.Null (Application.Mouse.MouseGrabView);
  190. // Don't check Application.ForceDriver
  191. // Assert.Empty (Application.ForceDriver);
  192. // Don't check Application.Force16Colors
  193. //Assert.False (Application.Force16Colors);
  194. Assert.Null (Application.Driver);
  195. Assert.False (Application.StopAfterFirstIteration);
  196. // Commented out because if CM changed the defaults, those changes should
  197. // persist across Inits.
  198. //Assert.Equal (Key.Tab.WithShift, Application.PrevTabKey);
  199. //Assert.Equal (Key.Tab, Application.NextTabKey);
  200. //Assert.Equal (Key.F6.WithShift, Application.PrevTabGroupKey);
  201. //Assert.Equal (Key.F6, Application.NextTabGroupKey);
  202. //Assert.Equal (Key.Esc, Application.QuitKey);
  203. // Internal properties
  204. Assert.False (Application.Initialized);
  205. Assert.Equal (Application.GetSupportedCultures (), Application.SupportedCultures);
  206. Assert.Equal (Application.GetAvailableCulturesFromEmbeddedResources (), Application.SupportedCultures);
  207. Assert.Null (Application.MainThreadId);
  208. Assert.Empty (Application.SessionStack);
  209. Assert.Empty (Application.CachedViewsUnderMouse);
  210. // Mouse
  211. // Do not reset _lastMousePosition
  212. //Assert.Null (Application._lastMousePosition);
  213. // Navigation
  214. // Assert.Null (Application.Navigation);
  215. // Popover
  216. //Assert.Null (Application.Popover);
  217. // Events - Can't check
  218. //Assert.Null (GetEventSubscribers (typeof (Application), "InitializedChanged"));
  219. //Assert.Null (GetEventSubscribers (typeof (Application), "SessionBegun"));
  220. //Assert.Null (GetEventSubscribers (typeof (Application), "Iteration"));
  221. //Assert.Null (GetEventSubscribers (typeof (Application), "ScreenChanged"));
  222. //Assert.Null (GetEventSubscribers (typeof (Application.Mouse), "MouseEvent"));
  223. //Assert.Null (GetEventSubscribers (typeof (Application.Keyboard), "KeyDown"));
  224. //Assert.Null (GetEventSubscribers (typeof (Application.Keyboard), "KeyUp"));
  225. }
  226. }
  227. [Fact]
  228. public void Init_Shutdown_Cleans_Up ()
  229. {
  230. // Verify initial state is per spec
  231. //Pre_Init_State ();
  232. Application.Init ("fake");
  233. // Verify post-Init state is correct
  234. //Post_Init_State ();
  235. Application.Shutdown ();
  236. // Verify state is back to initial
  237. //Pre_Init_State ();
  238. #if DEBUG_IDISPOSABLE
  239. // Validate there are no outstanding Responder-based instances
  240. // after a scenario was selected to run. This proves the main UI Catalog
  241. // 'app' closed cleanly.
  242. Assert.Empty (View.Instances);
  243. #endif
  244. }
  245. [Fact]
  246. public void Init_Shutdown_Fire_InitializedChanged ()
  247. {
  248. var initialized = false;
  249. var shutdown = false;
  250. Application.InitializedChanged += OnApplicationOnInitializedChanged;
  251. Application.Init (driverName: "fake");
  252. Assert.True (initialized);
  253. Assert.False (shutdown);
  254. Application.Shutdown ();
  255. Assert.True (initialized);
  256. Assert.True (shutdown);
  257. Application.InitializedChanged -= OnApplicationOnInitializedChanged;
  258. return;
  259. void OnApplicationOnInitializedChanged (object? s, EventArgs<bool> a)
  260. {
  261. if (a.Value)
  262. {
  263. initialized = true;
  264. }
  265. else
  266. {
  267. shutdown = true;
  268. }
  269. }
  270. }
  271. [Fact]
  272. [SetupFakeApplication]
  273. public void Init_Unbalanced_Throws ()
  274. {
  275. Assert.Throws<InvalidOperationException> (() =>
  276. Application.Init ("fake")
  277. );
  278. }
  279. [Fact]
  280. [SetupFakeApplication]
  281. public void Init_Unbalanced_Throws2 ()
  282. {
  283. // Now try the other way
  284. Assert.Throws<InvalidOperationException> (() => Application.Init ("fake"));
  285. }
  286. [Fact]
  287. public void Init_WithoutTopLevelFactory_Begin_End_Cleans_Up ()
  288. {
  289. Application.StopAfterFirstIteration = true;
  290. // NOTE: Run<T>, when called after Init has been called behaves differently than
  291. // when called if Init has not been called.
  292. Toplevel topLevel = new ();
  293. Application.Init ("fake");
  294. SessionToken? sessionToken = null;
  295. EventHandler<SessionTokenEventArgs> newSessionTokenFn = (s, e) =>
  296. {
  297. Assert.NotNull (e.State);
  298. sessionToken = e.State;
  299. };
  300. Application.SessionBegun += newSessionTokenFn;
  301. SessionToken rs = Application.Begin (topLevel);
  302. Assert.NotNull (rs);
  303. Assert.NotNull (sessionToken);
  304. Assert.Equal (rs, sessionToken);
  305. Assert.Equal (topLevel, Application.TopRunnable);
  306. Application.SessionBegun -= newSessionTokenFn;
  307. Application.End (sessionToken);
  308. Assert.NotNull (Application.TopRunnable);
  309. Assert.NotNull (Application.Driver);
  310. topLevel.Dispose ();
  311. Application.Shutdown ();
  312. Assert.Null (Application.TopRunnable);
  313. Assert.Null (Application.Driver);
  314. }
  315. [Fact]
  316. [SetupFakeApplication]
  317. public void Internal_Properties_Correct ()
  318. {
  319. Assert.True (Application.Initialized);
  320. Assert.Null (Application.TopRunnable);
  321. SessionToken rs = Application.Begin (new ());
  322. Assert.Equal (Application.TopRunnable, rs.Toplevel);
  323. Assert.Null (Application.Mouse.MouseGrabView); // public
  324. Application.TopRunnable!.Dispose ();
  325. }
  326. // Invoke Tests
  327. // TODO: Test with threading scenarios
  328. [Fact]
  329. [SetupFakeApplication]
  330. public void Invoke_Adds_Idle ()
  331. {
  332. Toplevel top = new ();
  333. SessionToken rs = Application.Begin (top);
  334. var actionCalled = 0;
  335. Application.Invoke ((_) => { actionCalled++; });
  336. Application.TimedEvents!.RunTimers ();
  337. Assert.Equal (1, actionCalled);
  338. top.Dispose ();
  339. }
  340. [Fact]
  341. public void Run_Iteration_Fires ()
  342. {
  343. var iteration = 0;
  344. Application.Init ("fake");
  345. Application.Iteration += Application_Iteration;
  346. Application.Run<Toplevel> ().Dispose ();
  347. Application.Iteration -= Application_Iteration;
  348. Assert.Equal (1, iteration);
  349. Application.Shutdown ();
  350. return;
  351. void Application_Iteration (object? sender, EventArgs<IApplication?> e)
  352. {
  353. if (iteration > 0)
  354. {
  355. Assert.Fail ();
  356. }
  357. iteration++;
  358. Application.RequestStop ();
  359. }
  360. }
  361. [Fact]
  362. [SetupFakeApplication]
  363. public void Screen_Size_Changes ()
  364. {
  365. IDriver? driver = Application.Driver;
  366. Application.Driver!.SetScreenSize (80, 25);
  367. Assert.Equal (new (0, 0, 80, 25), driver!.Screen);
  368. Assert.Equal (new (0, 0, 80, 25), Application.Screen);
  369. // TODO: Should not be possible to manually change these at whim!
  370. driver.Cols = 100;
  371. driver.Rows = 30;
  372. // IDriver.Screen isn't assignable
  373. //driver.Screen = new (0, 0, driver.Cols, Rows);
  374. Application.Driver!.SetScreenSize (100, 30);
  375. Assert.Equal (new (0, 0, 100, 30), driver.Screen);
  376. // Assert does not make sense
  377. // Assert.NotEqual (new (0, 0, 100, 30), Application.Screen);
  378. // Assert.Equal (new (0, 0, 80, 25), Application.Screen);
  379. Application.Screen = new (0, 0, driver.Cols, driver.Rows);
  380. Assert.Equal (new (0, 0, 100, 30), driver.Screen);
  381. }
  382. [Fact]
  383. public void Shutdown_Alone_Does_Nothing () { Application.Shutdown (); }
  384. //[Fact]
  385. //public void InitState_Throws_If_Driver_Is_Null ()
  386. //{
  387. // Assert.Throws<ArgumentNullException> (static () => Application.SubscribeDriverEvents ());
  388. //}
  389. #region RunTests
  390. [Fact]
  391. [SetupFakeApplication]
  392. public void Run_T_After_InitWithDriver_with_TopLevel_Does_Not_Throws ()
  393. {
  394. Application.StopAfterFirstIteration = true;
  395. // Run<Toplevel> when already initialized or not with a Driver will not throw (because Window is derived from Toplevel)
  396. // Using another type not derived from Toplevel will throws at compile time
  397. Application.Run<Window> ();
  398. Assert.True (Application.TopRunnable is Window);
  399. Application.TopRunnable!.Dispose ();
  400. }
  401. [Fact]
  402. public void Run_T_After_InitWithDriver_with_TopLevel_and_Driver_Does_Not_Throws ()
  403. {
  404. Application.StopAfterFirstIteration = true;
  405. // Run<Toplevel> when already initialized or not with a Driver will not throw (because Window is derived from Toplevel)
  406. // Using another type not derived from Toplevel will throws at compile time
  407. Application.Run<Window> (null, "fake");
  408. Assert.True (Application.TopRunnable is Window);
  409. Application.TopRunnable!.Dispose ();
  410. // Run<Toplevel> when already initialized or not with a Driver will not throw (because Dialog is derived from Toplevel)
  411. Application.Run<Dialog> (null, "fake");
  412. Assert.True (Application.TopRunnable is Dialog);
  413. Application.TopRunnable!.Dispose ();
  414. Application.Shutdown ();
  415. }
  416. [Fact]
  417. [SetupFakeApplication]
  418. public void Run_T_After_Init_Does_Not_Disposes_Application_Top ()
  419. {
  420. // Init doesn't create a Toplevel and assigned it to Application.TopRunnable
  421. // but Begin does
  422. var initTop = new Toplevel ();
  423. Application.Iteration += OnApplicationOnIteration;
  424. Application.Run<Toplevel> ();
  425. Application.Iteration -= OnApplicationOnIteration;
  426. #if DEBUG_IDISPOSABLE
  427. Assert.False (initTop.WasDisposed);
  428. initTop.Dispose ();
  429. Assert.True (initTop.WasDisposed);
  430. #endif
  431. Application.TopRunnable!.Dispose ();
  432. return;
  433. void OnApplicationOnIteration (object? s, EventArgs<IApplication?> a)
  434. {
  435. Assert.NotEqual (initTop, Application.TopRunnable);
  436. #if DEBUG_IDISPOSABLE
  437. Assert.False (initTop.WasDisposed);
  438. #endif
  439. Application.RequestStop ();
  440. }
  441. }
  442. [Fact]
  443. [SetupFakeApplication]
  444. public void Run_T_After_InitWithDriver_with_TestTopLevel_DoesNotThrow ()
  445. {
  446. Application.StopAfterFirstIteration = true;
  447. // Init has been called and we're passing no driver to Run<TestTopLevel>. This is ok.
  448. Application.Run<Toplevel> ();
  449. Application.TopRunnable!.Dispose ();
  450. }
  451. [Fact]
  452. [SetupFakeApplication]
  453. public void Run_T_After_InitNullDriver_with_TestTopLevel_DoesNotThrow ()
  454. {
  455. Application.StopAfterFirstIteration = true;
  456. // Init has been called, selecting FakeDriver; we're passing no driver to Run<TestTopLevel>. Should be fine.
  457. Application.Run<Toplevel> ();
  458. Application.TopRunnable!.Dispose ();
  459. }
  460. [Fact]
  461. [SetupFakeApplication]
  462. public void Run_T_Init_Driver_Cleared_with_TestTopLevel_Throws ()
  463. {
  464. Application.Driver = null;
  465. // Init has been called, but Driver has been set to null. Bad.
  466. Assert.Throws<InvalidOperationException> (() => Application.Run<Toplevel> ());
  467. }
  468. [Fact]
  469. [SetupFakeApplication]
  470. public void Run_T_NoInit_DoesNotThrow ()
  471. {
  472. Application.StopAfterFirstIteration = true;
  473. Application.Run<Toplevel> ().Dispose ();
  474. }
  475. [Fact]
  476. [SetupFakeApplication]
  477. public void Run_T_NoInit_WithDriver_DoesNotThrow ()
  478. {
  479. Application.StopAfterFirstIteration = true;
  480. // Init has NOT been called and we're passing a valid driver to Run<TestTopLevel>. This is ok.
  481. Application.Run<Toplevel> (null, "fake");
  482. Application.TopRunnable!.Dispose ();
  483. }
  484. [Fact]
  485. [SetupFakeApplication]
  486. public void Run_RequestStop_Stops ()
  487. {
  488. var top = new Toplevel ();
  489. SessionToken rs = Application.Begin (top);
  490. Assert.NotNull (rs);
  491. Application.Iteration += OnApplicationOnIteration;
  492. Application.Run (top);
  493. Application.Iteration -= OnApplicationOnIteration;
  494. top.Dispose ();
  495. return;
  496. void OnApplicationOnIteration (object? s, EventArgs<IApplication?> a) { Application.RequestStop (); }
  497. }
  498. [Fact]
  499. [SetupFakeApplication]
  500. public void Run_Sets_Running_True ()
  501. {
  502. var top = new Toplevel ();
  503. SessionToken rs = Application.Begin (top);
  504. Assert.NotNull (rs);
  505. Application.Iteration += OnApplicationOnIteration;
  506. Application.Run (top);
  507. Application.Iteration -= OnApplicationOnIteration;
  508. top.Dispose ();
  509. return;
  510. void OnApplicationOnIteration (object? s, EventArgs<IApplication?> a)
  511. {
  512. Assert.True (top.Running);
  513. top.RequestStop ();
  514. }
  515. }
  516. [Fact]
  517. [SetupFakeApplication]
  518. public void Run_RunningFalse_Stops ()
  519. {
  520. var top = new Toplevel ();
  521. SessionToken rs = Application.Begin (top);
  522. Assert.NotNull (rs);
  523. Application.Iteration += OnApplicationOnIteration;
  524. Application.Run (top);
  525. Application.Iteration -= OnApplicationOnIteration;
  526. top.Dispose ();
  527. return;
  528. void OnApplicationOnIteration (object? s, EventArgs<IApplication?> a) { top.Running = false; }
  529. }
  530. [Fact]
  531. [SetupFakeApplication]
  532. public void Run_Loaded_Ready_Unloaded_Events ()
  533. {
  534. Application.StopAfterFirstIteration = true;
  535. Toplevel top = new ();
  536. var count = 0;
  537. top.Loaded += (s, e) => count++;
  538. top.Ready += (s, e) => count++;
  539. top.Unloaded += (s, e) => count++;
  540. Application.Run (top);
  541. top.Dispose ();
  542. }
  543. // TODO: All Toplevel layout tests should be moved to ToplevelTests.cs
  544. [Fact]
  545. [SetupFakeApplication]
  546. public void Run_A_Modal_Toplevel_Refresh_Background_On_Moving ()
  547. {
  548. // Don't use Dialog here as it has more layout logic. Use Window instead.
  549. var w = new Window
  550. {
  551. Width = 5, Height = 5,
  552. Arrangement = ViewArrangement.Movable
  553. };
  554. Application.Driver!.SetScreenSize (10, 10);
  555. SessionToken rs = Application.Begin (w);
  556. // Don't use visuals to test as style of border can change over time.
  557. Assert.Equal (new (0, 0), w.Frame.Location);
  558. Application.RaiseMouseEvent (new () { Flags = MouseFlags.Button1Pressed });
  559. Assert.Equal (w.Border, Application.Mouse.MouseGrabView);
  560. Assert.Equal (new (0, 0), w.Frame.Location);
  561. // Move down and to the right.
  562. Application.RaiseMouseEvent (new () { ScreenPosition = new (1, 1), Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition });
  563. Assert.Equal (new (1, 1), w.Frame.Location);
  564. Application.End (rs);
  565. w.Dispose ();
  566. }
  567. [Fact]
  568. [SetupFakeApplication]
  569. public void End_Does_Not_Dispose ()
  570. {
  571. var top = new Toplevel ();
  572. Window w = new ();
  573. Application.StopAfterFirstIteration = true;
  574. Application.Run (w);
  575. #if DEBUG_IDISPOSABLE
  576. Assert.False (w.WasDisposed);
  577. #endif
  578. Assert.NotNull (w);
  579. Assert.Equal (string.Empty, w.Title); // Valid - w has not been disposed. The user may want to run it again
  580. Assert.NotNull (Application.TopRunnable);
  581. Assert.Equal (w, Application.TopRunnable);
  582. Assert.NotEqual (top, Application.TopRunnable);
  583. Application.Run (w); // Valid - w has not been disposed.
  584. #if DEBUG_IDISPOSABLE
  585. Assert.False (w.WasDisposed);
  586. Exception exception = Record.Exception (Application.Shutdown); // Invalid - w has not been disposed.
  587. Assert.NotNull (exception);
  588. w.Dispose ();
  589. Assert.True (w.WasDisposed);
  590. //exception = Record.Exception (
  591. // () => Application.Run (
  592. // w)); // Invalid - w has not been disposed. Run it in debug mode will throw, otherwise the user may want to run it again
  593. //Assert.NotNull (exception);
  594. // TODO: Re-enable this when we are done debug logging of ctx.Source.Title in RaiseSelecting
  595. //exception = Record.Exception (() => Assert.Equal (string.Empty, w.Title)); // Invalid - w has been disposed and cannot be accessed
  596. //Assert.NotNull (exception);
  597. //exception = Record.Exception (() => w.Title = "NewTitle"); // Invalid - w has been disposed and cannot be accessed
  598. //Assert.NotNull (exception);
  599. #endif
  600. }
  601. [Fact]
  602. public void Run_Creates_Top_Without_Init ()
  603. {
  604. Assert.Null (Application.TopRunnable);
  605. Application.StopAfterFirstIteration = true;
  606. Application.Iteration += OnApplicationOnIteration;
  607. Toplevel top = Application.Run (null, "fake");
  608. Application.Iteration -= OnApplicationOnIteration;
  609. #if DEBUG_IDISPOSABLE
  610. Assert.Equal (top, Application.TopRunnable);
  611. Assert.False (top.WasDisposed);
  612. Exception exception = Record.Exception (Application.Shutdown);
  613. Assert.NotNull (exception);
  614. Assert.False (top.WasDisposed);
  615. #endif
  616. // It's up to caller to dispose it
  617. top.Dispose ();
  618. #if DEBUG_IDISPOSABLE
  619. Assert.True (top.WasDisposed);
  620. #endif
  621. Assert.NotNull (Application.TopRunnable);
  622. Application.Shutdown ();
  623. Assert.Null (Application.TopRunnable);
  624. return;
  625. void OnApplicationOnIteration (object? s, EventArgs<IApplication?> e) { Assert.NotNull (Application.TopRunnable); }
  626. }
  627. [Fact]
  628. public void Run_T_Creates_Top_Without_Init ()
  629. {
  630. Assert.Null (Application.TopRunnable);
  631. Application.StopAfterFirstIteration = true;
  632. Application.Run<Toplevel> (null, "fake");
  633. #if DEBUG_IDISPOSABLE
  634. Assert.False (Application.TopRunnable!.WasDisposed);
  635. Exception exception = Record.Exception (Application.Shutdown);
  636. Assert.NotNull (exception);
  637. Assert.False (Application.TopRunnable!.WasDisposed);
  638. // It's up to caller to dispose it
  639. Application.TopRunnable!.Dispose ();
  640. Assert.True (Application.TopRunnable!.WasDisposed);
  641. #endif
  642. Assert.NotNull (Application.TopRunnable);
  643. Application.Shutdown ();
  644. Assert.Null (Application.TopRunnable);
  645. }
  646. [Fact]
  647. public void Run_t_Does_Not_Creates_Top_Without_Init ()
  648. {
  649. // When a Toplevel is created it must already have all the Application configuration loaded
  650. // This is only possible by two ways:
  651. // 1 - Using Application.Init first
  652. // 2 - Using Application.Run() or Application.Run<T>()
  653. // The Application.Run(new(Toplevel)) must always call Application.Init() first because
  654. // the new(Toplevel) may be a derived class that is possible using Application static
  655. // properties that is only available after the Application.Init was called
  656. Assert.Null (Application.TopRunnable);
  657. Assert.Throws<NotInitializedException> (() => Application.Run (new Toplevel ()));
  658. Application.Init ("fake");
  659. Application.Iteration += OnApplication_OnIteration;
  660. Application.Run (new Toplevel ());
  661. Application.Iteration -= OnApplication_OnIteration;
  662. #if DEBUG_IDISPOSABLE
  663. Assert.False (Application.TopRunnable!.WasDisposed);
  664. Exception exception = Record.Exception (Application.Shutdown);
  665. Assert.NotNull (exception);
  666. Assert.False (Application.TopRunnable!.WasDisposed);
  667. // It's up to caller to dispose it
  668. Application.TopRunnable!.Dispose ();
  669. Assert.True (Application.TopRunnable!.WasDisposed);
  670. #endif
  671. Assert.NotNull (Application.TopRunnable);
  672. Application.Shutdown ();
  673. Assert.Null (Application.TopRunnable);
  674. return;
  675. void OnApplication_OnIteration (object? s, EventArgs<IApplication?> e)
  676. {
  677. Assert.NotNull (Application.TopRunnable);
  678. Application.RequestStop ();
  679. }
  680. }
  681. private class TestToplevel : Toplevel
  682. { }
  683. [Fact]
  684. public void Run_T_With_V2_Driver_Does_Not_Call_ResetState_After_Init ()
  685. {
  686. Assert.False (Application.Initialized);
  687. Application.Init ("fake");
  688. Assert.True (Application.Initialized);
  689. Task.Run (() => { Task.Delay (300).Wait (); })
  690. .ContinueWith (
  691. (t, _) =>
  692. {
  693. // no longer loading
  694. Application.Invoke ((app) => { app.RequestStop (); });
  695. },
  696. TaskScheduler.FromCurrentSynchronizationContext ());
  697. Application.Run<TestToplevel> ();
  698. Assert.NotNull (Application.Driver);
  699. Assert.NotNull (Application.TopRunnable);
  700. Assert.False (Application.TopRunnable!.Running);
  701. Application.TopRunnable!.Dispose ();
  702. Application.Shutdown ();
  703. }
  704. // TODO: Add tests for Run that test errorHandler
  705. #endregion
  706. #region ShutdownTests
  707. [Fact]
  708. public async Task Shutdown_Allows_Async ()
  709. {
  710. var isCompletedSuccessfully = false;
  711. async Task TaskWithAsyncContinuation ()
  712. {
  713. await Task.Yield ();
  714. await Task.Yield ();
  715. isCompletedSuccessfully = true;
  716. }
  717. Application.Shutdown ();
  718. Assert.False (isCompletedSuccessfully);
  719. await TaskWithAsyncContinuation ();
  720. Thread.Sleep (100);
  721. Assert.True (isCompletedSuccessfully);
  722. }
  723. [Fact]
  724. public void Shutdown_Resets_SyncContext ()
  725. {
  726. Application.Shutdown ();
  727. Assert.Null (SynchronizationContext.Current);
  728. }
  729. #endregion
  730. }