Merge branch 'v2_develop' into v2_2491-Arrangement-Overlapped

This commit is contained in:
Tig
2024-08-13 15:59:06 -06:00
78 changed files with 2359 additions and 1095 deletions

View File

@@ -38,7 +38,7 @@ public class ConfigurationManagerTests
}
// act
Settings ["Application.QuitKey"].PropertyValue = Key.Q;
Settings! ["Application.QuitKey"].PropertyValue = Key.Q;
Settings ["Application.NextTabGroupKey"].PropertyValue = Key.F;
Settings ["Application.PrevTabGroupKey"].PropertyValue = Key.B;
@@ -130,7 +130,7 @@ public class ConfigurationManagerTests
{ "Disabled", new Attribute (Color.White) }, { "Normal", new Attribute (Color.Blue) }
};
dictCopy = (Dictionary<string, Attribute>)DeepMemberWiseCopy (dictSrc, dictDest);
Assert.Equal (2, dictCopy.Count);
Assert.Equal (2, dictCopy!.Count);
Assert.Equal (dictSrc ["Disabled"], dictCopy ["Disabled"]);
Assert.Equal (dictSrc ["Normal"], dictCopy ["Normal"]);
@@ -141,7 +141,7 @@ public class ConfigurationManagerTests
};
dictSrc = new Dictionary<string, Attribute> { { "Disabled", new Attribute (Color.White) } };
dictCopy = (Dictionary<string, Attribute>)DeepMemberWiseCopy (dictSrc, dictDest);
Assert.Equal (2, dictCopy.Count);
Assert.Equal (2, dictCopy!.Count);
Assert.Equal (dictSrc ["Disabled"], dictCopy ["Disabled"]);
Assert.Equal (dictDest ["Normal"], dictCopy ["Normal"]);
}
@@ -151,7 +151,7 @@ public class ConfigurationManagerTests
{
Reset ();
Settings ["Application.QuitKey"].PropertyValue = Key.Q;
Settings! ["Application.QuitKey"].PropertyValue = Key.Q;
Settings ["Application.NextTabGroupKey"].PropertyValue = Key.F;
Settings ["Application.PrevTabGroupKey"].PropertyValue = Key.B;
@@ -163,16 +163,16 @@ public class ConfigurationManagerTests
fired = true;
// assert
Assert.Equal (Key.Esc, ((Key)Settings ["Application.QuitKey"].PropertyValue).KeyCode);
Assert.Equal (Key.Esc, (((Key)Settings! ["Application.QuitKey"].PropertyValue)!).KeyCode);
Assert.Equal (
KeyCode.F6,
((Key)Settings ["Application.NextTabGroupKey"].PropertyValue).KeyCode
(((Key)Settings ["Application.NextTabGroupKey"].PropertyValue)!).KeyCode
);
Assert.Equal (
KeyCode.F6 | KeyCode.ShiftMask,
((Key)Settings ["Application.PrevTabGroupKey"].PropertyValue).KeyCode
(((Key)Settings ["Application.PrevTabGroupKey"].PropertyValue)!).KeyCode
);
}
@@ -228,7 +228,7 @@ public class ConfigurationManagerTests
// arrange
Reset ();
Settings ["Application.QuitKey"].PropertyValue = Key.Q;
Settings! ["Application.QuitKey"].PropertyValue = Key.Q;
Settings ["Application.NextTabGroupKey"].PropertyValue = Key.F;
Settings ["Application.PrevTabGroupKey"].PropertyValue = Key.B;
Settings.Apply ();
@@ -242,7 +242,7 @@ public class ConfigurationManagerTests
Reset ();
// assert
Assert.NotEmpty (Themes);
Assert.NotEmpty (Themes!);
Assert.Equal ("Default", Themes.Theme);
Assert.Equal (Key.Esc, Application.QuitKey);
Assert.Equal (Key.F6, Application.NextTabGroupKey);
@@ -274,7 +274,7 @@ public class ConfigurationManagerTests
{
Locations = ConfigLocations.DefaultOnly;
Reset ();
Assert.NotEmpty (Themes);
Assert.NotEmpty (Themes!);
Assert.Equal ("Default", Themes.Theme);
}
@@ -367,7 +367,7 @@ public class ConfigurationManagerTests
// Serialize to a JSON string
string json = ToJson ();
// Write the JSON string to the file
// Write the JSON string to the file
File.WriteAllText ("config.json", json);
}
@@ -377,23 +377,23 @@ public class ConfigurationManagerTests
Locations = ConfigLocations.All;
Reset ();
Assert.NotEmpty (Settings);
Assert.NotEmpty (Settings!);
// test that all ConfigProperties have our attribute
Assert.All (
Settings,
item => Assert.NotEmpty (
item.Value.PropertyInfo.CustomAttributes.Where (
a => a.AttributeType == typeof (SerializableConfigurationProperty)
)
item.Value.PropertyInfo!.CustomAttributes.Where (
a => a.AttributeType == typeof (SerializableConfigurationProperty)
)
)
);
Assert.Empty (
Settings.Where (
cp => cp.Value.PropertyInfo.GetCustomAttribute (
typeof (SerializableConfigurationProperty)
)
cp => cp.Value.PropertyInfo!.GetCustomAttribute (
typeof (SerializableConfigurationProperty)
)
== null
)
);
@@ -401,12 +401,12 @@ public class ConfigurationManagerTests
// Application is a static class
PropertyInfo pi = typeof (Application).GetProperty ("QuitKey");
Assert.Equal (pi, Settings ["Application.QuitKey"].PropertyInfo);
// FrameView is not a static class and DefaultBorderStyle is Scope.Scheme
pi = typeof (FrameView).GetProperty ("DefaultBorderStyle");
Assert.False (Settings.ContainsKey ("FrameView.DefaultBorderStyle"));
Assert.True (Themes ["Default"].ContainsKey ("FrameView.DefaultBorderStyle"));
Assert.True (Themes! ["Default"].ContainsKey ("FrameView.DefaultBorderStyle"));
Assert.Equal (pi, Themes! ["Default"] ["FrameView.DefaultBorderStyle"].PropertyInfo);
}
[Fact]
@@ -414,31 +414,31 @@ public class ConfigurationManagerTests
{
// Color.ColorSchemes is serialized as "ColorSchemes", not "Colors.ColorSchemes"
PropertyInfo pi = typeof (Colors).GetProperty ("ColorSchemes");
var scp = (SerializableConfigurationProperty)pi.GetCustomAttribute (typeof (SerializableConfigurationProperty));
Assert.True (scp.Scope == typeof (ThemeScope));
var scp = (SerializableConfigurationProperty)pi!.GetCustomAttribute (typeof (SerializableConfigurationProperty));
Assert.True (scp!.Scope == typeof (ThemeScope));
Assert.True (scp.OmitClassName);
Reset ();
Assert.Equal (pi, Themes ["Default"] ["ColorSchemes"].PropertyInfo);
Assert.Equal (pi, Themes! ["Default"] ["ColorSchemes"].PropertyInfo);
}
[Fact]
[AutoInitShutdown (configLocation: ConfigLocations.DefaultOnly)]
public void TestConfigurationManagerInitDriver ()
{
Assert.Equal ("Default", Themes.Theme);
Assert.Equal ("Default", Themes!.Theme);
Assert.Equal (new Color (Color.White), Colors.ColorSchemes ["Base"].Normal.Foreground);
Assert.Equal (new Color (Color.White), Colors.ColorSchemes ["Base"]!.Normal.Foreground);
Assert.Equal (new Color (Color.Blue), Colors.ColorSchemes ["Base"].Normal.Background);
// Change Base
Stream json = ToStream ();
Settings.Update (json, "TestConfigurationManagerInitDriver");
Settings!.Update (json, "TestConfigurationManagerInitDriver");
Dictionary<string, ColorScheme> colorSchemes =
(Dictionary<string, ColorScheme>)Themes [Themes.Theme] ["ColorSchemes"].PropertyValue;
Assert.Equal (Colors.ColorSchemes ["Base"], colorSchemes ["Base"]);
Assert.Equal (Colors.ColorSchemes ["Base"], colorSchemes! ["Base"]);
Assert.Equal (Colors.ColorSchemes ["TopLevel"], colorSchemes ["TopLevel"]);
Assert.Equal (Colors.ColorSchemes ["Error"], colorSchemes ["Error"]);
Assert.Equal (Colors.ColorSchemes ["Dialog"], colorSchemes ["Dialog"]);
@@ -489,7 +489,7 @@ public class ConfigurationManagerTests
}
}";
Settings.Update (json, "test");
Settings!.Update (json, "test");
// AbNormal is not a ColorScheme attribute
json = @"
@@ -514,7 +514,7 @@ public class ConfigurationManagerTests
Settings.Update (json, "test");
// Modify hotNormal background only
// Modify hotNormal background only
json = @"
{
""Themes"" : [
@@ -572,7 +572,7 @@ public class ConfigurationManagerTests
]
}";
var jsonException = Assert.Throws<JsonException> (() => Settings.Update (json, "test"));
var jsonException = Assert.Throws<JsonException> (() => Settings!.Update (json, "test"));
Assert.Equal ("Unexpected color name: brown.", jsonException.Message);
// AbNormal is not a ColorScheme attribute
@@ -596,10 +596,10 @@ public class ConfigurationManagerTests
]
}";
jsonException = Assert.Throws<JsonException> (() => Settings.Update (json, "test"));
jsonException = Assert.Throws<JsonException> (() => Settings!.Update (json, "test"));
Assert.Equal ("Unrecognized ColorScheme Attribute name: AbNormal.", jsonException.Message);
// Modify hotNormal background only
// Modify hotNormal background only
json = @"
{
""Themes"" : [
@@ -619,7 +619,7 @@ public class ConfigurationManagerTests
]
}";
jsonException = Assert.Throws<JsonException> (() => Settings.Update (json, "test"));
jsonException = Assert.Throws<JsonException> (() => Settings!.Update (json, "test"));
Assert.Equal ("Both Foreground and Background colors must be provided.", jsonException.Message);
// Unknown property
@@ -628,7 +628,7 @@ public class ConfigurationManagerTests
""Unknown"" : ""Not known""
}";
jsonException = Assert.Throws<JsonException> (() => Settings.Update (json, "test"));
jsonException = Assert.Throws<JsonException> (() => Settings!.Update (json, "test"));
Assert.StartsWith ("Unknown property", jsonException.Message);
Assert.Equal (0, _jsonErrors.Length);
@@ -644,7 +644,7 @@ public class ConfigurationManagerTests
GetHardCodedDefaults ();
Stream stream = ToStream ();
Settings.Update (stream, "TestConfigurationManagerToJson");
Settings!.Update (stream, "TestConfigurationManagerToJson");
}
[Fact]
@@ -790,19 +790,19 @@ public class ConfigurationManagerTests
Reset ();
ThrowOnJsonErrors = true;
Settings.Update (json, "TestConfigurationManagerUpdateFromJson");
Settings!.Update (json, "TestConfigurationManagerUpdateFromJson");
Assert.Equal (KeyCode.Esc, Application.QuitKey.KeyCode);
Assert.Equal (KeyCode.Z | KeyCode.AltMask, ((Key)Settings ["Application.QuitKey"].PropertyValue).KeyCode);
Assert.Equal (KeyCode.Z | KeyCode.AltMask, ((Key)Settings ["Application.QuitKey"].PropertyValue)!.KeyCode);
Assert.Equal ("Default", Themes.Theme);
Assert.Equal ("Default", Themes!.Theme);
Assert.Equal (new Color (Color.White), Colors.ColorSchemes ["Base"].Normal.Foreground);
Assert.Equal (new Color (Color.White), Colors.ColorSchemes ["Base"]!.Normal.Foreground);
Assert.Equal (new Color (Color.Blue), Colors.ColorSchemes ["Base"].Normal.Background);
Dictionary<string, ColorScheme> colorSchemes =
(Dictionary<string, ColorScheme>)Themes.First ().Value ["ColorSchemes"].PropertyValue;
Assert.Equal (new Color (Color.White), colorSchemes ["Base"].Normal.Foreground);
Assert.Equal (new Color (Color.White), colorSchemes! ["Base"].Normal.Foreground);
Assert.Equal (new Color (Color.Blue), colorSchemes ["Base"].Normal.Background);
// Now re-apply

