mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-26 15:57:56 +01:00
* Fixed MouseGrabView bug. Added extensive test coverage for `Keyboard`, `Mouse`, `Timeout`, and `Popover` functionalities, including edge cases and concurrent access. Introduced parameterized and data-driven tests to reduce redundancy and improve clarity. Refactored codebase for modularity and maintainability, introducing new namespaces and reorganizing classes. Enhanced `MouseImpl`, `KeyboardImpl`, and `Runnable` implementations with improved event handling, thread safety, and support for the Terminal.Gui Cancellable Work Pattern (CWP). Removed deprecated code and legacy tests, such as `LogarithmicTimeout` and `SmoothAcceleratingTimeout`. Fixed bugs related to mouse grabbing during drag operations and unbalanced `ApplicationImpl.Begin/End` calls. Improved documentation and code readability with modern C# features. * Code cleanup. * Update Tests/UnitTestsParallelizable/Application/Runnable/RunnableIntegrationTests.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Improve null handling and simplify test setup In `MouseImpl.cs`, added an early `return` after the `UngrabMouse()` call within the `if (view is null)` block to prevent further execution when `view` is `null`, improving null reference handling. In `RunnableIntegrationTests.cs`, removed the initialization of the `IApplication` object (`app`) from the `MultipleRunnables_IndependentResults` test method, simplifying the test setup and focusing on runnable behavior. * Code cleanup * API doc link cleanup --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace ApplicationTests;
|
||||
namespace ApplicationTests.Navigation;
|
||||
|
||||
public class ApplicationNavigationTests (ITestOutputHelper output)
|
||||
{
|
||||
|
||||
@@ -5,6 +5,66 @@ namespace ApplicationTests;
|
||||
|
||||
public class ApplicationImplTests
|
||||
{
|
||||
|
||||
[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<bool> ());
|
||||
Assert.Equal (app.TopRunnable, rs!.Runnable);
|
||||
Assert.Null (app.Mouse.MouseGrabView); // public
|
||||
|
||||
app.Dispose ();
|
||||
}
|
||||
|
||||
|
||||
#region DisposeTests
|
||||
|
||||
[Fact]
|
||||
public async Task Dispose_Allows_Async ()
|
||||
{
|
||||
var isCompletedSuccessfully = false;
|
||||
|
||||
async Task TaskWithAsyncContinuation ()
|
||||
{
|
||||
await Task.Yield ();
|
||||
await Task.Yield ();
|
||||
|
||||
isCompletedSuccessfully = true;
|
||||
}
|
||||
|
||||
IApplication app = Application.Create ();
|
||||
app.Dispose ();
|
||||
|
||||
Assert.False (isCompletedSuccessfully);
|
||||
await TaskWithAsyncContinuation ();
|
||||
Thread.Sleep (100);
|
||||
Assert.True (isCompletedSuccessfully);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Dispose_Resets_SyncContext ()
|
||||
{
|
||||
IApplication app = Application.Create ();
|
||||
app.Dispose ();
|
||||
Assert.Null (SynchronizationContext.Current);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Dispose_Alone_Does_Nothing ()
|
||||
{
|
||||
IApplication app = Application.Create ();
|
||||
app.Dispose ();
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Crates a new ApplicationImpl instance for testing. The input, output, and size monitor components are mocked.
|
||||
/// </summary>
|
||||
@@ -44,21 +104,6 @@ public class ApplicationImplTests
|
||||
.Verifiable (Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Init_CreatesKeybindings ()
|
||||
{
|
||||
IApplication app = NewMockedApplicationImpl ();
|
||||
|
||||
app.Keyboard.KeyBindings.Clear ();
|
||||
|
||||
Assert.Empty (app.Keyboard.KeyBindings.GetBindings ());
|
||||
|
||||
app.Init ("fake");
|
||||
|
||||
Assert.NotEmpty (app.Keyboard.KeyBindings.GetBindings ());
|
||||
|
||||
app.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NoInitThrowOnRun ()
|
||||
@@ -480,81 +525,4 @@ public class ApplicationImplTests
|
||||
Assert.Null (v2.TopRunnableView);
|
||||
Assert.Empty (v2.SessionStack!);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Init_Begin_End_Cleans_Up ()
|
||||
{
|
||||
IApplication? app = Application.Create ();
|
||||
|
||||
SessionToken? newSessionToken = null;
|
||||
|
||||
EventHandler<SessionTokenEventArgs> newSessionTokenFn = (s, e) =>
|
||||
{
|
||||
Assert.NotNull (e.State);
|
||||
newSessionToken = e.State;
|
||||
};
|
||||
app.SessionBegun += newSessionTokenFn;
|
||||
|
||||
Runnable<bool> runnable = new ();
|
||||
SessionToken sessionToken = app.Begin (runnable)!;
|
||||
Assert.NotNull (sessionToken);
|
||||
Assert.NotNull (newSessionToken);
|
||||
Assert.Equal (sessionToken, newSessionToken);
|
||||
|
||||
// Assert.Equal (runnable, Application.TopRunnable);
|
||||
|
||||
app.SessionBegun -= newSessionTokenFn;
|
||||
app.End (newSessionToken);
|
||||
|
||||
Assert.Null (app.TopRunnable);
|
||||
Assert.Null (app.Driver);
|
||||
|
||||
runnable.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Run_RequestStop_Stops ()
|
||||
{
|
||||
IApplication? app = Application.Create ();
|
||||
app.Init ("fake");
|
||||
|
||||
var top = new Runnable ();
|
||||
SessionToken? sessionToken = app.Begin (top);
|
||||
Assert.NotNull (sessionToken);
|
||||
|
||||
app.Iteration += OnApplicationOnIteration;
|
||||
app.Run (top);
|
||||
app.Iteration -= OnApplicationOnIteration;
|
||||
|
||||
top.Dispose ();
|
||||
|
||||
return;
|
||||
|
||||
void OnApplicationOnIteration (object? s, EventArgs<IApplication?> a) { app.RequestStop (); }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Run_T_Init_Driver_Cleared_with_Runnable_Throws ()
|
||||
{
|
||||
IApplication? app = Application.Create ();
|
||||
|
||||
app.Init ("fake");
|
||||
app.Driver = null;
|
||||
|
||||
app.StopAfterFirstIteration = true;
|
||||
|
||||
// Init has been called, but Driver has been set to null. Bad.
|
||||
Assert.Throws<InvalidOperationException> (() => app.Run<Runnable> ());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Init_Unbalanced_Throws ()
|
||||
{
|
||||
IApplication? app = Application.Create ();
|
||||
app.Init ("fake");
|
||||
|
||||
Assert.Throws<InvalidOperationException> (() =>
|
||||
app.Init ("fake")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,510 +0,0 @@
|
||||
#nullable enable
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace ApplicationTests;
|
||||
|
||||
/// <summary>
|
||||
/// Parallelizable tests for IApplication that don't require the main event loop.
|
||||
/// Tests using the modern non-static IApplication API.
|
||||
/// </summary>
|
||||
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<ArgumentNullException> (() => 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<bool> 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<bool> ());
|
||||
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<Runnable> ();
|
||||
app.Iteration -= Application_Iteration;
|
||||
|
||||
Assert.Equal (1, iteration);
|
||||
app.Dispose ();
|
||||
|
||||
return;
|
||||
|
||||
void Application_Iteration (object? sender, EventArgs<IApplication?> e)
|
||||
{
|
||||
|
||||
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<Runnable<bool>> 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<Window> (null, "fake");
|
||||
|
||||
// Run<Runnable<bool>> when already initialized or not with a Driver will not throw (because Dialog is derived from Runnable)
|
||||
app.Run<Dialog> (null, "fake");
|
||||
|
||||
app.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Run_T_After_Init_Does_Not_Disposes_Application_Top ()
|
||||
{
|
||||
IApplication app = Application.Create ();
|
||||
app.Init ("fake");
|
||||
|
||||
// Init doesn't create a Runnable and assigned it to app.TopRunnable
|
||||
// but Begin does
|
||||
var initTop = new Runnable ();
|
||||
|
||||
app.Iteration += OnApplicationOnIteration;
|
||||
|
||||
app.Run<Runnable> ();
|
||||
app.Iteration -= OnApplicationOnIteration;
|
||||
|
||||
#if DEBUG_IDISPOSABLE
|
||||
Assert.False (initTop.WasDisposed);
|
||||
initTop.Dispose ();
|
||||
Assert.True (initTop.WasDisposed);
|
||||
#endif
|
||||
initTop.Dispose ();
|
||||
|
||||
app.Dispose ();
|
||||
|
||||
return;
|
||||
|
||||
void OnApplicationOnIteration (object? s, EventArgs<IApplication?> a)
|
||||
{
|
||||
Assert.NotEqual (initTop, app.TopRunnableView);
|
||||
#if DEBUG_IDISPOSABLE
|
||||
Assert.False (initTop.WasDisposed);
|
||||
#endif
|
||||
app.RequestStop ();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Run_T_After_InitWithDriver_with_TestRunnable_DoesNotThrow ()
|
||||
{
|
||||
IApplication app = Application.Create ();
|
||||
app.Init ("fake");
|
||||
app.StopAfterFirstIteration = true;
|
||||
|
||||
// Init has been called and we're passing no driver to Run<TestRunnable>. This is ok.
|
||||
app.Run<Window> ();
|
||||
|
||||
app.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Run_T_After_InitNullDriver_with_TestRunnable_DoesNotThrow ()
|
||||
{
|
||||
IApplication app = Application.Create ();
|
||||
app.Init ("fake");
|
||||
app.StopAfterFirstIteration = true;
|
||||
|
||||
// Init has been called, selecting FakeDriver; we're passing no driver to Run<TestRunnable>. Should be fine.
|
||||
app.Run<Window> ();
|
||||
|
||||
app.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Run_T_NoInit_DoesNotThrow ()
|
||||
{
|
||||
IApplication app = Application.Create ();
|
||||
app.StopAfterFirstIteration = true;
|
||||
|
||||
app.Run<Window> ();
|
||||
|
||||
app.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Run_T_NoInit_WithDriver_DoesNotThrow ()
|
||||
{
|
||||
IApplication app = Application.Create ();
|
||||
app.StopAfterFirstIteration = true;
|
||||
|
||||
// Init has NOT been called and we're passing a valid driver to Run<TestRunnable>. This is ok.
|
||||
app.Run<Runnable> (null, "fake");
|
||||
|
||||
app.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Run_Sets_Running_True ()
|
||||
{
|
||||
IApplication app = Application.Create ();
|
||||
app.Init ("fake");
|
||||
|
||||
var top = new Runnable ();
|
||||
SessionToken? rs = app.Begin (top);
|
||||
Assert.NotNull (rs);
|
||||
|
||||
app.Iteration += OnApplicationOnIteration;
|
||||
app.Run (top);
|
||||
app.Iteration -= OnApplicationOnIteration;
|
||||
|
||||
top.Dispose ();
|
||||
|
||||
app.Dispose ();
|
||||
|
||||
return;
|
||||
|
||||
void OnApplicationOnIteration (object? s, EventArgs<IApplication?> a)
|
||||
{
|
||||
Assert.True (top.IsRunning);
|
||||
top.RequestStop ();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Run_A_Modal_Runnable_Refresh_Background_On_Moving ()
|
||||
{
|
||||
IApplication app = Application.Create ();
|
||||
app.Init ("fake");
|
||||
|
||||
// Don't use Dialog here as it has more layout logic. Use Window instead.
|
||||
var w = new Window
|
||||
{
|
||||
Width = 5, Height = 5,
|
||||
Arrangement = ViewArrangement.Movable
|
||||
};
|
||||
app.Driver!.SetScreenSize (10, 10);
|
||||
SessionToken? rs = app.Begin (w);
|
||||
|
||||
// Don't use visuals to test as style of border can change over time.
|
||||
Assert.Equal (new (0, 0), w.Frame.Location);
|
||||
|
||||
app.Mouse.RaiseMouseEvent (new () { Flags = MouseFlags.Button1Pressed });
|
||||
Assert.Equal (w.Border, app.Mouse.MouseGrabView);
|
||||
Assert.Equal (new (0, 0), w.Frame.Location);
|
||||
|
||||
// Move down and to the right.
|
||||
app.Mouse.RaiseMouseEvent (new () { ScreenPosition = new (1, 1), Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition });
|
||||
Assert.Equal (new (1, 1), w.Frame.Location);
|
||||
|
||||
app.End (rs!);
|
||||
w.Dispose ();
|
||||
|
||||
app.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Run_T_Creates_Top_Without_Init ()
|
||||
{
|
||||
IApplication app = Application.Create ();
|
||||
app.StopAfterFirstIteration = true;
|
||||
|
||||
app.SessionEnded += OnApplicationOnSessionEnded;
|
||||
|
||||
app.Run<Window> (null, "fake");
|
||||
|
||||
Assert.Null (app.TopRunnableView);
|
||||
|
||||
app.Dispose ();
|
||||
Assert.Null (app.TopRunnableView);
|
||||
|
||||
return;
|
||||
|
||||
void OnApplicationOnSessionEnded (object? sender, SessionTokenEventArgs e)
|
||||
{
|
||||
app.SessionEnded -= OnApplicationOnSessionEnded;
|
||||
e.State.Result = (e.State.Runnable as IRunnable<object?>)?.Result;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region DisposeTests
|
||||
|
||||
[Fact]
|
||||
public async Task Dispose_Allows_Async ()
|
||||
{
|
||||
var isCompletedSuccessfully = false;
|
||||
|
||||
async Task TaskWithAsyncContinuation ()
|
||||
{
|
||||
await Task.Yield ();
|
||||
await Task.Yield ();
|
||||
|
||||
isCompletedSuccessfully = true;
|
||||
}
|
||||
|
||||
IApplication app = Application.Create ();
|
||||
app.Dispose ();
|
||||
|
||||
Assert.False (isCompletedSuccessfully);
|
||||
await TaskWithAsyncContinuation ();
|
||||
Thread.Sleep (100);
|
||||
Assert.True (isCompletedSuccessfully);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Dispose_Resets_SyncContext ()
|
||||
{
|
||||
IApplication app = Application.Create ();
|
||||
app.Dispose ();
|
||||
Assert.Null (SynchronizationContext.Current);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace ApplicationTests;
|
||||
namespace ApplicationTests.BeginEnd;
|
||||
|
||||
/// <summary>
|
||||
/// Comprehensive tests for ApplicationImpl.Begin/End logic that manages Current and SessionStack.
|
||||
@@ -11,6 +11,74 @@ public class ApplicationImplBeginEndTests (ITestOutputHelper output)
|
||||
{
|
||||
private readonly ITestOutputHelper _output = output;
|
||||
|
||||
|
||||
[Fact]
|
||||
public void Init_Begin_End_Cleans_Up ()
|
||||
{
|
||||
IApplication? app = Application.Create ();
|
||||
|
||||
SessionToken? newSessionToken = null;
|
||||
|
||||
EventHandler<SessionTokenEventArgs> newSessionTokenFn = (s, e) =>
|
||||
{
|
||||
Assert.NotNull (e.State);
|
||||
newSessionToken = e.State;
|
||||
};
|
||||
app.SessionBegun += newSessionTokenFn;
|
||||
|
||||
Runnable<bool> runnable = new ();
|
||||
SessionToken sessionToken = app.Begin (runnable)!;
|
||||
Assert.NotNull (sessionToken);
|
||||
Assert.NotNull (newSessionToken);
|
||||
Assert.Equal (sessionToken, newSessionToken);
|
||||
|
||||
// Assert.Equal (runnable, Application.TopRunnable);
|
||||
|
||||
app.SessionBegun -= newSessionTokenFn;
|
||||
app.End (newSessionToken);
|
||||
|
||||
Assert.Null (app.TopRunnable);
|
||||
Assert.Null (app.Driver);
|
||||
|
||||
runnable.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Begin_Null_Runnable_Throws ()
|
||||
{
|
||||
IApplication app = Application.Create ();
|
||||
app.Init ("fake");
|
||||
|
||||
// Test null Runnable
|
||||
Assert.Throws<ArgumentNullException> (() => 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 Begin_WithNullRunnable_ThrowsArgumentNullException ()
|
||||
{
|
||||
@@ -1,7 +1,3 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using Terminal.Gui.App;
|
||||
using Xunit;
|
||||
namespace ApplicationTests;
|
||||
|
||||
public class ResultEventArgsTests
|
||||
@@ -9,7 +5,7 @@ public class ResultEventArgsTests
|
||||
[Fact]
|
||||
public void DefaultConstructor_InitializesProperties ()
|
||||
{
|
||||
var args = new ResultEventArgs<string> ();
|
||||
ResultEventArgs<string> args = new ();
|
||||
|
||||
Assert.Null (args.Result);
|
||||
Assert.False (args.Handled);
|
||||
@@ -18,7 +14,7 @@ public class ResultEventArgsTests
|
||||
[Fact]
|
||||
public void Constructor_WithResult_SetsResult ()
|
||||
{
|
||||
var args = new ResultEventArgs<int> (42);
|
||||
ResultEventArgs<int> args = new (42);
|
||||
|
||||
Assert.Equal (42, args.Result);
|
||||
Assert.False (args.Handled);
|
||||
@@ -27,7 +23,7 @@ public class ResultEventArgsTests
|
||||
[Fact]
|
||||
public void Constructor_WithNullResult_AllowsNull ()
|
||||
{
|
||||
var args = new ResultEventArgs<string?> (null);
|
||||
ResultEventArgs<string?> args = new (null);
|
||||
|
||||
Assert.Null (args.Result);
|
||||
Assert.False (args.Handled);
|
||||
@@ -36,7 +32,7 @@ public class ResultEventArgsTests
|
||||
[Fact]
|
||||
public void Result_CanBeSetAndRetrieved ()
|
||||
{
|
||||
var args = new ResultEventArgs<string> ();
|
||||
ResultEventArgs<string> args = new ();
|
||||
args.Result = "foo";
|
||||
|
||||
Assert.Equal ("foo", args.Result);
|
||||
@@ -48,7 +44,7 @@ public class ResultEventArgsTests
|
||||
[Fact]
|
||||
public void Handled_CanBeSetAndRetrieved ()
|
||||
{
|
||||
var args = new ResultEventArgs<object> ();
|
||||
ResultEventArgs<object> args = new ();
|
||||
Assert.False (args.Handled);
|
||||
|
||||
args.Handled = true;
|
||||
@@ -61,7 +57,7 @@ public class ResultEventArgsTests
|
||||
[Fact]
|
||||
public void WorksWithValueTypes ()
|
||||
{
|
||||
var args = new ResultEventArgs<int> ();
|
||||
ResultEventArgs<int> args = new ();
|
||||
Assert.Equal (0, args.Result); // default(int) is 0
|
||||
|
||||
args.Result = 123;
|
||||
@@ -72,7 +68,7 @@ public class ResultEventArgsTests
|
||||
public void WorksWithReferenceTypes ()
|
||||
{
|
||||
var obj = new object ();
|
||||
var args = new ResultEventArgs<object> (obj);
|
||||
ResultEventArgs<object> args = new (obj);
|
||||
|
||||
Assert.Same (obj, args.Result);
|
||||
|
||||
@@ -87,7 +83,8 @@ public class ResultEventArgsTests
|
||||
public void EventHandler_CanChangeResult_AndCallerSeesChange ()
|
||||
{
|
||||
// Arrange
|
||||
var args = new ResultEventArgs<string> ("initial");
|
||||
ResultEventArgs<string> args = new ("initial");
|
||||
|
||||
StringResultEvent += (sender, e) =>
|
||||
{
|
||||
// Handler changes the result
|
||||
@@ -101,17 +98,12 @@ public class ResultEventArgsTests
|
||||
Assert.Equal ("changed by handler", args.Result);
|
||||
}
|
||||
|
||||
|
||||
|
||||
[Fact]
|
||||
public void EventHandler_CanSetResultToNull ()
|
||||
{
|
||||
// Arrange
|
||||
var args = new ResultEventArgs<string> ("not null");
|
||||
StringResultEvent += (sender, e) =>
|
||||
{
|
||||
e.Result = null;
|
||||
};
|
||||
ResultEventArgs<string> args = new ("not null");
|
||||
StringResultEvent += (sender, e) => { e.Result = null; };
|
||||
|
||||
// Act
|
||||
StringResultEvent?.Invoke (this, args);
|
||||
@@ -124,7 +116,7 @@ public class ResultEventArgsTests
|
||||
public void MultipleHandlers_LastHandlerWins ()
|
||||
{
|
||||
// Arrange
|
||||
var args = new ResultEventArgs<int> (1);
|
||||
ResultEventArgs<int> args = new (1);
|
||||
EventHandler<ResultEventArgs<int>>? intEvent = null;
|
||||
intEvent += (s, e) => e.Result = 2;
|
||||
intEvent += (s, e) => e.Result = 3;
|
||||
@@ -141,7 +133,7 @@ public class ResultEventArgsTests
|
||||
public void EventHandler_CanChangeResult_Int ()
|
||||
{
|
||||
EventHandler<ResultEventArgs<int>> handler = (s, e) => e.Result = 99;
|
||||
var args = new ResultEventArgs<int> (1);
|
||||
ResultEventArgs<int> args = new (1);
|
||||
handler.Invoke (this, args);
|
||||
Assert.Equal (99, args.Result);
|
||||
}
|
||||
@@ -151,7 +143,7 @@ public class ResultEventArgsTests
|
||||
public void EventHandler_CanChangeResult_Double ()
|
||||
{
|
||||
EventHandler<ResultEventArgs<double>> handler = (s, e) => e.Result = 2.718;
|
||||
var args = new ResultEventArgs<double> (3.14);
|
||||
ResultEventArgs<double> args = new (3.14);
|
||||
handler.Invoke (this, args);
|
||||
Assert.Equal (2.718, args.Result);
|
||||
}
|
||||
@@ -161,29 +153,39 @@ public class ResultEventArgsTests
|
||||
public void EventHandler_CanChangeResult_Bool ()
|
||||
{
|
||||
EventHandler<ResultEventArgs<bool>> handler = (s, e) => e.Result = false;
|
||||
var args = new ResultEventArgs<bool> (true);
|
||||
ResultEventArgs<bool> args = new (true);
|
||||
handler.Invoke (this, args);
|
||||
Assert.False (args.Result);
|
||||
}
|
||||
|
||||
// Enum
|
||||
enum MyEnum { A, B, C }
|
||||
private enum MyEnum
|
||||
{
|
||||
A,
|
||||
B,
|
||||
C
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EventHandler_CanChangeResult_Enum ()
|
||||
{
|
||||
EventHandler<ResultEventArgs<MyEnum>> handler = (s, e) => e.Result = MyEnum.C;
|
||||
var args = new ResultEventArgs<MyEnum> (MyEnum.A);
|
||||
ResultEventArgs<MyEnum> args = new (MyEnum.A);
|
||||
handler.Invoke (this, args);
|
||||
Assert.Equal (MyEnum.C, args.Result);
|
||||
}
|
||||
|
||||
// Struct
|
||||
struct MyStruct { public int X; }
|
||||
private struct MyStruct
|
||||
{
|
||||
public int X;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EventHandler_CanChangeResult_Struct ()
|
||||
{
|
||||
EventHandler<ResultEventArgs<MyStruct>> handler = (s, e) => e.Result = new MyStruct { X = 42 };
|
||||
var args = new ResultEventArgs<MyStruct> (new MyStruct { X = 1 });
|
||||
EventHandler<ResultEventArgs<MyStruct>> handler = (s, e) => e.Result = new() { X = 42 };
|
||||
ResultEventArgs<MyStruct> args = new (new() { X = 1 });
|
||||
handler.Invoke (this, args);
|
||||
Assert.Equal (42, args.Result.X);
|
||||
}
|
||||
@@ -193,7 +195,7 @@ public class ResultEventArgsTests
|
||||
public void EventHandler_CanChangeResult_String ()
|
||||
{
|
||||
EventHandler<ResultEventArgs<string>> handler = (s, e) => e.Result = "changed";
|
||||
var args = new ResultEventArgs<string> ("original");
|
||||
ResultEventArgs<string> args = new ("original");
|
||||
handler.Invoke (this, args);
|
||||
Assert.Equal ("changed", args.Result);
|
||||
}
|
||||
@@ -204,7 +206,7 @@ public class ResultEventArgsTests
|
||||
{
|
||||
var newObj = new object ();
|
||||
EventHandler<ResultEventArgs<object>> handler = (s, e) => e.Result = newObj;
|
||||
var args = new ResultEventArgs<object> (new object ());
|
||||
ResultEventArgs<object> args = new (new ());
|
||||
handler.Invoke (this, args);
|
||||
Assert.Same (newObj, args.Result);
|
||||
}
|
||||
@@ -214,7 +216,7 @@ public class ResultEventArgsTests
|
||||
public void EventHandler_CanChangeResult_NullableInt ()
|
||||
{
|
||||
EventHandler<ResultEventArgs<int?>> handler = (s, e) => e.Result = null;
|
||||
var args = new ResultEventArgs<int?> (42);
|
||||
ResultEventArgs<int?> args = new (42);
|
||||
handler.Invoke (this, args);
|
||||
Assert.Null (args.Result);
|
||||
}
|
||||
@@ -225,7 +227,7 @@ public class ResultEventArgsTests
|
||||
{
|
||||
var newArr = new [] { "x", "y" };
|
||||
EventHandler<ResultEventArgs<string []>> handler = (s, e) => e.Result = newArr;
|
||||
var args = new ResultEventArgs<string []> (new [] { "a", "b" });
|
||||
ResultEventArgs<string []> args = new (new [] { "a", "b" });
|
||||
handler.Invoke (this, args);
|
||||
Assert.Equal (newArr, args.Result);
|
||||
}
|
||||
@@ -234,9 +236,9 @@ public class ResultEventArgsTests
|
||||
[Fact]
|
||||
public void EventHandler_CanChangeResult_List ()
|
||||
{
|
||||
var newList = new List<int> { 1, 2, 3 };
|
||||
List<int> newList = new() { 1, 2, 3 };
|
||||
EventHandler<ResultEventArgs<List<int>>> handler = (s, e) => e.Result = newList;
|
||||
var args = new ResultEventArgs<List<int>> (new List<int> { 9 });
|
||||
ResultEventArgs<List<int>> args = new (new() { 9 });
|
||||
handler.Invoke (this, args);
|
||||
Assert.Equal (newList, args.Result);
|
||||
}
|
||||
@@ -245,21 +247,22 @@ public class ResultEventArgsTests
|
||||
[Fact]
|
||||
public void EventHandler_CanChangeResult_Dictionary ()
|
||||
{
|
||||
var newDict = new Dictionary<string, int> { ["a"] = 1 };
|
||||
Dictionary<string, int> newDict = new() { ["a"] = 1 };
|
||||
EventHandler<ResultEventArgs<Dictionary<string, int>>> handler = (s, e) => e.Result = newDict;
|
||||
var args = new ResultEventArgs<Dictionary<string, int>> (new Dictionary<string, int> ());
|
||||
ResultEventArgs<Dictionary<string, int>> args = new (new ());
|
||||
handler.Invoke (this, args);
|
||||
Assert.Equal (newDict, args.Result);
|
||||
}
|
||||
|
||||
// Record
|
||||
public record MyRecord (int Id, string Name);
|
||||
|
||||
[Fact]
|
||||
public void EventHandler_CanChangeResult_Record ()
|
||||
{
|
||||
var rec = new MyRecord (1, "foo");
|
||||
EventHandler<ResultEventArgs<MyRecord>> handler = (s, e) => e.Result = rec;
|
||||
var args = new ResultEventArgs<MyRecord> (null);
|
||||
ResultEventArgs<MyRecord> args = new (null);
|
||||
handler.Invoke (this, args);
|
||||
Assert.Equal (rec, args.Result);
|
||||
}
|
||||
@@ -269,12 +272,12 @@ public class ResultEventArgsTests
|
||||
public void EventHandler_CanChangeResult_NullableInt_ToValue_AndNull ()
|
||||
{
|
||||
EventHandler<ResultEventArgs<int?>> handler = (s, e) => e.Result = 123;
|
||||
var args = new ResultEventArgs<int?> (null);
|
||||
ResultEventArgs<int?> args = new (null);
|
||||
handler.Invoke (this, args);
|
||||
Assert.Equal (123, args.Result);
|
||||
|
||||
handler = (s, e) => e.Result = null;
|
||||
args = new ResultEventArgs<int?> (456);
|
||||
args = new (456);
|
||||
handler.Invoke (this, args);
|
||||
Assert.Null (args.Result);
|
||||
}
|
||||
@@ -284,12 +287,12 @@ public class ResultEventArgsTests
|
||||
public void EventHandler_CanChangeResult_NullableDouble_ToValue_AndNull ()
|
||||
{
|
||||
EventHandler<ResultEventArgs<double?>> handler = (s, e) => e.Result = 3.14;
|
||||
var args = new ResultEventArgs<double?> (null);
|
||||
ResultEventArgs<double?> args = new (null);
|
||||
handler.Invoke (this, args);
|
||||
Assert.Equal (3.14, args.Result);
|
||||
|
||||
handler = (s, e) => e.Result = null;
|
||||
args = new ResultEventArgs<double?> (2.71);
|
||||
args = new (2.71);
|
||||
handler.Invoke (this, args);
|
||||
Assert.Null (args.Result);
|
||||
}
|
||||
@@ -299,12 +302,12 @@ public class ResultEventArgsTests
|
||||
public void EventHandler_CanChangeResult_NullableStruct_ToValue_AndNull ()
|
||||
{
|
||||
EventHandler<ResultEventArgs<MyStruct?>> handler = (s, e) => e.Result = new MyStruct { X = 7 };
|
||||
var args = new ResultEventArgs<MyStruct?> (null);
|
||||
ResultEventArgs<MyStruct?> args = new (null);
|
||||
handler.Invoke (this, args);
|
||||
Assert.Equal (7, args.Result?.X);
|
||||
|
||||
handler = (s, e) => e.Result = null;
|
||||
args = new ResultEventArgs<MyStruct?> (new MyStruct { X = 8 });
|
||||
args = new (new MyStruct { X = 8 });
|
||||
handler.Invoke (this, args);
|
||||
Assert.Null (args.Result);
|
||||
}
|
||||
@@ -314,29 +317,33 @@ public class ResultEventArgsTests
|
||||
public void EventHandler_CanChangeResult_NullableString_ToValue_AndNull ()
|
||||
{
|
||||
EventHandler<ResultEventArgs<string?>> handler = (s, e) => e.Result = "hello";
|
||||
var args = new ResultEventArgs<string?> (null);
|
||||
ResultEventArgs<string?> args = new (null);
|
||||
handler.Invoke (this, args);
|
||||
Assert.Equal ("hello", args.Result);
|
||||
|
||||
handler = (s, e) => e.Result = null;
|
||||
args = new ResultEventArgs<string?> ("world");
|
||||
args = new ("world");
|
||||
handler.Invoke (this, args);
|
||||
Assert.Null (args.Result);
|
||||
}
|
||||
|
||||
// Nullable custom class
|
||||
class MyClass { public int Y { get; set; } }
|
||||
private class MyClass
|
||||
{
|
||||
public int Y { get; set; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EventHandler_CanChangeResult_NullableClass_ToValue_AndNull ()
|
||||
{
|
||||
EventHandler<ResultEventArgs<MyClass?>> handler = (s, e) => e.Result = new MyClass { Y = 42 };
|
||||
var args = new ResultEventArgs<MyClass?> (null);
|
||||
EventHandler<ResultEventArgs<MyClass?>> handler = (s, e) => e.Result = new() { Y = 42 };
|
||||
ResultEventArgs<MyClass?> args = new (null);
|
||||
handler.Invoke (this, args);
|
||||
Assert.NotNull (args.Result);
|
||||
Assert.Equal (42, args.Result?.Y);
|
||||
|
||||
handler = (s, e) => e.Result = null;
|
||||
args = new ResultEventArgs<MyClass?> (new MyClass { Y = 99 });
|
||||
args = new (new() { Y = 99 });
|
||||
handler.Invoke (this, args);
|
||||
Assert.Null (args.Result);
|
||||
}
|
||||
|
||||
170
Tests/UnitTestsParallelizable/Application/InitTests.cs
Normal file
170
Tests/UnitTestsParallelizable/Application/InitTests.cs
Normal file
@@ -0,0 +1,170 @@
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace ApplicationTests.Init;
|
||||
|
||||
/// <summary>
|
||||
/// Comprehensive tests for ApplicationImpl.Begin/End logic that manages Current and SessionStack.
|
||||
/// These tests ensure the fragile state management logic is robust and catches regressions.
|
||||
/// Tests work directly with ApplicationImpl instances to avoid global Application state issues.
|
||||
/// </summary>
|
||||
public class InitTests (ITestOutputHelper output)
|
||||
{
|
||||
private readonly ITestOutputHelper _output = output;
|
||||
|
||||
[Fact]
|
||||
public void Init_Unbalanced_Throws ()
|
||||
{
|
||||
IApplication? app = Application.Create ();
|
||||
app.Init ("fake");
|
||||
|
||||
Assert.Throws<InvalidOperationException> (() =>
|
||||
app.Init ("fake")
|
||||
);
|
||||
}
|
||||
|
||||
[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<bool> 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// ReSharper disable AccessToDisposedClosure
|
||||
|
||||
#nullable enable
|
||||
namespace ApplicationTests;
|
||||
namespace ApplicationTests.Keyboard;
|
||||
|
||||
/// <summary>
|
||||
/// Tests to verify that KeyboardImpl is thread-safe for concurrent access scenarios.
|
||||
@@ -1,7 +1,7 @@
|
||||
#nullable enable
|
||||
using Terminal.Gui.App;
|
||||
|
||||
namespace ApplicationTests;
|
||||
namespace ApplicationTests.Keyboard;
|
||||
|
||||
/// <summary>
|
||||
/// Parallelizable tests for keyboard handling.
|
||||
@@ -9,6 +9,23 @@ namespace ApplicationTests;
|
||||
/// </summary>
|
||||
public class KeyboardTests
|
||||
{
|
||||
|
||||
[Fact]
|
||||
public void Init_CreatesKeybindings ()
|
||||
{
|
||||
IApplication app = Application.Create ();
|
||||
|
||||
app.Keyboard.KeyBindings.Clear ();
|
||||
|
||||
Assert.Empty (app.Keyboard.KeyBindings.GetBindings ());
|
||||
|
||||
app.Init ("fake");
|
||||
|
||||
Assert.NotEmpty (app.Keyboard.KeyBindings.GetBindings ());
|
||||
|
||||
app.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Constructor_InitializesKeyBindings ()
|
||||
{
|
||||
@@ -245,7 +262,7 @@ public class KeyboardTests
|
||||
}
|
||||
|
||||
// Migrated from UnitTests/Application/KeyboardTests.cs
|
||||
|
||||
|
||||
[Fact]
|
||||
public void KeyBindings_Add_Adds ()
|
||||
{
|
||||
@@ -465,7 +482,7 @@ public class KeyboardTests
|
||||
|
||||
// Get the commands from the old binding
|
||||
Assert.True (keyboard.KeyBindings.TryGet (oldKey, out KeyBinding oldBinding));
|
||||
Command[] oldCommands = oldBinding.Commands.ToArray ();
|
||||
Command [] oldCommands = oldBinding.Commands.ToArray ();
|
||||
|
||||
// Act
|
||||
keyboard.KeyBindings.Replace (oldKey, newKey);
|
||||
@@ -1,7 +1,7 @@
|
||||
#nullable enable
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace ApplicationTests;
|
||||
namespace ApplicationTests.Mouse;
|
||||
|
||||
[Trait ("Category", "Input")]
|
||||
public class ApplicationMouseEnterLeaveTests
|
||||
@@ -1,8 +1,6 @@
|
||||
#nullable enable
|
||||
using Terminal.Gui.App;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace ApplicationTests;
|
||||
namespace ApplicationTests.Mouse;
|
||||
|
||||
/// <summary>
|
||||
/// Parallelizable tests for IMouse interface.
|
||||
@@ -93,14 +91,14 @@ public class MouseInterfaceTests (ITestOutputHelper output)
|
||||
MouseEventArgs? capturedArgs = null;
|
||||
|
||||
mouse.MouseEvent += (sender, args) =>
|
||||
{
|
||||
eventFired = true;
|
||||
capturedArgs = args;
|
||||
};
|
||||
{
|
||||
eventFired = true;
|
||||
capturedArgs = args;
|
||||
};
|
||||
|
||||
MouseEventArgs testEvent = new ()
|
||||
{
|
||||
ScreenPosition = new Point (5, 10),
|
||||
ScreenPosition = new (5, 10),
|
||||
Flags = MouseFlags.Button1Pressed
|
||||
};
|
||||
|
||||
@@ -121,13 +119,13 @@ public class MouseInterfaceTests (ITestOutputHelper output)
|
||||
MouseImpl mouse = new ();
|
||||
var eventCount = 0;
|
||||
|
||||
void Handler (object? sender, MouseEventArgs args) => eventCount++;
|
||||
void Handler (object? sender, MouseEventArgs args) { eventCount++; }
|
||||
|
||||
mouse.MouseEvent += Handler;
|
||||
|
||||
MouseEventArgs testEvent = new ()
|
||||
{
|
||||
ScreenPosition = new Point (0, 0),
|
||||
ScreenPosition = new (0, 0),
|
||||
Flags = MouseFlags.Button1Pressed
|
||||
};
|
||||
|
||||
@@ -157,7 +155,7 @@ public class MouseInterfaceTests (ITestOutputHelper output)
|
||||
|
||||
MouseEventArgs testEvent = new ()
|
||||
{
|
||||
ScreenPosition = new Point (0, 0),
|
||||
ScreenPosition = new (0, 0),
|
||||
Flags = MouseFlags.Button1Pressed
|
||||
};
|
||||
|
||||
@@ -185,7 +183,7 @@ public class MouseInterfaceTests (ITestOutputHelper output)
|
||||
|
||||
MouseEventArgs testEvent = new ()
|
||||
{
|
||||
ScreenPosition = new Point (5, 5),
|
||||
ScreenPosition = new (5, 5),
|
||||
Flags = flags
|
||||
};
|
||||
|
||||
@@ -231,7 +229,7 @@ public class MouseInterfaceTests (ITestOutputHelper output)
|
||||
|
||||
MouseEventArgs testEvent = new ()
|
||||
{
|
||||
ScreenPosition = new Point (0, 0),
|
||||
ScreenPosition = new (0, 0),
|
||||
Flags = MouseFlags.Button1Pressed
|
||||
};
|
||||
|
||||
@@ -300,7 +298,7 @@ public class MouseInterfaceTests (ITestOutputHelper output)
|
||||
|
||||
MouseEventArgs testEvent = new ()
|
||||
{
|
||||
ScreenPosition = new Point (0, 0),
|
||||
ScreenPosition = new (0, 0),
|
||||
Flags = MouseFlags.Button1Pressed
|
||||
};
|
||||
|
||||
@@ -380,10 +378,10 @@ public class MouseInterfaceTests (ITestOutputHelper output)
|
||||
var eventFired = false;
|
||||
|
||||
mouse.GrabbingMouse += (sender, args) =>
|
||||
{
|
||||
eventFired = true;
|
||||
args.Cancel = true;
|
||||
};
|
||||
{
|
||||
eventFired = true;
|
||||
args.Cancel = true;
|
||||
};
|
||||
|
||||
// Act
|
||||
mouse.GrabMouse (testView);
|
||||
@@ -403,10 +401,10 @@ public class MouseInterfaceTests (ITestOutputHelper output)
|
||||
View? eventView = null;
|
||||
|
||||
mouse.GrabbedMouse += (sender, args) =>
|
||||
{
|
||||
eventFired = true;
|
||||
eventView = args.View;
|
||||
};
|
||||
{
|
||||
eventFired = true;
|
||||
eventView = args.View;
|
||||
};
|
||||
|
||||
// Act
|
||||
mouse.GrabMouse (testView);
|
||||
@@ -428,10 +426,10 @@ public class MouseInterfaceTests (ITestOutputHelper output)
|
||||
View? eventView = null;
|
||||
|
||||
mouse.UnGrabbedMouse += (sender, args) =>
|
||||
{
|
||||
eventFired = true;
|
||||
eventView = args.View;
|
||||
};
|
||||
{
|
||||
eventFired = true;
|
||||
eventView = args.View;
|
||||
};
|
||||
|
||||
// Act
|
||||
mouse.UngrabMouse ();
|
||||
@@ -1,6 +1,4 @@
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace ApplicationTests;
|
||||
namespace ApplicationTests.Mouse;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for the <see cref="IMouse"/> interface and <see cref="MouseImpl"/> implementation.
|
||||
@@ -2,7 +2,7 @@
|
||||
using Moq;
|
||||
using Terminal.Gui.App;
|
||||
|
||||
namespace ApplicationTests;
|
||||
namespace ApplicationTests.Popover;
|
||||
|
||||
public class ApplicationPopoverTests
|
||||
{
|
||||
@@ -1,13 +1,10 @@
|
||||
using System;
|
||||
using Terminal.Gui;
|
||||
using Terminal.Gui.App;
|
||||
using Xunit;
|
||||
namespace ApplicationTests;
|
||||
namespace ApplicationTests.Popover;
|
||||
|
||||
public class PopoverBaseImplTests
|
||||
{
|
||||
// Minimal concrete implementation for testing
|
||||
private class TestPopover : PopoverBaseImpl { }
|
||||
private class TestPopover : PopoverBaseImpl
|
||||
{ }
|
||||
|
||||
[Fact]
|
||||
public void Constructor_SetsDefaults ()
|
||||
@@ -40,12 +37,11 @@ public class PopoverBaseImplTests
|
||||
popover.ViewportSettings = ViewportSettingsFlags.None; // Remove required flags
|
||||
|
||||
var popoverManager = new ApplicationPopover ();
|
||||
|
||||
// Test missing Transparent flags
|
||||
Assert.ThrowsAny<Exception> (() => popoverManager.Show (popover));
|
||||
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void Show_ThrowsIfPopoverMissingQuitCommand ()
|
||||
{
|
||||
252
Tests/UnitTestsParallelizable/Application/RunTests.cs
Normal file
252
Tests/UnitTestsParallelizable/Application/RunTests.cs
Normal file
@@ -0,0 +1,252 @@
|
||||
#nullable enable
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace ApplicationTests;
|
||||
|
||||
public class RunTests
|
||||
{
|
||||
[Fact]
|
||||
public void Run_RequestStop_Stops ()
|
||||
{
|
||||
IApplication? app = Application.Create ();
|
||||
app.Init ("fake");
|
||||
|
||||
var top = new Runnable ();
|
||||
SessionToken? sessionToken = app.Begin (top);
|
||||
Assert.NotNull (sessionToken);
|
||||
|
||||
app.Iteration += OnApplicationOnIteration;
|
||||
app.Run (top);
|
||||
app.Iteration -= OnApplicationOnIteration;
|
||||
|
||||
top.Dispose ();
|
||||
|
||||
return;
|
||||
|
||||
void OnApplicationOnIteration (object? s, EventArgs<IApplication?> a) { app.RequestStop (); }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Run_T_Init_Driver_Cleared_with_Runnable_Throws ()
|
||||
{
|
||||
IApplication? app = Application.Create ();
|
||||
|
||||
app.Init ("fake");
|
||||
app.Driver = null;
|
||||
|
||||
app.StopAfterFirstIteration = true;
|
||||
|
||||
// Init has been called, but Driver has been set to null. Bad.
|
||||
Assert.Throws<InvalidOperationException> (() => app.Run<Runnable> ());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Run_Iteration_Fires ()
|
||||
{
|
||||
var iteration = 0;
|
||||
|
||||
IApplication app = Application.Create ();
|
||||
app.Init ("fake");
|
||||
|
||||
app.Iteration += Application_Iteration;
|
||||
app.Run<Runnable> ();
|
||||
app.Iteration -= Application_Iteration;
|
||||
|
||||
Assert.Equal (1, iteration);
|
||||
app.Dispose ();
|
||||
|
||||
return;
|
||||
|
||||
void Application_Iteration (object? sender, EventArgs<IApplication?> e)
|
||||
{
|
||||
|
||||
iteration++;
|
||||
app.RequestStop ();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void Run_T_After_InitWithDriver_with_Runnable_and_Driver_Does_Not_Throw ()
|
||||
{
|
||||
IApplication app = Application.Create ();
|
||||
app.StopAfterFirstIteration = true;
|
||||
|
||||
// Run<Runnable<bool>> 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<Window> (null, "fake");
|
||||
|
||||
// Run<Runnable<bool>> when already initialized or not with a Driver will not throw (because Dialog is derived from Runnable)
|
||||
app.Run<Dialog> (null, "fake");
|
||||
|
||||
app.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Run_T_After_Init_Does_Not_Disposes_Application_Top ()
|
||||
{
|
||||
IApplication app = Application.Create ();
|
||||
app.Init ("fake");
|
||||
|
||||
// Init doesn't create a Runnable and assigned it to app.TopRunnable
|
||||
// but Begin does
|
||||
var initTop = new Runnable ();
|
||||
|
||||
app.Iteration += OnApplicationOnIteration;
|
||||
|
||||
app.Run<Runnable> ();
|
||||
app.Iteration -= OnApplicationOnIteration;
|
||||
|
||||
#if DEBUG_IDISPOSABLE
|
||||
Assert.False (initTop.WasDisposed);
|
||||
initTop.Dispose ();
|
||||
Assert.True (initTop.WasDisposed);
|
||||
#endif
|
||||
initTop.Dispose ();
|
||||
|
||||
app.Dispose ();
|
||||
|
||||
return;
|
||||
|
||||
void OnApplicationOnIteration (object? s, EventArgs<IApplication?> a)
|
||||
{
|
||||
Assert.NotEqual (initTop, app.TopRunnableView);
|
||||
#if DEBUG_IDISPOSABLE
|
||||
Assert.False (initTop.WasDisposed);
|
||||
#endif
|
||||
app.RequestStop ();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Run_T_After_InitWithDriver_with_TestRunnable_DoesNotThrow ()
|
||||
{
|
||||
IApplication app = Application.Create ();
|
||||
app.Init ("fake");
|
||||
app.StopAfterFirstIteration = true;
|
||||
|
||||
// Init has been called and we're passing no driver to Run<TestRunnable>. This is ok.
|
||||
app.Run<Window> ();
|
||||
|
||||
app.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Run_T_After_InitNullDriver_with_TestRunnable_DoesNotThrow ()
|
||||
{
|
||||
IApplication app = Application.Create ();
|
||||
app.Init ("fake");
|
||||
app.StopAfterFirstIteration = true;
|
||||
|
||||
// Init has been called, selecting FakeDriver; we're passing no driver to Run<TestRunnable>. Should be fine.
|
||||
app.Run<Window> ();
|
||||
|
||||
app.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Run_T_NoInit_DoesNotThrow ()
|
||||
{
|
||||
IApplication app = Application.Create ();
|
||||
app.StopAfterFirstIteration = true;
|
||||
|
||||
app.Run<Window> ();
|
||||
|
||||
app.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Run_T_NoInit_WithDriver_DoesNotThrow ()
|
||||
{
|
||||
IApplication app = Application.Create ();
|
||||
app.StopAfterFirstIteration = true;
|
||||
|
||||
// Init has NOT been called and we're passing a valid driver to Run<TestRunnable>. This is ok.
|
||||
app.Run<Runnable> (null, "fake");
|
||||
|
||||
app.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Run_Sets_Running_True ()
|
||||
{
|
||||
IApplication app = Application.Create ();
|
||||
app.Init ("fake");
|
||||
|
||||
var top = new Runnable ();
|
||||
SessionToken? rs = app.Begin (top);
|
||||
Assert.NotNull (rs);
|
||||
|
||||
app.Iteration += OnApplicationOnIteration;
|
||||
app.Run (top);
|
||||
app.Iteration -= OnApplicationOnIteration;
|
||||
|
||||
top.Dispose ();
|
||||
|
||||
app.Dispose ();
|
||||
|
||||
return;
|
||||
|
||||
void OnApplicationOnIteration (object? s, EventArgs<IApplication?> a)
|
||||
{
|
||||
Assert.True (top.IsRunning);
|
||||
top.RequestStop ();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Run_A_Modal_Runnable_Refresh_Background_On_Moving ()
|
||||
{
|
||||
IApplication app = Application.Create ();
|
||||
app.Init ("fake");
|
||||
|
||||
// Don't use Dialog here as it has more layout logic. Use Window instead.
|
||||
var w = new Window
|
||||
{
|
||||
Width = 5, Height = 5,
|
||||
Arrangement = ViewArrangement.Movable
|
||||
};
|
||||
app.Driver!.SetScreenSize (10, 10);
|
||||
SessionToken? rs = app.Begin (w);
|
||||
|
||||
// Don't use visuals to test as style of border can change over time.
|
||||
Assert.Equal (new (0, 0), w.Frame.Location);
|
||||
|
||||
app.Mouse.RaiseMouseEvent (new () { Flags = MouseFlags.Button1Pressed });
|
||||
Assert.Equal (w.Border, app.Mouse.MouseGrabView);
|
||||
Assert.Equal (new (0, 0), w.Frame.Location);
|
||||
|
||||
// Move down and to the right.
|
||||
app.Mouse.RaiseMouseEvent (new () { ScreenPosition = new (1, 1), Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition });
|
||||
Assert.Equal (new (1, 1), w.Frame.Location);
|
||||
|
||||
app.End (rs!);
|
||||
w.Dispose ();
|
||||
|
||||
app.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Run_T_Creates_Top_Without_Init ()
|
||||
{
|
||||
IApplication app = Application.Create ();
|
||||
app.StopAfterFirstIteration = true;
|
||||
|
||||
app.SessionEnded += OnApplicationOnSessionEnded;
|
||||
|
||||
app.Run<Window> (null, "fake");
|
||||
|
||||
Assert.Null (app.TopRunnableView);
|
||||
|
||||
app.Dispose ();
|
||||
Assert.Null (app.TopRunnableView);
|
||||
|
||||
return;
|
||||
|
||||
void OnApplicationOnSessionEnded (object? sender, SessionTokenEventArgs e)
|
||||
{
|
||||
app.SessionEnded -= OnApplicationOnSessionEnded;
|
||||
e.State.Result = (e.State.Runnable as IRunnable<object?>)?.Result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
#nullable enable
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace ApplicationTests;
|
||||
namespace ApplicationTests.RunnableTests;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for edge cases and error conditions in IRunnable implementation.
|
||||
@@ -9,7 +8,7 @@ namespace ApplicationTests;
|
||||
public class RunnableEdgeCasesTests (ITestOutputHelper output)
|
||||
{
|
||||
private readonly ITestOutputHelper _output = output;
|
||||
|
||||
|
||||
[Fact]
|
||||
public void Runnable_MultipleEventSubscribers_AllInvoked ()
|
||||
{
|
||||
|
||||
@@ -1,28 +1,19 @@
|
||||
#nullable enable
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace ApplicationTests;
|
||||
namespace ApplicationTests.RunnableTests;
|
||||
|
||||
/// <summary>
|
||||
/// Integration tests for IApplication's IRunnable support.
|
||||
/// Tests the full lifecycle of IRunnable instances through Application methods.
|
||||
/// </summary>
|
||||
public class ApplicationRunnableIntegrationTests (ITestOutputHelper output) : IDisposable
|
||||
public class ApplicationRunnableIntegrationTests
|
||||
{
|
||||
private readonly ITestOutputHelper _output = output;
|
||||
private IApplication? _app;
|
||||
|
||||
public void Dispose ()
|
||||
{
|
||||
_app?.Dispose ();
|
||||
_app = null;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Begin_AddsRunnableToStack ()
|
||||
{
|
||||
// Arrange
|
||||
IApplication app = GetApp ();
|
||||
IApplication app = CreateAndInitApp ();
|
||||
Runnable<int> runnable = new ();
|
||||
int stackCountBefore = app.SessionStack?.Count ?? 0;
|
||||
|
||||
@@ -43,7 +34,7 @@ public class ApplicationRunnableIntegrationTests (ITestOutputHelper output) : ID
|
||||
public void Begin_CanBeCanceled_ByIsRunningChanging ()
|
||||
{
|
||||
// Arrange
|
||||
IApplication app = GetApp ();
|
||||
IApplication app = CreateAndInitApp ();
|
||||
CancelableRunnable runnable = new () { CancelStart = true };
|
||||
|
||||
// Act
|
||||
@@ -60,7 +51,7 @@ public class ApplicationRunnableIntegrationTests (ITestOutputHelper output) : ID
|
||||
public void Begin_RaisesIsModalChangedEvent ()
|
||||
{
|
||||
// Arrange
|
||||
IApplication app = GetApp ();
|
||||
IApplication app = CreateAndInitApp ();
|
||||
Runnable<int> runnable = new ();
|
||||
var isModalChangedRaised = false;
|
||||
bool? receivedValue = null;
|
||||
@@ -86,7 +77,7 @@ public class ApplicationRunnableIntegrationTests (ITestOutputHelper output) : ID
|
||||
public void Begin_RaisesIsRunningChangedEvent ()
|
||||
{
|
||||
// Arrange
|
||||
IApplication app = GetApp ();
|
||||
IApplication app = CreateAndInitApp ();
|
||||
Runnable<int> runnable = new ();
|
||||
var isRunningChangedRaised = false;
|
||||
bool? receivedValue = null;
|
||||
@@ -112,7 +103,7 @@ public class ApplicationRunnableIntegrationTests (ITestOutputHelper output) : ID
|
||||
public void Begin_RaisesIsRunningChangingEvent ()
|
||||
{
|
||||
// Arrange
|
||||
IApplication app = GetApp ();
|
||||
IApplication app = CreateAndInitApp ();
|
||||
Runnable<int> runnable = new ();
|
||||
var isRunningChangingRaised = false;
|
||||
bool? oldValue = null;
|
||||
@@ -141,7 +132,7 @@ public class ApplicationRunnableIntegrationTests (ITestOutputHelper output) : ID
|
||||
public void Begin_SetsIsModalToTrue ()
|
||||
{
|
||||
// Arrange
|
||||
IApplication app = GetApp ();
|
||||
IApplication app = CreateAndInitApp ();
|
||||
Runnable<int> runnable = new ();
|
||||
|
||||
// Act
|
||||
@@ -158,7 +149,7 @@ public class ApplicationRunnableIntegrationTests (ITestOutputHelper output) : ID
|
||||
public void Begin_SetsIsRunningToTrue ()
|
||||
{
|
||||
// Arrange
|
||||
IApplication app = GetApp ();
|
||||
IApplication app = CreateAndInitApp ();
|
||||
Runnable<int> runnable = new ();
|
||||
|
||||
// Act
|
||||
@@ -175,7 +166,7 @@ public class ApplicationRunnableIntegrationTests (ITestOutputHelper output) : ID
|
||||
public void Begin_ThrowsOnNullRunnable ()
|
||||
{
|
||||
// Arrange
|
||||
IApplication app = GetApp ();
|
||||
IApplication app = CreateAndInitApp ();
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentNullException> (() => app.Begin ((IRunnable)null!));
|
||||
@@ -185,7 +176,7 @@ public class ApplicationRunnableIntegrationTests (ITestOutputHelper output) : ID
|
||||
public void End_CanBeCanceled_ByIsRunningChanging ()
|
||||
{
|
||||
// Arrange
|
||||
IApplication app = GetApp ();
|
||||
IApplication app = CreateAndInitApp ();
|
||||
CancelableRunnable runnable = new () { CancelStop = true };
|
||||
SessionToken? token = app.Begin (runnable);
|
||||
runnable.CancelStop = true; // Enable cancellation
|
||||
@@ -205,7 +196,7 @@ public class ApplicationRunnableIntegrationTests (ITestOutputHelper output) : ID
|
||||
public void End_ClearsTokenRunnable ()
|
||||
{
|
||||
// Arrange
|
||||
IApplication app = GetApp ();
|
||||
IApplication app = CreateAndInitApp ();
|
||||
Runnable<int> runnable = new ();
|
||||
SessionToken? token = app.Begin (runnable);
|
||||
|
||||
@@ -220,7 +211,7 @@ public class ApplicationRunnableIntegrationTests (ITestOutputHelper output) : ID
|
||||
public void End_RaisesIsRunningChangedEvent ()
|
||||
{
|
||||
// Arrange
|
||||
IApplication app = GetApp ();
|
||||
IApplication app = CreateAndInitApp ();
|
||||
Runnable<int> runnable = new ();
|
||||
SessionToken? token = app.Begin (runnable);
|
||||
var isRunningChangedRaised = false;
|
||||
@@ -244,7 +235,7 @@ public class ApplicationRunnableIntegrationTests (ITestOutputHelper output) : ID
|
||||
public void End_RaisesIsRunningChangingEvent ()
|
||||
{
|
||||
// Arrange
|
||||
IApplication app = GetApp ();
|
||||
IApplication app = CreateAndInitApp ();
|
||||
Runnable<int> runnable = new ();
|
||||
SessionToken? token = app.Begin (runnable);
|
||||
var isRunningChangingRaised = false;
|
||||
@@ -271,7 +262,7 @@ public class ApplicationRunnableIntegrationTests (ITestOutputHelper output) : ID
|
||||
public void End_RemovesRunnableFromStack ()
|
||||
{
|
||||
// Arrange
|
||||
IApplication app = GetApp ();
|
||||
IApplication app = CreateAndInitApp ();
|
||||
Runnable<int> runnable = new ();
|
||||
SessionToken? token = app.Begin (runnable);
|
||||
int stackCountBefore = app.SessionStack?.Count ?? 0;
|
||||
@@ -287,7 +278,7 @@ public class ApplicationRunnableIntegrationTests (ITestOutputHelper output) : ID
|
||||
public void End_SetsIsModalToFalse ()
|
||||
{
|
||||
// Arrange
|
||||
IApplication app = GetApp ();
|
||||
IApplication app = CreateAndInitApp ();
|
||||
Runnable<int> runnable = new ();
|
||||
SessionToken? token = app.Begin (runnable);
|
||||
|
||||
@@ -302,7 +293,7 @@ public class ApplicationRunnableIntegrationTests (ITestOutputHelper output) : ID
|
||||
public void End_SetsIsRunningToFalse ()
|
||||
{
|
||||
// Arrange
|
||||
IApplication app = GetApp ();
|
||||
IApplication app = CreateAndInitApp ();
|
||||
Runnable<int> runnable = new ();
|
||||
SessionToken? token = app.Begin (runnable);
|
||||
|
||||
@@ -317,17 +308,33 @@ public class ApplicationRunnableIntegrationTests (ITestOutputHelper output) : ID
|
||||
public void End_ThrowsOnNullToken ()
|
||||
{
|
||||
// Arrange
|
||||
IApplication app = GetApp ();
|
||||
IApplication app = CreateAndInitApp ();
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentNullException> (() => app.End ((SessionToken)null!));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void End_ClearsMouseGrabView ()
|
||||
{
|
||||
// Arrange
|
||||
IApplication app = CreateAndInitApp ();
|
||||
|
||||
Runnable<int> runnable = new ();
|
||||
SessionToken? token = app.Begin (runnable);
|
||||
app.Mouse.GrabMouse (runnable);
|
||||
app.End (token!);
|
||||
|
||||
Assert.Null (app.Mouse.MouseGrabView);
|
||||
|
||||
runnable.Dispose ();
|
||||
app.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MultipleRunnables_IndependentResults ()
|
||||
{
|
||||
// Arrange
|
||||
IApplication app = GetApp ();
|
||||
Runnable<int> runnable1 = new ();
|
||||
Runnable<string> runnable2 = new ();
|
||||
|
||||
@@ -344,7 +351,7 @@ public class ApplicationRunnableIntegrationTests (ITestOutputHelper output) : ID
|
||||
public void NestedBegin_MaintainsStackOrder ()
|
||||
{
|
||||
// Arrange
|
||||
IApplication app = GetApp ();
|
||||
IApplication app = CreateAndInitApp ();
|
||||
Runnable<int> runnable1 = new () { Id = "1" };
|
||||
Runnable<int> runnable2 = new () { Id = "2" };
|
||||
|
||||
@@ -367,7 +374,7 @@ public class ApplicationRunnableIntegrationTests (ITestOutputHelper output) : ID
|
||||
public void NestedEnd_RestoresPreviousModal ()
|
||||
{
|
||||
// Arrange
|
||||
IApplication app = GetApp ();
|
||||
IApplication app = CreateAndInitApp ();
|
||||
Runnable<int> runnable1 = new () { Id = "1" };
|
||||
Runnable<int> runnable2 = new () { Id = "2" };
|
||||
SessionToken token1 = app.Begin (runnable1)!;
|
||||
@@ -390,7 +397,7 @@ public class ApplicationRunnableIntegrationTests (ITestOutputHelper output) : ID
|
||||
public void RequestStop_WithIRunnable_WorksCorrectly ()
|
||||
{
|
||||
// Arrange
|
||||
IApplication app = GetApp ();
|
||||
IApplication app = CreateAndInitApp ();
|
||||
StoppableRunnable runnable = new ();
|
||||
SessionToken? token = app.Begin (runnable);
|
||||
|
||||
@@ -409,7 +416,7 @@ public class ApplicationRunnableIntegrationTests (ITestOutputHelper output) : ID
|
||||
public void RequestStop_WithNull_UsesTopRunnable ()
|
||||
{
|
||||
// Arrange
|
||||
IApplication app = GetApp ();
|
||||
IApplication app = CreateAndInitApp ();
|
||||
StoppableRunnable runnable = new ();
|
||||
SessionToken? token = app.Begin (runnable);
|
||||
|
||||
@@ -427,7 +434,7 @@ public class ApplicationRunnableIntegrationTests (ITestOutputHelper output) : ID
|
||||
public void RunGeneric_CreatesAndReturnsRunnable ()
|
||||
{
|
||||
// Arrange
|
||||
IApplication app = GetApp ();
|
||||
IApplication app = CreateAndInitApp ();
|
||||
app.StopAfterFirstIteration = true;
|
||||
|
||||
// Act - With fluent API, Run<T>() returns IApplication for chaining
|
||||
@@ -456,15 +463,12 @@ public class ApplicationRunnableIntegrationTests (ITestOutputHelper output) : ID
|
||||
app.Dispose ();
|
||||
}
|
||||
|
||||
private IApplication GetApp ()
|
||||
private IApplication CreateAndInitApp ()
|
||||
{
|
||||
if (_app is null)
|
||||
{
|
||||
_app = Application.Create ();
|
||||
_app.Init ("fake");
|
||||
}
|
||||
IApplication app = Application.Create ();
|
||||
app.Init ("fake");
|
||||
|
||||
return _app;
|
||||
return app;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,17 +1,44 @@
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace ApplicationTests;
|
||||
namespace ApplicationTests.Screen;
|
||||
|
||||
/// <summary>
|
||||
/// Parallelizable tests for IApplication.ScreenChanged event and Screen property.
|
||||
/// Tests using the modern instance-based IApplication API.
|
||||
/// </summary>
|
||||
public class IApplicationScreenChangedTests (ITestOutputHelper output)
|
||||
public class ScreenTests (ITestOutputHelper output)
|
||||
{
|
||||
private readonly ITestOutputHelper _output = output;
|
||||
|
||||
#region ScreenChanged Event Tests
|
||||
|
||||
[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 ScreenChanged_Event_Fires_When_Driver_Size_Changes ()
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace ApplicationTests;
|
||||
namespace ApplicationTests.Timeout;
|
||||
|
||||
public class LogarithmicTimeoutTests
|
||||
{
|
||||
@@ -1,5 +1,4 @@
|
||||
namespace ApplicationTests;
|
||||
|
||||
namespace ApplicationTests.Timeout;
|
||||
|
||||
public class SmoothAcceleratingTimeoutTests
|
||||
{
|
||||
@@ -853,4 +853,23 @@ public class TimeoutTests
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[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 ();
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
using Terminal.Gui.App;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace ApplicationTests;
|
||||
namespace ApplicationTests.Mouse;
|
||||
|
||||
/// <summary>
|
||||
/// Parallelizable tests for mouse event routing and coordinate transformation.
|
||||
@@ -283,10 +283,10 @@ public class MouseEventRoutingTests (ITestOutputHelper output)
|
||||
#region Mouse Button Events
|
||||
|
||||
[Theory]
|
||||
[InlineData (MouseFlags.Button1Pressed, 1, 0, 0)]
|
||||
[InlineData (MouseFlags.Button1Released, 0, 1, 0)]
|
||||
[InlineData (MouseFlags.Button1Clicked, 0, 0, 1)]
|
||||
public void View_MouseButtonEvents_RaiseCorrectHandlers (MouseFlags flags, int expectedPressed, int expectedReleased, int expectedClicked)
|
||||
[InlineData (MouseFlags.Button1Pressed, 1, 0)]
|
||||
[InlineData (MouseFlags.Button1Released, 0, 1)]
|
||||
[InlineData (MouseFlags.Button1Clicked, 0, 0)]
|
||||
public void View_MouseButtonEvents_RaiseCorrectHandlers (MouseFlags flags, int expectedPressed, int expectedReleased)
|
||||
{
|
||||
// Arrange
|
||||
View view = new () { Width = 10, Height = 10 };
|
||||
|
||||
Reference in New Issue
Block a user