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