From 646b8dd59731b1dac7d8bef9862f9aab9ff4c8e6 Mon Sep 17 00:00:00 2001 From: Tig Date: Fri, 29 Mar 2024 09:47:48 -0600 Subject: [PATCH 1/4] Added Reset() calls to CM tests --- UnitTests/Configuration/ThemeTests.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/UnitTests/Configuration/ThemeTests.cs b/UnitTests/Configuration/ThemeTests.cs index e467b7ce48..f7d8c732fd 100644 --- a/UnitTests/Configuration/ThemeTests.cs +++ b/UnitTests/Configuration/ThemeTests.cs @@ -27,6 +27,8 @@ public void TestApply () Themes! [ThemeManager.SelectedTheme]!.Apply (); Assert.Equal (LineStyle.Double, FrameView.DefaultBorderStyle); + + Reset (); } [Fact] @@ -68,6 +70,7 @@ public void TestApply_UpdatesColors () // remove test ColorScheme from Colors to avoid failures on others unit tests with ColorScheme Colors.ColorSchemes.Remove ("test"); Assert.Equal (5, Colors.ColorSchemes.Count); + Reset (); } [Fact] @@ -84,6 +87,7 @@ public void TestSerialize_RoundTrip () Dialog.ButtonAlignments.Right, (Dialog.ButtonAlignments)deserialized ["Dialog.DefaultButtonAlignment"].PropertyValue ); + Reset (); } [Fact] @@ -132,6 +136,7 @@ public void TestUpdatFrom_Add () colorSchemes = (Dictionary)theme ["ColorSchemes"].PropertyValue; Assert.Equal (colorSchemes ["Test"].Normal, colorScheme.Normal); Assert.Equal (colorSchemes ["Test"].Focus, colorScheme.Focus); + Reset (); } [Fact] @@ -186,5 +191,6 @@ public void TestUpdatFrom_Change () Assert.Equal (new Color (Color.BrightBlue), colorSchemes ["Test"].Normal.Background); Assert.Equal (new Color (Color.Cyan), colorSchemes ["Test"].Focus.Foreground); Assert.Equal (new Color (Color.BrightCyan), colorSchemes ["Test"].Focus.Background); + Reset (); } } From f3536f8a863fcf0233aef3e70a01da392dd4a32b Mon Sep 17 00:00:00 2001 From: Tig Date: Fri, 29 Mar 2024 09:59:08 -0600 Subject: [PATCH 2/4] Added Reset() call to AutoInitShutdown.After --- UnitTests/TestHelpers.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/UnitTests/TestHelpers.cs b/UnitTests/TestHelpers.cs index 88d7ec9785..5842b3a388 100644 --- a/UnitTests/TestHelpers.cs +++ b/UnitTests/TestHelpers.cs @@ -86,6 +86,7 @@ public override void After (MethodInfo methodUnderTest) Responder.Instances.Clear (); } #endif + ConfigurationManager.Reset (); } } From 57a57375f4d0bb0de0080c6db33070ad5eaf80ec Mon Sep 17 00:00:00 2001 From: Tig Date: Fri, 29 Mar 2024 10:28:32 -0600 Subject: [PATCH 3/4] Updated code style for internal statics --- Terminal.Gui/Configuration/ConfigurationManager.cs | 14 +++++++++----- Terminal.sln.DotSettings | 3 +++ .../Configuration/ConfigurationMangerTests.cs | 8 ++++++-- UnitTests/Configuration/ThemeScopeTests.cs | 1 + 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/Terminal.Gui/Configuration/ConfigurationManager.cs b/Terminal.Gui/Configuration/ConfigurationManager.cs index 2dbd2ba442..1731c9556c 100644 --- a/Terminal.Gui/Configuration/ConfigurationManager.cs +++ b/Terminal.Gui/Configuration/ConfigurationManager.cs @@ -82,8 +82,10 @@ public enum ConfigLocations /// to get and set the property's value. /// /// Is until is called. + [System.Diagnostics.CodeAnalysis.SuppressMessage ("Style", "IDE1006:Naming Styles", Justification = "")] internal static Dictionary? _allConfigProperties; + [System.Diagnostics.CodeAnalysis.SuppressMessage ("Style", "IDE1006:Naming Styles", Justification = "")] internal static readonly JsonSerializerOptions _serializerOptions = new () { ReadCommentHandling = JsonCommentHandling.Skip, @@ -104,8 +106,10 @@ public enum ConfigLocations Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; - internal static StringBuilder jsonErrors = new (); + [System.Diagnostics.CodeAnalysis.SuppressMessage ("Style", "IDE1006:Naming Styles", Justification = "")] + internal static StringBuilder _jsonErrors = new (); + [System.Diagnostics.CodeAnalysis.SuppressMessage ("Style", "IDE1006:Naming Styles", Justification = "")] private static readonly string _configFilename = "config.json"; /// The backing property for . @@ -300,12 +304,12 @@ public static void OnUpdated () /// Prints any Json deserialization errors that occurred during deserialization to the console. public static void PrintJsonErrors () { - if (jsonErrors.Length > 0) + if (_jsonErrors.Length > 0) { Console.WriteLine ( @"Terminal.Gui ConfigurationManager encountered the following errors while deserializing configuration files:" ); - Console.WriteLine (jsonErrors.ToString ()); + Console.WriteLine (_jsonErrors.ToString ()); } } @@ -350,7 +354,7 @@ public static void Reset () internal static void AddJsonError (string error) { Debug.WriteLine ($"ConfigurationManager: {error}"); - jsonErrors.AppendLine (error); + _jsonErrors.AppendLine (error); } /// @@ -602,5 +606,5 @@ internal static Stream ToStream () return stream; } - private static void ClearJsonErrors () { jsonErrors.Clear (); } + private static void ClearJsonErrors () { _jsonErrors.Clear (); } } diff --git a/Terminal.sln.DotSettings b/Terminal.sln.DotSettings index ef874a7ca4..d04aeb7c3e 100644 --- a/Terminal.sln.DotSettings +++ b/Terminal.sln.DotSettings @@ -383,6 +383,9 @@ True True False + <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /> True True True diff --git a/UnitTests/Configuration/ConfigurationMangerTests.cs b/UnitTests/Configuration/ConfigurationMangerTests.cs index c238ad5fbf..d2c96b341d 100644 --- a/UnitTests/Configuration/ConfigurationMangerTests.cs +++ b/UnitTests/Configuration/ConfigurationMangerTests.cs @@ -41,6 +41,7 @@ void ConfigurationManager_Applied (object sender, ConfigurationManagerEventArgs Assert.True (fired); Applied -= ConfigurationManager_Applied; + Reset (); } [Fact] @@ -176,6 +177,7 @@ void ConfigurationManager_Updated (object sender, ConfigurationManagerEventArgs Assert.True (fired); Updated -= ConfigurationManager_Updated; + Reset (); } [Fact] @@ -264,6 +266,7 @@ public void Reset_and_ResetLoadWithLibraryResourcesOnly_are_same () Assert.Equal (KeyCode.PageDown | KeyCode.CtrlMask, Application.AlternateForwardKey.KeyCode); Assert.Equal (KeyCode.PageUp | KeyCode.CtrlMask, Application.AlternateBackwardKey.KeyCode); Assert.False (Application.IsMouseDisabled); + Reset (); } [Fact] @@ -537,7 +540,7 @@ public void TestConfigurationManagerInvalidJsonLogs () Settings.Update ("{}}", "test"); - Assert.NotEqual (0, jsonErrors.Length); + Assert.NotEqual (0, _jsonErrors.Length); Application.Shutdown (); @@ -630,7 +633,7 @@ public void TestConfigurationManagerInvalidJsonThrows () jsonException = Assert.Throws (() => Settings.Update (json, "test")); Assert.StartsWith ("Unknown property", jsonException.Message); - Assert.Equal (0, jsonErrors.Length); + Assert.Equal (0, _jsonErrors.Length); ThrowOnJsonErrors = false; } @@ -813,6 +816,7 @@ public void TestConfigurationManagerUpdateFromJson () Assert.Equal (new Color (Color.White), Colors.ColorSchemes ["Base"].Normal.Foreground); Assert.Equal (new Color (Color.Blue), Colors.ColorSchemes ["Base"].Normal.Background); + Reset (); } [Fact] diff --git a/UnitTests/Configuration/ThemeScopeTests.cs b/UnitTests/Configuration/ThemeScopeTests.cs index 900676c5a1..3da7f881a8 100644 --- a/UnitTests/Configuration/ThemeScopeTests.cs +++ b/UnitTests/Configuration/ThemeScopeTests.cs @@ -35,6 +35,7 @@ public void Apply_ShouldApplyUpdatedProperties () ThemeManager.Themes! [ThemeManager.SelectedTheme]!.Apply (); Assert.Equal (Dialog.ButtonAlignments.Right, Dialog.DefaultButtonAlignment); + Reset (); } [Fact] From e7a116eb46cbd8540764c7e39fcbc4bd3be8e595 Mon Sep 17 00:00:00 2001 From: Tig Date: Fri, 29 Mar 2024 10:36:12 -0600 Subject: [PATCH 4/4] Code cleanup --- Terminal.Gui/Configuration/ConfigProperty.cs | 8 +-- .../Configuration/ConfigurationManager.cs | 58 +++++++++---------- .../Configuration/ConfigurationMangerTests.cs | 37 ++++++------ 3 files changed, 51 insertions(+), 52 deletions(-) diff --git a/Terminal.Gui/Configuration/ConfigProperty.cs b/Terminal.Gui/Configuration/ConfigProperty.cs index 2418268cfc..0c4a41682a 100644 --- a/Terminal.Gui/Configuration/ConfigProperty.cs +++ b/Terminal.Gui/Configuration/ConfigProperty.cs @@ -41,7 +41,7 @@ public bool Apply () { if (PropertyInfo?.GetValue (null) is { }) { - PropertyInfo?.SetValue (null, DeepMemberwiseCopy (PropertyValue, PropertyInfo?.GetValue (null))); + PropertyInfo?.SetValue (null, DeepMemberWiseCopy (PropertyValue, PropertyInfo?.GetValue (null))); } } catch (TargetInvocationException tie) @@ -82,9 +82,9 @@ public bool Apply () /// public static string GetJsonPropertyName (PropertyInfo pi) { - var jpna = pi.GetCustomAttribute (typeof (JsonPropertyNameAttribute)) as JsonPropertyNameAttribute; + var attr = pi.GetCustomAttribute (typeof (JsonPropertyNameAttribute)) as JsonPropertyNameAttribute; - return jpna?.Name ?? pi.Name; + return attr?.Name ?? pi.Name; } /// @@ -118,7 +118,7 @@ public static string GetJsonPropertyName (PropertyInfo pi) if (PropertyValue is { }) { - PropertyValue = DeepMemberwiseCopy (source, PropertyValue); + PropertyValue = DeepMemberWiseCopy (source, PropertyValue); } else { diff --git a/Terminal.Gui/Configuration/ConfigurationManager.cs b/Terminal.Gui/Configuration/ConfigurationManager.cs index 1731c9556c..f72cdd775a 100644 --- a/Terminal.Gui/Configuration/ConfigurationManager.cs +++ b/Terminal.Gui/Configuration/ConfigurationManager.cs @@ -2,6 +2,7 @@ global using CM = Terminal.Gui.ConfigurationManager; using System.Collections; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Text.Encodings.Web; using System.Text.Json; @@ -33,19 +34,19 @@ namespace Terminal.Gui; /// Settings are applied using the following precedence (higher precedence settings overwrite lower precedence /// settings): /// -/// 1. Application configuration found in the users's home directory (~/.tui/appname.config.json) -- +/// 1. Application configuration found in the users' home directory (~/.tui/appname.config.json) -- /// Highest precedence /// /// /// 2. Application configuration found in the directory the app was launched from ( /// ./.tui/appname.config.json). /// -/// 3. Application configuration found in the applications's resources (Resources/config.json). +/// 3. Application configuration found in the applications' resources (Resources/config.json). /// 4. Global configuration found in the user's home directory (~/.tui/config.json). /// 5. Global configuration found in the directory the app was launched from (./.tui/config.json). /// /// 6. Global configuration in Terminal.Gui.dll's resources (Terminal.Gui.Resources.config.json) -- -/// Lowest Precidence. +/// Lowest Precedence. /// /// public static class ConfigurationManager @@ -82,10 +83,10 @@ public enum ConfigLocations /// to get and set the property's value. /// /// Is until is called. - [System.Diagnostics.CodeAnalysis.SuppressMessage ("Style", "IDE1006:Naming Styles", Justification = "")] + [SuppressMessage ("Style", "IDE1006:Naming Styles", Justification = "")] internal static Dictionary? _allConfigProperties; - [System.Diagnostics.CodeAnalysis.SuppressMessage ("Style", "IDE1006:Naming Styles", Justification = "")] + [SuppressMessage ("Style", "IDE1006:Naming Styles", Justification = "")] internal static readonly JsonSerializerOptions _serializerOptions = new () { ReadCommentHandling = JsonCommentHandling.Skip, @@ -106,10 +107,10 @@ public enum ConfigLocations Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; - [System.Diagnostics.CodeAnalysis.SuppressMessage ("Style", "IDE1006:Naming Styles", Justification = "")] + [SuppressMessage ("Style", "IDE1006:Naming Styles", Justification = "")] internal static StringBuilder _jsonErrors = new (); - [System.Diagnostics.CodeAnalysis.SuppressMessage ("Style", "IDE1006:Naming Styles", Justification = "")] + [SuppressMessage ("Style", "IDE1006:Naming Styles", Justification = "")] private static readonly string _configFilename = "config.json"; /// The backing property for . @@ -284,7 +285,7 @@ public static void Load (bool reset = false) public static void OnApplied () { Debug.WriteLine ("ConfigurationManager.OnApplied()"); - Applied?.Invoke (null, new ConfigurationManagerEventArgs ()); + Applied?.Invoke (null, new ()); // TODO: Refactor ConfigurationManager to not use an event handler for this. // Instead, have it call a method on any class appropriately attributed @@ -298,7 +299,7 @@ public static void OnApplied () public static void OnUpdated () { Debug.WriteLine (@"ConfigurationManager.OnApplied()"); - Updated?.Invoke (null, new ConfigurationManagerEventArgs ()); + Updated?.Invoke (null, new ()); } /// Prints any Json deserialization errors that occurred during deserialization to the console. @@ -330,9 +331,9 @@ public static void Reset () ClearJsonErrors (); - Settings = new SettingsScope (); + Settings = new (); ThemeManager.Reset (); - AppSettings = new AppScope (); + AppSettings = new (); // To enable some unit tests, we only load from resources if the flag is set if (Locations.HasFlag (ConfigLocations.DefaultOnly)) @@ -359,18 +360,15 @@ internal static void AddJsonError (string error) /// /// System.Text.Json does not support copying a deserialized object to an existing instance. To work around this, - /// we implement a 'deep, memberwise copy' method. + /// we implement a 'deep, member-wise copy' method. /// /// TOOD: When System.Text.Json implements `PopulateObject` revisit https://github.com/dotnet/corefx/issues/37627 /// /// /// updated from - internal static object? DeepMemberwiseCopy (object? source, object? destination) + internal static object? DeepMemberWiseCopy (object? source, object? destination) { - if (destination is null) - { - throw new ArgumentNullException (nameof (destination)); - } + ArgumentNullException.ThrowIfNull (destination); if (source is null) { @@ -410,7 +408,7 @@ internal static void AddJsonError (string error) if (((IDictionary)destination).Contains (srcKey)) { ((IDictionary)destination) [srcKey] = - DeepMemberwiseCopy (((IDictionary)source) [srcKey], ((IDictionary)destination) [srcKey]); + DeepMemberWiseCopy (((IDictionary)source) [srcKey], ((IDictionary)destination) [srcKey]); } else { @@ -442,7 +440,7 @@ where destProp.CanWrite if (destVal is { }) { // Recurse - destProp.SetValue (destination, DeepMemberwiseCopy (sourceVal, destVal)); + destProp.SetValue (destination, DeepMemberWiseCopy (sourceVal, destVal)); } else { @@ -482,7 +480,7 @@ internal static void GetHardCodedDefaults () throw new InvalidOperationException ("Initialize must be called first."); } - Settings = new SettingsScope (); + Settings = new (); ThemeManager.GetHardCodedDefaults (); AppSettings?.RetrieveValues (); @@ -498,7 +496,7 @@ internal static void GetHardCodedDefaults () /// internal static void Initialize () { - _allConfigProperties = new Dictionary (); + _allConfigProperties = new (); _settings = null; Dictionary classesWithConfigProps = new (StringComparer.InvariantCultureIgnoreCase); @@ -553,18 +551,18 @@ from p in enumerable scp.OmitClassName ? ConfigProperty.GetJsonPropertyName (p) : $"{p.DeclaringType?.Name}.{p.Name}", - new ConfigProperty { PropertyInfo = p, PropertyValue = null } + new() { PropertyInfo = p, PropertyValue = null } ); } else { - throw new Exception ( - $"Property { - p.Name - } in class { - p.DeclaringType?.Name - } is not static. All SerializableConfigurationProperty properties must be static." - ); + throw new ( + $"Property { + p.Name + } in class { + p.DeclaringType?.Name + } is not static. All SerializableConfigurationProperty properties must be static." + ); } } } @@ -580,7 +578,7 @@ from p in enumerable //_allConfigProperties.ToList ().ForEach (x => Debug.WriteLine ($" Property: {x.Key}")); - AppSettings = new AppScope (); + AppSettings = new (); } /// Creates a JSON document with the configuration specified. diff --git a/UnitTests/Configuration/ConfigurationMangerTests.cs b/UnitTests/Configuration/ConfigurationMangerTests.cs index d2c96b341d..6d32738470 100644 --- a/UnitTests/Configuration/ConfigurationMangerTests.cs +++ b/UnitTests/Configuration/ConfigurationMangerTests.cs @@ -1,6 +1,7 @@ using System.Reflection; using System.Text.Json; using static Terminal.Gui.ConfigurationManager; +#pragma warning disable IDE1006 namespace Terminal.Gui.ConfigurationTests; @@ -45,65 +46,65 @@ void ConfigurationManager_Applied (object sender, ConfigurationManagerEventArgs } [Fact] - public void DeepMemberwiseCopyTest () + public void DeepMemberWiseCopyTest () { // Value types var stringDest = "Destination"; var stringSrc = "Source"; - object stringCopy = DeepMemberwiseCopy (stringSrc, stringDest); + object stringCopy = DeepMemberWiseCopy (stringSrc, stringDest); Assert.Equal (stringSrc, stringCopy); stringDest = "Destination"; stringSrc = "Destination"; - stringCopy = DeepMemberwiseCopy (stringSrc, stringDest); + stringCopy = DeepMemberWiseCopy (stringSrc, stringDest); Assert.Equal (stringSrc, stringCopy); stringDest = "Destination"; stringSrc = null; - stringCopy = DeepMemberwiseCopy (stringSrc, stringDest); + stringCopy = DeepMemberWiseCopy (stringSrc, stringDest); Assert.Equal (stringSrc, stringCopy); stringDest = "Destination"; stringSrc = string.Empty; - stringCopy = DeepMemberwiseCopy (stringSrc, stringDest); + stringCopy = DeepMemberWiseCopy (stringSrc, stringDest); Assert.Equal (stringSrc, stringCopy); var boolDest = true; var boolSrc = false; - object boolCopy = DeepMemberwiseCopy (boolSrc, boolDest); + object boolCopy = DeepMemberWiseCopy (boolSrc, boolDest); Assert.Equal (boolSrc, boolCopy); boolDest = false; boolSrc = true; - boolCopy = DeepMemberwiseCopy (boolSrc, boolDest); + boolCopy = DeepMemberWiseCopy (boolSrc, boolDest); Assert.Equal (boolSrc, boolCopy); boolDest = true; boolSrc = true; - boolCopy = DeepMemberwiseCopy (boolSrc, boolDest); + boolCopy = DeepMemberWiseCopy (boolSrc, boolDest); Assert.Equal (boolSrc, boolCopy); boolDest = false; boolSrc = false; - boolCopy = DeepMemberwiseCopy (boolSrc, boolDest); + boolCopy = DeepMemberWiseCopy (boolSrc, boolDest); Assert.Equal (boolSrc, boolCopy); // Structs var attrDest = new Attribute (Color.Black); var attrSrc = new Attribute (Color.White); - object attrCopy = DeepMemberwiseCopy (attrSrc, attrDest); + object attrCopy = DeepMemberWiseCopy (attrSrc, attrDest); Assert.Equal (attrSrc, attrCopy); // Classes var colorschemeDest = new ColorScheme { Disabled = new Attribute (Color.Black) }; var colorschemeSrc = new ColorScheme { Disabled = new Attribute (Color.White) }; - object colorschemeCopy = DeepMemberwiseCopy (colorschemeSrc, colorschemeDest); + object colorschemeCopy = DeepMemberWiseCopy (colorschemeSrc, colorschemeDest); Assert.Equal (colorschemeSrc, colorschemeCopy); // Dictionaries Dictionary dictDest = new () { { "Disabled", new Attribute (Color.Black) } }; Dictionary dictSrc = new () { { "Disabled", new Attribute (Color.White) } }; - Dictionary dictCopy = (Dictionary)DeepMemberwiseCopy (dictSrc, dictDest); + Dictionary dictCopy = (Dictionary)DeepMemberWiseCopy (dictSrc, dictDest); Assert.Equal (dictSrc, dictCopy); dictDest = new Dictionary { { "Disabled", new Attribute (Color.Black) } }; @@ -112,7 +113,7 @@ public void DeepMemberwiseCopyTest () { { "Disabled", new Attribute (Color.White) }, { "Normal", new Attribute (Color.Blue) } }; - dictCopy = (Dictionary)DeepMemberwiseCopy (dictSrc, dictDest); + dictCopy = (Dictionary)DeepMemberWiseCopy (dictSrc, dictDest); Assert.Equal (dictSrc, dictCopy); // src adds an item @@ -122,7 +123,7 @@ public void DeepMemberwiseCopyTest () { { "Disabled", new Attribute (Color.White) }, { "Normal", new Attribute (Color.Blue) } }; - dictCopy = (Dictionary)DeepMemberwiseCopy (dictSrc, dictDest); + dictCopy = (Dictionary)DeepMemberWiseCopy (dictSrc, dictDest); Assert.Equal (2, dictCopy.Count); Assert.Equal (dictSrc ["Disabled"], dictCopy ["Disabled"]); Assert.Equal (dictSrc ["Normal"], dictCopy ["Normal"]); @@ -133,7 +134,7 @@ public void DeepMemberwiseCopyTest () { "Disabled", new Attribute (Color.Black) }, { "Normal", new Attribute (Color.White) } }; dictSrc = new Dictionary { { "Disabled", new Attribute (Color.White) } }; - dictCopy = (Dictionary)DeepMemberwiseCopy (dictSrc, dictDest); + dictCopy = (Dictionary)DeepMemberWiseCopy (dictSrc, dictDest); Assert.Equal (2, dictCopy.Count); Assert.Equal (dictSrc ["Disabled"], dictCopy ["Disabled"]); Assert.Equal (dictDest ["Normal"], dictCopy ["Normal"]); @@ -379,7 +380,7 @@ public void TestConfigProperties () Assert.NotEmpty (Settings); - // test that all ConfigProperites have our attribute + // test that all ConfigProperties have our attribute Assert.All ( Settings, item => Assert.NotEmpty ( @@ -411,7 +412,7 @@ public void TestConfigProperties () [Fact] public void TestConfigPropertyOmitClassName () { - // Color.ColorShemes is serialzied as "ColorSchemes", not "Colors.ColorSchemes" + // 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)); @@ -624,7 +625,7 @@ public void TestConfigurationManagerInvalidJsonThrows () jsonException = Assert.Throws (() => Settings.Update (json, "test")); Assert.Equal ("Both Foreground and Background colors must be provided.", jsonException.Message); - // Unknown proeprty + // Unknown property json = @" { ""Unknown"" : ""Not known""