View File

@@ -0,0 +1,108 @@
#nullable enable
using System.Reflection;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
namespace Terminal.Gui.ConfigurationTests;
public class SerializableConfigurationPropertyTests
{
[Fact]
public void Test_SerializableConfigurationProperty_Types_Added_To_JsonSerializerContext ()
{
// The assembly containing the types to inspect
var assembly = Assembly.GetAssembly (typeof (SourceGenerationContext));
// Get all types from the assembly
var types = assembly!.GetTypes ();
// Find all properties with the SerializableConfigurationProperty attribute
var properties = new List<PropertyInfo> ();
foreach (var type in types)
{
properties.AddRange (type.GetProperties ().Where (p =>
p.GetCustomAttributes (typeof (SerializableConfigurationProperty), false).Any ()));
}
// Get the types of the properties
var propertyTypes = properties.Select (p => p.PropertyType).Distinct ();
// Get the types registered in the JsonSerializerContext derived class
var contextType = typeof (SourceGenerationContext);
var contextTypes = GetRegisteredTypes (contextType);
// Ensure all property types are included in the JsonSerializerContext derived class
IEnumerable<Type> collection = contextTypes as Type [] ?? contextTypes.ToArray ();
foreach (var type in propertyTypes)
{
Assert.Contains (type, collection);
}
// Ensure no property has the generic JsonStringEnumConverter<>
EnsureNoSpecifiedConverters (properties, new [] { typeof (JsonStringEnumConverter<>) });
// Ensure no property has the type RuneJsonConverter
EnsureNoSpecifiedConverters (properties, new [] { typeof (RuneJsonConverter) });
// Ensure no property has the type KeyJsonConverter
EnsureNoSpecifiedConverters (properties, new [] { typeof (KeyJsonConverter) });
// Find all classes with the JsonConverter attribute of type ScopeJsonConverter<>
var classesWithScopeJsonConverter = types.Where (t =>
t.GetCustomAttributes (typeof (JsonConverterAttribute), false)
.Any (attr => ((JsonConverterAttribute)attr).ConverterType!.IsGenericType &&
((JsonConverterAttribute)attr).ConverterType!.GetGenericTypeDefinition () == typeof (ScopeJsonConverter<>)));
// Ensure all these classes are included in the JsonSerializerContext derived class
foreach (var type in classesWithScopeJsonConverter)
{
Assert.Contains (type, collection);
}
}
private static IEnumerable<Type> GetRegisteredTypes (Type contextType)
{
// Use reflection to find which types are registered in the JsonSerializerContext
var registeredTypes = new List<Type> ();
var properties = contextType.GetProperties (BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance);
foreach (var property in properties)
{
if (property.PropertyType.IsGenericType &&
property.PropertyType.GetGenericTypeDefinition () == typeof (JsonTypeInfo<>))
{
registeredTypes.Add (property.PropertyType.GetGenericArguments () [0]);
}
}
return registeredTypes.Distinct ();
}
private static void EnsureNoSpecifiedConverters (List<PropertyInfo> properties, IEnumerable<Type> converterTypes)
{
// Ensure no property has any of the specified converter types
foreach (var property in properties)
{
var jsonConverterAttributes = property.GetCustomAttributes (typeof (JsonConverterAttribute), false)
.Cast<JsonConverterAttribute> ();
foreach (var attribute in jsonConverterAttributes)
{
foreach (var converterType in converterTypes)
{
if (attribute.ConverterType!.IsGenericType &&
attribute.ConverterType.GetGenericTypeDefinition () == converterType)
{
Assert.Fail ($"Property '{property.Name}' should not use the converter '{converterType.Name}'.");
}
if (!attribute.ConverterType!.IsGenericType &&
attribute.ConverterType == converterType)
{
Assert.Fail ($"Property '{property.Name}' should not use the converter '{converterType.Name}'.");
}
}
}
}
}
}

