ApplicationTests.cs 31 KB

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