ApplicationTests.cs 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232
  1. using System.Diagnostics;
  2. using System.Reflection;
  3. using JetBrains.Annotations;
  4. using Terminal.Gui.Drivers;
  5. using UnitTests;
  6. using Xunit.Abstractions;
  7. using static Terminal.Gui.Configuration.ConfigurationManager;
  8. // Alias Console to MockConsole so we don't accidentally use Console
  9. namespace UnitTests.ApplicationTests;
  10. public class ApplicationTests
  11. {
  12. public ApplicationTests (ITestOutputHelper output)
  13. {
  14. _output = output;
  15. ConsoleDriver.RunningUnitTests = true;
  16. #if DEBUG_IDISPOSABLE
  17. View.EnableDebugIDisposableAsserts = true;
  18. View.Instances.Clear ();
  19. RunState.Instances.Clear ();
  20. #endif
  21. }
  22. private readonly ITestOutputHelper _output;
  23. private object _timeoutLock;
  24. [Fact]
  25. public void AddTimeout_Fires ()
  26. {
  27. Assert.Null (_timeoutLock);
  28. _timeoutLock = new ();
  29. uint timeoutTime = 250;
  30. var initialized = false;
  31. var iteration = 0;
  32. var shutdown = false;
  33. object timeout = null;
  34. var timeoutCount = 0;
  35. Application.InitializedChanged += OnApplicationOnInitializedChanged;
  36. var a = new AutoInitShutdownAttribute ();
  37. a.Before (null);
  38. Assert.True (initialized);
  39. Assert.False (shutdown);
  40. _output.WriteLine ("Application.Run<Toplevel> ().Dispose ()..");
  41. Application.Run<Toplevel> ().Dispose ();
  42. _output.WriteLine ("Back from Application.Run<Toplevel> ().Dispose ()");
  43. Assert.True (initialized);
  44. Assert.False (shutdown);
  45. Assert.Equal (1, timeoutCount);
  46. Application.Shutdown ();
  47. Application.InitializedChanged -= OnApplicationOnInitializedChanged;
  48. lock (_timeoutLock)
  49. {
  50. if (timeout is { })
  51. {
  52. Application.RemoveTimeout (timeout);
  53. timeout = null;
  54. }
  55. }
  56. Assert.True (initialized);
  57. Assert.True (shutdown);
  58. #if DEBUG_IDISPOSABLE
  59. Assert.Empty (View.Instances);
  60. #endif
  61. lock (_timeoutLock)
  62. {
  63. _timeoutLock = null;
  64. }
  65. a.After (null);
  66. return;
  67. void OnApplicationOnInitializedChanged (object s, EventArgs<bool> a)
  68. {
  69. if (a.Value)
  70. {
  71. Application.Iteration += OnApplicationOnIteration;
  72. initialized = true;
  73. lock (_timeoutLock)
  74. {
  75. _output.WriteLine ($"Setting timeout for {timeoutTime}ms");
  76. timeout = Application.AddTimeout (TimeSpan.FromMilliseconds (timeoutTime), TimeoutCallback);
  77. }
  78. }
  79. else
  80. {
  81. Application.Iteration -= OnApplicationOnIteration;
  82. shutdown = true;
  83. }
  84. }
  85. bool TimeoutCallback ()
  86. {
  87. lock (_timeoutLock)
  88. {
  89. _output.WriteLine ($"TimeoutCallback. Count: {++timeoutCount}. Application Iteration: {iteration}");
  90. if (timeout is { })
  91. {
  92. _output.WriteLine (" Nulling timeout.");
  93. timeout = null;
  94. }
  95. }
  96. // False means "don't re-do timer and remove it"
  97. return false;
  98. }
  99. void OnApplicationOnIteration (object s, IterationEventArgs a)
  100. {
  101. lock (_timeoutLock)
  102. {
  103. if (timeoutCount > 0)
  104. {
  105. _output.WriteLine ($"Iteration #{iteration} - Timeout fired. Calling Application.RequestStop.");
  106. Application.RequestStop ();
  107. return;
  108. }
  109. }
  110. iteration++;
  111. // Simulate a delay
  112. Thread.Sleep ((int)timeoutTime / 10);
  113. // Worst case scenario - something went wrong
  114. if (Application.Initialized && iteration > 25)
  115. {
  116. _output.WriteLine ($"Too many iterations ({iteration}): Calling Application.RequestStop.");
  117. Application.RequestStop ();
  118. }
  119. }
  120. }
  121. [Fact]
  122. [AutoInitShutdown]
  123. public void Begin_Null_Toplevel_Throws ()
  124. {
  125. // Test null Toplevel
  126. Assert.Throws<ArgumentNullException> (() => Application.Begin (null));
  127. Application.Shutdown ();
  128. Assert.Null (Application.Top);
  129. Assert.Null (Application.MainLoop);
  130. Assert.Null (Application.Driver);
  131. }
  132. [Fact]
  133. [AutoInitShutdown (verifyShutdown: true)]
  134. public void Begin_Sets_Application_Top_To_Console_Size ()
  135. {
  136. Assert.Null (Application.Top);
  137. AutoInitShutdownAttribute.FakeResize (new Size (80, 25));
  138. Toplevel top = new ();
  139. Application.Begin (top);
  140. Assert.Equal (new (0, 0, 80, 25), Application.Top!.Frame);
  141. AutoInitShutdownAttribute.FakeResize (new Size (5, 5));
  142. Assert.Equal (new (0, 0, 5, 5), Application.Top!.Frame);
  143. top.Dispose ();
  144. }
  145. [Fact]
  146. [AutoInitShutdown]
  147. public void End_And_Shutdown_Should_Not_Dispose_ApplicationTop ()
  148. {
  149. Assert.Null (Application.Top);
  150. RunState rs = Application.Begin (new ());
  151. Application.Top!.Title = "End_And_Shutdown_Should_Not_Dispose_ApplicationTop";
  152. Assert.Equal (rs.Toplevel, Application.Top);
  153. Application.End (rs);
  154. #if DEBUG_IDISPOSABLE
  155. Assert.True (rs.WasDisposed);
  156. Assert.False (Application.Top!.WasDisposed); // Is true because the rs.Toplevel is the same as Application.Top
  157. #endif
  158. Assert.Null (rs.Toplevel);
  159. Toplevel top = Application.Top;
  160. #if DEBUG_IDISPOSABLE
  161. Exception exception = Record.Exception (Application.Shutdown);
  162. Assert.NotNull (exception);
  163. Assert.False (top.WasDisposed);
  164. top.Dispose ();
  165. Assert.True (top.WasDisposed);
  166. #endif
  167. Application.Shutdown ();
  168. Assert.Null (Application.Top);
  169. }
  170. [Fact]
  171. [AutoInitShutdown]
  172. public void Init_Begin_End_Cleans_Up ()
  173. {
  174. // Start stopwatch
  175. Stopwatch stopwatch = new Stopwatch ();
  176. stopwatch.Start ();
  177. // Begin will cause Run() to be called, which will call Begin(). Thus will block the tests
  178. // if we don't stop
  179. Application.Iteration += (s, a) => { Application.RequestStop (); };
  180. RunState runstate = null;
  181. EventHandler<RunStateEventArgs> newRunStateFn = (s, e) =>
  182. {
  183. Assert.NotNull (e.State);
  184. runstate = e.State;
  185. };
  186. Application.NotifyNewRunState += newRunStateFn;
  187. var topLevel = new Toplevel ();
  188. RunState rs = Application.Begin (topLevel);
  189. Assert.NotNull (rs);
  190. Assert.NotNull (runstate);
  191. Assert.Equal (rs, runstate);
  192. Assert.Equal (topLevel, Application.Top);
  193. Application.NotifyNewRunState -= newRunStateFn;
  194. Application.End (runstate);
  195. Assert.NotNull (Application.Top);
  196. Assert.NotNull (Application.Driver);
  197. topLevel.Dispose ();
  198. Application.Shutdown ();
  199. Assert.Null (Application.Top);
  200. Assert.Null (Application.Driver);
  201. // Stop stopwatch
  202. stopwatch.Stop ();
  203. _output.WriteLine ($"Load took {stopwatch.ElapsedMilliseconds} ms");
  204. }
  205. // Legacy driver test - all InlineData commented out
  206. //[Theory]
  207. ////[InlineData (typeof (DotNetDriver))]
  208. ////[InlineData (typeof (ANSIDriver))]
  209. ////[InlineData (typeof (WindowsDriver))]
  210. ////[InlineData (typeof (UnixDriver))]
  211. //public void Init_DriverName_Should_Pick_Correct_Driver (Type driverType)
  212. //{
  213. // var driver = (IConsoleDriver)Activator.CreateInstance (driverType);
  214. // Application.Init (driverName: driverType.Name);
  215. // Assert.NotNull (Application.Driver);
  216. // Assert.NotEqual (driver, Application.Driver);
  217. // Assert.Equal (driverType, Application.Driver?.GetType ());
  218. // Application.Shutdown ();
  219. //}
  220. [Fact]
  221. public void Init_Null_Driver_Should_Pick_A_Driver ()
  222. {
  223. Application.Init ();
  224. Assert.NotNull (Application.Driver);
  225. Application.Shutdown ();
  226. }
  227. [Theory]
  228. [InlineData (typeof (FakeDriver))]
  229. //[InlineData (typeof (DotNetDriver))]
  230. //[InlineData (typeof (WindowsDriver))]
  231. //[InlineData (typeof (UnixDriver))]
  232. public void Init_ResetState_Resets_Properties (Type driverType)
  233. {
  234. ThrowOnJsonErrors = true;
  235. // For all the fields/properties of Application, check that they are reset to their default values
  236. // Set some values
  237. Application.Init (driverName: driverType.Name);
  238. // Application.IsInitialized = true;
  239. // Reset
  240. Application.ResetState ();
  241. void CheckReset ()
  242. {
  243. // Check that all fields and properties are set to their default values
  244. // Public Properties
  245. Assert.Null (Application.Top);
  246. Assert.Null (Application.Mouse.MouseGrabView);
  247. // Don't check Application.ForceDriver
  248. // Assert.Empty (Application.ForceDriver);
  249. // Don't check Application.Force16Colors
  250. //Assert.False (Application.Force16Colors);
  251. Assert.Null (Application.Driver);
  252. Assert.Null (Application.MainLoop);
  253. Assert.False (Application.EndAfterFirstIteration);
  254. // Commented out because if CM changed the defaults, those changes should
  255. // persist across Inits.
  256. //Assert.Equal (Key.Tab.WithShift, Application.PrevTabKey);
  257. //Assert.Equal (Key.Tab, Application.NextTabKey);
  258. //Assert.Equal (Key.F6.WithShift, Application.PrevTabGroupKey);
  259. //Assert.Equal (Key.F6, Application.NextTabGroupKey);
  260. //Assert.Equal (Key.Esc, Application.QuitKey);
  261. // Internal properties
  262. Assert.False (Application.Initialized);
  263. Assert.Equal (Application.GetSupportedCultures (), Application.SupportedCultures);
  264. Assert.Equal (Application.GetAvailableCulturesFromEmbeddedResources (), Application.SupportedCultures);
  265. Assert.False (Application._forceFakeConsole);
  266. Assert.Equal (-1, Application.MainThreadId);
  267. Assert.Empty (Application.TopLevels);
  268. Assert.Empty (Application.CachedViewsUnderMouse);
  269. // Mouse
  270. // Do not reset _lastMousePosition
  271. //Assert.Null (Application._lastMousePosition);
  272. // Navigation
  273. Assert.Null (Application.Navigation);
  274. // Popover
  275. Assert.Null (Application.Popover);
  276. // Events - Can't check
  277. //Assert.Null (Application.NotifyNewRunState);
  278. //Assert.Null (Application.NotifyNewRunState);
  279. //Assert.Null (Application.Iteration);
  280. //Assert.Null (Application.SizeChanging);
  281. //Assert.Null (Application.GrabbedMouse);
  282. //Assert.Null (Application.UnGrabbingMouse);
  283. //Assert.Null (Application.GrabbedMouse);
  284. //Assert.Null (Application.UnGrabbedMouse);
  285. //Assert.Null (Application.MouseEvent);
  286. //Assert.Null (Application.KeyDown);
  287. //Assert.Null (Application.KeyUp);
  288. }
  289. CheckReset ();
  290. // Set the values that can be set
  291. Application.Initialized = true;
  292. Application._forceFakeConsole = true;
  293. Application.MainThreadId = 1;
  294. //Application._topLevels = new List<Toplevel> ();
  295. Application.CachedViewsUnderMouse.Clear ();
  296. //Application.SupportedCultures = new List<CultureInfo> ();
  297. Application.Force16Colors = true;
  298. //Application.ForceDriver = "driver";
  299. Application.EndAfterFirstIteration = true;
  300. Application.PrevTabGroupKey = Key.A;
  301. Application.NextTabGroupKey = Key.B;
  302. Application.QuitKey = Key.C;
  303. Application.KeyBindings.Add (Key.D, Command.Cancel);
  304. Application.CachedViewsUnderMouse.Clear ();
  305. //Application.WantContinuousButtonPressedView = new View ();
  306. // Mouse
  307. Application.LastMousePosition = new Point (1, 1);
  308. Application.Navigation = new ();
  309. Application.ResetState ();
  310. CheckReset ();
  311. ThrowOnJsonErrors = false;
  312. }
  313. [Fact]
  314. public void Init_Shutdown_Cleans_Up ()
  315. {
  316. // Verify initial state is per spec
  317. //Pre_Init_State ();
  318. Application.Init (new FakeDriver ());
  319. // Verify post-Init state is correct
  320. //Post_Init_State ();
  321. Application.Shutdown ();
  322. // Verify state is back to initial
  323. //Pre_Init_State ();
  324. #if DEBUG_IDISPOSABLE
  325. // Validate there are no outstanding Responder-based instances
  326. // after a scenario was selected to run. This proves the main UI Catalog
  327. // 'app' closed cleanly.
  328. Assert.Empty (View.Instances);
  329. #endif
  330. }
  331. [Fact]
  332. public void Shutdown_Alone_Does_Nothing () { Application.Shutdown (); }
  333. [Theory]
  334. [InlineData (typeof (FakeDriver))]
  335. //[InlineData (typeof (DotNetDriver))]
  336. //[InlineData (typeof (WindowsDriver))]
  337. //[InlineData (typeof (UnixDriver))]
  338. public void Init_Shutdown_Fire_InitializedChanged (Type driverType)
  339. {
  340. var initialized = false;
  341. var shutdown = false;
  342. Application.InitializedChanged += OnApplicationOnInitializedChanged;
  343. Application.Init (driverName: driverType.Name);
  344. Assert.True (initialized);
  345. Assert.False (shutdown);
  346. Application.Shutdown ();
  347. Assert.True (initialized);
  348. Assert.True (shutdown);
  349. Application.InitializedChanged -= OnApplicationOnInitializedChanged;
  350. return;
  351. void OnApplicationOnInitializedChanged (object s, EventArgs<bool> a)
  352. {
  353. if (a.Value)
  354. {
  355. initialized = true;
  356. }
  357. else
  358. {
  359. shutdown = true;
  360. }
  361. }
  362. }
  363. [Fact]
  364. [AutoInitShutdown]
  365. public void Init_Unbalanced_Throws ()
  366. {
  367. Assert.Throws<InvalidOperationException> (
  368. () =>
  369. Application.InternalInit (
  370. new FakeDriver ()
  371. )
  372. );
  373. Application.Shutdown ();
  374. Assert.Null (Application.Top);
  375. Assert.Null (Application.MainLoop);
  376. Assert.Null (Application.Driver);
  377. }
  378. [Fact]
  379. [AutoInitShutdown]
  380. public void Init_Unbalanced_Throws2 ()
  381. {
  382. // Now try the other way
  383. Assert.Throws<InvalidOperationException> (() => Application.Init (new FakeDriver ()));
  384. Application.Shutdown ();
  385. Assert.Null (Application.Top);
  386. Assert.Null (Application.MainLoop);
  387. Assert.Null (Application.Driver);
  388. }
  389. [Fact]
  390. public void Init_WithoutTopLevelFactory_Begin_End_Cleans_Up ()
  391. {
  392. // Begin will cause Run() to be called, which will call Begin(). Thus will block the tests
  393. // if we don't stop
  394. Application.Iteration += (s, a) => { Application.RequestStop (); };
  395. // NOTE: Run<T>, when called after Init has been called behaves differently than
  396. // when called if Init has not been called.
  397. Toplevel topLevel = new ();
  398. Application.InternalInit (new FakeDriver ());
  399. RunState runstate = null;
  400. EventHandler<RunStateEventArgs> newRunStateFn = (s, e) =>
  401. {
  402. Assert.NotNull (e.State);
  403. runstate = e.State;
  404. };
  405. Application.NotifyNewRunState += newRunStateFn;
  406. RunState rs = Application.Begin (topLevel);
  407. Assert.NotNull (rs);
  408. Assert.NotNull (runstate);
  409. Assert.Equal (rs, runstate);
  410. Assert.Equal (topLevel, Application.Top);
  411. Application.NotifyNewRunState -= newRunStateFn;
  412. Application.End (runstate);
  413. Assert.NotNull (Application.Top);
  414. Assert.NotNull (Application.MainLoop);
  415. Assert.NotNull (Application.Driver);
  416. topLevel.Dispose ();
  417. Application.Shutdown ();
  418. Assert.Null (Application.Top);
  419. Assert.Null (Application.MainLoop);
  420. Assert.Null (Application.Driver);
  421. }
  422. [Fact (Skip = "FakeDriver is not allowed, use AutoInitShutdown attribute instead")]
  423. public void Init_NoParam_ForceDriver_Works ()
  424. {
  425. Application.ForceDriver = "Fake";
  426. Application.Init ();
  427. //Assert.IsType<FakeConsoleInput>(Application.Drive);
  428. //Assert.IsType<FakeDriver> (Application.Driver);
  429. Application.ResetState ();
  430. }
  431. [Fact]
  432. public void Init_KeyBindings_Are_Not_Reset ()
  433. {
  434. Debug.Assert (!IsEnabled);
  435. try
  436. {
  437. // arrange
  438. ThrowOnJsonErrors = true;
  439. Application.QuitKey = Key.Q;
  440. Assert.Equal (Key.Q, Application.QuitKey);
  441. Application.Init (new FakeDriver ());
  442. Assert.Equal (Key.Q, Application.QuitKey);
  443. }
  444. finally
  445. {
  446. Application.ResetState (false);
  447. }
  448. }
  449. [Fact]
  450. [AutoInitShutdown (verifyShutdown: true)]
  451. public void Internal_Properties_Correct ()
  452. {
  453. Assert.True (Application.Initialized);
  454. Assert.Null (Application.Top);
  455. RunState rs = Application.Begin (new ());
  456. Assert.Equal (Application.Top, rs.Toplevel);
  457. Assert.Null (Application.Mouse.MouseGrabView); // public
  458. Application.Top!.Dispose ();
  459. }
  460. // Invoke Tests
  461. // TODO: Test with threading scenarios
  462. [Fact]
  463. [AutoInitShutdown]
  464. public void Invoke_Adds_Idle ()
  465. {
  466. var top = new Toplevel ();
  467. RunState rs = Application.Begin (top);
  468. var firstIteration = false;
  469. var actionCalled = 0;
  470. Application.Invoke (() => { actionCalled++; });
  471. Application.RunIteration (ref rs, firstIteration);
  472. Assert.Equal (1, actionCalled);
  473. top.Dispose ();
  474. Application.Shutdown ();
  475. }
  476. [Fact]
  477. public void Run_Iteration_Fires ()
  478. {
  479. var iteration = 0;
  480. Application.Init (null, driverName: "fake");
  481. Application.Iteration += Application_Iteration;
  482. Application.Run<Toplevel> ().Dispose ();
  483. Assert.Equal (1, iteration);
  484. Application.Shutdown ();
  485. return;
  486. void Application_Iteration (object sender, IterationEventArgs e)
  487. {
  488. if (iteration > 0)
  489. {
  490. Assert.Fail ();
  491. }
  492. iteration++;
  493. Application.RequestStop ();
  494. }
  495. }
  496. [Fact]
  497. [AutoInitShutdown]
  498. public void Screen_Size_Changes ()
  499. {
  500. var driver = Application.Driver;
  501. AutoInitShutdownAttribute.FakeResize (new Size (80,25));
  502. Assert.Equal (new (0, 0, 80, 25), driver.Screen);
  503. Assert.Equal (new (0, 0, 80, 25), Application.Screen);
  504. // TODO: Should not be possible to manually change these at whim!
  505. driver.Cols = 100;
  506. driver.Rows = 30;
  507. // IConsoleDriver.Screen isn't assignable
  508. //driver.Screen = new (0, 0, driver.Cols, Rows);
  509. AutoInitShutdownAttribute.FakeResize (new Size (100, 30));
  510. Assert.Equal (new (0, 0, 100, 30), driver.Screen);
  511. // Assert does not make sense
  512. // Assert.NotEqual (new (0, 0, 100, 30), Application.Screen);
  513. // Assert.Equal (new (0, 0, 80, 25), Application.Screen);
  514. Application.Screen = new (0, 0, driver.Cols, driver.Rows);
  515. Assert.Equal (new (0, 0, 100, 30), driver.Screen);
  516. Application.Shutdown ();
  517. }
  518. [Fact]
  519. public void InitState_Throws_If_Driver_Is_Null ()
  520. {
  521. Assert.Throws<ArgumentNullException> (static () => Application.SubscribeDriverEvents ());
  522. }
  523. #region RunTests
  524. [Fact]
  525. [AutoInitShutdown]
  526. public void Run_T_After_InitWithDriver_with_TopLevel_Does_Not_Throws ()
  527. {
  528. Application.Iteration += (s, e) => Application.RequestStop ();
  529. // Run<Toplevel> when already initialized or not with a Driver will not throw (because Window is derived from Toplevel)
  530. // Using another type not derived from Toplevel will throws at compile time
  531. Application.Run<Window> ();
  532. Assert.True (Application.Top is Window);
  533. Application.Top!.Dispose ();
  534. Application.Shutdown ();
  535. Assert.Null (Application.Top);
  536. Assert.Null (Application.MainLoop);
  537. Assert.Null (Application.Driver);
  538. }
  539. [Fact]
  540. public void Run_T_After_InitWithDriver_with_TopLevel_and_Driver_Does_Not_Throws ()
  541. {
  542. Application.Iteration += (s, e) => Application.RequestStop ();
  543. // Run<Toplevel> when already initialized or not with a Driver will not throw (because Window is derived from Toplevel)
  544. // Using another type not derived from Toplevel will throws at compile time
  545. Application.Run<Window> (null, new FakeDriver ());
  546. Assert.True (Application.Top is Window);
  547. Application.Top!.Dispose ();
  548. // Run<Toplevel> when already initialized or not with a Driver will not throw (because Dialog is derived from Toplevel)
  549. Application.Run<Dialog> (null, new FakeDriver ());
  550. Assert.True (Application.Top is Dialog);
  551. Application.Top!.Dispose ();
  552. Application.Shutdown ();
  553. Assert.Null (Application.Top);
  554. Assert.Null (Application.MainLoop);
  555. Assert.Null (Application.Driver);
  556. }
  557. [Fact]
  558. [AutoInitShutdown]
  559. [TestRespondersDisposed]
  560. public void Run_T_After_Init_Does_Not_Disposes_Application_Top ()
  561. {
  562. // Init doesn't create a Toplevel and assigned it to Application.Top
  563. // but Begin does
  564. var initTop = new Toplevel ();
  565. Application.Iteration += (s, a) =>
  566. {
  567. Assert.NotEqual (initTop, Application.Top);
  568. #if DEBUG_IDISPOSABLE
  569. Assert.False (initTop.WasDisposed);
  570. #endif
  571. Application.RequestStop ();
  572. };
  573. Application.Run<Toplevel> ();
  574. #if DEBUG_IDISPOSABLE
  575. Assert.False (initTop.WasDisposed);
  576. initTop.Dispose ();
  577. Assert.True (initTop.WasDisposed);
  578. #endif
  579. Application.Top!.Dispose ();
  580. Application.Shutdown ();
  581. Assert.Null (Application.Top);
  582. Assert.Null (Application.MainLoop);
  583. Assert.Null (Application.Driver);
  584. }
  585. [Fact]
  586. [TestRespondersDisposed]
  587. [AutoInitShutdown]
  588. public void Run_T_After_InitWithDriver_with_TestTopLevel_DoesNotThrow ()
  589. {
  590. Application.Iteration += (s, a) => { Application.RequestStop (); };
  591. // Init has been called and we're passing no driver to Run<TestTopLevel>. This is ok.
  592. Application.Run<Toplevel> ();
  593. Application.Top!.Dispose ();
  594. Application.Shutdown ();
  595. Assert.Null (Application.Top);
  596. Assert.Null (Application.MainLoop);
  597. Assert.Null (Application.Driver);
  598. }
  599. [Fact]
  600. [TestRespondersDisposed]
  601. public void Run_T_After_InitNullDriver_with_TestTopLevel_DoesNotThrow ()
  602. {
  603. Application.ForceDriver = "FakeDriver";
  604. var a = new AutoInitShutdownAttribute ();
  605. a.Before (null);
  606. Application.Iteration += (s, a) => { Application.RequestStop (); };
  607. // Init has been called, selecting FakeDriver; we're passing no driver to Run<TestTopLevel>. Should be fine.
  608. Application.Run<Toplevel> ();
  609. Application.Top!.Dispose ();
  610. Application.Shutdown ();
  611. Assert.Null (Application.Top);
  612. Assert.Null (Application.MainLoop);
  613. Assert.Null (Application.Driver);
  614. a.After (null);
  615. }
  616. [Fact]
  617. [TestRespondersDisposed]
  618. [AutoInitShutdown]
  619. public void Run_T_Init_Driver_Cleared_with_TestTopLevel_Throws ()
  620. {
  621. Application.Driver = null;
  622. // Init has been called, but Driver has been set to null. Bad.
  623. Assert.Throws<InvalidOperationException> (() => Application.Run<Toplevel> ());
  624. Application.Shutdown ();
  625. Assert.Null (Application.Top);
  626. Assert.Null (Application.MainLoop);
  627. Assert.Null (Application.Driver);
  628. }
  629. [Fact(Skip = "FakeDriver is not allowed, use AutoInitShutdown attribute instead")]
  630. [TestRespondersDisposed]
  631. public void Run_T_NoInit_DoesNotThrow ()
  632. {
  633. Application.ForceDriver = "FakeDriver";
  634. Application.Iteration += (s, a) => { Application.RequestStop (); };
  635. Application.Run<Toplevel> ();
  636. Assert.Equal (typeof (FakeDriver), Application.Driver?.GetType ());
  637. Application.Top!.Dispose ();
  638. Application.Shutdown ();
  639. Assert.Null (Application.Top);
  640. Assert.Null (Application.MainLoop);
  641. Assert.Null (Application.Driver);
  642. }
  643. [Fact]
  644. [TestRespondersDisposed]
  645. public void Run_T_NoInit_WithDriver_DoesNotThrow ()
  646. {
  647. Application.Iteration += (s, a) => { Application.RequestStop (); };
  648. // Init has NOT been called and we're passing a valid driver to Run<TestTopLevel>. This is ok.
  649. Application.Run<Toplevel> (null, new FakeDriver ());
  650. Application.Top!.Dispose ();
  651. Application.Shutdown ();
  652. Assert.Null (Application.Top);
  653. Assert.Null (Application.MainLoop);
  654. Assert.Null (Application.Driver);
  655. }
  656. [Fact]
  657. [TestRespondersDisposed]
  658. [AutoInitShutdown]
  659. public void Run_RequestStop_Stops ()
  660. {
  661. var top = new Toplevel ();
  662. RunState rs = Application.Begin (top);
  663. Assert.NotNull (rs);
  664. Application.Iteration += (s, a) => { Application.RequestStop (); };
  665. Application.Run (top);
  666. top.Dispose ();
  667. Application.Shutdown ();
  668. Assert.Null (Application.Top);
  669. Assert.Null (Application.MainLoop);
  670. Assert.Null (Application.Driver);
  671. }
  672. [Fact]
  673. [AutoInitShutdown]
  674. public void Run_Sets_Running_True ()
  675. {
  676. var top = new Toplevel ();
  677. RunState rs = Application.Begin (top);
  678. Assert.NotNull (rs);
  679. Application.Iteration += (s, a) =>
  680. {
  681. Assert.True (top.Running);
  682. top.Running = false;
  683. };
  684. Application.Run (top);
  685. top.Dispose ();
  686. Application.Shutdown ();
  687. Assert.Null (Application.Top);
  688. Assert.Null (Application.MainLoop);
  689. Assert.Null (Application.Driver);
  690. }
  691. [Fact]
  692. [TestRespondersDisposed]
  693. [AutoInitShutdown]
  694. public void Run_RunningFalse_Stops ()
  695. {
  696. var top = new Toplevel ();
  697. RunState rs = Application.Begin (top);
  698. Assert.NotNull (rs);
  699. Application.Iteration += (s, a) => { top.Running = false; };
  700. Application.Run (top);
  701. top.Dispose ();
  702. Application.Shutdown ();
  703. Assert.Null (Application.Top);
  704. Assert.Null (Application.MainLoop);
  705. Assert.Null (Application.Driver);
  706. }
  707. [Fact]
  708. [AutoInitShutdown]
  709. [TestRespondersDisposed]
  710. public void Run_Loaded_Ready_Unloaded_Events ()
  711. {
  712. Toplevel top = new ();
  713. var count = 0;
  714. top.Loaded += (s, e) => count++;
  715. top.Ready += (s, e) => count++;
  716. top.Unloaded += (s, e) => count++;
  717. Application.Iteration += (s, a) => Application.RequestStop ();
  718. Application.Run (top);
  719. top.Dispose ();
  720. Application.Shutdown ();
  721. Assert.Equal (3, count);
  722. }
  723. // TODO: All Toplevel layout tests should be moved to ToplevelTests.cs
  724. [Fact]
  725. [AutoInitShutdown]
  726. public void Run_A_Modal_Toplevel_Refresh_Background_On_Moving ()
  727. {
  728. // Don't use Dialog here as it has more layout logic. Use Window instead.
  729. var w = new Window
  730. {
  731. Width = 5, Height = 5,
  732. Arrangement = ViewArrangement.Movable
  733. };
  734. AutoInitShutdownAttribute.FakeResize (new Size (10, 10));
  735. RunState rs = Application.Begin (w);
  736. // Don't use visuals to test as style of border can change over time.
  737. Assert.Equal (new (0, 0), w.Frame.Location);
  738. Application.RaiseMouseEvent (new () { Flags = MouseFlags.Button1Pressed });
  739. Assert.Equal (w.Border, Application.Mouse.MouseGrabView);
  740. Assert.Equal (new (0, 0), w.Frame.Location);
  741. // Move down and to the right.
  742. Application.RaiseMouseEvent (new () { ScreenPosition = new (1, 1), Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition });
  743. Assert.Equal (new (1, 1), w.Frame.Location);
  744. Application.End (rs);
  745. w.Dispose ();
  746. Application.Shutdown ();
  747. }
  748. [Fact]
  749. [AutoInitShutdown]
  750. public void End_Does_Not_Dispose ()
  751. {
  752. var top = new Toplevel ();
  753. Window w = new ();
  754. w.Ready += (s, e) => Application.RequestStop (); // Causes `End` to be called
  755. Application.Run (w);
  756. #if DEBUG_IDISPOSABLE
  757. Assert.False (w.WasDisposed);
  758. #endif
  759. Assert.NotNull (w);
  760. Assert.Equal (string.Empty, w.Title); // Valid - w has not been disposed. The user may want to run it again
  761. Assert.NotNull (Application.Top);
  762. Assert.Equal (w, Application.Top);
  763. Assert.NotEqual (top, Application.Top);
  764. Application.Run (w); // Valid - w has not been disposed.
  765. #if DEBUG_IDISPOSABLE
  766. Assert.False (w.WasDisposed);
  767. Exception exception = Record.Exception (Application.Shutdown); // Invalid - w has not been disposed.
  768. Assert.NotNull (exception);
  769. w.Dispose ();
  770. Assert.True (w.WasDisposed);
  771. //exception = Record.Exception (
  772. // () => Application.Run (
  773. // w)); // Invalid - w has been disposed. Run it in debug mode will throw, otherwise the user may want to run it again
  774. //Assert.NotNull (exception);
  775. // TODO: Re-enable this when we are done debug logging of ctx.Source.Title in RaiseSelecting
  776. //exception = Record.Exception (() => Assert.Equal (string.Empty, w.Title)); // Invalid - w has been disposed and cannot be accessed
  777. //Assert.NotNull (exception);
  778. //exception = Record.Exception (() => w.Title = "NewTitle"); // Invalid - w has been disposed and cannot be accessed
  779. //Assert.NotNull (exception);
  780. #endif
  781. Application.Shutdown ();
  782. Assert.NotNull (w);
  783. Assert.NotNull (top);
  784. Assert.Null (Application.Top);
  785. }
  786. [Fact]
  787. public void Run_Creates_Top_Without_Init ()
  788. {
  789. var driver = new FakeDriver ();
  790. Assert.Null (Application.Top);
  791. Application.Iteration += (s, e) =>
  792. {
  793. Assert.NotNull (Application.Top);
  794. Application.RequestStop ();
  795. };
  796. Toplevel top = Application.Run (null, driver);
  797. #if DEBUG_IDISPOSABLE
  798. Assert.Equal (top, Application.Top);
  799. Assert.False (top.WasDisposed);
  800. Exception exception = Record.Exception (Application.Shutdown);
  801. Assert.NotNull (exception);
  802. Assert.False (top.WasDisposed);
  803. #endif
  804. // It's up to caller to dispose it
  805. top.Dispose ();
  806. #if DEBUG_IDISPOSABLE
  807. Assert.True (top.WasDisposed);
  808. #endif
  809. Assert.NotNull (Application.Top);
  810. Application.Shutdown ();
  811. Assert.Null (Application.Top);
  812. }
  813. [Fact]
  814. public void Run_T_Creates_Top_Without_Init ()
  815. {
  816. var driver = new FakeDriver ();
  817. Assert.Null (Application.Top);
  818. Application.Iteration += (s, e) =>
  819. {
  820. Assert.NotNull (Application.Top);
  821. Application.RequestStop ();
  822. };
  823. Application.Run<Toplevel> (null, driver);
  824. #if DEBUG_IDISPOSABLE
  825. Assert.False (Application.Top!.WasDisposed);
  826. Exception exception = Record.Exception (Application.Shutdown);
  827. Assert.NotNull (exception);
  828. Assert.False (Application.Top!.WasDisposed);
  829. // It's up to caller to dispose it
  830. Application.Top!.Dispose ();
  831. Assert.True (Application.Top!.WasDisposed);
  832. #endif
  833. Assert.NotNull (Application.Top);
  834. Application.Shutdown ();
  835. Assert.Null (Application.Top);
  836. }
  837. [Fact]
  838. public void Run_t_Does_Not_Creates_Top_Without_Init ()
  839. {
  840. // When a Toplevel is created it must already have all the Application configuration loaded
  841. // This is only possible by two ways:
  842. // 1 - Using Application.Init first
  843. // 2 - Using Application.Run() or Application.Run<T>()
  844. // The Application.Run(new(Toplevel)) must always call Application.Init() first because
  845. // the new(Toplevel) may be a derived class that is possible using Application static
  846. // properties that is only available after the Application.Init was called
  847. var driver = new FakeDriver ();
  848. Assert.Null (Application.Top);
  849. Assert.Throws<NotInitializedException> (() => Application.Run (new Toplevel ()));
  850. Application.Init (driver);
  851. Application.Iteration += (s, e) =>
  852. {
  853. Assert.NotNull (Application.Top);
  854. Application.RequestStop ();
  855. };
  856. Application.Run (new Toplevel ());
  857. #if DEBUG_IDISPOSABLE
  858. Assert.False (Application.Top!.WasDisposed);
  859. Exception exception = Record.Exception (Application.Shutdown);
  860. Assert.NotNull (exception);
  861. Assert.False (Application.Top!.WasDisposed);
  862. // It's up to caller to dispose it
  863. Application.Top!.Dispose ();
  864. Assert.True (Application.Top!.WasDisposed);
  865. #endif
  866. Assert.NotNull (Application.Top);
  867. Application.Shutdown ();
  868. Assert.Null (Application.Top);
  869. }
  870. private class TestToplevel : Toplevel { }
  871. private readonly object _forceDriverLock = new ();
  872. /*
  873. [Theory]
  874. // This test wants to Run which results in console handle errors, it wants to rely non drivers checking ConsoleDriver.RunningUnitTests
  875. // And suppressing things that might fail, this is anti pattern, instead we should test this kind of thing with Mocking
  876. // [InlineData ("v2win", typeof (ConsoleDriverFacade<WindowsConsole.InputRecord>))]
  877. // [InlineData ("v2net", typeof (ConsoleDriverFacade<ConsoleKeyInfo>))]
  878. // FakeDriver is not allowed, use AutoInitShutdown attribute instead
  879. //[InlineData ("FakeDriver", typeof (FakeDriver))]
  880. //[InlineData ("DotNetDriver", typeof (DotNetDriver))]
  881. //[InlineData ("WindowsDriver", typeof (WindowsDriver))]
  882. //[InlineData ("UnixDriver", typeof (UnixDriver))]
  883. public void Run_T_Call_Init_ForceDriver_Should_Pick_Correct_Driver (string driverName, Type expectedType)
  884. {
  885. Assert.True (ConsoleDriver.RunningUnitTests);
  886. var result = false;
  887. lock (_forceDriverLock)
  888. {
  889. Task.Run (() =>
  890. {
  891. while (!Application.Initialized)
  892. {
  893. Task.Delay (300).Wait ();
  894. }
  895. })
  896. .ContinueWith (
  897. (t, _) =>
  898. {
  899. // no longer loading
  900. Assert.True (Application.Initialized);
  901. Application.Invoke (() =>
  902. {
  903. result = true;
  904. Application.RequestStop ();
  905. });
  906. },
  907. TaskScheduler.FromCurrentSynchronizationContext ());
  908. }
  909. Application.ForceDriver = driverName;
  910. Application.Run<TestToplevel> ();
  911. Assert.NotNull (Application.Driver);
  912. Assert.Equal (expectedType, Application.Driver?.GetType ());
  913. Assert.NotNull (Application.Top);
  914. Assert.False (Application.Top!.Running);
  915. Application.Top!.Dispose ();
  916. Application.Shutdown ();
  917. Assert.True (result);
  918. }
  919. */
  920. [Fact]
  921. public void Run_T_With_Legacy_Driver_Does_Not_Call_ResetState_After_Init ()
  922. {
  923. Assert.False (Application.Initialized);
  924. Application.Init ();
  925. Assert.True (Application.Initialized);
  926. Application.Iteration += (_, _) => Application.RequestStop ();
  927. Application.Run<TestToplevel> ();
  928. Assert.NotNull (Application.Driver);
  929. Assert.NotNull (Application.Top);
  930. Assert.False (Application.Top!.Running);
  931. Application.Top!.Dispose ();
  932. Application.Shutdown ();
  933. }
  934. [Fact]
  935. public void Run_T_With_V2_Driver_Does_Not_Call_ResetState_After_Init ()
  936. {
  937. Assert.False (Application.Initialized);
  938. Application.Init (null, "v2net");
  939. Assert.True (Application.Initialized);
  940. Task.Run (() =>
  941. {
  942. Task.Delay (300).Wait ();
  943. }).ContinueWith (
  944. (t, _) =>
  945. {
  946. // no longer loading
  947. Application.Invoke (() =>
  948. {
  949. Application.RequestStop ();
  950. });
  951. },
  952. TaskScheduler.FromCurrentSynchronizationContext ());
  953. Application.Run<TestToplevel> ();
  954. Assert.NotNull (Application.Driver);
  955. Assert.NotNull (Application.Top);
  956. Assert.False (Application.Top!.Running);
  957. Application.Top!.Dispose ();
  958. Application.Shutdown ();
  959. }
  960. // TODO: Add tests for Run that test errorHandler
  961. #endregion
  962. #region ShutdownTests
  963. [Fact]
  964. public async Task Shutdown_Allows_Async ()
  965. {
  966. var isCompletedSuccessfully = false;
  967. async Task TaskWithAsyncContinuation ()
  968. {
  969. await Task.Yield ();
  970. await Task.Yield ();
  971. isCompletedSuccessfully = true;
  972. }
  973. Application.Shutdown ();
  974. Assert.False (isCompletedSuccessfully);
  975. await TaskWithAsyncContinuation ();
  976. Thread.Sleep (100);
  977. Assert.True (isCompletedSuccessfully);
  978. }
  979. [Fact]
  980. public void Shutdown_Resets_SyncContext ()
  981. {
  982. Application.Shutdown ();
  983. Assert.Null (SynchronizationContext.Current);
  984. }
  985. #endregion
  986. }