Partial on #2975 - Replaces Menu v1 in many places with v2 (#4040)

* touching publish.yml

* Fixed UICatalog bugs. Added fluent tests.

* marked v1 menu stuff as obsolte

* Tweaks.
Added View.GetSubMenus<type>().

* fixed unit tests

* general messing around

* general messing around

* Playing with Fluent

* ColorScheme tweaks

* WIP: ColorScheme tweaks

* Playing with Fluent

* Merged from laptop2

* Hacky-ish fixes to:
- #4016
- #4014

* Fixed Region bug preventing menus without borders from working

* Tweaks

* Fixed a bunch of CM issues

* Fixed OoptionSelector

* ip

* FixedCM issues

* Fixed CM issues2

* Revert "FixedCM issues"

This reverts commit dd6c6a70a3.

* Reverted stuff

* Found and fixed bug in AllViews_Center_Properly

* Fixed CM issues2

* removed menuv2 onapplied.
Changed how UICatalog Applys CM

* changed test time out to see if it helkps with ubuntu fails

* reset app on fail?

* back to 1500ms

* Made StatusBar nullable.

* Code Cleanup.

* HexEditor Code Cleanup.

* HexEditor Code Cleanup.

* Back to 3000ms. Sigh.

* Trying different logic

* Trying different logic2

* Fixed potential crash in runlop

* Fixed potential crash in runlop2

* Tweaked Spinner stuff

* Removed TabView from TextEffects scenario. Not needed and possible culprit.

* back to 2000ms

* WIP: Revamping menu scenarios

* Menu Scenario refinements.
Fixed a few bugs.
Code cleanup.

* fixed unit test

* Fixed warnings

* Fixed warnings2

* Fixed File.Exit

* WIP: Dealing with QuitKey struggles

* WIP: Dealing with QuitKey struggles 2

* WIP: Dealing with QuitKey struggles 3

* Fixed ListView collection nav bug

* Fixed a bunch of menu stuff.
Fixed Appv2 stuff.

* Lots of refactoring and fixing

* Lots of unit test issues

* Fixed DebugIDisposable issues

* Fixed release build issue

* Fixed release build issue 2

* DebugIDisposable -> EnableDebugIDisposableAsserts and more

* DebugIDisposable -> EnableDebugIDisposableAsserts and more 2

* Fixed Menus scenario - context menu

* Added @bdisp suggested assert. Commented it out as it breaks tests.

* Code cleanup

* Fixed disposed but

* Fixed UICatalog exit

* Fixed Unit test I broke.
Added 'Minimal' Theme that turns off all borders etc...
This commit is contained in:
Tig
2025-04-24 05:17:58 -06:00
committed by GitHub
parent 8d3d039fb3
commit 47bcf1bf57
136 changed files with 5109 additions and 2214 deletions

View File

