using System.Diagnostics; using UICatalog; using Xunit.Abstractions; namespace StressTests; public class ScenariosStressTests { public ScenariosStressTests (ITestOutputHelper output) { #if DEBUG_IDISPOSABLE View.EnableDebugIDisposableAsserts = true; View.Instances.Clear (); #endif _output = output; } private readonly ITestOutputHelper _output; private object? _timeoutLock; /// /// /// This runs through all Scenarios defined in UI Catalog, calling Init, Setup, and Run and measuring the perf of /// each. /// /// [Theory] [MemberData (nameof (AllScenarioTypes))] public void All_Scenarios_Benchmark (Type scenarioType) { Assert.Null (_timeoutLock); _timeoutLock = new (); ConfigurationManager.Disable(true); // If a previous test failed, this will ensure that the Application is in a clean state Application.ResetState (true); uint maxIterations = 25; 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."); return; void OnApplicationOnInitializedChanged (object? s, EventArgs a) { if (a.Value) { lock (_timeoutLock) { timeout = Application.AddTimeout (TimeSpan.FromMilliseconds (abortTime), ForceCloseCallback); } Application.Iteration += OnApplicationOnIteration; Application.Driver!.ClearedContents += (sender, args) => clearedContentCount++; Application.SessionBegun += OnApplicationSessionBegun; stopwatch = Stopwatch.StartNew (); } else { stopwatch!.Stop (); } _output.WriteLine ($"Initialized == {a.Value}"); } void OnApplicationOnIteration (object? s, EventArgs a) { iterationCount++; if (iterationCount > maxIterations) { // Press QuitKey _output.WriteLine ("Attempting to quit scenario with RequestStop"); Application.Iteration -= OnApplicationOnIteration; Application.SessionBegun -= OnApplicationSessionBegun; Application.RequestStop (); } } void OnApplicationSessionBegun (object? sender, SessionTokenEventArgs e) { // Get a list of all subviews under Application.TopRunnable (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.SuperViewChanged += (s, a) => addedCount++; foreach (View subview in view.SubViews) { SubscribeAllSubViews (subview); } } SubscribeAllSubViews (Application.TopRunnableView!); } // 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.Iteration -= OnApplicationOnIteration; Application.SessionBegun -= OnApplicationSessionBegun; Application.RequestStop (); return false; } } public static IEnumerable AllScenarioTypes => typeof (Scenario).Assembly .GetTypes () .Where (type => type.IsClass && !type.IsAbstract && type.IsSubclassOf (typeof (Scenario))) .Select (type => new object [] { type }); }