using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Reflection;
using Xunit.Abstractions;
namespace UICatalog.Tests;
public class ScenarioTests : TestsAllViews
{
public ScenarioTests (ITestOutputHelper output)
{
#if DEBUG_IDISPOSABLE
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.
/// Should find any Scenarios which crash on load or do not respond to .
///
[Theory]
[MemberData (nameof (AllScenarioTypes))]
public void All_Scenarios_Quit_And_Init_Shutdown_Properly (Type scenarioType)
{
Assert.Null (_timeoutLock);
_timeoutLock = new ();
// Disable any UIConfig settings
ConfigurationManager.ConfigLocations savedConfigLocations = ConfigurationManager.Locations;
ConfigurationManager.Locations = ConfigurationManager.ConfigLocations.DefaultOnly;
// If a previous test failed, this will ensure that the Application is in a clean state
Application.ResetState (true);
_output.WriteLine ($"Running Scenario '{scenarioType}'");
var scenario = (Scenario)Activator.CreateInstance (scenarioType);
uint abortTime = 1500;
object timeout = null;
var initialized = false;
var shutdown = false;
int iterationCount = 0;
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;
}
}
Assert.True (initialized);
Assert.True (shutdown);
#if DEBUG_IDISPOSABLE
Assert.Empty (View.Instances);
#endif
lock (_timeoutLock)
{
_timeoutLock = null;
}
// Restore the configuration locations
ConfigurationManager.Locations = savedConfigLocations;
ConfigurationManager.Reset ();
return;
void OnApplicationOnInitializedChanged (object s, EventArgs a)
{
if (a.CurrentValue)
{
Application.Iteration += OnApplicationOnIteration;
initialized = true;
lock (_timeoutLock)
{
timeout = Application.AddTimeout (TimeSpan.FromMilliseconds (abortTime), ForceCloseCallback);
}
}
else
{
Application.Iteration -= OnApplicationOnIteration;
shutdown = true;
}
_output.WriteLine ($"Initialized == {a.CurrentValue}");
}
// If the scenario doesn't close within 500ms, this will force it to quit
bool ForceCloseCallback ()
{
lock (_timeoutLock)
{
if (timeout is { })
{
timeout = null;
}
}
Assert.Fail (
$"'{scenario.GetName ()}' failed to Quit with {Application.QuitKey} after {abortTime}ms and {iterationCount} iterations. Force quit.");
// Restore the configuration locations
ConfigurationManager.Locations = savedConfigLocations;
ConfigurationManager.Reset ();
Application.ResetState (true);
return false;
}
void OnApplicationOnIteration (object s, IterationEventArgs a)
{
iterationCount++;
if (Application.Initialized)
{
// Press QuitKey
_output.WriteLine ($"Attempting to quit with {Application.QuitKey}");
Application.RaiseKeyDownEvent (Application.QuitKey);
}
}
}
///
/// 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 ();
// Disable any UIConfig settings
ConfigurationManager.ConfigLocations savedConfigLocations = ConfigurationManager.Locations;
ConfigurationManager.Locations = ConfigurationManager.ConfigLocations.DefaultOnly;
// 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 initialized = false;
var shutdown = false;
int iterationCount = 0;
int clearedContentCount = 0;
int refreshedCount = 0;
int updatedCount = 0;
int drawCompleteCount = 0;
int addedCount = 0;
int 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 a)
{
if (a.CurrentValue)
{
lock (_timeoutLock)
{
timeout = Application.AddTimeout (TimeSpan.FromMilliseconds (abortTime), ForceCloseCallback);
}
initialized = true;
Application.Iteration += OnApplicationOnIteration;
Application.Driver!.ClearedContents += (sender, args) => clearedContentCount++;
Application.Driver!.Refreshed += (sender, args) =>
{
refreshedCount++;
if (args.CurrentValue)
{
updatedCount++;
}
};
Application.NotifyNewRunState += OnApplicationNotifyNewRunState;
stopwatch = Stopwatch.StartNew ();
}
else
{
shutdown = true;
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