mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-26 15:57:56 +01:00
Update `RaiseScreenChangedEvent` to no longer explicitly set the `Screen` property. Remove unnecessary `.ToArray()` conversion in `View.Draw`. Clarify `Screen` property documentation in `IApplication` to specify constraints on location and size. Update tests to reflect the new behavior where setting the `Screen` property raises the `ScreenChanged` event. Rename and adjust test cases accordingly.
453 lines
12 KiB
C#
453 lines
12 KiB
C#
using Xunit.Abstractions;
|
|
|
|
namespace ApplicationTests;
|
|
|
|
/// <summary>
|
|
/// Parallelizable tests for IApplication.ScreenChanged event and Screen property.
|
|
/// Tests using the modern instance-based IApplication API.
|
|
/// </summary>
|
|
public class IApplicationScreenChangedTests (ITestOutputHelper output)
|
|
{
|
|
private readonly ITestOutputHelper _output = output;
|
|
|
|
#region ScreenChanged Event Tests
|
|
|
|
[Fact]
|
|
public void ScreenChanged_Event_Fires_When_Driver_Size_Changes ()
|
|
{
|
|
// Arrange
|
|
using IApplication app = Application.Create ();
|
|
app.Init ("fake");
|
|
|
|
var eventFired = false;
|
|
Rectangle? newScreen = null;
|
|
|
|
EventHandler<EventArgs<Rectangle>> handler = (sender, args) =>
|
|
{
|
|
eventFired = true;
|
|
newScreen = args.Value;
|
|
};
|
|
|
|
app.ScreenChanged += handler;
|
|
|
|
try
|
|
{
|
|
// Act
|
|
app.Driver!.SetScreenSize (100, 40);
|
|
|
|
// Assert
|
|
Assert.True (eventFired);
|
|
Assert.NotNull (newScreen);
|
|
Assert.Equal (new (0, 0, 100, 40), newScreen.Value);
|
|
}
|
|
finally
|
|
{
|
|
app.ScreenChanged -= handler;
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void ScreenChanged_Event_Updates_Application_Screen_Property ()
|
|
{
|
|
// Arrange
|
|
using IApplication app = Application.Create ();
|
|
app.Init ("fake");
|
|
|
|
Rectangle initialScreen = app.Screen;
|
|
Assert.Equal (new (0, 0, 80, 25), initialScreen);
|
|
|
|
// Act
|
|
app.Driver!.SetScreenSize (120, 50);
|
|
|
|
// Assert
|
|
Assert.Equal (new (0, 0, 120, 50), app.Screen);
|
|
}
|
|
|
|
[Fact]
|
|
public void ScreenChanged_Event_Sender_Is_IApplication ()
|
|
{
|
|
// Arrange
|
|
using IApplication app = Application.Create ();
|
|
app.Init ("fake");
|
|
|
|
object? eventSender = null;
|
|
|
|
EventHandler<EventArgs<Rectangle>> handler = (sender, args) => { eventSender = sender; };
|
|
|
|
app.ScreenChanged += handler;
|
|
|
|
try
|
|
{
|
|
// Act
|
|
app.Driver!.SetScreenSize (100, 30);
|
|
|
|
// Assert
|
|
Assert.NotNull (eventSender);
|
|
Assert.IsAssignableFrom<IApplication> (eventSender);
|
|
}
|
|
finally
|
|
{
|
|
app.ScreenChanged -= handler;
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void ScreenChanged_Event_Provides_Correct_Rectangle_In_EventArgs ()
|
|
{
|
|
// Arrange
|
|
using IApplication app = Application.Create ();
|
|
app.Init ("fake");
|
|
|
|
Rectangle? capturedRectangle = null;
|
|
|
|
EventHandler<EventArgs<Rectangle>> handler = (sender, args) => { capturedRectangle = args.Value; };
|
|
|
|
app.ScreenChanged += handler;
|
|
|
|
try
|
|
{
|
|
// Act
|
|
app.Driver!.SetScreenSize (200, 60);
|
|
|
|
// Assert
|
|
Assert.NotNull (capturedRectangle);
|
|
Assert.Equal (0, capturedRectangle.Value.X);
|
|
Assert.Equal (0, capturedRectangle.Value.Y);
|
|
Assert.Equal (200, capturedRectangle.Value.Width);
|
|
Assert.Equal (60, capturedRectangle.Value.Height);
|
|
}
|
|
finally
|
|
{
|
|
app.ScreenChanged -= handler;
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void ScreenChanged_Event_Fires_Multiple_Times_For_Multiple_Resizes ()
|
|
{
|
|
// Arrange
|
|
using IApplication app = Application.Create ();
|
|
app.Init ("fake");
|
|
|
|
var eventCount = 0;
|
|
List<Size> sizes = new ();
|
|
|
|
EventHandler<EventArgs<Rectangle>> handler = (sender, args) =>
|
|
{
|
|
eventCount++;
|
|
sizes.Add (args.Value.Size);
|
|
};
|
|
|
|
app.ScreenChanged += handler;
|
|
|
|
try
|
|
{
|
|
// Act
|
|
app.Driver!.SetScreenSize (100, 30);
|
|
app.Driver!.SetScreenSize (120, 40);
|
|
app.Driver!.SetScreenSize (80, 25);
|
|
|
|
// Assert
|
|
Assert.Equal (3, eventCount);
|
|
Assert.Equal (3, sizes.Count);
|
|
Assert.Equal (new (100, 30), sizes [0]);
|
|
Assert.Equal (new (120, 40), sizes [1]);
|
|
Assert.Equal (new (80, 25), sizes [2]);
|
|
}
|
|
finally
|
|
{
|
|
app.ScreenChanged -= handler;
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void ScreenChanged_Event_Does_Not_Fire_When_No_Resize_Occurs ()
|
|
{
|
|
// Arrange
|
|
using IApplication app = Application.Create ();
|
|
app.Init ("fake");
|
|
|
|
var eventFired = false;
|
|
|
|
EventHandler<EventArgs<Rectangle>> handler = (sender, args) => { eventFired = true; };
|
|
|
|
app.ScreenChanged += handler;
|
|
|
|
try
|
|
{
|
|
// Act - Don't resize, just access Screen property
|
|
Rectangle screen = app.Screen;
|
|
|
|
// Assert
|
|
Assert.False (eventFired);
|
|
Assert.Equal (new (0, 0, 80, 25), screen);
|
|
}
|
|
finally
|
|
{
|
|
app.ScreenChanged -= handler;
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void ScreenChanged_Event_Can_Be_Unsubscribed ()
|
|
{
|
|
// Arrange
|
|
using IApplication app = Application.Create ();
|
|
app.Init ("fake");
|
|
|
|
var eventCount = 0;
|
|
|
|
EventHandler<EventArgs<Rectangle>> handler = (sender, args) => { eventCount++; };
|
|
|
|
app.ScreenChanged += handler;
|
|
|
|
// Act - First resize should fire
|
|
app.Driver!.SetScreenSize (100, 30);
|
|
Assert.Equal (1, eventCount);
|
|
|
|
// Unsubscribe
|
|
app.ScreenChanged -= handler;
|
|
|
|
// Second resize should not fire
|
|
app.Driver!.SetScreenSize (120, 40);
|
|
|
|
// Assert
|
|
Assert.Equal (1, eventCount);
|
|
}
|
|
|
|
[Fact]
|
|
public void ScreenChanged_Event_Sets_Runnables_To_NeedsLayout ()
|
|
{
|
|
// Arrange
|
|
using IApplication app = Application.Create ();
|
|
app.Init ("fake");
|
|
|
|
using var runnable = new Runnable ();
|
|
SessionToken? token = app.Begin (runnable);
|
|
|
|
Assert.NotNull (app.TopRunnableView);
|
|
app.LayoutAndDraw ();
|
|
|
|
// Clear the NeedsLayout flag
|
|
Assert.False (app.TopRunnableView.NeedsLayout);
|
|
|
|
try
|
|
{
|
|
// Act
|
|
app.Driver!.SetScreenSize (100, 30);
|
|
|
|
// Assert
|
|
Assert.True (app.TopRunnableView.NeedsLayout);
|
|
}
|
|
finally
|
|
{
|
|
// Cleanup
|
|
if (token is { })
|
|
{
|
|
app.End (token);
|
|
}
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void ScreenChanged_Event_Handles_Multiple_Runnables_In_Session_Stack ()
|
|
{
|
|
// Arrange
|
|
using IApplication app = Application.Create ();
|
|
app.Init ("fake");
|
|
|
|
using var runnable1 = new Runnable ();
|
|
SessionToken? token1 = app.Begin (runnable1);
|
|
app.LayoutAndDraw ();
|
|
|
|
using var runnable2 = new Runnable ();
|
|
SessionToken? token2 = app.Begin (runnable2);
|
|
app.LayoutAndDraw ();
|
|
|
|
// Both should not need layout after drawing
|
|
Assert.False (runnable1.NeedsLayout);
|
|
Assert.False (runnable2.NeedsLayout);
|
|
|
|
try
|
|
{
|
|
// Act - Resize should mark both as needing layout
|
|
app.Driver!.SetScreenSize (100, 30);
|
|
|
|
// Assert
|
|
Assert.True (runnable1.NeedsLayout);
|
|
Assert.True (runnable2.NeedsLayout);
|
|
}
|
|
finally
|
|
{
|
|
// Cleanup
|
|
if (token2 is { })
|
|
{
|
|
app.End (token2);
|
|
}
|
|
|
|
if (token1 is { })
|
|
{
|
|
app.End (token1);
|
|
}
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void ScreenChanged_Event_With_No_Active_Runnables_Does_Not_Throw ()
|
|
{
|
|
// Arrange
|
|
using IApplication app = Application.Create ();
|
|
app.Init ("fake");
|
|
|
|
var eventFired = false;
|
|
|
|
EventHandler<EventArgs<Rectangle>> handler = (sender, args) => { eventFired = true; };
|
|
|
|
app.ScreenChanged += handler;
|
|
|
|
try
|
|
{
|
|
// Act - Resize with no runnables
|
|
Exception? exception = Record.Exception (() => app.Driver!.SetScreenSize (100, 30));
|
|
|
|
// Assert
|
|
Assert.Null (exception);
|
|
Assert.True (eventFired);
|
|
}
|
|
finally
|
|
{
|
|
app.ScreenChanged -= handler;
|
|
}
|
|
}
|
|
|
|
#endregion ScreenChanged Event Tests
|
|
|
|
#region Screen Property Tests
|
|
|
|
[Fact]
|
|
public void Screen_Property_Returns_Driver_Screen_When_Not_Set ()
|
|
{
|
|
// Arrange
|
|
using IApplication app = Application.Create ();
|
|
app.Init ("fake");
|
|
|
|
// Act
|
|
Rectangle screen = app.Screen;
|
|
|
|
// Assert
|
|
Assert.Equal (app.Driver!.Screen, screen);
|
|
Assert.Equal (new (0, 0, 80, 25), screen);
|
|
}
|
|
|
|
[Fact]
|
|
public void Screen_Property_Returns_Default_Size_When_Driver_Not_Initialized ()
|
|
{
|
|
// Arrange
|
|
using IApplication app = Application.Create ();
|
|
|
|
// Act - Don't call Init
|
|
Rectangle screen = app.Screen;
|
|
|
|
// Assert - Should return default size
|
|
Assert.Equal (new (0, 0, 2048, 2048), screen);
|
|
}
|
|
|
|
[Fact]
|
|
public void Screen_Property_Throws_When_Setting_Non_Zero_Origin ()
|
|
{
|
|
// Arrange
|
|
using IApplication app = Application.Create ();
|
|
app.Init ("fake");
|
|
|
|
// Act & Assert
|
|
var exception = Assert.Throws<NotImplementedException> (() =>
|
|
app.Screen = new (10, 10, 80, 25));
|
|
|
|
Assert.Contains ("Screen locations other than 0, 0", exception.Message);
|
|
}
|
|
|
|
[Fact]
|
|
public void Screen_Property_Allows_Setting_With_Zero_Origin ()
|
|
{
|
|
// Arrange
|
|
using IApplication app = Application.Create ();
|
|
app.Init ("fake");
|
|
|
|
// Act
|
|
Exception? exception = Record.Exception (() =>
|
|
app.Screen = new (0, 0, 100, 50));
|
|
|
|
// Assert
|
|
Assert.Null (exception);
|
|
Assert.Equal (new (0, 0, 100, 50), app.Screen);
|
|
}
|
|
|
|
[Fact]
|
|
public void Screen_Property_Setting_Raises_ScreenChanged_Event ()
|
|
{
|
|
// Arrange
|
|
using IApplication app = Application.Create ();
|
|
app.Init ("fake");
|
|
|
|
var eventFired = false;
|
|
|
|
EventHandler<EventArgs<Rectangle>> handler = (sender, args) => { eventFired = true; };
|
|
|
|
app.ScreenChanged += handler;
|
|
|
|
try
|
|
{
|
|
// Act - Manually set Screen property
|
|
app.Screen = new (0, 0, 100, 50);
|
|
|
|
Assert.True (eventFired);
|
|
Assert.Equal (new (0, 0, 100, 50), app.Screen);
|
|
}
|
|
finally
|
|
{
|
|
app.ScreenChanged -= handler;
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Screen_Property_Thread_Safe_Access ()
|
|
{
|
|
// Arrange
|
|
using IApplication app = Application.Create ();
|
|
app.Init ("fake");
|
|
|
|
List<Exception> exceptions = new ();
|
|
List<Task> tasks = new ();
|
|
|
|
// Act - Access Screen property from multiple threads
|
|
for (var i = 0; i < 10; i++)
|
|
{
|
|
tasks.Add (
|
|
Task.Run (() =>
|
|
{
|
|
try
|
|
{
|
|
Rectangle screen = app.Screen;
|
|
Assert.NotEqual (Rectangle.Empty, screen);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
lock (exceptions)
|
|
{
|
|
exceptions.Add (ex);
|
|
}
|
|
}
|
|
}));
|
|
}
|
|
|
|
#pragma warning disable xUnit1031
|
|
Task.WaitAll (tasks.ToArray ());
|
|
#pragma warning restore xUnit1031
|
|
|
|
// Assert - No exceptions should occur
|
|
Assert.Empty (exceptions);
|
|
}
|
|
|
|
#endregion Screen Property Tests
|
|
}
|