ScenariosStressTests.cs 5.6 KB

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