using System.Collections.Frozen; using System.Collections.Immutable; using System.Diagnostics; using System.Reflection; using System.Text; using System.Text.Json; using Xunit.Abstractions; using static Terminal.Gui.Configuration.ConfigurationManager; using File = System.IO.File; #pragma warning disable IDE1006 namespace UnitTests.ConfigurationTests; public class ConfigurationManagerTests (ITestOutputHelper output) { [Fact] public void ModuleInitializer_Was_Called () { Assert.False (IsEnabled); Assert.True (IsInitialized ()); } [Fact] public void Initialize_Throws_If_Called_Explicitly () { Assert.False (IsEnabled); Assert.Throws (Initialize); } [Fact] public void HardCodedDefaultCache_Properties_Are_Copies () { Assert.False (IsEnabled); Enable (ConfigLocations.HardCoded); Assert.Equal (Key.Esc, Application.QuitKey); ConfigProperty fromSettings = Settings! ["Application.QuitKey"]; FrozenDictionary initialCache = GetHardCodedConfigPropertyCache (); Assert.NotNull (initialCache); ConfigProperty fromCache = initialCache ["Application.QuitKey"]; // Assert Assert.NotEqual (fromCache, fromSettings); Disable (true); } [Fact] public void GetHardCodedDefaultCache_Always_Returns_Same_Ref () { // It's important it always returns the same cache ref, so no copies are made // Otherwise it's a big performance hit Assert.False (IsEnabled); try { FrozenDictionary initialCache = GetHardCodedConfigPropertyCache (); FrozenDictionary cache = GetHardCodedConfigPropertyCache (); Assert.Equal (initialCache, cache); } finally { Disable (true); } } [Fact] public void HardCodedDefaultCache_Properties_Are_Immutable () { Assert.False (IsEnabled); try { Enable (ConfigLocations.HardCoded); Assert.Equal (Key.Esc, Application.QuitKey); FrozenDictionary initialCache = GetHardCodedConfigPropertyCache (); Assert.NotNull (initialCache); Assert.Equal (Key.Esc, (Key)initialCache ["Application.QuitKey"].PropertyValue); // Act Settings! ["Application.QuitKey"].PropertyValue = Key.Q; Assert.Equal (Key.Q, (Key)Settings ["Application.QuitKey"].PropertyValue); Settings ["Application.QuitKey"].Apply (); Assert.Equal (Key.Q, Application.QuitKey); Application.QuitKey = Key.K; // Assert FrozenDictionary cache = GetHardCodedConfigPropertyCache (); Assert.True (initialCache ["Application.QuitKey"].Immutable); Assert.Equal (Key.Esc, (Key)initialCache ["Application.QuitKey"].PropertyValue); } finally { Disable (true); Application.ResetState (true); } } [Fact] public void Disable_Settings_Is_NotNull () { Assert.False (IsEnabled); Disable (); Assert.NotNull (Settings); } [Fact] public void Disable_With_ResetToHardCodedDefaults_True_Works_When_Disabled () { Assert.False (IsEnabled); Disable (true); } [Fact] public void Enable_Settings_Is_Valid () { Assert.False (IsEnabled); Enable (ConfigLocations.HardCoded); Assert.NotNull (Settings); Disable (true); } [Fact] public void Enable_HardCoded_Resets_Schemes_After_Runtime_Config () { Assert.False (IsEnabled); try { // Arrange: Start from hard-coded defaults and capture baseline scheme values. Enable (ConfigLocations.HardCoded); Dictionary schemes = SchemeManager.GetSchemes (); Assert.NotNull (schemes); Assert.NotEmpty (schemes); Color baselineFg = schemes ["Base"].Normal.Foreground; Color baselineBg = schemes ["Base"].Normal.Background; // Sanity: defaults should be stable Assert.NotEqual (default (Color), baselineFg); Assert.NotEqual (default (Color), baselineBg); // Act: Override the Base scheme via runtime JSON and apply ThrowOnJsonErrors = true; RuntimeConfig = """ { "Themes": [ { "Default": { "Schemes": [ { "Base": { "Normal": { "Foreground": "Black", "Background": "Gray" } } } ] } } ] } """; Load (ConfigLocations.Runtime); Apply (); // Verify override took effect Dictionary overridden = SchemeManager.GetSchemes (); Assert.Equal (Color.Black, overridden ["Base"].Normal.Foreground); Assert.Equal (Color.Gray, overridden ["Base"].Normal.Background); // Now simulate "CM.Enable(true)" semantics: re-enable with HardCoded to reset Disable (); Enable (ConfigLocations.HardCoded); // Assert: schemes are reset to the original hard-coded baseline Dictionary reset = SchemeManager.GetSchemes (); Assert.Equal (baselineFg, reset ["Base"].Normal.Foreground); Assert.Equal (baselineBg, reset ["Base"].Normal.Background); } finally { Disable (true); Application.ResetState (true); } } [Fact] public void Enable_HardCoded_Resets_Theme_Dictionary_And_Selection () { Assert.False (IsEnabled); try { // Arrange: Enable defaults Enable (ConfigLocations.HardCoded); Assert.Equal (ThemeManager.DEFAULT_THEME_NAME, ThemeManager.Theme); Assert.Single (ThemeManager.Themes!); Assert.True (ThemeManager.Themes.ContainsKey (ThemeManager.DEFAULT_THEME_NAME)); // Act: Load a runtime config that introduces a custom theme and selects it ThrowOnJsonErrors = true; RuntimeConfig = """ { "Theme": "Custom", "Themes": [ { "Custom": { "Schemes": [ { "Base": { "Normal": { "Foreground": "Yellow", "Background": "Black" } } } ] } } ] } """; // Capture dynamically created hardCoded hard-coded scheme colors ImmutableSortedDictionary hardCodedSchemes = SchemeManager.GetHardCodedSchemes ()!; Color hardCodedBaseNormalFg = hardCodedSchemes ["Base"].Normal.Foreground; Assert.Equal (new Color (StandardColor.LightBlue).ToString (), hardCodedBaseNormalFg.ToString ()); Load (ConfigLocations.Runtime); Apply (); // Verify the runtime selection took effect Assert.Equal ("Custom", ThemeManager.Theme); // Now simulate "CM.Enable(true)" semantics: re-enable with HardCoded to reset Disable (); Enable (ConfigLocations.HardCoded); // Assert: selection and dictionary have been reset to hard-coded defaults Assert.Equal (ThemeManager.DEFAULT_THEME_NAME, ThemeManager.Theme); Assert.Single (ThemeManager.Themes!); Assert.True (ThemeManager.Themes.ContainsKey (ThemeManager.DEFAULT_THEME_NAME)); // Also assert the Base scheme is back to defaults (sanity check) Scheme baseScheme = SchemeManager.GetSchemes () ["Base"]; Assert.Equal (hardCodedBaseNormalFg.ToString (), SchemeManager.GetSchemes () ["Base"]!.Normal.Foreground.ToString ()); } finally { Disable (true); Application.ResetState (true); } } [Fact] public void Apply_Applies_Theme () { Assert.False (IsEnabled); Enable (ConfigLocations.HardCoded); var theme = new ThemeScope (); theme.LoadHardCodedDefaults (); Assert.NotEmpty (theme); Assert.True (ThemeManager.Themes!.TryAdd ("testTheme", theme)); Assert.Equal (2, ThemeManager.Themes.Count); Assert.Equal (LineStyle.Rounded, FrameView.DefaultBorderStyle); theme ["FrameView.DefaultBorderStyle"].PropertyValue = LineStyle.Double; ThemeManager.Theme = "testTheme"; Apply (); Assert.Equal (LineStyle.Double, FrameView.DefaultBorderStyle); Disable (true); } [Fact] public void Apply_Raises_Applied () { Assert.False (IsEnabled); Enable (ConfigLocations.HardCoded); Applied += ConfigurationManagerApplied; var fired = false; void ConfigurationManagerApplied (object sender, ConfigurationManagerEventArgs obj) { fired = true; // assert Assert.Equal (KeyCode.Q, Application.QuitKey.KeyCode); Assert.Equal (KeyCode.F, Application.NextTabGroupKey.KeyCode); Assert.Equal (KeyCode.B, Application.PrevTabGroupKey.KeyCode); } // act Settings! ["Application.QuitKey"].PropertyValue = Key.Q; Settings ["Application.NextTabGroupKey"].PropertyValue = Key.F; Settings ["Application.PrevTabGroupKey"].PropertyValue = Key.B; Apply (); // assert Assert.True (fired); Applied -= ConfigurationManagerApplied; Disable (true); Application.ResetState (true); } [Fact] public void Load_Raises_Updated () { Assert.False (IsEnabled); var fired = false; Enable (ConfigLocations.HardCoded); ThrowOnJsonErrors = true; Assert.Equal (Key.Esc, ((Key)Settings! ["Application.QuitKey"].PropertyValue)!.KeyCode); Updated += ConfigurationManagerUpdated; // Act // Only select locations under test control Load (ConfigLocations.LibraryResources | ConfigLocations.AppResources | ConfigLocations.Runtime); // assert Assert.True (fired); // clean up Updated -= ConfigurationManagerUpdated; Disable (true); Application.ResetState (true); return; void ConfigurationManagerUpdated (object sender, ConfigurationManagerEventArgs obj) { fired = true; } } [Fact] public void Load_And_Apply_Performance_Check () { Assert.False (IsEnabled); Enable (ConfigLocations.HardCoded); try { // Start stopwatch var stopwatch = new Stopwatch (); stopwatch.Start (); // Act Load (ConfigLocations.All); Apply (); // Stop stopwatch stopwatch.Stop (); // Assert output.WriteLine ($"Load took {stopwatch.ElapsedMilliseconds} ms"); // Ensure load time is reasonable (adjust threshold as needed) Assert.True ( stopwatch.ElapsedMilliseconds < 1000, $"Loading configuration took {stopwatch.ElapsedMilliseconds}ms, which exceeds reasonable threshold"); } finally { Disable (true); Application.ResetState (true); } } [Fact] public void Load_Loads_Custom_Json () { Assert.False (IsEnabled); try { Enable (ConfigLocations.HardCoded); ThrowOnJsonErrors = true; Assert.Equal (Key.Esc, (Key)Settings! ["Application.QuitKey"].PropertyValue); // act RuntimeConfig = """ { "Application.QuitKey": "Ctrl-Q" } """; Load (ConfigLocations.Runtime); // assert Assert.Equal (Key.Q.WithCtrl, (Key)Settings ["Application.QuitKey"].PropertyValue); } finally { // clean up Disable (true); Application.ResetState (true); } } [Fact (Skip = "Events disabled")] public void ResetToCurrentValues_Raises_Updated () { Assert.False (IsEnabled); var fired = false; try { Enable (ConfigLocations.HardCoded); Settings! ["Application.QuitKey"].PropertyValue = Key.Q; Updated += ConfigurationManagerUpdated; // Act UpdateToCurrentValues (); // assert Assert.True (fired); } finally { Updated -= ConfigurationManagerUpdated; Disable (true); Application.ResetState (true); } return; void ConfigurationManagerUpdated (object sender, ConfigurationManagerEventArgs obj) { fired = true; } } [Fact] public void ResetToHardCodedDefaults_and_Load_LibraryResourcesOnly_are_same () { Assert.False (IsEnabled); try { // arrange Enable (ConfigLocations.HardCoded); Settings! ["Application.QuitKey"].PropertyValue = Key.Q; Settings ["Application.NextTabGroupKey"].PropertyValue = Key.F; Settings ["Application.PrevTabGroupKey"].PropertyValue = Key.B; Settings.Apply (); // assert apply worked Assert.Equal (KeyCode.Q, Application.QuitKey.KeyCode); Assert.Equal (KeyCode.F, Application.NextTabGroupKey.KeyCode); Assert.Equal (KeyCode.B, Application.PrevTabGroupKey.KeyCode); //act ResetToHardCodedDefaults (); // assert Assert.NotEmpty (ThemeManager.Themes!); Assert.Equal ("Default", ThemeManager.Theme); Assert.Equal (Key.Esc, Application.QuitKey); Assert.Equal (Key.F6, Application.NextTabGroupKey); Assert.Equal (Key.F6.WithShift, Application.PrevTabGroupKey); // arrange Settings ["Application.QuitKey"].PropertyValue = Key.Q; Settings ["Application.NextTabGroupKey"].PropertyValue = Key.F; Settings ["Application.PrevTabGroupKey"].PropertyValue = Key.B; Settings.Apply (); // act Load (ConfigLocations.LibraryResources); Apply (); // assert Assert.NotEmpty (ThemeManager.Themes); Assert.Equal ("Default", ThemeManager.Theme); Assert.Equal (KeyCode.Esc, Application.QuitKey.KeyCode); Assert.Equal (Key.F6, Application.NextTabGroupKey); Assert.Equal (Key.F6.WithShift, Application.PrevTabGroupKey); } finally { Disable (true); Application.ResetState (true); } } [Fact] public void ResetToHardCodedDefaults_Resets () { Assert.False (IsEnabled); try { Enable (ConfigLocations.HardCoded); // Capture dynamically created hardCoded hard-coded scheme colors ImmutableSortedDictionary hardCodedSchemesViaSchemeManager = SchemeManager.GetHardCodedSchemes ()!; Dictionary hardCodedSchemes = GetHardCodedConfigPropertiesByScope ("ThemeScope")!.ToFrozenDictionary () ["Schemes"].PropertyValue as Dictionary; Color hardCodedBaseNormalFg = hardCodedSchemesViaSchemeManager ["Base"].Normal.Foreground; Assert.Equal (new Color (StandardColor.LightBlue).ToString (), hardCodedBaseNormalFg.ToString ()); // Capture current scheme colors Dictionary currentSchemes = SchemeManager.GetSchemes ()!; Color currentBaseNormalFg = currentSchemes ["Base"].Normal.Foreground; Assert.Equal (hardCodedBaseNormalFg.ToString (), currentBaseNormalFg.ToString ()); // Arrange var json = @" { ""$schema"": ""https://gui-cs.github.io/Terminal.Gui/schemas/tui-config-schema.json"", ""Application.QuitKey"": ""Alt-Z"", ""Theme"": ""Default"", ""Themes"": [ { ""Default"": { ""MessageBox.DefaultButtonAlignment"": ""End"", ""Schemes"": [ { ""Runnable"": { ""Normal"": { ""Foreground"": ""BrightGreen"", ""Background"": ""Black"" }, ""Focus"": { ""Foreground"": ""White"", ""Background"": ""Cyan"" }, ""HotNormal"": { ""Foreground"": ""Yellow"", ""Background"": ""Black"" }, ""HotFocus"": { ""Foreground"": ""Blue"", ""Background"": ""Cyan"" }, ""Disabled"": { ""Foreground"": ""DarkGray"", ""Background"": ""Black"" } } }, { ""Base"": { ""Normal"": { ""Foreground"": ""White"", ""Background"": ""Blue"" }, ""Focus"": { ""Foreground"": ""Black"", ""Background"": ""Gray"" }, ""HotNormal"": { ""Foreground"": ""BrightCyan"", ""Background"": ""Blue"" }, ""HotFocus"": { ""Foreground"": ""BrightBlue"", ""Background"": ""Gray"" }, ""Disabled"": { ""Foreground"": ""DarkGray"", ""Background"": ""Blue"" } } }, { ""Dialog"": { ""Normal"": { ""Foreground"": ""Black"", ""Background"": ""Gray"" }, ""Focus"": { ""Foreground"": ""White"", ""Background"": ""DarkGray"" }, ""HotNormal"": { ""Foreground"": ""Blue"", ""Background"": ""Gray"" }, ""HotFocus"": { ""Foreground"": ""BrightYellow"", ""Background"": ""DarkGray"" }, ""Disabled"": { ""Foreground"": ""Gray"", ""Background"": ""DarkGray"" } } }, { ""Menu"": { ""Normal"": { ""Foreground"": ""White"", ""Background"": ""DarkGray"" }, ""Focus"": { ""Foreground"": ""White"", ""Background"": ""Black"" }, ""HotNormal"": { ""Foreground"": ""BrightYellow"", ""Background"": ""DarkGray"" }, ""HotFocus"": { ""Foreground"": ""BrightYellow"", ""Background"": ""Black"" }, ""Disabled"": { ""Foreground"": ""Gray"", ""Background"": ""DarkGray"" } } }, { ""Error"": { ""Normal"": { ""Foreground"": ""Red"", ""Background"": ""White"" }, ""Focus"": { ""Foreground"": ""Black"", ""Background"": ""BrightRed"" }, ""HotNormal"": { ""Foreground"": ""Black"", ""Background"": ""White"" }, ""HotFocus"": { ""Foreground"": ""White"", ""Background"": ""BrightRed"" }, ""Disabled"": { ""Foreground"": ""DarkGray"", ""Background"": ""White"" } } } ] } } ] } "; // ResetToCurrentValues (); ThrowOnJsonErrors = true; ConfigurationManager.SourcesManager?.Load (Settings, json, "UpdateFromJson", ConfigLocations.Runtime); Assert.Equal ("Default", ThemeManager.Theme); Assert.Equal (KeyCode.Esc, Application.QuitKey.KeyCode); Assert.Equal (KeyCode.Z | KeyCode.AltMask, ((Key)Settings! ["Application.QuitKey"].PropertyValue)!.KeyCode); Assert.Equal (Alignment.Center, MessageBox.DefaultButtonAlignment); // Get current scheme colors again currentSchemes = SchemeManager.GetSchemes ()!; currentBaseNormalFg = currentSchemes ["Base"].Normal.Foreground; Assert.Equal (Color.White.ToString (), currentBaseNormalFg.ToString ()); // Now Apply Apply (); Assert.Equal ("Default", ThemeManager.Theme); Assert.Equal (KeyCode.Z | KeyCode.AltMask, Application.QuitKey.KeyCode); Assert.Equal (Alignment.End, MessageBox.DefaultButtonAlignment); Assert.Equal (Color.White.ToString (), currentBaseNormalFg.ToString ()); // Reset ResetToHardCodedDefaults (); hardCodedSchemes = GetHardCodedConfigPropertiesByScope ("ThemeScope")!.ToFrozenDictionary () ["Schemes"].PropertyValue as Dictionary; hardCodedBaseNormalFg = hardCodedSchemes! ["Base"].Normal.Foreground; Assert.Equal (new Color (StandardColor.LightBlue).ToString (), hardCodedBaseNormalFg.ToString ()); FrozenDictionary hardCodedCache = GetHardCodedConfigPropertyCache ()!; Assert.Equal (hardCodedCache ["Theme"].PropertyValue, ThemeManager.Theme); Assert.Equal (hardCodedCache ["Application.QuitKey"].PropertyValue, Application.QuitKey); // Themes Assert.Equal (hardCodedCache ["MessageBox.DefaultButtonAlignment"].PropertyValue, MessageBox.DefaultButtonAlignment); Assert.Equal (GetHardCodedConfigPropertyCache ()! ["MessageBox.DefaultButtonAlignment"].PropertyValue, MessageBox.DefaultButtonAlignment); // Schemes currentSchemes = SchemeManager.GetSchemes ()!; currentBaseNormalFg = currentSchemes ["Base"].Normal.Foreground; Assert.Equal (hardCodedBaseNormalFg.ToString (), currentBaseNormalFg.ToString ()); Scheme baseScheme = SchemeManager.GetScheme ("Base"); Attribute attr = baseScheme.Normal; // Use ToString so Assert.Equal shows the actual vs expected values on failure Assert.Equal (hardCodedBaseNormalFg.ToString (), attr.Foreground.ToString ()); } finally { output.WriteLine ("Disabling CM to clean up."); Disable (true); } } [Fact (Skip = "ResetToCurrentValues corrupts hard coded cache")] public void ResetToCurrentValues_Enabled_Resets () { Assert.False (IsEnabled); try { // Act Enable (ConfigLocations.HardCoded); Application.QuitKey = Key.A; UpdateToCurrentValues (); Assert.Equal (Key.A, (Key)Settings! ["Application.QuitKey"].PropertyValue); Assert.NotNull (Settings); Assert.NotNull (AppSettings); Assert.NotNull (ThemeManager.Themes); // Default Theme should be "Default" Assert.Single (ThemeManager.Themes); Assert.Equal (ThemeManager.DEFAULT_THEME_NAME, ThemeManager.Theme); } finally { Disable (true); } } [Fact] public void ConfigurationManager_DefaultPrecedence_IsRespected () { Assert.False (IsEnabled); try { // arrange Enable (ConfigLocations.HardCoded); ThrowOnJsonErrors = true; // Setup multiple configurations with the same setting // with different precedence levels RuntimeConfig = """ { "Application.QuitKey": "Alt+Q" } """; var defaultConfig = """ { "Application.QuitKey": "Ctrl+X" } """; // Update default config first (lower precedence) ConfigurationManager.SourcesManager?.Load (Settings, defaultConfig, "default-test", ConfigLocations.LibraryResources); // Then load runtime config, which should override default Load (ConfigLocations.Runtime); // Assert - the runtime config should win due to precedence Assert.Equal (Key.Q.WithAlt, (Key)Settings! ["Application.QuitKey"].PropertyValue); } finally { Disable (true); } } /// Save the `config.json` file; this can be used to update the file in `Terminal.Gui.Resources.config.json'. /// /// IMPORTANT: For the file generated to be valid, this must be the ONLY test run. Config Properties are all /// static and thus can be overwritten by other tests. /// [Fact] public void Save_HardCodedDefaults_To_config_json () { Assert.False (IsEnabled); try { Enable (ConfigLocations.HardCoded); // Get the hard coded settings ResetToHardCodedDefaults (); // Serialize to a JSON string string json = ConfigurationManager.SourcesManager?.ToJson (Settings); // Write the JSON string to the file File.WriteAllText ("hard_coded_defaults_config.json", json); // Verify the file was created Assert.True (File.Exists ("hard_coded_defaults_config.json"), "Failed to create config.json file"); } finally { Disable (true); } } /// Save the `config.json` file; this can be used to update the file in `Terminal.Gui.Resources.config.json'. /// /// IMPORTANT: For the file generated to be valid, this must be the ONLY test run. Config Properties are all /// static and thus can be overwritten by other tests. /// [Fact] public void Save_Library_Defaults_To_config_json () { Assert.False (IsEnabled); try { Enable (ConfigLocations.LibraryResources); // Serialize to a JSON string string json = ConfigurationManager.SourcesManager?.ToJson (Settings); // Write the JSON string to the file File.WriteAllText ("library_defaults_config.json", json); // Verify the file was created Assert.True (File.Exists ("library_defaults_config.json"), "Failed to create config.json file"); } finally { Disable (true); } } [Fact] public void TestConfigProperties () { Assert.False (IsEnabled); try { Enable (ConfigLocations.HardCoded); Assert.NotEmpty (Settings!); // test that all ConfigProperties have our attribute Assert.All ( Settings, item => Assert.Contains ( item.Value.PropertyInfo!.CustomAttributes, a => a.AttributeType == typeof (ConfigurationPropertyAttribute) )); #pragma warning disable xUnit2030 Assert.DoesNotContain ( Settings, cp => cp.Value.PropertyInfo!.GetCustomAttribute ( typeof (ConfigurationPropertyAttribute) ) == null ); #pragma warning restore xUnit2030 // 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 (ThemeManager.GetCurrentTheme ().ContainsKey ("FrameView.DefaultBorderStyle")); Assert.Equal (pi, ThemeManager.GetCurrentTheme () ["FrameView.DefaultBorderStyle"].PropertyInfo); } finally { Disable (true); } } [Fact] public void Load_And_Apply_HardCoded () { Assert.False (IsEnabled); try { // Spot check by setting some of the config properties Application.QuitKey = Key.X.WithCtrl; FileDialog.MaxSearchResults = 1; Enable (ConfigLocations.HardCoded); Load (ConfigLocations.HardCoded); // Spot check Assert.Equal (Key.Esc, Settings ["Application.QuitKey"].PropertyValue as Key); Assert.Equal (10000, (int)Settings ["FileDialog.MaxSearchResults"].PropertyValue!); Assert.Single (ThemeManager.Themes!); Assert.Equal ("Default", ThemeManager.Theme); Assert.NotEmpty (ThemeManager.Themes [ThemeManager.Theme]); // Verify schemes are properly initialized Assert.NotNull (SchemeManager.GetSchemes ()); Assert.NotEmpty (SchemeManager.GetSchemes ()); // Verify "Base" has correct values //Assert.Equal (Color.White, SchemeManager.GetSchemes () ["Base"]!.Normal.Foreground); //Assert.Equal (Color.Blue, SchemeManager.GetSchemes () ["Base"].Normal.Background); Apply (); Assert.Equal (Key.Esc, Application.QuitKey); Assert.Equal (10000, FileDialog.MaxSearchResults); } finally { Disable (true); } } [Fact] public void Load_And_Apply_LibraryResources () { Assert.False (IsEnabled); try { // Spot check by setting some of the config properties Application.QuitKey = Key.X.WithCtrl; FileDialog.MaxSearchResults = 1; Enable (ConfigLocations.HardCoded); Load (ConfigLocations.LibraryResources); // Spot check Assert.Equal (Key.Esc, Settings! ["Application.QuitKey"].PropertyValue as Key); Assert.Equal (10000, (int)Settings ["FileDialog.MaxSearchResults"].PropertyValue!); Assert.NotEmpty (ThemeManager.Themes!); Assert.Equal ("Default", ThemeManager.Theme); Assert.NotEmpty (ThemeManager.Themes [ThemeManager.Theme]); // Verify schemes are properly initialized Assert.NotNull (SchemeManager.GetSchemes ()); Assert.NotEmpty (SchemeManager.GetSchemes ()); // This is too fragile as the default scheme may change // Verify "Base" has correct values //Assert.Equal (Color.White, SchemeManager.Schemes ["Base"]!.Normal.Foreground); //Assert.Equal (Color.Blue, SchemeManager.Schemes ["Base"].Normal.Background); Apply (); Assert.Equal (Key.Esc, Application.QuitKey); Assert.Equal (10000, FileDialog.MaxSearchResults); } finally { Disable (true); } } [Fact] public void Load_And_Apply_RuntimeConfig () { Assert.False (IsEnabled); try { // Spot check by setting some of the config properties Application.QuitKey = Key.X.WithCtrl; FileDialog.MaxSearchResults = 1; Glyphs.Apple = new ('z'); ThrowOnJsonErrors = true; Enable (ConfigLocations.HardCoded); RuntimeConfig = """ { "Application.QuitKey": "Alt-Q", "FileDialog.MaxSearchResults":9, "Themes" : [ { "Default" : { "Glyphs.Apple": "a" } } ] } """; Load (ConfigLocations.Runtime); Assert.Equal (Key.Q.WithAlt, Settings! ["Application.QuitKey"].PropertyValue as Key); Assert.Equal (9, (int)Settings ["FileDialog.MaxSearchResults"].PropertyValue!); Assert.Equal (new Rune ('a'), ThemeManager.GetCurrentTheme () ["Glyphs.Apple"].PropertyValue); Apply (); Assert.Equal (Key.Q.WithAlt, Application.QuitKey); Assert.Equal (9, FileDialog.MaxSearchResults); Assert.Equal (new ('a'), Glyphs.Apple); } finally { Disable (true); } } [Fact] public void InvalidJsonLogs () { Assert.False (IsEnabled); Enable (ConfigLocations.HardCoded); ThrowOnJsonErrors = false; // "brown" is not a color var json = @" { ""Themes"" : [ { ""Default"" : { ""Schemes"": [ { ""UserDefined"": { ""hotNormal"": { ""foreground"": ""brown"", ""background"": ""1234"" } } } ] } } } }"; ConfigurationManager.SourcesManager?.Load (Settings, json, "test", ConfigLocations.Runtime); // AbNormal is not a Scheme attribute json = @" { ""Themes"" : [ { ""Default"" : { ""Schemes"": [ { ""UserDefined"": { ""AbNormal"": { ""foreground"": ""green"", ""background"": ""black"" } } } ] } } } }"; ConfigurationManager.SourcesManager?.Load (Settings, json, "test", ConfigLocations.Runtime); // Modify hotNormal background only json = @" { ""Themes"" : [ { ""Default"" : { ""Schemes"": [ { ""UserDefined"": { ""hotNormal"": { ""background"": ""cyan"" } } } ] } } } }"; ConfigurationManager.SourcesManager?.Load (Settings, json, "test", ConfigLocations.Runtime); ConfigurationManager.SourcesManager?.Load (Settings, "{}}", "test", ConfigLocations.Runtime); Assert.NotEqual (0, _jsonErrors.Length); ThrowOnJsonErrors = false; Disable (true); } [Fact] public void InvalidJsonThrows () { Assert.False (IsEnabled); Enable (ConfigLocations.HardCoded); ThrowOnJsonErrors = true; // "yellow" is not a color var json = @" { ""Themes"" : [ { ""Default"" : { ""Schemes"": [ { ""UserDefined"": { ""hotNormal"": { ""foreground"": ""brownish"", ""background"": ""1234"" } } } ] } } ] }"; var jsonException = Assert.Throws (() => ConfigurationManager.SourcesManager?.Load (Settings, json, "test", ConfigLocations.Runtime)); Assert.StartsWith ("foreground: \"\"brownish\"\"", jsonException.Message); // AbNormal is not a Scheme attribute json = @" { ""Themes"" : [ { ""Default"" : { ""Schemes"": [ { ""UserDefined"": { ""AbNormal"": { ""foreground"": ""green"", ""background"": ""black"" } } } ] } } ] }"; jsonException = Assert.Throws (() => ConfigurationManager.SourcesManager?.Load (Settings, json, "test", ConfigLocations.Runtime)); Assert.StartsWith ("AbNormal:", jsonException.Message); // Modify hotNormal background only json = @" { ""Themes"" : [ { ""Default"" : { ""Schemes"": [ { ""UserDefined"": { ""hotNormal"": { ""background"": ""cyan"" } } } ] } } ] }"; jsonException = Assert.Throws (() => ConfigurationManager.SourcesManager?.Load (Settings, json, "test", ConfigLocations.Runtime)); Assert.StartsWith ("background:", jsonException.Message); // Unknown property json = @" { ""Unknown"" : ""Not known"" }"; jsonException = Assert.Throws (() => ConfigurationManager.SourcesManager?.Load (Settings, json, "test", ConfigLocations.Runtime)); Assert.StartsWith ("Unknown:", jsonException.Message); Assert.Equal (0, _jsonErrors.Length); ThrowOnJsonErrors = false; Disable (true); } [Fact] public void SourcesManager_Load_FromJson_Loads () { Assert.False (IsEnabled); try { Enable (ConfigLocations.HardCoded); // Arrange var json = @" { ""$schema"": ""https://gui-cs.github.io/Terminal.Gui/schemas/tui-config-schema.json"", ""Application.QuitKey"": ""Alt-Z"", ""Theme"": ""Default"", ""Themes"": [ { ""Default"": { ""MessageBox.DefaultButtonAlignment"": ""End"", ""Schemes"": [ { ""Runnable"": { ""Normal"": { ""Foreground"": ""BrightGreen"", ""Background"": ""Black"" }, ""Focus"": { ""Foreground"": ""White"", ""Background"": ""Cyan"" }, ""HotNormal"": { ""Foreground"": ""Yellow"", ""Background"": ""Black"" }, ""HotFocus"": { ""Foreground"": ""Blue"", ""Background"": ""Cyan"" }, ""Disabled"": { ""Foreground"": ""DarkGray"", ""Background"": ""Black"" } } }, { ""Base"": { ""Normal"": { ""Foreground"": ""White"", ""Background"": ""Blue"" }, ""Focus"": { ""Foreground"": ""Black"", ""Background"": ""Gray"" }, ""HotNormal"": { ""Foreground"": ""BrightCyan"", ""Background"": ""Blue"" }, ""HotFocus"": { ""Foreground"": ""BrightBlue"", ""Background"": ""Gray"" }, ""Disabled"": { ""Foreground"": ""DarkGray"", ""Background"": ""Blue"" } } }, { ""Dialog"": { ""Normal"": { ""Foreground"": ""Black"", ""Background"": ""Gray"" }, ""Focus"": { ""Foreground"": ""White"", ""Background"": ""DarkGray"" }, ""HotNormal"": { ""Foreground"": ""Blue"", ""Background"": ""Gray"" }, ""HotFocus"": { ""Foreground"": ""BrightYellow"", ""Background"": ""DarkGray"" }, ""Disabled"": { ""Foreground"": ""Gray"", ""Background"": ""DarkGray"" } } }, { ""Menu"": { ""Normal"": { ""Foreground"": ""White"", ""Background"": ""DarkGray"" }, ""Focus"": { ""Foreground"": ""White"", ""Background"": ""Black"" }, ""HotNormal"": { ""Foreground"": ""BrightYellow"", ""Background"": ""DarkGray"" }, ""HotFocus"": { ""Foreground"": ""BrightYellow"", ""Background"": ""Black"" }, ""Disabled"": { ""Foreground"": ""Gray"", ""Background"": ""DarkGray"" } } }, { ""Error"": { ""Normal"": { ""Foreground"": ""Red"", ""Background"": ""White"" }, ""Focus"": { ""Foreground"": ""Black"", ""Background"": ""BrightRed"" }, ""HotNormal"": { ""Foreground"": ""Black"", ""Background"": ""White"" }, ""HotFocus"": { ""Foreground"": ""White"", ""Background"": ""BrightRed"" }, ""Disabled"": { ""Foreground"": ""DarkGray"", ""Background"": ""White"" } } } ] } } ] } "; //ResetToCurrentValues (); ThrowOnJsonErrors = true; ConfigurationManager.SourcesManager?.Load (Settings, json, "UpdateFromJson", ConfigLocations.Runtime); Assert.Equal ("Default", ThemeManager.Theme); Assert.Equal (KeyCode.Esc, Application.QuitKey.KeyCode); Assert.Equal (KeyCode.Z | KeyCode.AltMask, ((Key)Settings! ["Application.QuitKey"].PropertyValue)!.KeyCode); Assert.Equal (Alignment.Center, MessageBox.DefaultButtonAlignment); // Now Apply Apply (); Assert.Equal ("Default", ThemeManager.Theme); Assert.Equal (KeyCode.Z | KeyCode.AltMask, Application.QuitKey.KeyCode); Assert.Equal (Alignment.End, MessageBox.DefaultButtonAlignment); Assert.Equal (Color.White, SchemeManager.GetSchemes ()! ["Base"].Normal.Foreground); Assert.Equal (Color.Blue, SchemeManager.GetSchemes ()! ["Base"].Normal.Background); } finally { output.WriteLine ("Disabling CM to clean up."); Disable (true); } } [ConfigurationProperty (Scope = typeof (CMTestsScope))] public static bool? TestProperty { get; set; } private class CMTestsScope : Scope { } [Fact] public void GetConfigPropertiesByScope_Gets () { var props = GetUninitializedConfigPropertiesByScope ("CMTestsScope"); Assert.NotNull (props); Assert.NotEmpty (props); } }