View File

@@ -29,13 +29,18 @@ public class ThemeScopeTests
{
Reset ();
Assert.NotEmpty (Themes);
Assert.Equal (Alignment.End, Dialog.DefaultButtonAlignment);
Alignment savedValue = Dialog.DefaultButtonAlignment;
Alignment newValue = Alignment.Center != savedValue ? Alignment.Center : Alignment.Start;
Themes ["Default"] ["Dialog.DefaultButtonAlignment"].PropertyValue = Alignment.Center;
Themes ["Default"] ["Dialog.DefaultButtonAlignment"].PropertyValue = newValue;
ThemeManager.Themes! [ThemeManager.SelectedTheme]!.Apply ();
Assert.Equal (Alignment.Center, Dialog.DefaultButtonAlignment);
Reset ();
Assert.Equal (newValue, Dialog.DefaultButtonAlignment);
// Replace with the savedValue to avoid failures on other unit tests that rely on the default value
Themes ["Default"] ["Dialog.DefaultButtonAlignment"].PropertyValue = savedValue;
ThemeManager.Themes! [ThemeManager.SelectedTheme]!.Apply ();
Assert.Equal (savedValue, Dialog.DefaultButtonAlignment);
}
[Fact]

View File

@@ -76,6 +76,9 @@ public class ThemeTests
[Fact]
public void TestSerialize_RoundTrip ()
{
// This is needed to test only this alone
Reset ();
var theme = new ThemeScope ();
theme ["Dialog.DefaultButtonAlignment"].PropertyValue = Alignment.End;

View File

@@ -533,7 +533,7 @@ public class KeyTests
var b = Key.A;
Assert.True (a.Equals (b));
}
[Fact]
public void Equals_Handled_Changed_ShouldReturnTrue_WhenEqual ()
{
@@ -552,4 +552,20 @@ public class KeyTests
var b = Key.A;
Assert.False (a.Equals (b));
}
[Fact]
public void Set_Key_Separator_With_Rune_Default_Ensure_Using_The_Default_Plus ()
{
Key key = new (Key.A.WithCtrl);
Assert.Equal ((Rune)'+', Key.Separator);
Assert.Equal ("Ctrl+A", key.ToString ());
Key.Separator = new ('-');
Assert.Equal ((Rune)'-', Key.Separator);
Assert.Equal ("Ctrl-A", key.ToString ());
Key.Separator = new ();
Assert.Equal ((Rune)'+', Key.Separator);
Assert.Equal ("Ctrl+A", key.ToString ());
}
}

