123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193 |
- using System.Diagnostics;
- using Terminal.Gui;
- using UICatalog;
- using UnitTests;
- using Xunit.Abstractions;
- namespace StressTests;
- public class ScenariosStressTests : TestsAllViews
- {
- public ScenariosStressTests (ITestOutputHelper output)
- {
- #if DEBUG_IDISPOSABLE
- View.DebugIDisposable = true;
- View.Instances.Clear ();
- #endif
- _output = output;
- }
- private readonly ITestOutputHelper _output;
- private object? _timeoutLock;
- /// <summary>
- /// <para>
- /// This runs through all Scenarios defined in UI Catalog, calling Init, Setup, and Run and measuring the perf of
- /// each.
- /// </para>
- /// </summary>
- [Theory]
- [MemberData (nameof (AllScenarioTypes))]
- public void All_Scenarios_Benchmark (Type scenarioType)
- {
- Assert.Null (_timeoutLock);
- _timeoutLock = new ();
- // Disable any UIConfig settings
- ConfigLocations savedConfigLocations = ConfigurationManager.Locations;
- ConfigurationManager.Locations = ConfigLocations.Default;
- // If a previous test failed, this will ensure that the Application is in a clean state
- Application.ResetState (true);
- uint maxIterations = 1000;
- uint abortTime = 2000;
- object? timeout = null;
- var iterationCount = 0;
- var clearedContentCount = 0;
- var refreshedCount = 0;
- var updatedCount = 0;
- var drawCompleteCount = 0;
- var addedCount = 0;
- var laidOutCount = 0;
- _output.WriteLine ($"Running Scenario '{scenarioType}'");
- var scenario = (Scenario)Activator.CreateInstance (scenarioType)!;
- Stopwatch? stopwatch = null;
- Application.InitializedChanged += OnApplicationOnInitializedChanged;
- Application.ForceDriver = "FakeDriver";
- scenario!.Main ();
- scenario.Dispose ();
- scenario = null;
- Application.ForceDriver = string.Empty;
- Application.InitializedChanged -= OnApplicationOnInitializedChanged;
- lock (_timeoutLock)
- {
- if (timeout is { })
- {
- timeout = null;
- }
- }
- lock (_timeoutLock)
- {
- _timeoutLock = null;
- }
- _output.WriteLine ($"Scenario {scenarioType}");
- _output.WriteLine ($" took {stopwatch!.ElapsedMilliseconds} ms to run.");
- _output.WriteLine ($" called Driver.ClearContents {clearedContentCount} times.");
- _output.WriteLine ($" called Driver.Refresh {refreshedCount} times.");
- _output.WriteLine ($" which updated the screen {updatedCount} times.");
- _output.WriteLine ($" called View.Draw {drawCompleteCount} times.");
- _output.WriteLine ($" added {addedCount} views.");
- _output.WriteLine ($" called View.LayoutComplete {laidOutCount} times.");
- // Restore the configuration locations
- ConfigurationManager.Locations = savedConfigLocations;
- ConfigurationManager.Reset ();
- return;
- void OnApplicationOnInitializedChanged (object? s, EventArgs<bool> a)
- {
- if (a.CurrentValue)
- {
- lock (_timeoutLock)
- {
- timeout = Application.AddTimeout (TimeSpan.FromMilliseconds (abortTime), ForceCloseCallback);
- }
- Application.Iteration += OnApplicationOnIteration;
- Application.Driver!.ClearedContents += (sender, args) => clearedContentCount++;
- if (Application.Driver is ConsoleDriver cd)
- {
- cd!.Refreshed += (sender, args) =>
- {
- refreshedCount++;
- if (args.CurrentValue)
- {
- updatedCount++;
- }
- };
- }
- Application.NotifyNewRunState += OnApplicationNotifyNewRunState;
- stopwatch = Stopwatch.StartNew ();
- }
- else
- {
- Application.NotifyNewRunState -= OnApplicationNotifyNewRunState;
- Application.Iteration -= OnApplicationOnIteration;
- stopwatch!.Stop ();
- }
- _output.WriteLine ($"Initialized == {a.CurrentValue}");
- }
- void OnApplicationOnIteration (object? s, IterationEventArgs a)
- {
- iterationCount++;
- if (iterationCount > maxIterations)
- {
- // Press QuitKey
- _output.WriteLine ("Attempting to quit scenario with RequestStop");
- Application.RequestStop ();
- }
- }
- void OnApplicationNotifyNewRunState (object? sender, RunStateEventArgs e)
- {
- // Get a list of all subviews under Application.Top (and their subviews, etc.)
- // and subscribe to their DrawComplete event
- void SubscribeAllSubviews (View view)
- {
- view.DrawComplete += (s, a) => drawCompleteCount++;
- view.SubviewsLaidOut += (s, a) => laidOutCount++;
- view.Added += (s, a) => addedCount++;
- foreach (View subview in view.Subviews)
- {
- SubscribeAllSubviews (subview);
- }
- }
- SubscribeAllSubviews (Application.Top!);
- }
- // If the scenario doesn't close within the abort time, this will force it to quit
- bool ForceCloseCallback ()
- {
- lock (_timeoutLock)
- {
- if (timeout is { })
- {
- timeout = null;
- }
- }
- _output.WriteLine (
- $"'{scenario!.GetName ()}' failed to Quit with {Application.QuitKey} after {abortTime}ms and {iterationCount} iterations. Force quit.");
- Application.RequestStop ();
- return false;
- }
- }
- public static IEnumerable<object []> AllScenarioTypes =>
- typeof (Scenario).Assembly
- .GetTypes ()
- .Where (type => type.IsClass && !type.IsAbstract && type.IsSubclassOf (typeof (Scenario)))
- .Select (type => new object [] { type });
- }
|