mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2026-01-01 08:50:25 +01:00
* Initial plan * Refactor Application.Mouse - Create IMouse interface and Mouse implementation Co-authored-by: tig <585482+tig@users.noreply.github.com> * Add enhanced documentation for Application.Mouse property Co-authored-by: tig <585482+tig@users.noreply.github.com> * Add parallelizable unit tests for IMouse interface Co-authored-by: tig <585482+tig@users.noreply.github.com> * Refactor Application.Mouse for decoupling and parallelism Co-authored-by: tig <585482+tig@users.noreply.github.com> * Move HandleMouseGrab method to IMouseGrabHandler interface Co-authored-by: tig <585482+tig@users.noreply.github.com> * Add parallelizable tests for IMouse and IMouseGrabHandler interfaces Co-authored-by: tig <585482+tig@users.noreply.github.com> * Add MouseEventRoutingTests - 27 parallelizable tests for View mouse event handling Co-authored-by: tig <585482+tig@users.noreply.github.com> * Fix terminology: Replace parent/child with superView/subView in MouseEventRoutingTests Co-authored-by: tig <585482+tig@users.noreply.github.com> * Fix coding standards: Use explicit types and target-typed new() in test files Co-authored-by: tig <585482+tig@users.noreply.github.com> * Update coding standards documentation with explicit var and target-typed new() guidance Co-authored-by: tig <585482+tig@users.noreply.github.com> * Refactor Application classes and improve maintainability Refactored `Sixel` property to be immutable, enhancing thread safety. Cleaned up `ApplicationImpl` by removing redundant fields, restructuring methods (`CreateDriver`, `CreateSubcomponents`), and improving exception handling. Updated `Run<T>` and `Shutdown` methods for consistency. Standardized logging/debugging messages and fixed formatting issues. Reorganized `IApplication` interface, added detailed XML documentation, and grouped related methods logically. Performed general code cleanup, including fixing typos, improving readability, and removing legacy/unnecessary code to reduce technical debt. * Code cleanup * Remove unreferenced LayoutAndDraw method from ApplicationImpl * Code cleanup and TODOs - Updated namespaces to reflect the new structure. - Added `Driver`, `Force16Colors`, and `ForceDriver` properties. - Introduced `Sixel` collection for sixel image management. - Added lifecycle methods: `GetDriverTypes`, `Shutdown`, and events. - Refactored `Init` to support legacy and modern drivers. - Improved driver event handling and screen abstraction. - Updated `Run` method to align with the application lifecycle. - Simplified `IConsoleDriver` documentation. - Removed redundant methods and improved code readability. * Refactor LayoutAndDraw logic for better encapsulation Refactored `Application.Run` to delegate `LayoutAndDraw` to `ApplicationImpl.Instance.LayoutAndDraw`, improving separation of concerns. Renamed `forceDraw` to `forceRedraw` for clarity and moved `LayoutAndDraw` implementation to `ApplicationImpl`. Added a new `LayoutAndDraw` method in `ApplicationImpl` to handle layout and drawing, including managing `TopLevels`, handling active popovers, and refreshing the screen. Updated the `IApplication` interface to reflect the new method and improved its documentation. Implemented `RequestStop` in `ApplicationImpl` and fixed formatting inconsistencies in `Run<T>`. Added TODOs for future refactoring to encapsulate `Top` and `TopLevels` into an `IViewHierarchy` and move certain properties to `IApplication`. * Refactor ApplicationImpl to enhance mouse and keyboard support Added a new `Mouse` property to the `ApplicationImpl` class, replacing its previous declaration, to improve mouse functionality. Updated `MouseGrabHandler` to initialize with a default instance of `MouseGrabHandler`. Added comments to ensure the preservation of existing keyboard settings (`QuitKey`, `ArrangeKey`, `NextTabKey`) for backward compatibility. These changes enhance clarity, functionality, and maintainability of the class. * Merge IMouseGrabHandler into IMouse - consolidate mouse handling into single interface Co-authored-by: tig <585482+tig@users.noreply.github.com> * Rename Mouse to MouseImpl and Keyboard to KeyboardImpl for consistency Co-authored-by: tig <585482+tig@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: tig <585482+tig@users.noreply.github.com> Co-authored-by: Tig <tig@users.noreply.github.com>
This commit is contained in:
@@ -13,7 +13,7 @@ public class KeyboardTests
|
||||
public void Constructor_InitializesKeyBindings ()
|
||||
{
|
||||
// Arrange & Act
|
||||
var keyboard = new Keyboard ();
|
||||
var keyboard = new KeyboardImpl ();
|
||||
|
||||
// Assert
|
||||
Assert.NotNull (keyboard.KeyBindings);
|
||||
@@ -25,7 +25,7 @@ public class KeyboardTests
|
||||
public void QuitKey_DefaultValue_IsEsc ()
|
||||
{
|
||||
// Arrange
|
||||
var keyboard = new Keyboard ();
|
||||
var keyboard = new KeyboardImpl ();
|
||||
|
||||
// Assert
|
||||
Assert.Equal (Key.Esc, keyboard.QuitKey);
|
||||
@@ -35,7 +35,7 @@ public class KeyboardTests
|
||||
public void QuitKey_SetValue_UpdatesKeyBindings ()
|
||||
{
|
||||
// Arrange
|
||||
var keyboard = new Keyboard ();
|
||||
var keyboard = new KeyboardImpl ();
|
||||
Key newQuitKey = Key.Q.WithCtrl;
|
||||
|
||||
// Act
|
||||
@@ -51,7 +51,7 @@ public class KeyboardTests
|
||||
public void ArrangeKey_DefaultValue_IsCtrlF5 ()
|
||||
{
|
||||
// Arrange
|
||||
var keyboard = new Keyboard ();
|
||||
var keyboard = new KeyboardImpl ();
|
||||
|
||||
// Assert
|
||||
Assert.Equal (Key.F5.WithCtrl, keyboard.ArrangeKey);
|
||||
@@ -61,7 +61,7 @@ public class KeyboardTests
|
||||
public void NextTabKey_DefaultValue_IsTab ()
|
||||
{
|
||||
// Arrange
|
||||
var keyboard = new Keyboard ();
|
||||
var keyboard = new KeyboardImpl ();
|
||||
|
||||
// Assert
|
||||
Assert.Equal (Key.Tab, keyboard.NextTabKey);
|
||||
@@ -71,7 +71,7 @@ public class KeyboardTests
|
||||
public void PrevTabKey_DefaultValue_IsShiftTab ()
|
||||
{
|
||||
// Arrange
|
||||
var keyboard = new Keyboard ();
|
||||
var keyboard = new KeyboardImpl ();
|
||||
|
||||
// Assert
|
||||
Assert.Equal (Key.Tab.WithShift, keyboard.PrevTabKey);
|
||||
@@ -81,7 +81,7 @@ public class KeyboardTests
|
||||
public void NextTabGroupKey_DefaultValue_IsF6 ()
|
||||
{
|
||||
// Arrange
|
||||
var keyboard = new Keyboard ();
|
||||
var keyboard = new KeyboardImpl ();
|
||||
|
||||
// Assert
|
||||
Assert.Equal (Key.F6, keyboard.NextTabGroupKey);
|
||||
@@ -91,7 +91,7 @@ public class KeyboardTests
|
||||
public void PrevTabGroupKey_DefaultValue_IsShiftF6 ()
|
||||
{
|
||||
// Arrange
|
||||
var keyboard = new Keyboard ();
|
||||
var keyboard = new KeyboardImpl ();
|
||||
|
||||
// Assert
|
||||
Assert.Equal (Key.F6.WithShift, keyboard.PrevTabGroupKey);
|
||||
@@ -101,7 +101,7 @@ public class KeyboardTests
|
||||
public void KeyBindings_Add_CanAddCustomBinding ()
|
||||
{
|
||||
// Arrange
|
||||
var keyboard = new Keyboard ();
|
||||
var keyboard = new KeyboardImpl ();
|
||||
Key customKey = Key.K.WithCtrl;
|
||||
|
||||
// Act
|
||||
@@ -116,7 +116,7 @@ public class KeyboardTests
|
||||
public void KeyBindings_Remove_CanRemoveBinding ()
|
||||
{
|
||||
// Arrange
|
||||
var keyboard = new Keyboard ();
|
||||
var keyboard = new KeyboardImpl ();
|
||||
Key customKey = Key.K.WithCtrl;
|
||||
keyboard.KeyBindings.Add (customKey, Command.Accept);
|
||||
|
||||
@@ -131,7 +131,7 @@ public class KeyboardTests
|
||||
public void KeyDown_Event_CanBeSubscribed ()
|
||||
{
|
||||
// Arrange
|
||||
var keyboard = new Keyboard ();
|
||||
var keyboard = new KeyboardImpl ();
|
||||
bool eventRaised = false;
|
||||
|
||||
// Act
|
||||
@@ -148,7 +148,7 @@ public class KeyboardTests
|
||||
public void KeyUp_Event_CanBeSubscribed ()
|
||||
{
|
||||
// Arrange
|
||||
var keyboard = new Keyboard ();
|
||||
var keyboard = new KeyboardImpl ();
|
||||
bool eventRaised = false;
|
||||
|
||||
// Act
|
||||
@@ -165,7 +165,7 @@ public class KeyboardTests
|
||||
public void InvokeCommand_WithInvalidCommand_ThrowsNotSupportedException ()
|
||||
{
|
||||
// Arrange
|
||||
var keyboard = new Keyboard ();
|
||||
var keyboard = new KeyboardImpl ();
|
||||
// Pick a command that isn't registered
|
||||
Command invalidCommand = (Command)9999;
|
||||
Key testKey = Key.A;
|
||||
@@ -179,8 +179,8 @@ public class KeyboardTests
|
||||
public void Multiple_Keyboards_CanExistIndependently ()
|
||||
{
|
||||
// Arrange & Act
|
||||
var keyboard1 = new Keyboard ();
|
||||
var keyboard2 = new Keyboard ();
|
||||
var keyboard1 = new KeyboardImpl ();
|
||||
var keyboard2 = new KeyboardImpl ();
|
||||
|
||||
keyboard1.QuitKey = Key.Q.WithCtrl;
|
||||
keyboard2.QuitKey = Key.X.WithCtrl;
|
||||
@@ -195,7 +195,7 @@ public class KeyboardTests
|
||||
public void KeyBindings_Replace_UpdatesExistingBinding ()
|
||||
{
|
||||
// Arrange
|
||||
var keyboard = new Keyboard ();
|
||||
var keyboard = new KeyboardImpl ();
|
||||
Key oldKey = Key.Esc;
|
||||
Key newKey = Key.Q.WithCtrl;
|
||||
|
||||
@@ -217,7 +217,7 @@ public class KeyboardTests
|
||||
public void KeyBindings_Clear_RemovesAllBindings ()
|
||||
{
|
||||
// Arrange
|
||||
var keyboard = new Keyboard ();
|
||||
var keyboard = new KeyboardImpl ();
|
||||
// Verify initial state has bindings
|
||||
Assert.True (keyboard.KeyBindings.TryGet (keyboard.QuitKey, out _));
|
||||
|
||||
@@ -232,7 +232,7 @@ public class KeyboardTests
|
||||
public void AddKeyBindings_PopulatesDefaultBindings ()
|
||||
{
|
||||
// Arrange
|
||||
var keyboard = new Keyboard ();
|
||||
var keyboard = new KeyboardImpl ();
|
||||
keyboard.KeyBindings.Clear ();
|
||||
Assert.False (keyboard.KeyBindings.TryGet (keyboard.QuitKey, out _));
|
||||
|
||||
@@ -250,7 +250,7 @@ public class KeyboardTests
|
||||
public void KeyBindings_Add_Adds ()
|
||||
{
|
||||
// Arrange
|
||||
var keyboard = new Keyboard ();
|
||||
var keyboard = new KeyboardImpl ();
|
||||
|
||||
// Act
|
||||
keyboard.KeyBindings.Add (Key.A, Command.Accept);
|
||||
@@ -267,7 +267,7 @@ public class KeyboardTests
|
||||
public void KeyBindings_Remove_Removes ()
|
||||
{
|
||||
// Arrange
|
||||
var keyboard = new Keyboard ();
|
||||
var keyboard = new KeyboardImpl ();
|
||||
keyboard.KeyBindings.Add (Key.A, Command.Accept);
|
||||
Assert.True (keyboard.KeyBindings.TryGet (Key.A, out _));
|
||||
|
||||
@@ -282,7 +282,7 @@ public class KeyboardTests
|
||||
public void QuitKey_Default_Is_Esc ()
|
||||
{
|
||||
// Arrange & Act
|
||||
var keyboard = new Keyboard ();
|
||||
var keyboard = new KeyboardImpl ();
|
||||
|
||||
// Assert
|
||||
Assert.Equal (Key.Esc, keyboard.QuitKey);
|
||||
@@ -292,7 +292,7 @@ public class KeyboardTests
|
||||
public void QuitKey_Setter_UpdatesBindings ()
|
||||
{
|
||||
// Arrange
|
||||
var keyboard = new Keyboard ();
|
||||
var keyboard = new KeyboardImpl ();
|
||||
Key prevKey = keyboard.QuitKey;
|
||||
|
||||
// Act - Change QuitKey
|
||||
@@ -309,7 +309,7 @@ public class KeyboardTests
|
||||
public void NextTabKey_Setter_UpdatesBindings ()
|
||||
{
|
||||
// Arrange
|
||||
var keyboard = new Keyboard ();
|
||||
var keyboard = new KeyboardImpl ();
|
||||
Key prevKey = keyboard.NextTabKey;
|
||||
Key newKey = Key.N.WithCtrl;
|
||||
|
||||
@@ -326,7 +326,7 @@ public class KeyboardTests
|
||||
public void PrevTabKey_Setter_UpdatesBindings ()
|
||||
{
|
||||
// Arrange
|
||||
var keyboard = new Keyboard ();
|
||||
var keyboard = new KeyboardImpl ();
|
||||
Key newKey = Key.P.WithCtrl;
|
||||
|
||||
// Act
|
||||
@@ -342,7 +342,7 @@ public class KeyboardTests
|
||||
public void NextTabGroupKey_Setter_UpdatesBindings ()
|
||||
{
|
||||
// Arrange
|
||||
var keyboard = new Keyboard ();
|
||||
var keyboard = new KeyboardImpl ();
|
||||
Key newKey = Key.PageDown.WithCtrl;
|
||||
|
||||
// Act
|
||||
@@ -359,7 +359,7 @@ public class KeyboardTests
|
||||
public void PrevTabGroupKey_Setter_UpdatesBindings ()
|
||||
{
|
||||
// Arrange
|
||||
var keyboard = new Keyboard ();
|
||||
var keyboard = new KeyboardImpl ();
|
||||
Key newKey = Key.PageUp.WithCtrl;
|
||||
|
||||
// Act
|
||||
@@ -376,7 +376,7 @@ public class KeyboardTests
|
||||
public void ArrangeKey_Setter_UpdatesBindings ()
|
||||
{
|
||||
// Arrange
|
||||
var keyboard = new Keyboard ();
|
||||
var keyboard = new KeyboardImpl ();
|
||||
Key newKey = Key.A.WithCtrl;
|
||||
|
||||
// Act
|
||||
@@ -392,7 +392,7 @@ public class KeyboardTests
|
||||
public void KeyBindings_AddWithTarget_StoresTarget ()
|
||||
{
|
||||
// Arrange
|
||||
var keyboard = new Keyboard ();
|
||||
var keyboard = new KeyboardImpl ();
|
||||
var view = new View ();
|
||||
|
||||
// Act
|
||||
@@ -410,7 +410,7 @@ public class KeyboardTests
|
||||
public void InvokeCommandsBoundToKey_ReturnsNull_WhenNoBindingExists ()
|
||||
{
|
||||
// Arrange
|
||||
var keyboard = new Keyboard ();
|
||||
var keyboard = new KeyboardImpl ();
|
||||
Key unboundKey = Key.Z.WithAlt.WithCtrl;
|
||||
|
||||
// Act
|
||||
@@ -424,7 +424,7 @@ public class KeyboardTests
|
||||
public void InvokeCommandsBoundToKey_InvokesCommand_WhenBindingExists ()
|
||||
{
|
||||
// Arrange
|
||||
var keyboard = new Keyboard ();
|
||||
var keyboard = new KeyboardImpl ();
|
||||
// QuitKey has a bound command by default
|
||||
|
||||
// Act
|
||||
@@ -440,8 +440,8 @@ public class KeyboardTests
|
||||
public void Multiple_Keyboards_Independent_KeyBindings ()
|
||||
{
|
||||
// Arrange
|
||||
var keyboard1 = new Keyboard ();
|
||||
var keyboard2 = new Keyboard ();
|
||||
var keyboard1 = new KeyboardImpl ();
|
||||
var keyboard2 = new KeyboardImpl ();
|
||||
|
||||
// Act
|
||||
keyboard1.KeyBindings.Add (Key.X, Command.Accept);
|
||||
@@ -459,7 +459,7 @@ public class KeyboardTests
|
||||
public void KeyBindings_Replace_PreservesCommandsForNewKey ()
|
||||
{
|
||||
// Arrange
|
||||
var keyboard = new Keyboard ();
|
||||
var keyboard = new KeyboardImpl ();
|
||||
Key oldKey = Key.Esc;
|
||||
Key newKey = Key.Q.WithCtrl;
|
||||
|
||||
|
||||
444
Tests/UnitTestsParallelizable/Application/MouseInterfaceTests.cs
Normal file
444
Tests/UnitTestsParallelizable/Application/MouseInterfaceTests.cs
Normal file
@@ -0,0 +1,444 @@
|
||||
using Terminal.Gui.App;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace UnitTests_Parallelizable.ApplicationTests;
|
||||
|
||||
/// <summary>
|
||||
/// Parallelizable tests for IMouse interface.
|
||||
/// Tests the decoupled mouse handling without Application.Init or global state.
|
||||
/// </summary>
|
||||
[Trait ("Category", "Input")]
|
||||
public class MouseInterfaceTests (ITestOutputHelper output)
|
||||
{
|
||||
private readonly ITestOutputHelper _output = output;
|
||||
|
||||
#region IMouse Basic Properties
|
||||
|
||||
[Fact]
|
||||
public void Mouse_LastMousePosition_InitiallyNull ()
|
||||
{
|
||||
// Arrange
|
||||
MouseImpl mouse = new ();
|
||||
|
||||
// Act & Assert
|
||||
Assert.Null (mouse.LastMousePosition);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (0, 0)]
|
||||
[InlineData (10, 20)]
|
||||
[InlineData (-5, -10)]
|
||||
[InlineData (100, 200)]
|
||||
public void Mouse_LastMousePosition_CanBeSetAndRetrieved (int x, int y)
|
||||
{
|
||||
// Arrange
|
||||
MouseImpl mouse = new ();
|
||||
Point testPosition = new (x, y);
|
||||
|
||||
// Act
|
||||
mouse.LastMousePosition = testPosition;
|
||||
|
||||
// Assert
|
||||
Assert.Equal (testPosition, mouse.LastMousePosition);
|
||||
Assert.Equal (testPosition, mouse.GetLastMousePosition ());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Mouse_IsMouseDisabled_DefaultsFalse ()
|
||||
{
|
||||
// Arrange
|
||||
MouseImpl mouse = new ();
|
||||
|
||||
// Act & Assert
|
||||
Assert.False (mouse.IsMouseDisabled);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (true)]
|
||||
[InlineData (false)]
|
||||
public void Mouse_IsMouseDisabled_CanBeSetAndRetrieved (bool disabled)
|
||||
{
|
||||
// Arrange
|
||||
MouseImpl mouse = new ();
|
||||
|
||||
// Act
|
||||
mouse.IsMouseDisabled = disabled;
|
||||
|
||||
// Assert
|
||||
Assert.Equal (disabled, mouse.IsMouseDisabled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Mouse_CachedViewsUnderMouse_InitiallyEmpty ()
|
||||
{
|
||||
// Arrange
|
||||
MouseImpl mouse = new ();
|
||||
|
||||
// Act & Assert
|
||||
Assert.NotNull (mouse.CachedViewsUnderMouse);
|
||||
Assert.Empty (mouse.CachedViewsUnderMouse);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IMouse Event Handling
|
||||
|
||||
[Fact]
|
||||
public void Mouse_MouseEvent_CanSubscribeAndFire ()
|
||||
{
|
||||
// Arrange
|
||||
MouseImpl mouse = new ();
|
||||
var eventFired = false;
|
||||
MouseEventArgs capturedArgs = null;
|
||||
|
||||
mouse.MouseEvent += (sender, args) =>
|
||||
{
|
||||
eventFired = true;
|
||||
capturedArgs = args;
|
||||
};
|
||||
|
||||
MouseEventArgs testEvent = new ()
|
||||
{
|
||||
ScreenPosition = new Point (5, 10),
|
||||
Flags = MouseFlags.Button1Pressed
|
||||
};
|
||||
|
||||
// Act
|
||||
mouse.RaiseMouseEvent (testEvent);
|
||||
|
||||
// Assert
|
||||
Assert.True (eventFired);
|
||||
Assert.NotNull (capturedArgs);
|
||||
Assert.Equal (testEvent.ScreenPosition, capturedArgs.ScreenPosition);
|
||||
Assert.Equal (testEvent.Flags, capturedArgs.Flags);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Mouse_MouseEvent_CanUnsubscribe ()
|
||||
{
|
||||
// Arrange
|
||||
MouseImpl mouse = new ();
|
||||
var eventCount = 0;
|
||||
|
||||
void Handler (object sender, MouseEventArgs args) => eventCount++;
|
||||
|
||||
mouse.MouseEvent += Handler;
|
||||
|
||||
MouseEventArgs testEvent = new ()
|
||||
{
|
||||
ScreenPosition = new Point (0, 0),
|
||||
Flags = MouseFlags.Button1Pressed
|
||||
};
|
||||
|
||||
// Act - Fire once
|
||||
mouse.RaiseMouseEvent (testEvent);
|
||||
Assert.Equal (1, eventCount);
|
||||
|
||||
// Unsubscribe
|
||||
mouse.MouseEvent -= Handler;
|
||||
|
||||
// Fire again
|
||||
mouse.RaiseMouseEvent (testEvent);
|
||||
|
||||
// Assert - Count should not increase
|
||||
Assert.Equal (1, eventCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Mouse_RaiseMouseEvent_WithDisabledMouse_DoesNotFireEvent ()
|
||||
{
|
||||
// Arrange
|
||||
MouseImpl mouse = new ();
|
||||
var eventFired = false;
|
||||
|
||||
mouse.MouseEvent += (sender, args) => { eventFired = true; };
|
||||
mouse.IsMouseDisabled = true;
|
||||
|
||||
MouseEventArgs testEvent = new ()
|
||||
{
|
||||
ScreenPosition = new Point (0, 0),
|
||||
Flags = MouseFlags.Button1Pressed
|
||||
};
|
||||
|
||||
// Act
|
||||
mouse.RaiseMouseEvent (testEvent);
|
||||
|
||||
// Assert
|
||||
Assert.False (eventFired);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData (MouseFlags.Button1Pressed)]
|
||||
[InlineData (MouseFlags.Button1Released)]
|
||||
[InlineData (MouseFlags.Button1Clicked)]
|
||||
[InlineData (MouseFlags.Button2Pressed)]
|
||||
[InlineData (MouseFlags.WheeledUp)]
|
||||
[InlineData (MouseFlags.ReportMousePosition)]
|
||||
public void Mouse_RaiseMouseEvent_CorrectlyPassesFlags (MouseFlags flags)
|
||||
{
|
||||
// Arrange
|
||||
MouseImpl mouse = new ();
|
||||
MouseFlags? capturedFlags = null;
|
||||
|
||||
mouse.MouseEvent += (sender, args) => { capturedFlags = args.Flags; };
|
||||
|
||||
MouseEventArgs testEvent = new ()
|
||||
{
|
||||
ScreenPosition = new Point (5, 5),
|
||||
Flags = flags
|
||||
};
|
||||
|
||||
// Act
|
||||
mouse.RaiseMouseEvent (testEvent);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull (capturedFlags);
|
||||
Assert.Equal (flags, capturedFlags.Value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IMouse ResetState
|
||||
|
||||
[Fact]
|
||||
public void Mouse_ResetState_ClearsCachedViews ()
|
||||
{
|
||||
// Arrange
|
||||
MouseImpl mouse = new ();
|
||||
View testView = new () { Width = 10, Height = 10 };
|
||||
|
||||
mouse.CachedViewsUnderMouse.Add (testView);
|
||||
Assert.Single (mouse.CachedViewsUnderMouse);
|
||||
|
||||
// Act
|
||||
mouse.ResetState ();
|
||||
|
||||
// Assert
|
||||
Assert.Empty (mouse.CachedViewsUnderMouse);
|
||||
|
||||
testView.Dispose ();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Mouse_ResetState_ClearsEventHandlers ()
|
||||
{
|
||||
// Arrange
|
||||
MouseImpl mouse = new ();
|
||||
var eventCount = 0;
|
||||
|
||||
mouse.MouseEvent += (sender, args) => eventCount++;
|
||||
|
||||
MouseEventArgs testEvent = new ()
|
||||
{
|
||||
ScreenPosition = new Point (0, 0),
|
||||
Flags = MouseFlags.Button1Pressed
|
||||
};
|
||||
|
||||
// Verify event fires before reset
|
||||
mouse.RaiseMouseEvent (testEvent);
|
||||
Assert.Equal (1, eventCount);
|
||||
|
||||
// Act
|
||||
mouse.ResetState ();
|
||||
|
||||
// Raise event again
|
||||
mouse.RaiseMouseEvent (testEvent);
|
||||
|
||||
// Assert - Event count should not increase after reset
|
||||
Assert.Equal (1, eventCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Mouse_ResetState_DoesNotClearLastMousePosition ()
|
||||
{
|
||||
// Arrange
|
||||
MouseImpl mouse = new ();
|
||||
Point testPosition = new (42, 84);
|
||||
|
||||
mouse.LastMousePosition = testPosition;
|
||||
|
||||
// Act
|
||||
mouse.ResetState ();
|
||||
|
||||
// Assert - LastMousePosition should NOT be cleared (per design)
|
||||
Assert.Equal (testPosition, mouse.LastMousePosition);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IMouse Isolation
|
||||
|
||||
[Fact]
|
||||
public void Mouse_Instances_AreIndependent ()
|
||||
{
|
||||
// Arrange
|
||||
MouseImpl mouse1 = new ();
|
||||
MouseImpl mouse2 = new ();
|
||||
|
||||
// Act
|
||||
mouse1.IsMouseDisabled = true;
|
||||
mouse1.LastMousePosition = new Point (10, 10);
|
||||
|
||||
// Assert - mouse2 should be unaffected
|
||||
Assert.False (mouse2.IsMouseDisabled);
|
||||
Assert.Null (mouse2.LastMousePosition);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Mouse_Events_AreIndependent ()
|
||||
{
|
||||
// Arrange
|
||||
MouseImpl mouse1 = new ();
|
||||
var mouse1EventCount = 0;
|
||||
|
||||
MouseImpl mouse2 = new ();
|
||||
var mouse2EventCount = 0;
|
||||
|
||||
mouse1.MouseEvent += (sender, args) => mouse1EventCount++;
|
||||
mouse2.MouseEvent += (sender, args) => mouse2EventCount++;
|
||||
|
||||
MouseEventArgs testEvent = new ()
|
||||
{
|
||||
ScreenPosition = new Point (0, 0),
|
||||
Flags = MouseFlags.Button1Pressed
|
||||
};
|
||||
|
||||
// Act
|
||||
mouse1.RaiseMouseEvent (testEvent);
|
||||
|
||||
// Assert
|
||||
Assert.Equal (1, mouse1EventCount);
|
||||
Assert.Equal (0, mouse2EventCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Mouse_CachedViews_AreIndependent ()
|
||||
{
|
||||
// Arrange
|
||||
MouseImpl mouse1 = new ();
|
||||
MouseImpl mouse2 = new ();
|
||||
|
||||
View view1 = new ();
|
||||
View view2 = new ();
|
||||
|
||||
// Act
|
||||
mouse1.CachedViewsUnderMouse.Add (view1);
|
||||
mouse2.CachedViewsUnderMouse.Add (view2);
|
||||
|
||||
// Assert
|
||||
Assert.Single (mouse1.CachedViewsUnderMouse);
|
||||
Assert.Single (mouse2.CachedViewsUnderMouse);
|
||||
Assert.Contains (view1, mouse1.CachedViewsUnderMouse);
|
||||
Assert.Contains (view2, mouse2.CachedViewsUnderMouse);
|
||||
Assert.DoesNotContain (view2, mouse1.CachedViewsUnderMouse);
|
||||
Assert.DoesNotContain (view1, mouse2.CachedViewsUnderMouse);
|
||||
|
||||
view1.Dispose ();
|
||||
view2.Dispose ();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Mouse Grab Tests
|
||||
|
||||
[Fact]
|
||||
public void Mouse_GrabMouse_SetsMouseGrabView ()
|
||||
{
|
||||
// Arrange
|
||||
MouseImpl mouse = new ();
|
||||
View testView = new ();
|
||||
|
||||
// Act
|
||||
mouse.GrabMouse (testView);
|
||||
|
||||
// Assert
|
||||
Assert.Equal (testView, mouse.MouseGrabView);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Mouse_UngrabMouse_ClearsMouseGrabView ()
|
||||
{
|
||||
// Arrange
|
||||
MouseImpl mouse = new ();
|
||||
View testView = new ();
|
||||
mouse.GrabMouse (testView);
|
||||
|
||||
// Act
|
||||
mouse.UngrabMouse ();
|
||||
|
||||
// Assert
|
||||
Assert.Null (mouse.MouseGrabView);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Mouse_GrabbingMouse_CanBeCanceled ()
|
||||
{
|
||||
// Arrange
|
||||
MouseImpl mouse = new ();
|
||||
View testView = new ();
|
||||
var eventFired = false;
|
||||
|
||||
mouse.GrabbingMouse += (sender, args) =>
|
||||
{
|
||||
eventFired = true;
|
||||
args.Cancel = true;
|
||||
};
|
||||
|
||||
// Act
|
||||
mouse.GrabMouse (testView);
|
||||
|
||||
// Assert
|
||||
Assert.True (eventFired);
|
||||
Assert.Null (mouse.MouseGrabView); // Should not be set because it was cancelled
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Mouse_GrabbedMouse_EventFired ()
|
||||
{
|
||||
// Arrange
|
||||
MouseImpl mouse = new ();
|
||||
View testView = new ();
|
||||
var eventFired = false;
|
||||
View? eventView = null;
|
||||
|
||||
mouse.GrabbedMouse += (sender, args) =>
|
||||
{
|
||||
eventFired = true;
|
||||
eventView = args.View;
|
||||
};
|
||||
|
||||
// Act
|
||||
mouse.GrabMouse (testView);
|
||||
|
||||
// Assert
|
||||
Assert.True (eventFired);
|
||||
Assert.Equal (testView, eventView);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Mouse_UnGrabbedMouse_EventFired ()
|
||||
{
|
||||
// Arrange
|
||||
MouseImpl mouse = new ();
|
||||
View testView = new ();
|
||||
mouse.GrabMouse (testView);
|
||||
|
||||
var eventFired = false;
|
||||
View? eventView = null;
|
||||
|
||||
mouse.UnGrabbedMouse += (sender, args) =>
|
||||
{
|
||||
eventFired = true;
|
||||
eventView = args.View;
|
||||
};
|
||||
|
||||
// Act
|
||||
mouse.UngrabMouse ();
|
||||
|
||||
// Assert
|
||||
Assert.True (eventFired);
|
||||
Assert.Equal (testView, eventView);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
125
Tests/UnitTestsParallelizable/Application/MouseTests.cs
Normal file
125
Tests/UnitTestsParallelizable/Application/MouseTests.cs
Normal file
@@ -0,0 +1,125 @@
|
||||
using Terminal.Gui.App;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace UnitTests_Parallelizable.ApplicationTests;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for the <see cref="IMouse"/> interface and <see cref="MouseImpl"/> implementation.
|
||||
/// These tests demonstrate the decoupled mouse handling that enables parallel test execution.
|
||||
/// </summary>
|
||||
public class MouseTests (ITestOutputHelper output)
|
||||
{
|
||||
private readonly ITestOutputHelper _output = output;
|
||||
|
||||
[Fact]
|
||||
public void Mouse_Instance_CreatedSuccessfully ()
|
||||
{
|
||||
// Arrange & Act
|
||||
MouseImpl mouse = new ();
|
||||
|
||||
// Assert
|
||||
Assert.NotNull (mouse);
|
||||
Assert.False (mouse.IsMouseDisabled);
|
||||
Assert.Null (mouse.LastMousePosition);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Mouse_LastMousePosition_CanBeSetAndRetrieved ()
|
||||
{
|
||||
// Arrange
|
||||
MouseImpl mouse = new ();
|
||||
Point expectedPosition = new (10, 20);
|
||||
|
||||
// Act
|
||||
mouse.LastMousePosition = expectedPosition;
|
||||
Point? actualPosition = mouse.GetLastMousePosition ();
|
||||
|
||||
// Assert
|
||||
Assert.Equal (expectedPosition, actualPosition);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Mouse_IsMouseDisabled_CanBeSetAndRetrieved ()
|
||||
{
|
||||
// Arrange
|
||||
MouseImpl mouse = new ();
|
||||
|
||||
// Act
|
||||
mouse.IsMouseDisabled = true;
|
||||
|
||||
// Assert
|
||||
Assert.True (mouse.IsMouseDisabled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Mouse_CachedViewsUnderMouse_InitializedEmpty ()
|
||||
{
|
||||
// Arrange
|
||||
MouseImpl mouse = new ();
|
||||
|
||||
// Assert
|
||||
Assert.NotNull (mouse.CachedViewsUnderMouse);
|
||||
Assert.Empty (mouse.CachedViewsUnderMouse);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Mouse_ResetState_ClearsEventAndCachedViews ()
|
||||
{
|
||||
// Arrange
|
||||
MouseImpl mouse = new ();
|
||||
var eventFired = false;
|
||||
mouse.MouseEvent += (sender, args) => eventFired = true;
|
||||
mouse.CachedViewsUnderMouse.Add (new View ());
|
||||
|
||||
// Act
|
||||
mouse.ResetState ();
|
||||
|
||||
// Assert - CachedViewsUnderMouse should be cleared
|
||||
Assert.Empty (mouse.CachedViewsUnderMouse);
|
||||
|
||||
// Event handlers should be cleared
|
||||
MouseEventArgs mouseEvent = new () { ScreenPosition = new Point (0, 0), Flags = MouseFlags.Button1Pressed };
|
||||
mouse.RaiseMouseEvent (mouseEvent);
|
||||
Assert.False (eventFired, "Event should not fire after ResetState");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Mouse_RaiseMouseEvent_DoesNotUpdateLastPositionWhenNotInitialized ()
|
||||
{
|
||||
// Arrange
|
||||
MouseImpl mouse = new ();
|
||||
MouseEventArgs mouseEvent = new () { ScreenPosition = new Point (5, 10), Flags = MouseFlags.Button1Pressed };
|
||||
|
||||
// Act - Application is not initialized, so LastMousePosition should not be set
|
||||
mouse.RaiseMouseEvent (mouseEvent);
|
||||
|
||||
// Assert
|
||||
// Since Application.Initialized is false, LastMousePosition should remain null
|
||||
// This behavior matches the original implementation
|
||||
Assert.Null (mouse.LastMousePosition);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Mouse_MouseEvent_CanBeSubscribedAndUnsubscribed ()
|
||||
{
|
||||
// Arrange
|
||||
MouseImpl mouse = new ();
|
||||
var eventCount = 0;
|
||||
EventHandler<MouseEventArgs> handler = (sender, args) => eventCount++;
|
||||
|
||||
// Act - Subscribe
|
||||
mouse.MouseEvent += handler;
|
||||
MouseEventArgs mouseEvent = new () { ScreenPosition = new Point (0, 0), Flags = MouseFlags.Button1Pressed };
|
||||
mouse.RaiseMouseEvent (mouseEvent);
|
||||
|
||||
// Assert - Event fired once
|
||||
Assert.Equal (1, eventCount);
|
||||
|
||||
// Act - Unsubscribe
|
||||
mouse.MouseEvent -= handler;
|
||||
mouse.RaiseMouseEvent (mouseEvent);
|
||||
|
||||
// Assert - Event count unchanged
|
||||
Assert.Equal (1, eventCount);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user