@@ -783,7 +783,7 @@ public class RegionTests
Assert.True (region1.Contains (40, 40));
}
[Fact (Skip = "Union is broken")]
[Fact]
public void Union_Third_Rect_Covering_Two_Disjoint_Merges ()
{
var origRegion = new Region ();
@@ -791,19 +791,19 @@ public class RegionTests
var region1 = new Region (new (0, 0, 1, 1));
var region2 = new Region (new (1, 0, 1, 1));
origRegion.Union(region1);
origRegion.Union(region2);
origRegion.Union (region1);
origRegion.Union (region2);
Assert.Equal (new Rectangle (0, 0, 2, 1), origRegion.GetBounds ());
Assert.Equal (2, origRegion.GetRectangles ().Length);
origRegion.Union(new Region(new (0, 0, 4, 1)));
origRegion.Union (new Region (new (0, 0, 4, 1)));
Assert.Equal (new Rectangle (0, 1, 4, 1), origRegion.GetBounds ());
Assert.Single (origRegion.GetRectangles ());
Assert.Equal (new Rectangle (0, 0, 4, 1), origRegion.GetBounds ());
Assert.Equal (3, origRegion.GetRectangles ().Length);
}
[Fact (Skip = "MinimalUnion is broken")]
[Fact]
public void MinimalUnion_Third_Rect_Covering_Two_Disjoint_Merges ()
{
var origRegion = new Region ();
@@ -819,7 +819,7 @@ public class RegionTests
origRegion.MinimalUnion (new Region (new (0, 0, 4, 1)));
Assert.Equal (new Rectangle (0, 1, 4, 1), origRegion.GetBounds ());
Assert.Equal (new Rectangle (0, 0, 4, 1), origRegion.GetBounds ());
Assert.Single (origRegion.GetRectangles ());
}
@@ -928,6 +928,180 @@ public class RegionTests
Assert.Contains (new (2, 0, 0, 1), result);
}
[Fact]
public void MergeRectangles_Sort_Handles_Coincident_Events_Without_Crashing ()
{
// Arrange: Create rectangles designed to produce coincident start/end events
// Rect1 ends at x=10. Rect2 and Rect3 start at x=10.
// Rect4 ends at x=15. Rect5 starts at x=15.
var rect1 = new Rectangle (0, 0, 10, 10); // Ends at x=10
var rect2 = new Rectangle (10, 0, 10, 5); // Starts at x=10
var rect3 = new Rectangle (10, 5, 10, 5); // Starts at x=10, adjacent to rect2 vertically
var rect4 = new Rectangle (5, 10, 10, 5); // Ends at x=15
var rect5 = new Rectangle (15, 10, 5, 5); // Starts at x=15
var combinedList = new List<Rectangle> { rect1, rect2, rect3, rect4, rect5 };
// Act & Assert:
// The core assertion is that calling MergeRectangles with this list
// does *not* throw the ArgumentException related to sorting.
var exception = Record.Exception (() => Region.MergeRectangles (combinedList, false));
// Assert
Assert.Null (exception);
// Optional secondary assertion: Check if the merge produced a reasonable number of rectangles
// This isn't strictly necessary for proving the sort fix, but can be useful.
// var merged = Region.MergeRectangles(combinedList, false);
// Assert.True(merged.Count > 0 && merged.Count <= combinedList.Count);
}
[Fact]
public void MergeRectangles_Sort_Handles_Multiple_Coincident_Starts ()
{
// Arrange: Multiple rectangles starting at the same X
var rect1 = new Rectangle (5, 0, 10, 5);
var rect2 = new Rectangle (5, 5, 10, 5);
var rect3 = new Rectangle (5, 10, 10, 5);
var combinedList = new List<Rectangle> { rect1, rect2, rect3 };
// Act & Assert: Ensure no sorting exception
var exception = Record.Exception (() => Region.MergeRectangles (combinedList, false));
Assert.Null (exception);
}
[Fact]
public void MergeRectangles_Sort_Handles_Multiple_Coincident_Ends ()
{
// Arrange: Multiple rectangles ending at the same X
var rect1 = new Rectangle (0, 0, 10, 5);
var rect2 = new Rectangle (0, 5, 10, 5);
var rect3 = new Rectangle (0, 10, 10, 5);
var combinedList = new List<Rectangle> { rect1, rect2, rect3 };
// Act & Assert: Ensure no sorting exception
var exception = Record.Exception (() => Region.MergeRectangles (combinedList, false));
Assert.Null (exception);
}
[Fact]
public void MergeRectangles_Sort_Handles_Coincident_Mixed_Events_Without_Crashing ()
{
// Arrange: Create rectangles specifically designed to produce multiple
// Start AND End events at the same x-coordinate (e.g., x=10),
// mimicking the pattern observed in the crash log.
var rectA = new Rectangle (0, 0, 10, 5); // Ends at x=10, y=[0, 5)
var rectB = new Rectangle (0, 10, 10, 5); // Ends at x=10, y=[10, 15)
var rectC = new Rectangle (10, 0, 10, 5); // Starts at x=10, y=[0, 5)
var rectD = new Rectangle (10, 10, 10, 5); // Starts at x=10, y=[10, 15)
// Add another set at a different X to increase complexity
var rectE = new Rectangle (5, 20, 10, 5); // Ends at x=15, y=[20, 25)
var rectF = new Rectangle (5, 30, 10, 5); // Ends at x=15, y=[30, 35)
var rectG = new Rectangle (15, 20, 10, 5); // Starts at x=15, y=[20, 25)
var rectH = new Rectangle (15, 30, 10, 5); // Starts at x=15, y=[30, 35)
// Add some unrelated rectangles
var rectI = new Rectangle (0, 40, 5, 5);
var rectJ = new Rectangle (100, 100, 5, 5);
var combinedList = new List<Rectangle> {
rectA, rectB, rectC, rectD,
rectE, rectF, rectG, rectH,
rectI, rectJ
};
// Act & Assert:
// Call MergeRectangles with the current code.
// This test *should* fail by throwing ArgumentException due to unstable sort.
var exception = Record.Exception (() => Region.MergeRectangles (combinedList, false));
// Assert that no exception was thrown (this assertion will fail with the current code)
Assert.Null (exception);
}
[Fact]
public void MergeRectangles_Sort_Reproduces_UICatalog_Crash_Pattern_Directly ()
{
// Arrange: Rectangles derived *directly* from the events list that caused the crash at x=67
// This aims to replicate the exact problematic pattern.
var rect_End_67_7_30 = new Rectangle (60, 7, 7, 23); // Ends at x=67, y=[7, 30) -> Event [33]
var rect_Start_67_2_30 = new Rectangle (67, 2, 10, 28); // Starts at x=67, y=[2, 30) -> Event [34]
var rect_Start_67_1_1 = new Rectangle (67, 1, 10, 0); // Starts at x=67, y=[1, 1) -> Event [49] (Height 0)
var rect_End_67_1_1 = new Rectangle (60, 1, 7, 0); // Ends at x=67, y=[1, 1) -> Event [64] (Height 0)
// Add rectangles for x=94/95 pattern
var rect_End_94_1_30 = new Rectangle (90, 1, 4, 29); // Ends at x=94, y=[1, 30) -> Event [55]
var rect_Start_94_1_1 = new Rectangle (94, 1, 10, 0); // Starts at x=94, y=[1, 1) -> Event [56]
var rect_Start_94_7_30 = new Rectangle (94, 7, 10, 23); // Starts at x=94, y=[7, 30) -> Event [58]
var rect_End_95_1_1 = new Rectangle (90, 1, 5, 0); // Ends at x=95, y=[1, 1) -> Event [57]
var rect_End_95_7_30 = new Rectangle (90, 7, 5, 23); // Ends at x=95, y=[7, 30) -> Event [59]
var rect_Start_95_0_30 = new Rectangle (95, 0, 10, 30); // Starts at x=95, y=[0, 30) -> Event [60]
var combinedList = new List<Rectangle> {
rect_End_67_7_30, rect_Start_67_2_30, rect_Start_67_1_1, rect_End_67_1_1,
rect_End_94_1_30, rect_Start_94_1_1, rect_Start_94_7_30,
rect_End_95_1_1, rect_End_95_7_30, rect_Start_95_0_30
};
// Act & Assert:
// Call MergeRectangles. This test is specifically designed to fail with the current code.
var exception = Record.Exception (() => Region.MergeRectangles (combinedList, false));
// Assert that no exception was thrown (this assertion *should* fail with the current code)
Assert.Null (exception);
}
[Fact]
public void MergeRectangles_Sort_Reproduces_UICatalog_Crash_From_Captured_Data ()
{
// Arrange: The exact list of rectangles captured during the UICatalog crash
var rectanglesFromCrash = new List<Rectangle> {
new Rectangle(38, 7, 1, 11),
new Rectangle(39, 7, 5, 23),
new Rectangle(44, 7, 1, 23),
new Rectangle(45, 7, 6, 23),
new Rectangle(51, 7, 1, 23),
new Rectangle(52, 7, 1, 23),
new Rectangle(53, 7, 1, 23),
new Rectangle(54, 7, 1, 23),
new Rectangle(55, 7, 1, 23),
new Rectangle(56, 7, 1, 23),
new Rectangle(57, 7, 1, 23),
new Rectangle(58, 7, 1, 23),
new Rectangle(59, 7, 1, 23),
new Rectangle(60, 7, 1, 23),
new Rectangle(61, 7, 3, 23),
new Rectangle(64, 7, 1, 23),
new Rectangle(65, 7, 2, 23),
new Rectangle(67, 2, 2, 28),
new Rectangle(69, 2, 3, 28),
new Rectangle(72, 2, 3, 28),
new Rectangle(75, 2, 1, 28),
new Rectangle(76, 2, 2, 28),
new Rectangle(78, 2, 2, 28),
new Rectangle(80, 7, 1, 23),
new Rectangle(81, 1, 7, 29),
new Rectangle(88, 1, 1, 29),
new Rectangle(89, 1, 2, 29),
new Rectangle(91, 1, 3, 29),
new Rectangle(94, 1, 1, 0), // Note: Zero height
new Rectangle(94, 7, 1, 23),
new Rectangle(95, 0, 1, 30),
new Rectangle(96, 0, 23, 30),
new Rectangle(67, 1, 0, 0) // Note: Zero width and height
};
// Act & Assert:
// Call MergeRectangles with the current code.
// This test *should* fail by throwing ArgumentException due to unstable sort.
var exception = Record.Exception (() => Region.MergeRectangles (rectanglesFromCrash, false));
// Assert that no exception was thrown (this assertion will fail with the current code)
Assert.Null (exception);
}
}