View File

@@ -86,6 +86,11 @@ public class AutoInitShutdownAttribute : BeforeAfterTestAttribute
}
#endif
ConfigurationManager.Reset ();
if (CM.Locations != CM.ConfigLocations.None)
{
SetCurrentConfig (_savedValues);
}
}
}
@@ -110,10 +115,77 @@ public class AutoInitShutdownAttribute : BeforeAfterTestAttribute
}
#endif
Application.Init ((ConsoleDriver)Activator.CreateInstance (_driverType));
if (CM.Locations != CM.ConfigLocations.None)
{
_savedValues = GetCurrentConfig ();
}
}
}
private bool AutoInit { get; }
private List<object> _savedValues;
private List<object> GetCurrentConfig ()
{
CM.Reset ();
List<object> savedValues =
[
Dialog.DefaultButtonAlignment,
Dialog.DefaultButtonAlignmentModes,
MessageBox.DefaultBorderStyle
];
CM.Themes! ["Default"] ["Dialog.DefaultButtonAlignment"].PropertyValue = Alignment.End;
CM.Themes! ["Default"] ["Dialog.DefaultButtonAlignmentModes"].PropertyValue = AlignmentModes.AddSpaceBetweenItems;
CM.Themes! ["Default"] ["MessageBox.DefaultBorderStyle"].PropertyValue = LineStyle.Double;
ThemeManager.Themes! [ThemeManager.SelectedTheme]!.Apply ();
return savedValues;
}
private void SetCurrentConfig (List<object> values)
{
CM.Reset ();
bool needApply = false;
foreach (object value in values)
{
switch (value)
{
case Alignment alignment:
if ((Alignment)CM.Themes! ["Default"] ["Dialog.DefaultButtonAlignment"].PropertyValue! != alignment)
{
needApply = true;
CM.Themes! ["Default"] ["Dialog.DefaultButtonAlignment"].PropertyValue = alignment;
}
break;
case AlignmentModes alignmentModes:
if ((AlignmentModes)CM.Themes! ["Default"] ["Dialog.DefaultButtonAlignmentModes"].PropertyValue! != alignmentModes)
{
needApply = true;
CM.Themes! ["Default"] ["Dialog.DefaultButtonAlignmentModes"].PropertyValue = alignmentModes;
}
break;
case LineStyle lineStyle:
if ((LineStyle)CM.Themes! ["Default"] ["Dialog.DefaultButtonAlignment"].PropertyValue! != lineStyle)
{
needApply = true;
CM.Themes! ["Default"] ["MessageBox.DefaultBorderStyle"].PropertyValue = lineStyle;
}
break;
}
}
if (needApply)
{
ThemeManager.Themes! [ThemeManager.SelectedTheme]!.Apply ();
}
}
}
[AttributeUsage (AttributeTargets.Class | AttributeTargets.Method)]

View File

