ApplicationTests.cs 29 KB

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