#nullable enable
using Xunit.Abstractions;
namespace ApplicationTests;
///
/// Parallelizable tests for IApplication that don't require the main event loop.
/// Tests using the modern non-static IApplication API.
///
public class ApplicationTests (ITestOutputHelper output)
{
private readonly ITestOutputHelper _output = output;
[Fact]
public void Begin_Null_Runnable_Throws ()
{
IApplication app = Application.Create ();
app.Init ("fake");
// Test null Runnable
Assert.Throws (() => app.Begin (null!));
app.Dispose ();
}
[Fact]
public void Begin_Sets_Application_Top_To_Console_Size ()
{
IApplication app = Application.Create ();
app.Init ("fake");
Assert.Null (app.TopRunnableView);
app.Driver!.SetScreenSize (80, 25);
Runnable top = new ();
SessionToken? token = app.Begin (top);
Assert.Equal (new (0, 0, 80, 25), app.TopRunnableView!.Frame);
app.Driver!.SetScreenSize (5, 5);
app.LayoutAndDraw ();
Assert.Equal (new (0, 0, 5, 5), app.TopRunnableView!.Frame);
if (token is { })
{
app.End (token);
}
top.Dispose ();
app.Dispose ();
}
[Fact]
public void Init_Null_Driver_Should_Pick_A_Driver ()
{
IApplication app = Application.Create ();
app.Init ();
Assert.NotNull (app.Driver);
app.Dispose ();
}
[Fact]
public void Init_Dispose_Cleans_Up ()
{
IApplication app = Application.Create ();
app.Init ("fake");
app.Dispose ();
#if DEBUG_IDISPOSABLE
// Validate there are no outstanding Responder-based instances
// after cleanup
// Note: We can't check View.Instances in parallel tests as it's a static field
// that would be shared across parallel test runs
#endif
}
[Fact]
public void Init_Dispose_Fire_InitializedChanged ()
{
var initialized = false;
var Dispose = false;
IApplication app = Application.Create ();
app.InitializedChanged += OnApplicationOnInitializedChanged;
app.Init (driverName: "fake");
Assert.True (initialized);
Assert.False (Dispose);
app.Dispose ();
Assert.True (initialized);
Assert.True (Dispose);
app.InitializedChanged -= OnApplicationOnInitializedChanged;
return;
void OnApplicationOnInitializedChanged (object? s, EventArgs a)
{
if (a.Value)
{
initialized = true;
}
else
{
Dispose = true;
}
}
}
[Fact]
public void Init_KeyBindings_Are_Not_Reset ()
{
IApplication app = Application.Create ();
// Set via Keyboard property (modern API)
app.Keyboard.QuitKey = Key.Q;
Assert.Equal (Key.Q, app.Keyboard.QuitKey);
app.Init ("fake");
Assert.Equal (Key.Q, app.Keyboard.QuitKey);
app.Dispose ();
}
[Fact]
public void Init_NoParam_ForceDriver_Works ()
{
using IApplication app = Application.Create ();
app.ForceDriver = "fake";
// Note: Init() without params picks up driver configuration
app.Init ();
Assert.Equal ("fake", app.Driver!.GetName ());
}
[Fact]
public void Init_Dispose_Resets_Instance_Properties ()
{
IApplication app = Application.Create ();
// Init the app
app.Init (driverName: "fake");
// Verify initialized
Assert.True (app.Initialized);
Assert.NotNull (app.Driver);
// Dispose cleans up
app.Dispose ();
// Check reset state on the instance
CheckReset (app);
// Create a new instance and set values
app = Application.Create ();
app.Init ("fake");
app.StopAfterFirstIteration = true;
app.Keyboard.PrevTabGroupKey = Key.A;
app.Keyboard.NextTabGroupKey = Key.B;
app.Keyboard.QuitKey = Key.C;
app.Keyboard.KeyBindings.Add (Key.D, Command.Cancel);
app.Mouse.CachedViewsUnderMouse.Clear ();
app.Mouse.LastMousePosition = new Point (1, 1);
// Dispose and check reset
app.Dispose ();
CheckReset (app);
return;
void CheckReset (IApplication application)
{
// Check that all fields and properties are reset on the instance
// Public Properties
Assert.Null (application.TopRunnableView);
Assert.Null (application.Mouse.MouseGrabView);
Assert.Null (application.Driver);
Assert.False (application.StopAfterFirstIteration);
// Internal properties
Assert.False (application.Initialized);
Assert.Null (application.MainThreadId);
Assert.Empty (application.Mouse.CachedViewsUnderMouse);
}
}
[Fact]
public void Internal_Properties_Correct ()
{
IApplication app = Application.Create ();
app.Init ("fake");
Assert.True (app.Initialized);
Assert.Null (app.TopRunnableView);
SessionToken? rs = app.Begin (new Runnable ());
Assert.Equal (app.TopRunnable, rs!.Runnable);
Assert.Null (app.Mouse.MouseGrabView); // public
app.Dispose ();
}
[Fact]
public void Invoke_Adds_Idle ()
{
IApplication app = Application.Create ();
app.Init ("fake");
Runnable top = new ();
SessionToken? rs = app.Begin (top);
var actionCalled = 0;
app.Invoke ((_) => { actionCalled++; });
app.TimedEvents!.RunTimers ();
Assert.Equal (1, actionCalled);
top.Dispose ();
app.Dispose ();
}
[Fact]
public void Run_Iteration_Fires ()
{
var iteration = 0;
IApplication app = Application.Create ();
app.Init ("fake");
app.Iteration += Application_Iteration;
app.Run ();
app.Iteration -= Application_Iteration;
Assert.Equal (1, iteration);
app.Dispose ();
return;
void Application_Iteration (object? sender, EventArgs e)
{
//Assert.Equal (0, iteration);
iteration++;
app.RequestStop ();
}
}
[Fact]
public void Screen_Size_Changes ()
{
IApplication app = Application.Create ();
app.Init ("fake");
IDriver? driver = app.Driver;
app.Driver!.SetScreenSize (80, 25);
Assert.Equal (new (0, 0, 80, 25), driver!.Screen);
Assert.Equal (new (0, 0, 80, 25), app.Screen);
// TODO: Should not be possible to manually change these at whim!
driver.Cols = 100;
driver.Rows = 30;
app.Driver!.SetScreenSize (100, 30);
Assert.Equal (new (0, 0, 100, 30), driver.Screen);
app.Screen = new (0, 0, driver.Cols, driver.Rows);
Assert.Equal (new (0, 0, 100, 30), driver.Screen);
app.Dispose ();
}
[Fact]
public void Dispose_Alone_Does_Nothing ()
{
IApplication app = Application.Create ();
app.Dispose ();
}
#region RunTests
[Fact]
public void Run_T_After_InitWithDriver_with_Runnable_and_Driver_Does_Not_Throw ()
{
IApplication app = Application.Create ();
app.StopAfterFirstIteration = true;
// Run> when already initialized or not with a Driver will not throw (because Window is derived from Runnable)
// Using another type not derived from Runnable will throws at compile time
app.Run (null, "fake");
// Run> when already initialized or not with a Driver will not throw (because Dialog is derived from Runnable)
app.Run