@@ -98,15 +98,15 @@ public class CheckBoxTests (ITestOutputHelper output)
{
var checkBox = new CheckBox { Text = "Check this out 你" };
Assert.Equal (CheckState.UnChecked, checkBox.State);
Assert.Equal (CheckState.UnChecked, checkBox.CheckedState);
Assert.True (checkBox.NewKeyDownEvent (Key.Space));
Assert.Equal (CheckState.Checked, checkBox.State);
Assert.Equal (CheckState.Checked, checkBox.CheckedState);
Assert.True (checkBox.NewMouseEvent (new () { Position = new (0, 0), Flags = MouseFlags.Button1Clicked }));
Assert.Equal (CheckState.UnChecked, checkBox.State);
Assert.Equal (CheckState.UnChecked, checkBox.CheckedState);
checkBox.AllowCheckStateNone = true;
Assert.True (checkBox.NewKeyDownEvent (Key.Space));
Assert.Equal (CheckState.None, checkBox.State);
Assert.Equal (CheckState.None, checkBox.CheckedState);
checkBox.Draw ();
TestHelpers.AssertDriverContentsWithFrameAre (
@@ -115,14 +115,14 @@ public class CheckBoxTests (ITestOutputHelper output)
output
);
Assert.True (checkBox.NewMouseEvent (new () { Position = new (0, 0), Flags = MouseFlags.Button1Clicked }));
Assert.Equal (CheckState.Checked, checkBox.State);
Assert.Equal (CheckState.Checked, checkBox.CheckedState);
Assert.True (checkBox.NewKeyDownEvent (Key.Space));
Assert.Equal (CheckState.UnChecked, checkBox.State);
Assert.Equal (CheckState.UnChecked, checkBox.CheckedState);
Assert.True (checkBox.NewMouseEvent (new () { Position = new (0, 0), Flags = MouseFlags.Button1Clicked }));
Assert.Equal (CheckState.None, checkBox.State);
Assert.Equal (CheckState.None, checkBox.CheckedState);
checkBox.AllowCheckStateNone = false;
Assert.Equal (CheckState.UnChecked, checkBox.State);
Assert.Equal (CheckState.UnChecked, checkBox.CheckedState);
}
[Fact]
@@ -131,17 +131,17 @@ public class CheckBoxTests (ITestOutputHelper output)
var ckb = new CheckBox ();
Assert.True (ckb.Width is DimAuto);
Assert.True (ckb.Height is DimAuto);
Assert.Equal (CheckState.UnChecked, ckb.State);
Assert.Equal (CheckState.UnChecked, ckb.CheckedState);
Assert.False (ckb.AllowCheckStateNone);
Assert.Equal (string.Empty, ckb.Text);
Assert.Equal ($"{CM.Glyphs.CheckStateUnChecked} ", ckb.TextFormatter.Text);
Assert.True (ckb.CanFocus);
Assert.Equal (new (0, 0, 2, 1), ckb.Frame);
ckb = new () { Text = "Test", State = CheckState.Checked };
ckb = new () { Text = "Test", CheckedState = CheckState.Checked };
Assert.True (ckb.Width is DimAuto);
Assert.True (ckb.Height is DimAuto);
Assert.Equal (CheckState.Checked, ckb.State);
Assert.Equal (CheckState.Checked, ckb.CheckedState);
Assert.False (ckb.AllowCheckStateNone);
Assert.Equal ("Test", ckb.Text);
Assert.Equal ($"{CM.Glyphs.CheckStateChecked} Test", ckb.TextFormatter.Text);
@@ -151,17 +151,17 @@ public class CheckBoxTests (ITestOutputHelper output)
ckb = new () { Text = "Test", X = 1, Y = 2 };
Assert.True (ckb.Width is DimAuto);
Assert.True (ckb.Height is DimAuto);
Assert.Equal (CheckState.UnChecked, ckb.State);
Assert.Equal (CheckState.UnChecked, ckb.CheckedState);
Assert.False (ckb.AllowCheckStateNone);
Assert.Equal ("Test", ckb.Text);
Assert.Equal ($"{CM.Glyphs.CheckStateUnChecked} Test", ckb.TextFormatter.Text);
Assert.True (ckb.CanFocus);
Assert.Equal (new (1, 2, 6, 1), ckb.Frame);
ckb = new () { Text = "Test", X = 3, Y = 4, State = CheckState.Checked };
ckb = new () { Text = "Test", X = 3, Y = 4, CheckedState = CheckState.Checked };
Assert.True (ckb.Width is DimAuto);
Assert.True (ckb.Height is DimAuto);
Assert.Equal (CheckState.Checked, ckb.State);
Assert.Equal (CheckState.Checked, ckb.CheckedState);
Assert.False (ckb.AllowCheckStateNone);
Assert.Equal ("Test", ckb.Text);
Assert.Equal ($"{CM.Glyphs.CheckStateChecked} Test", ckb.TextFormatter.Text);
@@ -174,16 +174,16 @@ public class CheckBoxTests (ITestOutputHelper output)
{
var toggled = false;
var ckb = new CheckBox ();
ckb.Toggle += (s, e) => toggled = true;
ckb.CheckedStateChanging += (s, e) => toggled = true;
Assert.Equal (CheckState.UnChecked, ckb.State);
Assert.Equal (CheckState.UnChecked, ckb.CheckedState);
Assert.False (toggled);
Assert.Equal (Key.Empty, ckb.HotKey);
ckb.Text = "_Test";
Assert.Equal (Key.T, ckb.HotKey);
Assert.True (ckb.NewKeyDownEvent (Key.T));
Assert.Equal (CheckState.Checked, ckb.State);
Assert.Equal (CheckState.Checked, ckb.CheckedState);
Assert.True (toggled);
ckb.Text = "T_est";
@@ -191,28 +191,28 @@ public class CheckBoxTests (ITestOutputHelper output)
Assert.Equal (Key.E, ckb.HotKey);
Assert.True (ckb.NewKeyDownEvent (Key.E.WithAlt));
Assert.True (toggled);
Assert.Equal (CheckState.UnChecked, ckb.State);
Assert.Equal (CheckState.UnChecked, ckb.CheckedState);
toggled = false;
Assert.Equal (Key.E, ckb.HotKey);
Assert.True (ckb.NewKeyDownEvent (Key.E));
Assert.True (toggled);
Assert.Equal (CheckState.Checked, ckb.State);
Assert.Equal (CheckState.Checked, ckb.CheckedState);
toggled = false;
Assert.True (ckb.NewKeyDownEvent (Key.Space));
Assert.True (toggled);
Assert.Equal (CheckState.UnChecked, ckb.State);
Assert.Equal (CheckState.UnChecked, ckb.CheckedState);
toggled = false;
Assert.True (ckb.NewKeyDownEvent (Key.Space));
Assert.True (toggled);
Assert.Equal (CheckState.Checked, ckb.State);
Assert.Equal (CheckState.Checked, ckb.CheckedState);
toggled = false;
Assert.False (ckb.NewKeyDownEvent (Key.Enter));
Assert.False (toggled);
Assert.Equal (CheckState.Checked, ckb.State);
Assert.Equal (CheckState.Checked, ckb.CheckedState);
}
[Fact]
@@ -271,7 +271,7 @@ public class CheckBoxTests (ITestOutputHelper output)
Rectangle pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
Assert.Equal (new (0, 0, 30, 5), pos);
checkBox.State = CheckState.Checked;
checkBox.CheckedState = CheckState.Checked;
Application.Refresh ();
expected = @$"
@@ -333,10 +333,10 @@ public class CheckBoxTests (ITestOutputHelper output)
Rectangle pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
Assert.Equal (new (0, 0, 30, 6), pos);
checkBox1.State = CheckState.Checked;
checkBox1.CheckedState = CheckState.Checked;
Assert.Equal (new (1, 1, 25, 1), checkBox1.Frame);
Assert.Equal (_size25x1, checkBox1.TextFormatter.ConstrainToSize);
checkBox2.State = CheckState.Checked;
checkBox2.CheckedState = CheckState.Checked;
Assert.Equal (new (1, 2, 25, 1), checkBox2.Frame);
Assert.Equal (_size25x1, checkBox2.TextFormatter.ConstrainToSize);
Application.Refresh ();
@@ -389,7 +389,7 @@ public class CheckBoxTests (ITestOutputHelper output)
Rectangle pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
Assert.Equal (new (0, 0, 30, 5), pos);
checkBox.State = CheckState.Checked;
checkBox.CheckedState = CheckState.Checked;
Application.Refresh ();
expected = @$"
@@ -440,7 +440,7 @@ public class CheckBoxTests (ITestOutputHelper output)
Rectangle pos = TestHelpers.AssertDriverContentsWithFrameAre (expected, output);
Assert.Equal (new (0, 0, 30, 5), pos);
checkBox.State = CheckState.Checked;
checkBox.CheckedState = CheckState.Checked;
Application.Refresh ();
expected = @$"
@@ -481,14 +481,14 @@ public class CheckBoxTests (ITestOutputHelper output)
var ckb = new CheckBox { AllowCheckStateNone = true };
var checkedInvoked = false;
ckb.Toggle += CheckBoxToggle;
ckb.CheckedStateChanging += CheckBoxToggle;
ckb.State = initialState;
Assert.Equal (initialState, ckb.State);
bool? ret = ckb.OnToggle ();
ckb.CheckedState = initialState;
Assert.Equal (initialState, ckb.CheckedState);
bool? ret = ckb.AdvanceCheckState ();
Assert.True (ret);
Assert.True (checkedInvoked);
Assert.Equal (initialState, ckb.State);
Assert.Equal (initialState, ckb.CheckedState);
return;

