| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452 |
- 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
- }
|