View File

@@ -0,0 +1,11 @@
namespace UnitTests.Parallelizable;
/// <summary>
/// Base class for parallelizable tests. Ensures that tests can run in parallel without interference
/// by setting various Terminal.Gui static properties to their default values. E.g. View.EnableDebugIDisposableAsserts.
/// </summary>
[Collection ("Global Test Setup")]
public abstract class ParallelizableBase
{
// Common setup or utilities for all tests can go here
}

View File

@@ -0,0 +1,101 @@
namespace UnitTests.Parallelizable;
/// <summary>
/// Ensures that tests can run in parallel without interference
/// by setting various Terminal.Gui static properties to their default values. E.g. View.EnableDebugIDisposableAsserts.
/// Annotate all test classes with [Collection("Global Test Setup")] or have it inherit from this class.
/// </summary>
public class GlobalTestSetup : IDisposable
{
public GlobalTestSetup ()
{
#if DEBUG_IDISPOSABLE
// Ensure EnableDebugIDisposableAsserts is false before tests run
View.EnableDebugIDisposableAsserts = false;
#endif
CheckDefaultState ();
}
public void Dispose ()
{
// Optionally reset EnableDebugIDisposableAsserts after tests. Don't do this.
// View.EnableDebugIDisposableAsserts = true;
// Reset application state just in case a test changed something.
// TODO: Add an Assert to ensure none of the state of Application changed.
// TODO: Add an Assert to ensure none of the state of ConfigurationManager changed.
CheckDefaultState ();
Application.ResetState (true);
}
// IMPORTANT: Ensure this matches the code in Init_ResetState_Resets_Properties
// here: .\Tests\UnitTests\Application\ApplicationTests.cs
private void CheckDefaultState ()
{
#if DEBUG_IDISPOSABLE
Assert.False (View.EnableDebugIDisposableAsserts, "View.EnableDebugIDisposableAsserts should be false for Parallelizable tests.");
#endif
// Check that all Application fields and properties are set to their default values
// Public Properties
Assert.Null (Application.Top);
Assert.Null (Application.MouseGrabView);
Assert.Null (Application.WantContinuousButtonPressedView);
// Don't check Application.ForceDriver
// Assert.Empty (Application.ForceDriver);
// Don't check Application.Force16Colors
//Assert.False (Application.Force16Colors);
Assert.Null (Application.Driver);
Assert.Null (Application.MainLoop);
Assert.False (Application.EndAfterFirstIteration);
Assert.Equal (Key.Tab.WithShift, Application.PrevTabKey);
Assert.Equal (Key.Tab, Application.NextTabKey);
Assert.Equal (Key.F6.WithShift, Application.PrevTabGroupKey);
Assert.Equal (Key.F6, Application.NextTabGroupKey);
Assert.Equal (Key.Esc, Application.QuitKey);
// Internal properties
Assert.False (Application.Initialized);
Assert.Equal (Application.GetSupportedCultures (), Application.SupportedCultures);
Assert.Equal (Application.GetAvailableCulturesFromEmbeddedResources (), Application.SupportedCultures);
Assert.False (Application._forceFakeConsole);
Assert.Equal (-1, Application.MainThreadId);
Assert.Empty (Application.TopLevels);
Assert.Empty (Application._cachedViewsUnderMouse);
// Mouse
// Do not reset _lastMousePosition
//Assert.Null (Application._lastMousePosition);
// Navigation
Assert.Null (Application.Navigation);
// Popover
Assert.Null (Application.Popover);
// Events - Can't check
//Assert.Null (Application.NotifyNewRunState);
//Assert.Null (Application.NotifyNewRunState);
//Assert.Null (Application.Iteration);
//Assert.Null (Application.SizeChanging);
//Assert.Null (Application.GrabbedMouse);
//Assert.Null (Application.UnGrabbingMouse);
//Assert.Null (Application.GrabbedMouse);
//Assert.Null (Application.UnGrabbedMouse);
//Assert.Null (Application.MouseEvent);
//Assert.Null (Application.KeyDown);
//Assert.Null (Application.KeyUp);
}
}
// Define a collection for the global setup
[CollectionDefinition ("Global Test Setup")]
public class GlobalTestSetupCollection : ICollectionFixture<GlobalTestSetup>
{
// This class has no code and is never instantiated.
// Its purpose is to apply the [CollectionDefinition] attribute
// and associate the GlobalTestSetup with the test collection.
}

