ScenariosStressTests.cs 6.0 KB

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