ScenariosStressTests.cs 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. using System.Diagnostics;
  2. using Terminal.Gui;
  3. using UICatalog;
  4. using UnitTests;
  5. using Xunit.Abstractions;
  6. namespace StressTests;
  7. public class ScenariosStressTests : TestsAllViews
  8. {
  9. public ScenariosStressTests (ITestOutputHelper output)
  10. {
  11. #if DEBUG_IDISPOSABLE
  12. View.DebugIDisposable = true;
  13. View.Instances.Clear ();
  14. #endif
  15. _output = output;
  16. }
  17. private readonly ITestOutputHelper _output;
  18. private object? _timeoutLock;
  19. /// <summary>
  20. /// <para>
  21. /// This runs through all Scenarios defined in UI Catalog, calling Init, Setup, and Run and measuring the perf of
  22. /// each.
  23. /// </para>
  24. /// </summary>
  25. [Theory]
  26. [MemberData (nameof (AllScenarioTypes))]
  27. public void All_Scenarios_Benchmark (Type scenarioType)
  28. {
  29. Assert.Null (_timeoutLock);
  30. _timeoutLock = new ();
  31. // Disable any UIConfig settings
  32. ConfigLocations savedConfigLocations = ConfigurationManager.Locations;
  33. ConfigurationManager.Locations = ConfigLocations.Default;
  34. // If a previous test failed, this will ensure that the Application is in a clean state
  35. Application.ResetState (true);
  36. uint maxIterations = 1000;
  37. uint abortTime = 2000;
  38. object? timeout = null;
  39. var iterationCount = 0;
  40. var clearedContentCount = 0;
  41. var refreshedCount = 0;
  42. var updatedCount = 0;
  43. var drawCompleteCount = 0;
  44. var addedCount = 0;
  45. var laidOutCount = 0;
  46. _output.WriteLine ($"Running Scenario '{scenarioType}'");
  47. var scenario = (Scenario)Activator.CreateInstance (scenarioType)!;
  48. Stopwatch? stopwatch = null;
  49. Application.InitializedChanged += OnApplicationOnInitializedChanged;
  50. Application.ForceDriver = "FakeDriver";
  51. scenario!.Main ();
  52. scenario.Dispose ();
  53. scenario = null;
  54. Application.ForceDriver = string.Empty;
  55. Application.InitializedChanged -= OnApplicationOnInitializedChanged;
  56. lock (_timeoutLock)
  57. {
  58. if (timeout is { })
  59. {
  60. timeout = null;
  61. }
  62. }
  63. lock (_timeoutLock)
  64. {
  65. _timeoutLock = null;
  66. }
  67. _output.WriteLine ($"Scenario {scenarioType}");
  68. _output.WriteLine ($" took {stopwatch!.ElapsedMilliseconds} ms to run.");
  69. _output.WriteLine ($" called Driver.ClearContents {clearedContentCount} times.");
  70. _output.WriteLine ($" called Driver.Refresh {refreshedCount} times.");
  71. _output.WriteLine ($" which updated the screen {updatedCount} times.");
  72. _output.WriteLine ($" called View.Draw {drawCompleteCount} times.");
  73. _output.WriteLine ($" added {addedCount} views.");
  74. _output.WriteLine ($" called View.LayoutComplete {laidOutCount} times.");
  75. // Restore the configuration locations
  76. ConfigurationManager.Locations = savedConfigLocations;
  77. ConfigurationManager.Reset ();
  78. return;
  79. void OnApplicationOnInitializedChanged (object? s, EventArgs<bool> a)
  80. {
  81. if (a.CurrentValue)
  82. {
  83. lock (_timeoutLock)
  84. {
  85. timeout = Application.AddTimeout (TimeSpan.FromMilliseconds (abortTime), ForceCloseCallback);
  86. }
  87. Application.Iteration += OnApplicationOnIteration;
  88. Application.Driver!.ClearedContents += (sender, args) => clearedContentCount++;
  89. if (Application.Driver is ConsoleDriver cd)
  90. {
  91. cd!.Refreshed += (sender, args) =>
  92. {
  93. refreshedCount++;
  94. if (args.CurrentValue)
  95. {
  96. updatedCount++;
  97. }
  98. };
  99. }
  100. Application.NotifyNewRunState += OnApplicationNotifyNewRunState;
  101. stopwatch = Stopwatch.StartNew ();
  102. }
  103. else
  104. {
  105. Application.NotifyNewRunState -= OnApplicationNotifyNewRunState;
  106. Application.Iteration -= OnApplicationOnIteration;
  107. stopwatch!.Stop ();
  108. }
  109. _output.WriteLine ($"Initialized == {a.CurrentValue}");
  110. }
  111. void OnApplicationOnIteration (object? s, IterationEventArgs a)
  112. {
  113. iterationCount++;
  114. if (iterationCount > maxIterations)
  115. {
  116. // Press QuitKey
  117. _output.WriteLine ("Attempting to quit scenario with RequestStop");
  118. Application.RequestStop ();
  119. }
  120. }
  121. void OnApplicationNotifyNewRunState (object? sender, RunStateEventArgs e)
  122. {
  123. // Get a list of all subviews under Application.Top (and their subviews, etc.)
  124. // and subscribe to their DrawComplete event
  125. void SubscribeAllSubviews (View view)
  126. {
  127. view.DrawComplete += (s, a) => drawCompleteCount++;
  128. view.SubviewsLaidOut += (s, a) => laidOutCount++;
  129. view.Added += (s, a) => addedCount++;
  130. foreach (View subview in view.Subviews)
  131. {
  132. SubscribeAllSubviews (subview);
  133. }
  134. }
  135. SubscribeAllSubviews (Application.Top!);
  136. }
  137. // If the scenario doesn't close within the abort time, this will force it to quit
  138. bool ForceCloseCallback ()
  139. {
  140. lock (_timeoutLock)
  141. {
  142. if (timeout is { })
  143. {
  144. timeout = null;
  145. }
  146. }
  147. _output.WriteLine (
  148. $"'{scenario!.GetName ()}' failed to Quit with {Application.QuitKey} after {abortTime}ms and {iterationCount} iterations. Force quit.");
  149. Application.RequestStop ();
  150. return false;
  151. }
  152. }
  153. public static IEnumerable<object []> AllScenarioTypes =>
  154. typeof (Scenario).Assembly
  155. .GetTypes ()
  156. .Where (type => type.IsClass && !type.IsAbstract && type.IsSubclassOf (typeof (Scenario)))
  157. .Select (type => new object [] { type });
  158. }