ScenarioTests.cs 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873
  1. using System.Collections.ObjectModel;
  2. using System.Diagnostics;
  3. using System.Reflection;
  4. using Xunit.Abstractions;
  5. namespace UICatalog.Tests;
  6. public class ScenarioTests : TestsAllViews
  7. {
  8. public ScenarioTests (ITestOutputHelper output)
  9. {
  10. #if DEBUG_IDISPOSABLE
  11. View.Instances.Clear ();
  12. #endif
  13. _output = output;
  14. }
  15. private readonly ITestOutputHelper _output;
  16. private object _timeoutLock;
  17. /// <summary>
  18. /// <para>This runs through all Scenarios defined in UI Catalog, calling Init, Setup, and Run.</para>
  19. /// <para>Should find any Scenarios which crash on load or do not respond to <see cref="Application.RequestStop()"/>.</para>
  20. /// </summary>
  21. [Theory]
  22. [MemberData (nameof (AllScenarioTypes))]
  23. public void All_Scenarios_Quit_And_Init_Shutdown_Properly (Type scenarioType)
  24. {
  25. Assert.Null (_timeoutLock);
  26. _timeoutLock = new ();
  27. // Disable any UIConfig settings
  28. ConfigLocations savedConfigLocations = ConfigurationManager.Locations;
  29. ConfigurationManager.Locations = ConfigLocations.Default;
  30. // If a previous test failed, this will ensure that the Application is in a clean state
  31. Application.ResetState (true);
  32. _output.WriteLine ($"Running Scenario '{scenarioType}'");
  33. var scenario = (Scenario)Activator.CreateInstance (scenarioType);
  34. uint abortTime = 1500;
  35. object timeout = null;
  36. var initialized = false;
  37. var shutdown = false;
  38. int iterationCount = 0;
  39. Application.InitializedChanged += OnApplicationOnInitializedChanged;
  40. Application.ForceDriver = "FakeDriver";
  41. scenario.Main ();
  42. scenario.Dispose ();
  43. scenario = null;
  44. Application.ForceDriver = string.Empty;
  45. Application.InitializedChanged -= OnApplicationOnInitializedChanged;
  46. lock (_timeoutLock)
  47. {
  48. if (timeout is { })
  49. {
  50. timeout = null;
  51. }
  52. }
  53. Assert.True (initialized);
  54. Assert.True (shutdown);
  55. #if DEBUG_IDISPOSABLE
  56. Assert.Empty (View.Instances);
  57. #endif
  58. lock (_timeoutLock)
  59. {
  60. _timeoutLock = null;
  61. }
  62. // Restore the configuration locations
  63. ConfigurationManager.Locations = savedConfigLocations;
  64. ConfigurationManager.Reset ();
  65. return;
  66. void OnApplicationOnInitializedChanged (object s, EventArgs<bool> a)
  67. {
  68. if (a.CurrentValue)
  69. {
  70. Application.Iteration += OnApplicationOnIteration;
  71. initialized = true;
  72. lock (_timeoutLock)
  73. {
  74. timeout = Application.AddTimeout (TimeSpan.FromMilliseconds (abortTime), ForceCloseCallback);
  75. }
  76. }
  77. else
  78. {
  79. Application.Iteration -= OnApplicationOnIteration;
  80. shutdown = true;
  81. }
  82. _output.WriteLine ($"Initialized == {a.CurrentValue}");
  83. }
  84. // If the scenario doesn't close within 500ms, this will force it to quit
  85. bool ForceCloseCallback ()
  86. {
  87. lock (_timeoutLock)
  88. {
  89. if (timeout is { })
  90. {
  91. timeout = null;
  92. }
  93. }
  94. Assert.Fail (
  95. $"'{scenario.GetName ()}' failed to Quit with {Application.QuitKey} after {abortTime}ms and {iterationCount} iterations. Force quit.");
  96. // Restore the configuration locations
  97. ConfigurationManager.Locations = savedConfigLocations;
  98. ConfigurationManager.Reset ();
  99. Application.ResetState (true);
  100. return false;
  101. }
  102. void OnApplicationOnIteration (object s, IterationEventArgs a)
  103. {
  104. iterationCount++;
  105. if (Application.Initialized)
  106. {
  107. // Press QuitKey
  108. _output.WriteLine ($"Attempting to quit with {Application.QuitKey}");
  109. Application.RaiseKeyDownEvent (Application.QuitKey);
  110. }
  111. }
  112. }
  113. /// <summary>
  114. /// <para>This runs through all Scenarios defined in UI Catalog, calling Init, Setup, and Run and measuring the perf of each.</para>
  115. /// </summary>
  116. [Theory]
  117. [MemberData (nameof (AllScenarioTypes))]
  118. public void All_Scenarios_Benchmark (Type scenarioType)
  119. {
  120. Assert.Null (_timeoutLock);
  121. _timeoutLock = new ();
  122. // Disable any UIConfig settings
  123. ConfigLocations savedConfigLocations = ConfigurationManager.Locations;
  124. ConfigurationManager.Locations = ConfigLocations.Default;
  125. // If a previous test failed, this will ensure that the Application is in a clean state
  126. Application.ResetState (true);
  127. uint maxIterations = 1000;
  128. uint abortTime = 2000;
  129. object timeout = null;
  130. var initialized = false;
  131. var shutdown = false;
  132. int iterationCount = 0;
  133. int clearedContentCount = 0;
  134. int refreshedCount = 0;
  135. int updatedCount = 0;
  136. int drawCompleteCount = 0;
  137. int addedCount = 0;
  138. int laidOutCount = 0;
  139. _output.WriteLine ($"Running Scenario '{scenarioType}'");
  140. var scenario = (Scenario)Activator.CreateInstance (scenarioType);
  141. Stopwatch stopwatch = null;
  142. Application.InitializedChanged += OnApplicationOnInitializedChanged;
  143. Application.ForceDriver = "FakeDriver";
  144. scenario!.Main ();
  145. scenario.Dispose ();
  146. scenario = null;
  147. Application.ForceDriver = string.Empty;
  148. Application.InitializedChanged -= OnApplicationOnInitializedChanged;
  149. lock (_timeoutLock)
  150. {
  151. if (timeout is { })
  152. {
  153. timeout = null;
  154. }
  155. }
  156. lock (_timeoutLock)
  157. {
  158. _timeoutLock = null;
  159. }
  160. _output.WriteLine ($"Scenario {scenarioType}");
  161. _output.WriteLine ($" took {stopwatch.ElapsedMilliseconds} ms to run.");
  162. _output.WriteLine ($" called Driver.ClearContents {clearedContentCount} times.");
  163. _output.WriteLine ($" called Driver.Refresh {refreshedCount} times.");
  164. _output.WriteLine ($" which updated the screen {updatedCount} times.");
  165. _output.WriteLine ($" called View.Draw {drawCompleteCount} times.");
  166. _output.WriteLine ($" added {addedCount} views.");
  167. _output.WriteLine ($" called View.LayoutComplete {laidOutCount} times.");
  168. // Restore the configuration locations
  169. ConfigurationManager.Locations = savedConfigLocations;
  170. ConfigurationManager.Reset ();
  171. return;
  172. void OnApplicationOnInitializedChanged (object s, EventArgs<bool> a)
  173. {
  174. if (a.CurrentValue)
  175. {
  176. lock (_timeoutLock)
  177. {
  178. timeout = Application.AddTimeout (TimeSpan.FromMilliseconds (abortTime), ForceCloseCallback);
  179. }
  180. initialized = true;
  181. Application.Iteration += OnApplicationOnIteration;
  182. Application.Driver!.ClearedContents += (sender, args) => clearedContentCount++;
  183. if (Application.Driver is ConsoleDriver cd)
  184. {
  185. cd!.Refreshed += (sender, args) =>
  186. {
  187. refreshedCount++;
  188. if (args.CurrentValue)
  189. {
  190. updatedCount++;
  191. }
  192. };
  193. }
  194. Application.NotifyNewRunState += OnApplicationNotifyNewRunState;
  195. stopwatch = Stopwatch.StartNew ();
  196. }
  197. else
  198. {
  199. shutdown = true;
  200. Application.NotifyNewRunState -= OnApplicationNotifyNewRunState;
  201. Application.Iteration -= OnApplicationOnIteration;
  202. stopwatch!.Stop ();
  203. }
  204. _output.WriteLine ($"Initialized == {a.CurrentValue}");
  205. }
  206. void OnApplicationOnIteration (object s, IterationEventArgs a)
  207. {
  208. iterationCount++;
  209. if (iterationCount > maxIterations)
  210. {
  211. // Press QuitKey
  212. _output.WriteLine ($"Attempting to quit scenario with RequestStop");
  213. Application.RequestStop ();
  214. }
  215. }
  216. void OnApplicationNotifyNewRunState (object sender, RunStateEventArgs e)
  217. {
  218. // Get a list of all subviews under Application.Top (and their subviews, etc.)
  219. // and subscribe to their DrawComplete event
  220. void SubscribeAllSubviews (View view)
  221. {
  222. view.DrawComplete += (s, a) => drawCompleteCount++;
  223. view.SubviewsLaidOut += (s, a) => laidOutCount++;
  224. view.Added += (s, a) => addedCount++;
  225. foreach (View subview in view.Subviews)
  226. {
  227. SubscribeAllSubviews (subview);
  228. }
  229. }
  230. SubscribeAllSubviews (Application.Top);
  231. }
  232. // If the scenario doesn't close within the abort time, this will force it to quit
  233. bool ForceCloseCallback ()
  234. {
  235. lock (_timeoutLock)
  236. {
  237. if (timeout is { })
  238. {
  239. timeout = null;
  240. }
  241. }
  242. _output.WriteLine(
  243. $"'{scenario.GetName ()}' failed to Quit with {Application.QuitKey} after {abortTime}ms and {iterationCount} iterations. Force quit.");
  244. Application.RequestStop ();
  245. return false;
  246. }
  247. }
  248. public static IEnumerable<object []> AllScenarioTypes =>
  249. typeof (Scenario).Assembly
  250. .GetTypes ()
  251. .Where (type => type.IsClass && !type.IsAbstract && type.IsSubclassOf (typeof (Scenario)))
  252. .Select (type => new object [] { type });
  253. [Fact]
  254. public void Run_All_Views_Tester_Scenario ()
  255. {
  256. // Disable any UIConfig settings
  257. ConfigLocations savedConfigLocations = ConfigurationManager.Locations;
  258. ConfigurationManager.Locations = ConfigLocations.Default;
  259. Window _leftPane;
  260. ListView _classListView;
  261. FrameView _hostPane;
  262. Dictionary<string, Type> _viewClasses;
  263. View _curView = null;
  264. // Settings
  265. FrameView _settingsPane;
  266. FrameView _locationFrame;
  267. RadioGroup _xRadioGroup;
  268. TextField _xText;
  269. var _xVal = 0;
  270. RadioGroup _yRadioGroup;
  271. TextField _yText;
  272. var _yVal = 0;
  273. FrameView _sizeFrame;
  274. RadioGroup _wRadioGroup;
  275. TextField _wText;
  276. var _wVal = 0;
  277. RadioGroup _hRadioGroup;
  278. TextField _hText;
  279. var _hVal = 0;
  280. List<string> posNames = new () { "Percent", "AnchorEnd", "Center", "Absolute" };
  281. List<string> dimNames = new () { "Auto", "Percent", "Fill", "Absolute" };
  282. Application.Init (new FakeDriver ());
  283. var top = new Toplevel ();
  284. _viewClasses = TestHelpers.GetAllViewClasses ().ToDictionary (t => t.Name);
  285. _leftPane = new ()
  286. {
  287. Title = "Classes",
  288. X = 0,
  289. Y = 0,
  290. Width = 15,
  291. Height = Dim.Fill (1), // for status bar
  292. CanFocus = false,
  293. ColorScheme = Colors.ColorSchemes ["TopLevel"]
  294. };
  295. _classListView = new ()
  296. {
  297. X = 0,
  298. Y = 0,
  299. Width = Dim.Fill (),
  300. Height = Dim.Fill (),
  301. AllowsMarking = false,
  302. ColorScheme = Colors.ColorSchemes ["TopLevel"],
  303. Source = new ListWrapper<string> (new (_viewClasses.Keys.ToList ()))
  304. };
  305. _leftPane.Add (_classListView);
  306. _settingsPane = new ()
  307. {
  308. X = Pos.Right (_leftPane),
  309. Y = 0, // for menu
  310. Width = Dim.Fill (),
  311. Height = 10,
  312. CanFocus = false,
  313. ColorScheme = Colors.ColorSchemes ["TopLevel"],
  314. Title = "Settings"
  315. };
  316. var radioItems = new [] { "Percent(x)", "AnchorEnd(x)", "Center", "Absolute(x)" };
  317. _locationFrame = new ()
  318. {
  319. X = 0,
  320. Y = 0,
  321. Height = 3 + radioItems.Length,
  322. Width = 36,
  323. Title = "Location (Pos)"
  324. };
  325. _settingsPane.Add (_locationFrame);
  326. var label = new Label { X = 0, Y = 0, Text = "x:" };
  327. _locationFrame.Add (label);
  328. _xRadioGroup = new () { X = 0, Y = Pos.Bottom (label), RadioLabels = radioItems };
  329. _xText = new () { X = Pos.Right (label) + 1, Y = 0, Width = 4, Text = $"{_xVal}" };
  330. _locationFrame.Add (_xText);
  331. _locationFrame.Add (_xRadioGroup);
  332. radioItems = new [] { "Percent(y)", "AnchorEnd(y)", "Center", "Absolute(y)" };
  333. label = new () { X = Pos.Right (_xRadioGroup) + 1, Y = 0, Text = "y:" };
  334. _locationFrame.Add (label);
  335. _yText = new () { X = Pos.Right (label) + 1, Y = 0, Width = 4, Text = $"{_yVal}" };
  336. _locationFrame.Add (_yText);
  337. _yRadioGroup = new () { X = Pos.X (label), Y = Pos.Bottom (label), RadioLabels = radioItems };
  338. _locationFrame.Add (_yRadioGroup);
  339. _sizeFrame = new ()
  340. {
  341. X = Pos.Right (_locationFrame),
  342. Y = Pos.Y (_locationFrame),
  343. Height = 3 + radioItems.Length,
  344. Width = 40,
  345. Title = "Size (Dim)"
  346. };
  347. radioItems = new [] { "Auto()", "Percent(width)", "Fill(width)", "Absolute(width)" };
  348. label = new () { X = 0, Y = 0, Text = "width:" };
  349. _sizeFrame.Add (label);
  350. _wRadioGroup = new () { X = 0, Y = Pos.Bottom (label), RadioLabels = radioItems };
  351. _wText = new () { X = Pos.Right (label) + 1, Y = 0, Width = 4, Text = $"{_wVal}" };
  352. _sizeFrame.Add (_wText);
  353. _sizeFrame.Add (_wRadioGroup);
  354. radioItems = new [] { "Auto()", "Percent(height)", "Fill(height)", "Absolute(height)" };
  355. label = new () { X = Pos.Right (_wRadioGroup) + 1, Y = 0, Text = "height:" };
  356. _sizeFrame.Add (label);
  357. _hText = new () { X = Pos.Right (label) + 1, Y = 0, Width = 4, Text = $"{_hVal}" };
  358. _sizeFrame.Add (_hText);
  359. _hRadioGroup = new () { X = Pos.X (label), Y = Pos.Bottom (label), RadioLabels = radioItems };
  360. _sizeFrame.Add (_hRadioGroup);
  361. _settingsPane.Add (_sizeFrame);
  362. _hostPane = new ()
  363. {
  364. X = Pos.Right (_leftPane),
  365. Y = Pos.Bottom (_settingsPane),
  366. Width = Dim.Fill (),
  367. Height = Dim.Fill (1), // + 1 for status bar
  368. ColorScheme = Colors.ColorSchemes ["Dialog"]
  369. };
  370. _classListView.OpenSelectedItem += (s, a) => { _settingsPane.SetFocus (); };
  371. _classListView.SelectedItemChanged += (s, args) =>
  372. {
  373. // Remove existing class, if any
  374. if (_curView != null)
  375. {
  376. _curView.SubviewsLaidOut -= LayoutCompleteHandler;
  377. _hostPane.Remove (_curView);
  378. _curView.Dispose ();
  379. _curView = null;
  380. _hostPane.FillRect (_hostPane.Viewport);
  381. }
  382. _curView = CreateClass (_viewClasses.Values.ToArray () [_classListView.SelectedItem]);
  383. };
  384. _xRadioGroup.SelectedItemChanged += (s, selected) => DimPosChanged (_curView);
  385. _xText.TextChanged += (s, args) =>
  386. {
  387. try
  388. {
  389. _xVal = int.Parse (_xText.Text);
  390. DimPosChanged (_curView);
  391. }
  392. catch
  393. { }
  394. };
  395. _yText.TextChanged += (s, e) =>
  396. {
  397. try
  398. {
  399. _yVal = int.Parse (_yText.Text);
  400. DimPosChanged (_curView);
  401. }
  402. catch
  403. { }
  404. };
  405. _yRadioGroup.SelectedItemChanged += (s, selected) => DimPosChanged (_curView);
  406. _wRadioGroup.SelectedItemChanged += (s, selected) => DimPosChanged (_curView);
  407. _wText.TextChanged += (s, args) =>
  408. {
  409. try
  410. {
  411. _wVal = int.Parse (_wText.Text);
  412. DimPosChanged (_curView);
  413. }
  414. catch
  415. { }
  416. };
  417. _hText.TextChanged += (s, args) =>
  418. {
  419. try
  420. {
  421. _hVal = int.Parse (_hText.Text);
  422. DimPosChanged (_curView);
  423. }
  424. catch
  425. { }
  426. };
  427. _hRadioGroup.SelectedItemChanged += (s, selected) => DimPosChanged (_curView);
  428. top.Add (_leftPane, _settingsPane, _hostPane);
  429. top.LayoutSubviews ();
  430. _curView = CreateClass (_viewClasses.First ().Value);
  431. var iterations = 0;
  432. Application.Iteration += (s, a) =>
  433. {
  434. iterations++;
  435. if (iterations < _viewClasses.Count)
  436. {
  437. _classListView.MoveDown ();
  438. Assert.Equal (
  439. _curView.GetType ().Name,
  440. _viewClasses.Values.ToArray () [_classListView.SelectedItem].Name
  441. );
  442. }
  443. else
  444. {
  445. Application.RequestStop ();
  446. }
  447. };
  448. Application.Run (top);
  449. Assert.Equal (_viewClasses.Count, iterations);
  450. top.Dispose ();
  451. Application.Shutdown ();
  452. // Restore the configuration locations
  453. ConfigurationManager.Locations = savedConfigLocations;
  454. ConfigurationManager.Reset ();
  455. void DimPosChanged (View view)
  456. {
  457. if (view == null)
  458. {
  459. return;
  460. }
  461. try
  462. {
  463. switch (_xRadioGroup.SelectedItem)
  464. {
  465. case 0:
  466. view.X = Pos.Percent (_xVal);
  467. break;
  468. case 1:
  469. view.X = Pos.AnchorEnd (_xVal);
  470. break;
  471. case 2:
  472. view.X = Pos.Center ();
  473. break;
  474. case 3:
  475. view.X = Pos.Absolute (_xVal);
  476. break;
  477. }
  478. switch (_yRadioGroup.SelectedItem)
  479. {
  480. case 0:
  481. view.Y = Pos.Percent (_yVal);
  482. break;
  483. case 1:
  484. view.Y = Pos.AnchorEnd (_yVal);
  485. break;
  486. case 2:
  487. view.Y = Pos.Center ();
  488. break;
  489. case 3:
  490. view.Y = Pos.Absolute (_yVal);
  491. break;
  492. }
  493. switch (_wRadioGroup.SelectedItem)
  494. {
  495. case 0:
  496. view.Width = Dim.Percent (_wVal);
  497. break;
  498. case 1:
  499. view.Width = Dim.Fill (_wVal);
  500. break;
  501. case 2:
  502. view.Width = Dim.Absolute (_wVal);
  503. break;
  504. }
  505. switch (_hRadioGroup.SelectedItem)
  506. {
  507. case 0:
  508. view.Height = Dim.Percent (_hVal);
  509. break;
  510. case 1:
  511. view.Height = Dim.Fill (_hVal);
  512. break;
  513. case 2:
  514. view.Height = Dim.Absolute (_hVal);
  515. break;
  516. }
  517. }
  518. catch (Exception e)
  519. {
  520. MessageBox.ErrorQuery ("Exception", e.Message, "Ok");
  521. }
  522. UpdateTitle (view);
  523. }
  524. void UpdateSettings (View view)
  525. {
  526. var x = view.X.ToString ();
  527. var y = view.Y.ToString ();
  528. try
  529. {
  530. _xRadioGroup.SelectedItem = posNames.IndexOf (posNames.First (s => x.Contains (s)));
  531. _yRadioGroup.SelectedItem = posNames.IndexOf (posNames.First (s => y.Contains (s)));
  532. }
  533. catch (InvalidOperationException e)
  534. {
  535. // This is a hack to work around the fact that the Pos enum doesn't have an "Align" value yet
  536. Debug.WriteLine ($"{e}");
  537. }
  538. _xText.Text = $"{view.Frame.X}";
  539. _yText.Text = $"{view.Frame.Y}";
  540. var w = view.Width.ToString ();
  541. var h = view.Height.ToString ();
  542. _wRadioGroup.SelectedItem = dimNames.IndexOf (dimNames.First (s => w.Contains (s)));
  543. _hRadioGroup.SelectedItem = dimNames.IndexOf (dimNames.First (s => h.Contains (s)));
  544. _wText.Text = $"{view.Frame.Width}";
  545. _hText.Text = $"{view.Frame.Height}";
  546. }
  547. void UpdateTitle (View view) { _hostPane.Title = $"{view.GetType ().Name} - {view.X}, {view.Y}, {view.Width}, {view.Height}"; }
  548. View CreateClass (Type type)
  549. {
  550. // If we are to create a generic Type
  551. if (type.IsGenericType)
  552. {
  553. // For each of the <T> arguments
  554. List<Type> typeArguments = new ();
  555. // use <object>
  556. foreach (Type arg in type.GetGenericArguments ())
  557. {
  558. typeArguments.Add (typeof (object));
  559. }
  560. // And change what type we are instantiating from MyClass<T> to MyClass<object>
  561. type = type.MakeGenericType (typeArguments.ToArray ());
  562. }
  563. // Instantiate view
  564. var view = (View)Activator.CreateInstance (type);
  565. if (view is null)
  566. {
  567. return null;
  568. }
  569. if (view.Width is not DimAuto)
  570. {
  571. view.Width = Dim.Percent (75);
  572. }
  573. if (view.Height is not DimAuto)
  574. {
  575. view.Height = Dim.Percent (75);
  576. }
  577. // Set the colorscheme to make it stand out if is null by default
  578. view.ColorScheme ??= Colors.ColorSchemes ["Base"];
  579. // If the view supports a Text property, set it so we have something to look at
  580. if (view.GetType ().GetProperty ("Text") != null)
  581. {
  582. try
  583. {
  584. view.GetType ().GetProperty ("Text")?.GetSetMethod ()?.Invoke (view, new [] { "Test Text" });
  585. }
  586. catch (TargetInvocationException e)
  587. {
  588. MessageBox.ErrorQuery ("Exception", e.InnerException.Message, "Ok");
  589. view = null;
  590. }
  591. }
  592. // If the view supports a Title property, set it so we have something to look at
  593. if (view != null && view.GetType ().GetProperty ("Title") != null)
  594. {
  595. if (view.GetType ().GetProperty ("Title").PropertyType == typeof (string))
  596. {
  597. view?.GetType ().GetProperty ("Title")?.GetSetMethod ()?.Invoke (view, new [] { "Test Title" });
  598. }
  599. else
  600. {
  601. view?.GetType ().GetProperty ("Title")?.GetSetMethod ()?.Invoke (view, new [] { "Test Title" });
  602. }
  603. }
  604. // If the view supports a Source property, set it so we have something to look at
  605. if (view != null
  606. && view.GetType ().GetProperty ("Source") != null
  607. && view.GetType ().GetProperty ("Source").PropertyType == typeof (IListDataSource))
  608. {
  609. ListWrapper<string> source = new (["Test Text #1", "Test Text #2", "Test Text #3"]);
  610. view?.GetType ().GetProperty ("Source")?.GetSetMethod ()?.Invoke (view, new [] { source });
  611. }
  612. // Add
  613. _hostPane.Add (view);
  614. //DimPosChanged ();
  615. _hostPane.LayoutSubviews ();
  616. _hostPane.ClearViewport ();
  617. _hostPane.SetNeedsDraw ();
  618. UpdateSettings (view);
  619. UpdateTitle (view);
  620. view.SubviewsLaidOut += LayoutCompleteHandler;
  621. return view;
  622. }
  623. void LayoutCompleteHandler (object sender, LayoutEventArgs args) { UpdateTitle (_curView); }
  624. }
  625. [Fact]
  626. public void Run_Generic ()
  627. {
  628. // Disable any UIConfig settings
  629. ConfigLocations savedConfigLocations = ConfigurationManager.Locations;
  630. ConfigurationManager.Locations = ConfigLocations.Default;
  631. ObservableCollection<Scenario> scenarios = Scenario.GetScenarios ();
  632. Assert.NotEmpty (scenarios);
  633. int item = scenarios.IndexOf (s => s.GetName ().Equals ("Generic", StringComparison.OrdinalIgnoreCase));
  634. Scenario generic = scenarios [item];
  635. Application.Init (new FakeDriver ());
  636. // BUGBUG: (#2474) For some reason ReadKey is not returning the QuitKey for some Scenarios
  637. // by adding this Space it seems to work.
  638. FakeConsole.PushMockKeyPress ((KeyCode)Application.QuitKey);
  639. var ms = 100;
  640. var abortCount = 0;
  641. Func<bool> abortCallback = () =>
  642. {
  643. abortCount++;
  644. _output.WriteLine ($"'Generic' abortCount {abortCount}");
  645. Application.RequestStop ();
  646. return false;
  647. };
  648. var iterations = 0;
  649. object token = null;
  650. Application.Iteration += (s, a) =>
  651. {
  652. if (token == null)
  653. {
  654. // Timeout only must start at first iteration
  655. token = Application.AddTimeout (TimeSpan.FromMilliseconds (ms), abortCallback);
  656. }
  657. iterations++;
  658. _output.WriteLine ($"'Generic' iteration {iterations}");
  659. // Stop if we run out of control...
  660. if (iterations == 10)
  661. {
  662. _output.WriteLine ("'Generic' had to be force quit!");
  663. Application.RequestStop ();
  664. }
  665. };
  666. Application.KeyDown += (sender, args) =>
  667. {
  668. Assert.Equal (Application.QuitKey, args.KeyCode);
  669. };
  670. generic.Main ();
  671. Assert.Equal (0, abortCount);
  672. // # of key up events should match # of iterations
  673. Assert.Equal (1, iterations);
  674. generic.Dispose ();
  675. // Shutdown must be called to safely clean up Application if Init has been called
  676. Application.Shutdown ();
  677. // Restore the configuration locations
  678. ConfigurationManager.Locations = savedConfigLocations;
  679. ConfigurationManager.Reset ();
  680. #if DEBUG_IDISPOSABLE
  681. Assert.Empty (View.Instances);
  682. #endif
  683. }
  684. private int CreateInput (string input)
  685. {
  686. FakeConsole.MockKeyPresses.Clear ();
  687. // Put a QuitKey in at the end
  688. FakeConsole.PushMockKeyPress ((KeyCode)Application.QuitKey);
  689. foreach (char c in input.Reverse ())
  690. {
  691. var key = KeyCode.Null;
  692. if (char.IsLetter (c))
  693. {
  694. key = (KeyCode)char.ToUpper (c) | (char.IsUpper (c) ? KeyCode.ShiftMask : 0);
  695. }
  696. else
  697. {
  698. key = (KeyCode)c;
  699. }
  700. FakeConsole.PushMockKeyPress (key);
  701. }
  702. return FakeConsole.MockKeyPresses.Count;
  703. }
  704. }