View File

@@ -1,4 +1,5 @@
using Xunit.Abstractions;
using System.Text;
using Xunit.Abstractions;
namespace Terminal.Gui.ViewsTests;
@@ -217,7 +218,7 @@ public class MenuBarTests (ITestOutputHelper output)
Assert.Null (menuBarItem.Action);
Assert.Null (menuBarItem.CanExecute);
Assert.Null (menuBarItem.Parent);
Assert.Equal (KeyCode.Null, menuBarItem.Shortcut);
Assert.Equal (Key.Empty, menuBarItem.ShortcutKey);
}
[Fact]
@@ -1229,7 +1230,7 @@ wo
)]
[InlineData ("Closed", "None", "About", KeyCode.F9, KeyCode.CursorRight, KeyCode.CursorRight, KeyCode.Enter)]
// Hotkeys
//// Hotkeys
[InlineData ("_File", "_New", "", KeyCode.AltMask | KeyCode.F)]
[InlineData ("Closed", "None", "", KeyCode.AltMask | KeyCode.ShiftMask | KeyCode.F)]
[InlineData ("Closed", "None", "", KeyCode.AltMask | KeyCode.F, KeyCode.Esc)]
@@ -1245,9 +1246,10 @@ wo
[InlineData ("_Edit", "_1st", "", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.D3)]
[InlineData ("Closed", "None", "1", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.D3, KeyCode.D1)]
[InlineData ("Closed", "None", "1", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.D3, KeyCode.Enter)]
[InlineData ("_Edit", "_3rd Level", "", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.D3, KeyCode.D4)]
[InlineData ("Closed", "None", "2", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.D3, KeyCode.D2)]
[InlineData ("_Edit", "_5th", "", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.D3, KeyCode.D4)]
[InlineData ("Closed", "None", "5", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.D4, KeyCode.D5)]
[InlineData ("_About", "_About", "", KeyCode.AltMask | KeyCode.A)]
[InlineData ("Closed", "None", "About", KeyCode.AltMask | KeyCode.A)]
public void KeyBindings_Navigation_Commands (
string expectedBarTitle,
string expectedItemTitle,
@@ -1283,7 +1285,7 @@ wo
top.Add (menu);
Application.Begin (top);
foreach (KeyCode key in keys)
foreach (Key key in keys)
{
top.NewKeyDownEvent (new (key));
Application.MainLoop.RunIteration ();
@@ -3725,4 +3727,87 @@ Edit
Assert.True (btnClicked);
top.Dispose ();
}
[Fact]
public void Update_ShortcutKey_KeyBindings_Old_ShortcutKey_Is_Removed ()
{
var menuBar = new MenuBar ()
{
Menus =
[
new MenuBarItem (
"_File",
new MenuItem []
{
new MenuItem ("New", "Create New", null, null, null, Key.A.WithCtrl)
}
)
]
};
Assert.Contains (Key.A.WithCtrl, menuBar.KeyBindings.Bindings);
menuBar.Menus [0].Children [0].ShortcutKey = Key.B.WithCtrl;
Assert.DoesNotContain (Key.A.WithCtrl, menuBar.KeyBindings.Bindings);
Assert.Contains (Key.B.WithCtrl, menuBar.KeyBindings.Bindings);
}
[Fact]
public void SetMenus_With_Same_HotKey_Does_Not_Throws ()
{
var mb = new MenuBar ();
var i1 = new MenuBarItem ("_heey", "fff", () => { }, () => true);
mb.Menus = new MenuBarItem [] { i1 };
mb.Menus = new MenuBarItem [] { i1 };
Assert.Equal (Key.H, mb.Menus [0].HotKey);
}
[Fact]
[AutoInitShutdown]
public void AddMenuBarItem_RemoveMenuItem_Dynamically ()
{
var menuBar = new MenuBar ();
var menuBarItem = new MenuBarItem { Title = "_New" };
var action = "";
var menuItem = new MenuItem { Title = "_Item", Action = () => action = "I", Parent = menuBarItem };
Assert.Equal ("n", menuBarItem.HotKey);
Assert.Equal ("i", menuItem.HotKey);
Assert.Empty (menuBar.Menus);
menuBarItem.AddMenuBarItem (menuItem);
menuBar.Menus = [menuBarItem];
Assert.Single (menuBar.Menus);
Assert.Single (menuBar.Menus [0].Children);
Assert.Contains (Key.N.WithAlt, menuBar.KeyBindings.Bindings);
Assert.DoesNotContain (Key.I, menuBar.KeyBindings.Bindings);
var top = new Toplevel ();
top.Add (menuBar);
Application.Begin (top);
top.NewKeyDownEvent (Key.N.WithAlt);
Application.MainLoop.RunIteration ();
Assert.True (menuBar.IsMenuOpen);
Assert.Equal ("", action);
top.NewKeyDownEvent (Key.I);
Application.MainLoop.RunIteration ();
Assert.False (menuBar.IsMenuOpen);
Assert.Equal ("I", action);
menuItem.RemoveMenuItem ();
Assert.Single (menuBar.Menus);
Assert.Equal (null, menuBar.Menus [0].Children);
Assert.Contains (Key.N.WithAlt, menuBar.KeyBindings.Bindings);
Assert.DoesNotContain (Key.I, menuBar.KeyBindings.Bindings);
menuBarItem.RemoveMenuItem ();
Assert.Empty (menuBar.Menus);
Assert.DoesNotContain (Key.N.WithAlt, menuBar.KeyBindings.Bindings);
top.Dispose ();
}
}

