ApplicationTests.cs 29 KB

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