#nullable enable using System.Collections.Concurrent; using System.Diagnostics.Metrics; using System.Text; using UnitTests.ConfigurationTests; using Xunit.Abstractions; using static Terminal.Gui.Configuration.ConfigurationManager; namespace UnitTests.ConfigurationTests; public class ThemeManagerTests (ITestOutputHelper output) { [Fact] public void ResetToCurrentValues_Adds_Default_Theme () { try { Enable (ConfigLocations.HardCoded); Assert.NotEmpty (ThemeManager.Themes!); ThemeManager.UpdateToCurrentValues (); Assert.NotEmpty (ThemeManager.Themes!); // Default theme exists Assert.NotNull (ThemeManager.Themes? [ThemeManager.DEFAULT_THEME_NAME]); } finally { Disable (resetToHardCodedDefaults: true); } } // ResetToCurrentValues // OnThemeChanged #region Tests Settings["Theme"] and ThemeManager.Theme [Fact] public void Theme_Settings_Theme_Equals_ThemeManager_Theme () { Assert.False (IsEnabled); Assert.Equal (Settings! ["Theme"].PropertyValue, ThemeManager.Theme); Assert.Equal (ThemeManager.DEFAULT_THEME_NAME, ThemeManager.Theme); } [Fact] public void Theme_Enabled_Settings_Theme_Equals_ThemeManager_Theme () { Assert.False (IsEnabled); Enable (ConfigLocations.HardCoded); Assert.Equal (Settings! ["Theme"].PropertyValue, ThemeManager.Theme); Assert.Equal (ThemeManager.DEFAULT_THEME_NAME, ThemeManager.Theme); Disable (resetToHardCodedDefaults: true); } [Fact] public void Theme_Set_Sets () { Assert.False (IsEnabled); Enable (ConfigLocations.HardCoded); Assert.Equal (ThemeManager.DEFAULT_THEME_NAME, ThemeManager.Theme); ThemeManager.Theme = "Test"; Assert.Equal ("Test", ThemeManager.Theme); Assert.Equal (Settings! ["Theme"].PropertyValue, ThemeManager.Theme); Assert.Equal ("Test", Settings! ["Theme"].PropertyValue); Disable (resetToHardCodedDefaults: true); } [Fact] public void Theme_ResetToHardCodedDefaults_Sets_To_Default () { try { Assert.False (IsEnabled); Assert.Equal (ThemeManager.DEFAULT_THEME_NAME, ThemeManager.Theme); Enable (ConfigLocations.HardCoded); Assert.Equal ("Default", ThemeManager.Theme); ThemeManager.Theme = "Test"; Assert.Equal ("Test", ThemeManager.Theme); Assert.Equal (Settings! ["Theme"].PropertyValue, ThemeManager.Theme); Assert.Equal ("Test", Settings! ["Theme"].PropertyValue); ResetToHardCodedDefaults (); Assert.Equal ("Default", ThemeManager.Theme); } finally { Disable(true); } } #endregion Tests Settings["Theme"] and ThemeManager.Theme #region Tests Settings["Themes"] and ThemeManager.Themes [Fact] public void Themes_Set_Throws_If_Not_Enabled () { Assert.False (IsEnabled); Assert.Single (ThemeManager.Themes!); Assert.Throws (() => ThemeManager.Themes = new ()); Assert.Single (ThemeManager.Themes!); } [Fact] public void Themes_Set_Sets_If_Enabled () { Assert.False (IsEnabled); Enable (ConfigLocations.HardCoded); Assert.Single (ThemeManager.Themes!); // Use ConcurrentDictionary instead of a regular dictionary ThemeManager.Themes = new ConcurrentDictionary ( new Dictionary { { "Default", new ThemeScope() }, { "test", new ThemeScope() } }, StringComparer.InvariantCultureIgnoreCase ); Assert.Contains ("test", ThemeManager.Themes!); Disable (resetToHardCodedDefaults: true); } [Fact] public void Themes_Set_Throws_If_No_Default_Theme_In_Dictionary () { Assert.False (IsEnabled); Enable (ConfigLocations.HardCoded); Assert.Single (ThemeManager.Themes!); Assert.Throws ( () => ThemeManager.Themes = new ConcurrentDictionary ( new Dictionary { { "test", new ThemeScope() } }, StringComparer.InvariantCultureIgnoreCase )); Assert.Single (ThemeManager.Themes!); Disable (resetToHardCodedDefaults: true); } [Fact] public void Themes_Get () { } #endregion Tests Settings["Themes"] and ThemeManager.Themes [Fact] public void Themes_TryAdd_Adds () { Enable (ConfigLocations.HardCoded); // Verify that the Themes dictionary contains only the Default theme Assert.Single (ThemeManager.Themes!); Assert.Contains (ThemeManager.DEFAULT_THEME_NAME, ThemeManager.Themes!); var theme = new ThemeScope (); theme.LoadHardCodedDefaults (); Assert.NotEmpty (theme); Assert.True (ThemeManager.Themes!.TryAdd ("testTheme", theme)); Assert.Equal (2, ThemeManager.Themes.Count); Disable (resetToHardCodedDefaults: true); } [Fact] public void Apply_Applies () { 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; // default is Single ThemeManager.Theme = "testTheme"; ThemeManager.Themes! [ThemeManager.Theme]!.Apply (); Assert.Equal (LineStyle.Double, FrameView.DefaultBorderStyle); Disable (resetToHardCodedDefaults: true); } [Fact] public void Theme_Reload_Consistency () { try { Enable (ConfigLocations.HardCoded); // BUGBUG: Setting Schemes to empty array is not valid! // Create a test theme RuntimeConfig = """ { "Theme": "TestTheme", "Themes": [ { "TestTheme": { "Schemes": [] } } ] } """; // Load the test theme ThrowOnJsonErrors = true; Load (ConfigLocations.Runtime); Assert.Equal ("TestTheme", ThemeManager.Theme); // Now reset everything and reload ResetToHardCodedDefaults (); // Verify we're back to default Assert.Equal ("Default", ThemeManager.Theme); } finally { Disable (resetToHardCodedDefaults: true); } } [Fact] public void In_Memory_Themes_Size_Is_Reasonable () { output.WriteLine ($"Start: Color size: {(MemorySizeEstimator.EstimateSize (Color.Red))} b"); output.WriteLine ($"Start: Attribute size: {(MemorySizeEstimator.EstimateSize (Attribute.Default))} b"); output.WriteLine ($"Start: Base Scheme size: {(MemorySizeEstimator.EstimateSize (Scheme.GetHardCodedSchemes ()))} b"); output.WriteLine ($"Start: PropertyInfo size: {(MemorySizeEstimator.EstimateSize (ConfigurationManager.Settings! ["Application.QuitKey"]))} b"); ThemeScope themeScope = new ThemeScope (); output.WriteLine ($"Start: ThemeScope ({themeScope.Count}) size: {(MemorySizeEstimator.EstimateSize (themeScope))} b"); themeScope.AddValue ("Schemes", Scheme.GetHardCodedSchemes ()); output.WriteLine ($"Start: ThemeScope ({themeScope.Count}) size: {(MemorySizeEstimator.EstimateSize (themeScope))} b"); output.WriteLine ($"Start: HardCoded Schemes ({SchemeManager.Schemes!.Count}) size: {(MemorySizeEstimator.EstimateSize (SchemeManager.Schemes!))} b"); output.WriteLine ($"Start: Themes dictionary ({ThemeManager.Themes!.Count}) size: {(MemorySizeEstimator.EstimateSize (ThemeManager.Themes!)) / 1024} Kb"); Enable (ConfigLocations.HardCoded); output.WriteLine ($"Enabled: Themes dictionary ({ThemeManager.Themes.Count}) size: {(MemorySizeEstimator.EstimateSize (ThemeManager.Themes!)) / 1024} Kb"); Load (ConfigLocations.LibraryResources); output.WriteLine ($"After Load: Themes dictionary ({ThemeManager.Themes!.Count}) size: {(MemorySizeEstimator.EstimateSize (ThemeManager.Themes!)) / 1024} Kb"); output.WriteLine ($"Total Settings Size: {(MemorySizeEstimator.EstimateSize (Settings!)) / 1024} Kb"); string json = ConfigurationManager.SourcesManager?.ToJson (Settings)!; // In memory size should be less than the size of the json output.WriteLine ($"JSON size: {json.Length / 1024} Kb"); Assert.True (70000 > MemorySizeEstimator.EstimateSize (ThemeManager.Themes!), $"In memory size ({(MemorySizeEstimator.EstimateSize (Settings!)) / 1024} Kb) is > json size ({json.Length / 1024} Kb)"); Disable (resetToHardCodedDefaults: true); } }