Files
Terminal.Gui/Tests/UnitTestsParallelizable/Input/CommandContextTests.cs
Copilot f2d260a853 Add TryGetSource extension methods for WeakReference<View> access (#4694)
* 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>
2026-02-06 14:44:04 -07:00

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
}