View File

@@ -22,7 +22,7 @@
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
<DefineDebug>true</DefineDebug>
<DefineConstants>$(DefineConstants)</DefineConstants>
<DefineConstants>$(DefineConstants);DEBUG_IDISPOSABLE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'">
<Optimize>true</Optimize>

View File

@@ -2,6 +2,7 @@
namespace Terminal.Gui.ViewTests;
[Collection ("Global Test Setup")]
public class AdornmentSubViewTests ()
{
[Fact]

View File

@@ -1,5 +1,6 @@
namespace Terminal.Gui.ViewTests;
[Collection ("Global Test Setup")]
public class AdornmentTests
{
[Fact]

View File

@@ -1,5 +1,7 @@
namespace Terminal.Gui.ViewTests;
[Collection ("Global Test Setup")]
public class ShadowStyleTests
{
[Fact]

View File

@@ -3,6 +3,7 @@ using Xunit.Abstractions;
namespace Terminal.Gui.ViewTests;
[Collection ("Global Test Setup")]
public class HotKeyTests
{
[Theory]
@@ -372,46 +373,4 @@ public class HotKeyTests
Assert.Equal ("", view.Title);
Assert.Equal (KeyCode.Null, view.HotKey);
}
[Fact]
public void HotKey_Raises_HotKeyCommand ()
{
var hotKeyRaised = false;
var acceptRaised = false;
var selectRaised = false;
Application.Top = new Toplevel ();
var view = new View
{
CanFocus = true,
HotKeySpecifier = new Rune ('_'),
Title = "_Test"
};
Application.Top.Add (view);
view.HandlingHotKey += (s, e) => hotKeyRaised = true;
view.Accepting += (s, e) => acceptRaised = true;
view.Selecting += (s, e) => selectRaised = true;
Assert.Equal (KeyCode.T, view.HotKey);
Assert.True (Application.RaiseKeyDownEvent (Key.T));
Assert.True (hotKeyRaised);
Assert.False (acceptRaised);
Assert.False (selectRaised);
hotKeyRaised = false;
Assert.True (Application.RaiseKeyDownEvent (Key.T.WithAlt));
Assert.True (hotKeyRaised);
Assert.False (acceptRaised);
Assert.False (selectRaised);
hotKeyRaised = false;
view.HotKey = KeyCode.E;
Assert.True (Application.RaiseKeyDownEvent (Key.E.WithAlt));
Assert.True (hotKeyRaised);
Assert.False (acceptRaised);
Assert.False (selectRaised);
Application.Top.Dispose ();
Application.ResetState (true);
}
}

View File

@@ -5,6 +5,7 @@ using Xunit.Abstractions;
namespace Terminal.Gui.ViewTests;
[Collection ("Global Test Setup")]
public class KeyboardEventTests (ITestOutputHelper output) : TestsAllViews
{
/// <summary>

View File

@@ -6,6 +6,7 @@ using static Terminal.Gui.Dim;
namespace Terminal.Gui.LayoutTests;
[Collection ("Global Test Setup")]
public class DimTests
{
[Fact]

View File

@@ -1,6 +1,8 @@
namespace Terminal.Gui.LayoutTests;
using UnitTests.Parallelizable;
public class SetLayoutTests
namespace Terminal.Gui.LayoutTests;
public class SetLayoutTests : GlobalTestSetup
{
[Fact]
public void Add_Does_Not_Call_Layout ()

View File

@@ -3,6 +3,8 @@ using Xunit.Abstractions;
namespace Terminal.Gui.ViewMouseTests;
[Collection ("Global Test Setup")]
[Trait ("Category", "Input")]
public class MouseTests (ITestOutputHelper output) : TestsAllViews
{

View File

@@ -3,6 +3,7 @@ using Xunit.Abstractions;
namespace Terminal.Gui.ViewTests;
[Collection ("Global Test Setup")]
public class AddRemoveNavigationTests () : TestsAllViews
{
[Fact]

View File

@@ -3,6 +3,7 @@ using Xunit.Abstractions;
namespace Terminal.Gui.ViewTests;
[Collection ("Global Test Setup")]
public class CanFocusTests () : TestsAllViews
{
[Fact]

View File

@@ -2,6 +2,7 @@
namespace Terminal.Gui.ViewTests;
[Collection ("Global Test Setup")]
public class EnabledTests : TestsAllViews
{
[Fact]

View File

@@ -3,6 +3,7 @@ using Xunit.Abstractions;
namespace Terminal.Gui.ViewTests;
[Collection ("Global Test Setup")]
public class HasFocusChangeEventTests () : TestsAllViews
{
#region HasFocusChanging_NewValue_True

View File

@@ -3,6 +3,7 @@ using Xunit.Abstractions;
namespace Terminal.Gui.ViewTests;
[Collection ("Global Test Setup")]
public class HasFocusTests () : TestsAllViews
{

View File

@@ -3,6 +3,7 @@ using Xunit.Abstractions;
namespace Terminal.Gui.ViewTests;
[Collection ("Global Test Setup")]
public class RestoreFocusTests () : TestsAllViews
{
[Fact]

View File

@@ -3,6 +3,7 @@ using Xunit.Abstractions;
namespace Terminal.Gui.ViewTests;
[Collection ("Global Test Setup")]
public class SetFocusTests () : TestsAllViews
{
[Fact]

View File

@@ -3,6 +3,7 @@ using Xunit.Abstractions;
namespace Terminal.Gui.ViewTests;
[Collection ("Global Test Setup")]
public class VisibleTests () : TestsAllViews
{
[Fact]

View File

@@ -1,5 +1,6 @@
namespace Terminal.Gui.ViewTests;
[Collection ("Global Test Setup")]
public class SubViewTests
{
[Fact]
@@ -591,4 +592,70 @@ public class SubViewTests
Assert.NotEqual (superView, subView.SuperView);
Assert.Empty (superView.SubViews);
}
[Fact]
public void RemoveAll_Removes_All_SubViews ()
{
// Arrange
var superView = new View ();
var subView1 = new View ();
var subView2 = new View ();
var subView3 = new View ();
superView.Add (subView1, subView2, subView3);
// Act
var removedViews = superView.RemoveAll ();
// Assert
Assert.Empty (superView.SubViews);
Assert.Equal (3, removedViews.Count);
Assert.Contains (subView1, removedViews);
Assert.Contains (subView2, removedViews);
Assert.Contains (subView3, removedViews);
}
[Fact]
public void RemoveAllTView_Removes_All_SubViews_Of_Specific_Type ()
{
// Arrange
var superView = new View ();
var subView1 = new View ();
var subView2 = new View ();
var subView3 = new View ();
var subView4 = new Button ();
superView.Add (subView1, subView2, subView3, subView4);
// Act
var removedViews = superView.RemoveAll<Button> ();
// Assert
Assert.Equal (3, superView.SubViews.Count);
Assert.DoesNotContain (subView4, superView.SubViews);
Assert.Single (removedViews);
Assert.Contains (subView4, removedViews);
}
[Fact]
public void RemoveAllTView_Does_Not_Remove_Other_Types ()
{
// Arrange
var superView = new View ();
var subView1 = new View ();
var subView2 = new Button ();
var subView3 = new Label ();
superView.Add (subView1, subView2, subView3);
// Act
var removedViews = superView.RemoveAll<Button> ();
// Assert
Assert.Equal (2, superView.SubViews.Count);
Assert.Contains (subView1, superView.SubViews);
Assert.Contains (subView3, superView.SubViews);
Assert.Single (removedViews);
Assert.Contains (subView2, removedViews);
}
}

View File

@@ -5,6 +5,7 @@ using Xunit.Abstractions;
namespace Terminal.Gui.ViewsTests;
[Collection ("Global Test Setup")]
public class AllViewsTests (ITestOutputHelper output) : TestsAllViews
{
[Theory]
@@ -25,6 +26,8 @@ public class AllViewsTests (ITestOutputHelper output) : TestsAllViews
{
Assert.True (type.FullName == view.GetType ().FullName);
}
view?.Dispose ();
}
return true;
@@ -72,11 +75,12 @@ public class AllViewsTests (ITestOutputHelper output) : TestsAllViews
Assert.Equal (1, selectingCount);
Assert.Equal (0, acceptedCount);
}
view?.Dispose ();
}
[Theory]
[MemberData (nameof (AllViewTypes))]
public void AllViews_Command_Accept_Raises_Accepted (Type viewType)
public void AllViews_Command_Accept_Raises_Accepting (Type viewType)
{
var view = CreateInstanceIfNotGeneric (viewType);
@@ -95,14 +99,15 @@ public class AllViewsTests (ITestOutputHelper output) : TestsAllViews
var selectingCount = 0;
view.Selecting += (s, e) => selectingCount++;
var acceptedCount = 0;
view.Accepting += (s, e) => { acceptedCount++; };
var acceptingCount = 0;
view.Accepting += (s, e) => { acceptingCount++; };
if (view.InvokeCommand (Command.Accept) == true)
{
Assert.Equal (0, selectingCount);
Assert.Equal (1, acceptedCount);
Assert.Equal (1, acceptingCount);
}
view?.Dispose ();
}
[Theory]
@@ -138,5 +143,6 @@ public class AllViewsTests (ITestOutputHelper output) : TestsAllViews
Assert.Equal (1, handlingHotKeyCount);
Assert.Equal (0, acceptedCount);
}
view?.Dispose ();
}
}

View File

@@ -1,124 +1,214 @@
namespace Terminal.Gui.ViewsTests;
public class FlagSelectorTests
{
[Fact]
public void Initialization_ShouldSetDefaults()
public void Initialization_ShouldSetDefaults ()
{
var flagSelector = new FlagSelector();
var flagSelector = new FlagSelector ();
Assert.True(flagSelector.CanFocus);
Assert.Equal(Dim.Auto(DimAutoStyle.Content), flagSelector.Width);
Assert.Equal(Dim.Auto(DimAutoStyle.Content), flagSelector.Height);
Assert.Equal(Orientation.Vertical, flagSelector.Orientation);
Assert.True (flagSelector.CanFocus);
Assert.Equal (Dim.Auto (DimAutoStyle.Content), flagSelector.Width);
Assert.Equal (Dim.Auto (DimAutoStyle.Content), flagSelector.Height);
Assert.Equal (Orientation.Vertical, flagSelector.Orientation);
}
[Fact]
public void SetFlags_WithDictionary_ShouldSetFlags()
public void SetFlags_WithDictionary_ShouldSetFlags ()
{
var flagSelector = new FlagSelector();
var flagSelector = new FlagSelector ();
var flags = new Dictionary<uint, string>
{
{ 1, "Flag1" },
{ 2, "Flag2" }
};
flagSelector.SetFlags(flags);
flagSelector.SetFlags (flags);
Assert.Equal(flags, flagSelector.Flags);
Assert.Equal (flags, flagSelector.Flags);
}
[Fact]
public void SetFlags_WithEnum_ShouldSetFlags()
public void SetFlags_WithDictionary_ShouldSetValue ()
{
var flagSelector = new FlagSelector();
var flagSelector = new FlagSelector ();
var flags = new Dictionary<uint, string>
{
{ 1, "Flag1" },
{ 2, "Flag2" }
};
flagSelector.SetFlags<FlagSelectorStyles>();
flagSelector.SetFlags (flags);
var expectedFlags = Enum.GetValues<FlagSelectorStyles>()
.ToDictionary(f => Convert.ToUInt32(f), f => f.ToString());
Assert.Equal(expectedFlags, flagSelector.Flags);
Assert.Equal ((uint)1, flagSelector.Value);
}
[Fact]
public void SetFlags_WithEnumAndCustomNames_ShouldSetFlags()
public void SetFlags_WithEnum_ShouldSetFlags ()
{
var flagSelector = new FlagSelector();
var flagSelector = new FlagSelector ();
flagSelector.SetFlags<FlagSelectorStyles>(f => f switch
flagSelector.SetFlags<FlagSelectorStyles> ();
var expectedFlags = Enum.GetValues<FlagSelectorStyles> ()
.ToDictionary (f => Convert.ToUInt32 (f), f => f.ToString ());
Assert.Equal (expectedFlags, flagSelector.Flags);
}
[Fact]
public void SetFlags_WithEnumAndCustomNames_ShouldSetFlags ()
{
var flagSelector = new FlagSelector ();
flagSelector.SetFlags<FlagSelectorStyles> (f => f switch
{
FlagSelectorStyles.ShowNone => "Show None Value",
FlagSelectorStyles.ShowValueEdit => "Show Value Editor",
FlagSelectorStyles.All => "Everything",
_ => f.ToString()
_ => f.ToString ()
});
var expectedFlags = Enum.GetValues<FlagSelectorStyles>()
.ToDictionary(f => Convert.ToUInt32(f), f => f switch
var expectedFlags = Enum.GetValues<FlagSelectorStyles> ()
.ToDictionary (f => Convert.ToUInt32 (f), f => f switch
{
FlagSelectorStyles.ShowNone => "Show None Value",
FlagSelectorStyles.ShowValueEdit => "Show Value Editor",
FlagSelectorStyles.All => "Everything",
_ => f.ToString()
_ => f.ToString ()
});
Assert.Equal(expectedFlags, flagSelector.Flags);
Assert.Equal (expectedFlags, flagSelector.Flags);
}
[Fact]
public void Value_Set_ShouldUpdateCheckedState()
public void Value_Set_ShouldUpdateCheckedState ()
{
var flagSelector = new FlagSelector();
var flagSelector = new FlagSelector ();
var flags = new Dictionary<uint, string>
{
{ 1, "Flag1" },
{ 2, "Flag2" }
};
flagSelector.SetFlags(flags);
flagSelector.SetFlags (flags);
flagSelector.Value = 1;
var checkBox = flagSelector.SubViews.OfType<CheckBox>().First(cb => (uint)cb.Data == 1);
Assert.Equal(CheckState.Checked, checkBox.CheckedState);
var checkBox = flagSelector.SubViews.OfType<CheckBox> ().First (cb => (uint)cb.Data == 1);
Assert.Equal (CheckState.Checked, checkBox.CheckedState);
checkBox = flagSelector.SubViews.OfType<CheckBox>().First(cb => (uint)cb.Data == 2);
Assert.Equal(CheckState.UnChecked, checkBox.CheckedState);
checkBox = flagSelector.SubViews.OfType<CheckBox> ().First (cb => (uint)cb.Data == 2);
Assert.Equal (CheckState.UnChecked, checkBox.CheckedState);
}
[Fact]
public void Styles_Set_ShouldCreateSubViews()
public void Styles_Set_ShouldCreateSubViews ()
{
var flagSelector = new FlagSelector();
var flagSelector = new FlagSelector ();
var flags = new Dictionary<uint, string>
{
{ 1, "Flag1" },
{ 2, "Flag2" }
};
flagSelector.SetFlags(flags);
flagSelector.SetFlags (flags);
flagSelector.Styles = FlagSelectorStyles.ShowNone;
Assert.Contains(flagSelector.SubViews, sv => sv is CheckBox cb && cb.Title == "None");
Assert.Contains (flagSelector.SubViews, sv => sv is CheckBox cb && cb.Title == "None");
}
[Fact]
public void ValueChanged_Event_ShouldBeRaised()
public void ValueChanged_Event_ShouldBeRaised ()
{
var flagSelector = new FlagSelector();
var flagSelector = new FlagSelector ();
var flags = new Dictionary<uint, string>
{
{ 1, "Flag1" },
{ 2, "Flag2" }
};
flagSelector.SetFlags(flags);
flagSelector.SetFlags (flags);
bool eventRaised = false;
flagSelector.ValueChanged += (sender, args) => eventRaised = true;
flagSelector.Value = 1;
flagSelector.Value = 2;
Assert.True(eventRaised);
Assert.True (eventRaised);
}
// Tests for FlagSelector<TEnum>
[Fact]
public void GenericInitialization_ShouldSetDefaults ()
{
var flagSelector = new FlagSelector<FlagSelectorStyles> ();
Assert.True (flagSelector.CanFocus);
Assert.Equal (Dim.Auto (DimAutoStyle.Content), flagSelector.Width);
Assert.Equal (Dim.Auto (DimAutoStyle.Content), flagSelector.Height);
Assert.Equal (Orientation.Vertical, flagSelector.Orientation);
}
[Fact]
public void Generic_SetFlags_Methods_Throw ()
{
var flagSelector = new FlagSelector<FlagSelectorStyles> ();
Assert.Throws<InvalidOperationException> (() => flagSelector.SetFlags (new Dictionary<uint, string> ()));
Assert.Throws<InvalidOperationException> (() => flagSelector.SetFlags<FlagSelectorStyles> ());
Assert.Throws<InvalidOperationException> (() => flagSelector.SetFlags<FlagSelectorStyles> (styles => null));
}
[Fact]
public void GenericSetFlagNames_ShouldSetFlagNames ()
{
var flagSelector = new FlagSelector<FlagSelectorStyles> ();
flagSelector.SetFlagNames (f => f switch
{
FlagSelectorStyles.ShowNone => "Show None Value",
FlagSelectorStyles.ShowValueEdit => "Show Value Editor",
FlagSelectorStyles.All => "Everything",
_ => f.ToString ()
});
var expectedFlags = Enum.GetValues<FlagSelectorStyles> ()
.ToDictionary (f => Convert.ToUInt32 (f), f => f switch
{
FlagSelectorStyles.ShowNone => "Show None Value",
FlagSelectorStyles.ShowValueEdit => "Show Value Editor",
FlagSelectorStyles.All => "Everything",
_ => f.ToString ()
});
Assert.Equal (expectedFlags, flagSelector.Flags);
}
[Fact]
public void GenericValue_Set_ShouldUpdateCheckedState ()
{
var flagSelector = new FlagSelector<FlagSelectorStyles> ();
flagSelector.SetFlagNames (f => f.ToString ());
flagSelector.Value = FlagSelectorStyles.ShowNone;
var checkBox = flagSelector.SubViews.OfType<CheckBox> ().First (cb => (uint)cb.Data == Convert.ToUInt32 (FlagSelectorStyles.ShowNone));
Assert.Equal (CheckState.Checked, checkBox.CheckedState);
checkBox = flagSelector.SubViews.OfType<CheckBox> ().First (cb => (uint)cb.Data == Convert.ToUInt32 (FlagSelectorStyles.ShowValueEdit));
Assert.Equal (CheckState.UnChecked, checkBox.CheckedState);
}
[Fact]
public void GenericValueChanged_Event_ShouldBeRaised ()
{
var flagSelector = new FlagSelector<FlagSelectorStyles> ();
flagSelector.SetFlagNames (f => f.ToString ());
bool eventRaised = false;
flagSelector.ValueChanged += (sender, args) => eventRaised = true;
flagSelector.Value = FlagSelectorStyles.ShowNone;
Assert.True (eventRaised);
}
}

View File

@@ -2,6 +2,8 @@
namespace Terminal.Gui.ViewsTests;
[Collection ("Global Test Setup")]
[TestSubject (typeof (Shortcut))]
public class ShortcutTests
{
@@ -420,7 +422,7 @@ public class ShortcutTests
Assert.False (shortcut.CommandView.CanFocus);
shortcut.CommandView = new () { CanFocus = true };
Assert.False (shortcut.CommandView.CanFocus);
Assert.True (shortcut.CommandView.CanFocus);
shortcut.CommandView.CanFocus = true;
Assert.True (shortcut.CommandView.CanFocus);