View File

@@ -31,7 +31,7 @@ public class MenuTests
Assert.Null (menuItem.Action);
Assert.Null (menuItem.CanExecute);
Assert.Null (menuItem.Parent);
Assert.Equal (KeyCode.Null, menuItem.Shortcut);
Assert.Equal (Key.Empty, menuItem.ShortcutKey);
menuItem = new MenuItem ("Test", "Help", Run, () => { return true; }, new MenuItem (), KeyCode.F1);
Assert.Equal ("Test", menuItem.Title);
@@ -39,7 +39,7 @@ public class MenuTests
Assert.Equal (Run, menuItem.Action);
Assert.NotNull (menuItem.CanExecute);
Assert.NotNull (menuItem.Parent);
Assert.Equal (KeyCode.F1, menuItem.Shortcut);
Assert.Equal (KeyCode.F1, menuItem.ShortcutKey);
void Run () { }
}

View File

@@ -0,0 +1,239 @@
using System.Globalization;
using Xunit.Abstractions;
namespace Terminal.Gui.ViewsTests;
public class NumericUpDownTests (ITestOutputHelper _output)
{
[Fact]
public void WhenCreated_ShouldHaveDefaultValues_int ()
{
NumericUpDown<int> numericUpDown = new ();
Assert.Equal (0, numericUpDown.Value);
Assert.Equal (1, numericUpDown.Increment);
}
[Fact]
public void WhenCreated_ShouldHaveDefaultValues_long ()
{
NumericUpDown<long> numericUpDown = new ();
Assert.Equal (0, numericUpDown.Value);
Assert.Equal (1, numericUpDown.Increment);
}
[Fact]
public void WhenCreated_ShouldHaveDefaultValues_float ()
{
NumericUpDown<float> numericUpDown = new ();
Assert.Equal (0F, numericUpDown.Value);
Assert.Equal (1.0F, numericUpDown.Increment);
}
[Fact]
public void WhenCreated_ShouldHaveDefaultValues_double ()
{
NumericUpDown<double> numericUpDown = new ();
Assert.Equal (0F, numericUpDown.Value);
Assert.Equal (1.0F, numericUpDown.Increment);
}
[Fact]
public void WhenCreated_ShouldHaveDefaultValues_decimal ()
{
NumericUpDown<decimal> numericUpDown = new ();
Assert.Equal (0, numericUpDown.Value);
Assert.Equal (1, numericUpDown.Increment);
}
[Fact]
public void WhenCreatedWithCustomValues_ShouldHaveCustomValues_int ()
{
NumericUpDown<int> numericUpDown = new()
{
Value = 10,
Increment = 2
};
Assert.Equal (10, numericUpDown.Value);
Assert.Equal (2, numericUpDown.Increment);
}
[Fact]
public void WhenCreatedWithCustomValues_ShouldHaveCustomValues_float ()
{
NumericUpDown<float> numericUpDown = new()
{
Value = 10.5F,
Increment = 2.5F
};
Assert.Equal (10.5F, numericUpDown.Value);
Assert.Equal (2.5F, numericUpDown.Increment);
}
[Fact]
public void WhenCreatedWithCustomValues_ShouldHaveCustomValues_decimal ()
{
NumericUpDown<decimal> numericUpDown = new ()
{
Value = 10.5m,
Increment = 2.5m
};
Assert.Equal (10.5m, numericUpDown.Value);
Assert.Equal (2.5m, numericUpDown.Increment);
}
[Fact]
public void WhenCreatedWithInvalidType_ShouldThrowInvalidOperationException ()
{
Assert.Throws<InvalidOperationException> (() => new NumericUpDown<string> ());
}
[Fact]
public void WhenCreatedWithInvalidTypeObject_ShouldNotThrowInvalidOperationException ()
{
Exception exception = Record.Exception (() => new NumericUpDown<object> ());
Assert.Null (exception);
}
[Fact]
public void WhenCreated_ShouldHaveDefaultWidthAndHeight_int ()
{
NumericUpDown<int> numericUpDown = new ();
numericUpDown.SetRelativeLayout (Application.Screen.Size);
Assert.Equal (3, numericUpDown.Frame.Width);
Assert.Equal (1, numericUpDown.Frame.Height);
}
[Fact]
public void WhenCreated_ShouldHaveDefaultWidthAndHeight_float ()
{
NumericUpDown<float> numericUpDown = new ();
numericUpDown.SetRelativeLayout (Application.Screen.Size);
Assert.Equal (3, numericUpDown.Frame.Width);
Assert.Equal (1, numericUpDown.Frame.Height);
}
[Fact]
public void WhenCreated_ShouldHaveDefaultWidthAndHeight_double ()
{
NumericUpDown<double> numericUpDown = new ();
numericUpDown.SetRelativeLayout (Application.Screen.Size);
Assert.Equal (3, numericUpDown.Frame.Width);
Assert.Equal (1, numericUpDown.Frame.Height);
}
[Fact]
public void WhenCreated_ShouldHaveDefaultWidthAndHeight_long ()
{
NumericUpDown<long> numericUpDown = new ();
numericUpDown.SetRelativeLayout (Application.Screen.Size);
Assert.Equal (3, numericUpDown.Frame.Width);
Assert.Equal (1, numericUpDown.Frame.Height);
}
[Fact]
public void WhenCreated_ShouldHaveDefaultWidthAndHeight_decimal ()
{
NumericUpDown<decimal> numericUpDown = new ();
numericUpDown.SetRelativeLayout (Application.Screen.Size);
Assert.Equal (3, numericUpDown.Frame.Width);
Assert.Equal (1, numericUpDown.Frame.Height);
}
[Fact]
public void WhenCreated_Text_Should_Be_Correct_int ()
{
NumericUpDown<int> numericUpDown = new ();
Assert.Equal ("0", numericUpDown.Text);
}
[Fact]
public void WhenCreated_Text_Should_Be_Correct_float ()
{
NumericUpDown<float> numericUpDown = new ();
Assert.Equal ("0", numericUpDown.Text);
}
[Fact]
public void Format_Default ()
{
NumericUpDown<float> numericUpDown = new ();
Assert.Equal ("{0}", numericUpDown.Format);
}
[Theory]
[InlineData (0F, "{0}", "0")]
[InlineData (1.1F, "{0}", "1.1")]
[InlineData (0F, "{0:0%}", "0%")]
[InlineData (.75F, "{0:0%}", "75%")]
public void Format_decimal (float value, string format, string expectedText)
{
CultureInfo currentCulture = CultureInfo.CurrentCulture;
CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
NumericUpDown<float> numericUpDown = new ();
numericUpDown.Format = format;
numericUpDown.Value = value;
Assert.Equal (expectedText, numericUpDown.Text);
CultureInfo.CurrentCulture = currentCulture;
}
[Theory]
[InlineData (0, "{0}", "0")]
[InlineData (11, "{0}", "11")]
[InlineData (-1, "{0}", "-1")]
[InlineData (911, "{0:X}", "38F")]
[InlineData (911, "0x{0:X04}", "0x038F")]
public void Format_int (int value, string format, string expectedText)
{
CultureInfo currentCulture = CultureInfo.CurrentCulture;
CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
NumericUpDown<int> numericUpDown = new ();
numericUpDown.Format = format;
numericUpDown.Value = value;
Assert.Equal (expectedText, numericUpDown.Text);
CultureInfo.CurrentCulture = currentCulture;
}
[Fact]
public void KeyDown_CursorUp_Increments ()
{
NumericUpDown<int> numericUpDown = new ();
numericUpDown.NewKeyDownEvent (Key.CursorUp);
Assert.Equal (1, numericUpDown.Value);
}
[Fact]
public void KeyDown_CursorDown_Decrements ()
{
NumericUpDown<int> numericUpDown = new ();
numericUpDown.NewKeyDownEvent (Key.CursorDown);
Assert.Equal (-1, numericUpDown.Value);
}
}