mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2026-02-10 04:03:41 +01:00
* Initial plan * CommandContext infrastructure: WeakReference and extension methods Co-authored-by: tig <585482+tig@users.noreply.github.com> * Align CommandContext infrastructure with PR #4620 - Update ViewExtensions.cs to use Terminal.Gui.ViewBase namespace - Update WeakReferenceExtensions.cs to match PR #4620 format - Change CommandContext constructor to take WeakReference<View>? directly - Add CommandContext.ToString() implementation - Update ICommandContext.Source documentation - Update View.Command.cs to explicitly create WeakReference - Update Shortcut.cs to handle WeakReference Source - Update ComboBox.cs Source comparison - Update Dialog.cs and DialogTResult.cs pattern matching - Update MenuBar.cs and PopoverMenu.cs to work with WeakReference - Update ScrollSlider.cs and OptionSelector.cs Remaining: Fix Examples code and tests Co-authored-by: tig <585482+tig@users.noreply.github.com> * Fix Examples code for WeakReference Source - Update UICatalogRunnable.cs to use TryGetTarget for Data access - Update Menus.cs to use ToIdentifyingString() and TryGetTarget - Update MouseTester.cs to use ToIdentifyingString() for all Source.Id access All Examples now build successfully Co-authored-by: tig <585482+tig@users.noreply.github.com> * Update tests for WeakReference Source - Fix CommandContextTests.cs to wrap Source in WeakReference - Fix InputBindingTests.cs to wrap Source in WeakReference - Update assertions to use TryGetTarget for safe access - All CommandContext and InputBinding tests pass Co-authored-by: tig <585482+tig@users.noreply.github.com> * Add TryGetSource extension methods and replace usage - Add TryGetSource extension to WeakReferenceExtensions.cs - Add CommandContextExtensions.cs with TryGetSource for ICommandContext - Replace 14 instances of Source?.TryGetTarget pattern across codebase - Updated Terminal.Gui files: ComboBox, Dialog, DialogTResult, MenuBar, PopoverMenu, OptionSelector, Shortcut - Updated Examples files: UICatalogRunnable, Menus - All files build successfully Co-authored-by: tig <585482+tig@users.noreply.github.com> * Add tests for TryGetSource extension methods - Add 6 tests for TryGetSource extension methods - Test WeakReference<View>.TryGetSource with valid/null references - Test ICommandContext.TryGetSource with valid/null contexts - Test pattern matching usage with TryGetSource - All 23 CommandContext tests pass - Full test suite: 15,094 passed Co-authored-by: tig <585482+tig@users.noreply.github.com> * code cleanup --------- 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>
436 lines
14 KiB
C#
436 lines
14 KiB
C#
namespace InputTests;
|
|
|
|
/// <summary>
|
|
/// Tests for <see cref="CommandContext"/> record struct.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Copilot generated.
|
|
/// </remarks>
|
|
public class CommandContextTests
|
|
{
|
|
#region Basic Property Tests
|
|
|
|
[Fact]
|
|
public void CommandContext_WithKeyBinding_SetsProperties ()
|
|
{
|
|
View sourceView = new () { Id = "sourceView" };
|
|
KeyBinding keyBinding = new ([Command.Activate]) { Key = Key.Enter };
|
|
|
|
CommandContext ctx = new () { Command = Command.Activate, Source = new WeakReference<View>(sourceView), Binding = keyBinding };
|
|
|
|
Assert.Equal (Command.Activate, ctx.Command);
|
|
Assert.NotNull (ctx.Source);
|
|
Assert.True (ctx.Source.TryGetTarget (out View? view));
|
|
Assert.Equal (sourceView, view);
|
|
Assert.NotNull (ctx.Binding);
|
|
|
|
if (ctx.Binding is KeyBinding kb)
|
|
{
|
|
Assert.Equal (Key.Enter, kb.Key);
|
|
}
|
|
else
|
|
{
|
|
Assert.Fail ("Binding should be KeyBinding");
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void CommandContext_WithMouseBinding_SetsProperties ()
|
|
{
|
|
View sourceView = new () { Id = "sourceView" };
|
|
MouseBinding mouseBinding = new ([Command.Activate], MouseFlags.LeftButtonClicked);
|
|
|
|
CommandContext ctx = new () { Command = Command.Activate, Source = new WeakReference<View>(sourceView), Binding = mouseBinding };
|
|
|
|
Assert.Equal (Command.Activate, ctx.Command);
|
|
Assert.NotNull (ctx.Source);
|
|
Assert.True (ctx.Source.TryGetTarget (out View? view));
|
|
Assert.Equal (sourceView, view);
|
|
Assert.NotNull (ctx.Binding);
|
|
|
|
if (ctx.Binding is MouseBinding mb)
|
|
{
|
|
Assert.NotNull (mb.MouseEvent);
|
|
Assert.Equal (MouseFlags.LeftButtonClicked, mb.MouseEvent!.Flags);
|
|
}
|
|
else
|
|
{
|
|
Assert.Fail ("Binding should be MouseBinding");
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ICommandContext Interface Tests
|
|
|
|
[Fact]
|
|
public void CommandContext_ImplementsICommandContext ()
|
|
{
|
|
CommandContext ctx = new () { Command = Command.Accept, Source = new WeakReference<View>(new View ()) };
|
|
|
|
ICommandContext iCtx = ctx;
|
|
|
|
Assert.Equal (Command.Accept, iCtx.Command);
|
|
Assert.NotNull (iCtx.Source);
|
|
}
|
|
|
|
[Fact]
|
|
public void Source_IsMutable_ThroughInterface ()
|
|
{
|
|
View originalSource = new () { Id = "original" };
|
|
View newSource = new () { Id = "new" };
|
|
|
|
CommandContext ctx = new () { Command = Command.Accept, Source = new WeakReference<View>(originalSource) };
|
|
|
|
ICommandContext iCtx = ctx;
|
|
iCtx.Source = new WeakReference<View>(newSource);
|
|
|
|
Assert.NotNull (iCtx.Source);
|
|
Assert.True (iCtx.Source.TryGetTarget (out View? view));
|
|
Assert.Equal (newSource, view);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Pattern Matching Tests
|
|
|
|
[Fact]
|
|
public void PatternMatching_KeyBinding_Works ()
|
|
{
|
|
ICommandContext ctx = new CommandContext
|
|
{
|
|
Command = Command.Activate,
|
|
Source = new WeakReference<View>(new View ()),
|
|
Binding = new KeyBinding ([Command.Activate]) { Key = Key.Enter }
|
|
};
|
|
|
|
if (ctx.Binding is KeyBinding { Key: { } key })
|
|
{
|
|
Assert.Equal (Key.Enter, key);
|
|
Assert.Equal (Command.Activate, ctx.Command);
|
|
}
|
|
else
|
|
{
|
|
Assert.Fail ("Pattern matching should have succeeded");
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void PatternMatching_MouseBinding_WithMouseEvent_Works ()
|
|
{
|
|
MouseBinding mouseBinding = new ([Command.Activate], MouseFlags.LeftButtonClicked) { Source = new View { Id = "mouseSource" } };
|
|
mouseBinding.MouseEvent = new Mouse { Flags = MouseFlags.LeftButtonClicked, Position = new Point (10, 20) };
|
|
|
|
ICommandContext ctx = new CommandContext { Command = Command.Activate, Source = new WeakReference<View>(new View ()), Binding = mouseBinding };
|
|
|
|
// This is the actual pattern used in production code
|
|
if (ctx.Binding is MouseBinding { MouseEvent: { } mouse })
|
|
{
|
|
Assert.Equal (MouseFlags.LeftButtonClicked, mouse.Flags);
|
|
Assert.Equal (new Point (10, 20), mouse.Position);
|
|
}
|
|
else
|
|
{
|
|
Assert.Fail ("Pattern matching should have succeeded");
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void PatternMatching_MouseBinding_NullMouseEvent_DoesNotMatch ()
|
|
{
|
|
MouseBinding mouseBinding = new ([Command.Activate], MouseFlags.LeftButtonClicked)
|
|
{
|
|
MouseEvent = null // Explicitly set to null
|
|
};
|
|
|
|
ICommandContext ctx = new CommandContext { Command = Command.Activate, Source = new WeakReference<View>(new View ()), Binding = mouseBinding };
|
|
|
|
// Pattern should NOT match when MouseEvent is null
|
|
bool matched = ctx.Binding is MouseBinding { MouseEvent: { } };
|
|
|
|
Assert.False (matched);
|
|
}
|
|
|
|
[Fact]
|
|
public void PatternMatching_DifferentBindingTypes_DoNotMatch ()
|
|
{
|
|
ICommandContext ctx = new CommandContext
|
|
{
|
|
Command = Command.Activate,
|
|
Source = new WeakReference<View>(new View ()),
|
|
Binding = new KeyBinding ([Command.Activate])
|
|
};
|
|
|
|
// KeyBinding should not match MouseBinding pattern
|
|
bool matchedMouse = ctx.Binding is MouseBinding;
|
|
|
|
Assert.False (matchedMouse);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Binding Source Property Tests
|
|
|
|
[Fact]
|
|
public void KeyBinding_Source_IsAccessibleThroughContext ()
|
|
{
|
|
View bindingSource = new () { Id = "bindingSource" };
|
|
View contextSource = new () { Id = "contextSource" };
|
|
|
|
KeyBinding keyBinding = new ([Command.Activate]) { Key = Key.A, Source = bindingSource };
|
|
|
|
CommandContext ctx = new () { Command = Command.Activate, Source = new WeakReference<View>(contextSource), Binding = keyBinding };
|
|
|
|
// Both sources are accessible
|
|
Assert.NotNull (ctx.Source);
|
|
Assert.True (ctx.Source.TryGetTarget (out View? ctxView));
|
|
Assert.Equal ("contextSource", ctxView!.Id);
|
|
|
|
if (ctx.Binding is KeyBinding kb)
|
|
{
|
|
Assert.Equal ("bindingSource", kb.Source?.Id);
|
|
}
|
|
else
|
|
{
|
|
Assert.Fail ("Binding should be KeyBinding");
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void MouseBinding_Source_IsAccessibleThroughContext ()
|
|
{
|
|
View bindingSource = new () { Id = "bindingSource" };
|
|
View contextSource = new () { Id = "contextSource" };
|
|
|
|
MouseBinding mouseBinding = new ([Command.Activate], MouseFlags.LeftButtonClicked) { Source = bindingSource };
|
|
|
|
CommandContext ctx = new () { Command = Command.Activate, Source = new WeakReference<View>(contextSource), Binding = mouseBinding };
|
|
|
|
// Both sources are accessible
|
|
Assert.NotNull (ctx.Source);
|
|
Assert.True (ctx.Source.TryGetTarget (out View? ctxView));
|
|
Assert.Equal ("contextSource", ctxView!.Id);
|
|
|
|
if (ctx.Binding is MouseBinding mb)
|
|
{
|
|
Assert.Equal ("bindingSource", mb.Source?.Id);
|
|
}
|
|
else
|
|
{
|
|
Assert.Fail ("Binding should be MouseBinding");
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Command Event Args Integration Tests
|
|
|
|
[Fact]
|
|
public void CommandEventArgs_Context_WithKeyBinding_Works ()
|
|
{
|
|
KeyBinding keyBinding = new ([Command.Accept]) { Key = Key.Enter, Source = new View { Id = "keySource" } };
|
|
|
|
CommandContext ctx = new () { Command = Command.Accept, Source = new WeakReference<View>(new View { Id = "invoker" }), Binding = keyBinding };
|
|
|
|
CommandEventArgs args = new () { Context = ctx };
|
|
|
|
Assert.NotNull (args.Context);
|
|
Assert.Equal (Command.Accept, args.Context.Command);
|
|
|
|
if (args.Context.Binding is KeyBinding { Key: { } key })
|
|
{
|
|
Assert.Equal (Key.Enter, key);
|
|
}
|
|
else
|
|
{
|
|
Assert.Fail ("Should be able to pattern match KeyBinding from CommandEventArgs.Context.Binding");
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void CommandEventArgs_Context_WithMouseBinding_Works ()
|
|
{
|
|
MouseBinding mouseBinding = new ([Command.Activate], MouseFlags.RightButtonClicked) { Source = new View { Id = "mouseSource" } };
|
|
|
|
CommandContext ctx = new () { Command = Command.Activate, Source = new WeakReference<View>(new View { Id = "invoker" }), Binding = mouseBinding };
|
|
|
|
CommandEventArgs args = new () { Context = ctx };
|
|
|
|
Assert.NotNull (args.Context);
|
|
|
|
if (args.Context.Binding is MouseBinding { MouseEvent: { } mouse })
|
|
{
|
|
Assert.Equal (MouseFlags.RightButtonClicked, mouse.Flags);
|
|
}
|
|
else
|
|
{
|
|
Assert.Fail ("Should be able to pattern match MouseBinding from CommandEventArgs.Context.Binding");
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ICommandContext.Binding Property Tests
|
|
|
|
[Fact]
|
|
public void Binding_Property_ReturnsBindingAsIInputBinding ()
|
|
{
|
|
KeyBinding keyBinding = new ([Command.Activate]) { Key = Key.Enter };
|
|
|
|
CommandContext ctx = new () { Command = Command.Activate, Binding = keyBinding };
|
|
|
|
// Access via ICommandContext interface
|
|
ICommandContext iCtx = ctx;
|
|
|
|
Assert.NotNull (iCtx.Binding);
|
|
Assert.IsType<KeyBinding> (iCtx.Binding);
|
|
Assert.Equal (keyBinding.Commands, iCtx.Binding!.Commands);
|
|
}
|
|
|
|
[Fact]
|
|
public void Binding_Property_AllowsPolymorphicPatternMatching ()
|
|
{
|
|
KeyBinding keyBinding = new ([Command.Accept]) { Key = Key.F5 };
|
|
ICommandContext ctx = new CommandContext { Command = Command.Accept, Binding = keyBinding };
|
|
|
|
// Pattern match on Binding from the interface
|
|
if (ctx.Binding is KeyBinding kb)
|
|
{
|
|
Assert.Equal (Key.F5, kb.Key);
|
|
}
|
|
else
|
|
{
|
|
Assert.Fail ("Should be able to pattern match KeyBinding from ICommandContext.Binding");
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Binding_Property_WithMouseBinding_Works ()
|
|
{
|
|
MouseBinding mouseBinding = new ([Command.Activate], MouseFlags.RightButtonClicked);
|
|
ICommandContext ctx = new CommandContext { Command = Command.Activate, Binding = mouseBinding };
|
|
|
|
// Pattern match on Binding from the interface
|
|
if (ctx.Binding is MouseBinding mb)
|
|
{
|
|
Assert.Equal (MouseFlags.RightButtonClicked, mb.MouseEvent?.Flags);
|
|
}
|
|
else
|
|
{
|
|
Assert.Fail ("Should be able to pattern match MouseBinding from ICommandContext.Binding");
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Binding_Property_WithInputBinding_Works ()
|
|
{
|
|
InputBinding inputBinding = new ([Command.Accept], new View { Id = "programmatic" }, "data");
|
|
ICommandContext ctx = new CommandContext { Command = Command.Accept, Binding = inputBinding };
|
|
|
|
// Pattern match on Binding from the interface
|
|
if (ctx.Binding is InputBinding ib)
|
|
{
|
|
Assert.Equal ("programmatic", ib.Source?.Id);
|
|
Assert.Equal ("data", ib.Data);
|
|
}
|
|
else
|
|
{
|
|
Assert.Fail ("Should be able to pattern match InputBinding from ICommandContext.Binding");
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Binding_Property_NullBinding_ReturnsNull ()
|
|
{
|
|
CommandContext ctx = new () { Command = Command.Activate };
|
|
|
|
ICommandContext iCtx = ctx;
|
|
|
|
// When Binding is not set, it should be null
|
|
Assert.Null (iCtx.Binding);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region TryGetSource Extension Method Tests
|
|
|
|
[Fact]
|
|
public void TryGetSource_WithValidWeakReference_ReturnsTrue ()
|
|
{
|
|
View sourceView = new () { Id = "testView" };
|
|
WeakReference<View> weakRef = new (sourceView);
|
|
|
|
bool result = weakRef.TryGetSource (out View? retrievedView);
|
|
|
|
Assert.True (result);
|
|
Assert.NotNull (retrievedView);
|
|
Assert.Equal (sourceView, retrievedView);
|
|
Assert.Equal ("testView", retrievedView!.Id);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryGetSource_WithNullWeakReference_ReturnsFalse ()
|
|
{
|
|
WeakReference<View>? weakRef = null;
|
|
|
|
bool result = weakRef.TryGetSource (out View? retrievedView);
|
|
|
|
Assert.False (result);
|
|
Assert.Null (retrievedView);
|
|
}
|
|
|
|
[Fact]
|
|
public void CommandContext_TryGetSource_WithValidSource_ReturnsTrue ()
|
|
{
|
|
View sourceView = new () { Id = "contextSource" };
|
|
CommandContext ctx = new () { Command = Command.Accept, Source = new WeakReference<View> (sourceView) };
|
|
|
|
bool result = ctx.TryGetSource (out View? retrievedView);
|
|
|
|
Assert.True (result);
|
|
Assert.NotNull (retrievedView);
|
|
Assert.Equal (sourceView, retrievedView);
|
|
Assert.Equal ("contextSource", retrievedView!.Id);
|
|
}
|
|
|
|
[Fact]
|
|
public void CommandContext_TryGetSource_WithNullContext_ReturnsFalse ()
|
|
{
|
|
ICommandContext? ctx = null;
|
|
|
|
bool result = ctx.TryGetSource (out View? retrievedView);
|
|
|
|
Assert.False (result);
|
|
Assert.Null (retrievedView);
|
|
}
|
|
|
|
[Fact]
|
|
public void CommandContext_TryGetSource_WithNullSource_ReturnsFalse ()
|
|
{
|
|
CommandContext ctx = new () { Command = Command.Accept, Source = null };
|
|
|
|
bool result = ctx.TryGetSource (out View? retrievedView);
|
|
|
|
Assert.False (result);
|
|
Assert.Null (retrievedView);
|
|
}
|
|
|
|
[Fact]
|
|
public void CommandContext_TryGetSource_CanBeUsedInPatternMatching ()
|
|
{
|
|
View sourceView = new Button { Id = "testButton" };
|
|
CommandContext ctx = new () { Command = Command.Accept, Source = new WeakReference<View> (sourceView) };
|
|
|
|
if (ctx.TryGetSource (out View? view) && view is Button button)
|
|
{
|
|
Assert.Equal ("testButton", button.Id);
|
|
}
|
|
else
|
|
{
|
|
Assert.Fail ("Should have retrieved Button from context");
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|