SettingsScopeTests.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. #nullable enable
  2. using System.Collections.Concurrent;
  3. using System.Collections.Frozen;
  4. using System.Collections.Immutable;
  5. using System.Text.Json;
  6. using static Terminal.Gui.Configuration.ConfigurationManager;
  7. namespace Terminal.Gui.ConfigurationTests;
  8. public class SettingsScopeTests
  9. {
  10. [Fact]
  11. public void Load_Overrides_Defaults ()
  12. {
  13. // arrange
  14. Enable (ConfigLocations.HardCoded);
  15. Assert.Equal (Key.Esc, (Key)Settings! ["Application.QuitKey"].PropertyValue!);
  16. ThrowOnJsonErrors = true;
  17. // act
  18. RuntimeConfig = """
  19. {
  20. "Application.QuitKey": "Ctrl-Q"
  21. }
  22. """;
  23. Load (ConfigLocations.Runtime);
  24. // assert
  25. Assert.Equal (Key.Q.WithCtrl, (Key)Settings ["Application.QuitKey"].PropertyValue!);
  26. // clean up
  27. Disable (true);
  28. }
  29. [Fact]
  30. public void Load_Dictionary_Property_Overrides_Defaults ()
  31. {
  32. // arrange
  33. Enable (ConfigLocations.HardCoded);
  34. ThrowOnJsonErrors = true;
  35. ConfigProperty themesConfigProperty = Settings! ["Themes"];
  36. ConcurrentDictionary<string, ThemeScope> dict = (themesConfigProperty.PropertyValue as ConcurrentDictionary<string, ThemeScope>)!;
  37. Assert.NotNull (dict);
  38. Assert.Single (dict);
  39. Assert.NotEmpty (((ConcurrentDictionary<string, ThemeScope>)themesConfigProperty.PropertyValue!)!);
  40. ThemeScope scope = dict [ThemeManager.DEFAULT_THEME_NAME];
  41. Assert.NotNull (scope);
  42. Assert.Equal (MouseState.In | MouseState.Pressed | MouseState.PressedOutside, scope ["Button.DefaultHighlightStates"].PropertyValue);
  43. RuntimeConfig = """
  44. {
  45. "Themes": [
  46. {
  47. "Default":
  48. {
  49. "Button.DefaultHighlightStates": "None"
  50. }
  51. },
  52. {
  53. "NewTheme":
  54. {
  55. "Button.DefaultHighlightStates": "In"
  56. }
  57. }
  58. ]
  59. }
  60. """;
  61. Load (ConfigLocations.Runtime);
  62. // assert
  63. Assert.Equal (2, ThemeManager.Themes!.Count);
  64. Assert.Equal (MouseState.None, (MouseState)ThemeManager.GetCurrentTheme () ["Button.DefaultHighlightStates"].PropertyValue!);
  65. Assert.Equal (MouseState.In, (MouseState)ThemeManager.Themes ["NewTheme"] ["Button.DefaultHighlightStates"].PropertyValue!);
  66. RuntimeConfig = """
  67. {
  68. "Themes": [
  69. {
  70. "Default":
  71. {
  72. "Button.DefaultHighlightStates": "Pressed"
  73. }
  74. }
  75. ]
  76. }
  77. """;
  78. Load (ConfigLocations.Runtime);
  79. // assert
  80. Assert.Equal (2, ThemeManager.Themes.Count);
  81. Assert.Equal (MouseState.Pressed, (MouseState)ThemeManager.Themes! [ThemeManager.DEFAULT_THEME_NAME] ["Button.DefaultHighlightStates"].PropertyValue!);
  82. Assert.Equal (MouseState.In, (MouseState)ThemeManager.Themes! ["NewTheme"] ["Button.DefaultHighlightStates"].PropertyValue!);
  83. // clean up
  84. Disable (true);
  85. }
  86. [Fact]
  87. public void Apply_ShouldApplyProperties ()
  88. {
  89. Enable (ConfigLocations.HardCoded);
  90. Load (ConfigLocations.LibraryResources);
  91. // arrange
  92. Assert.Equal (Key.Esc, (Key)Settings! ["Application.QuitKey"].PropertyValue!);
  93. Assert.Equal (
  94. Key.F6,
  95. (Key)Settings ["Application.NextTabGroupKey"].PropertyValue!
  96. );
  97. Assert.Equal (
  98. Key.F6.WithShift,
  99. (Key)Settings ["Application.PrevTabGroupKey"].PropertyValue!
  100. );
  101. // act
  102. Settings ["Application.QuitKey"].PropertyValue = Key.Q;
  103. Settings ["Application.NextTabGroupKey"].PropertyValue = Key.F;
  104. Settings ["Application.PrevTabGroupKey"].PropertyValue = Key.B;
  105. Settings.Apply ();
  106. // assert
  107. Assert.Equal (Key.Q, Application.QuitKey);
  108. Assert.Equal (Key.F, Application.NextTabGroupKey);
  109. Assert.Equal (Key.B, Application.PrevTabGroupKey);
  110. Disable (true);
  111. }
  112. [Fact]
  113. public void CopyUpdatedPropertiesFrom_ShouldCopyChangedPropertiesOnly ()
  114. {
  115. Enable (ConfigLocations.HardCoded);
  116. Settings! ["Application.QuitKey"].PropertyValue = Key.End;
  117. var updatedSettings = new SettingsScope ();
  118. updatedSettings.LoadHardCodedDefaults ();
  119. // Don't set Quitkey
  120. updatedSettings ["Application.QuitKey"].HasValue = false;
  121. updatedSettings ["Application.QuitKey"].PropertyValue = null;
  122. updatedSettings ["Application.NextTabGroupKey"].PropertyValue = Key.F;
  123. updatedSettings ["Application.PrevTabGroupKey"].PropertyValue = Key.B;
  124. Settings.UpdateFrom (updatedSettings);
  125. Assert.Equal (KeyCode.End, ((Key)Settings ["Application.QuitKey"].PropertyValue!).KeyCode);
  126. Assert.Equal (KeyCode.F, ((Key)updatedSettings ["Application.NextTabGroupKey"].PropertyValue!).KeyCode);
  127. Assert.Equal (KeyCode.B, ((Key)updatedSettings ["Application.PrevTabGroupKey"].PropertyValue!).KeyCode);
  128. Disable (true);
  129. }
  130. [Fact]
  131. public void ResetToHardCodedDefaults_Resets_Config_And_Applies ()
  132. {
  133. try
  134. {
  135. Enable (ConfigLocations.HardCoded);
  136. Load (ConfigLocations.LibraryResources);
  137. Assert.True (Settings! ["Application.QuitKey"].PropertyValue is Key);
  138. Assert.Equal (Key.Esc, Settings ["Application.QuitKey"].PropertyValue as Key);
  139. Settings ["Application.QuitKey"].PropertyValue = Key.Q;
  140. Apply ();
  141. Assert.Equal (Key.Q, Application.QuitKey);
  142. // Act
  143. ResetToHardCodedDefaults ();
  144. Assert.Equal (Key.Esc, Settings ["Application.QuitKey"].PropertyValue as Key);
  145. Assert.Equal (Key.Esc, Application.QuitKey);
  146. }
  147. finally
  148. {
  149. Disable (true);
  150. }
  151. }
  152. [Fact]
  153. public void Themes_Property_Exists ()
  154. {
  155. var settingsScope = new SettingsScope ();
  156. Assert.NotEmpty (settingsScope);
  157. // Themes exists, but is not initialized
  158. Assert.Null (settingsScope ["Themes"].PropertyValue);
  159. //settingsScope.UpdateToCurrentValues ();
  160. //Assert.NotEmpty (settingsScope);
  161. }
  162. [Fact]
  163. public void LoadHardCodedDefaults_Resets ()
  164. {
  165. // Arrange
  166. Assert.Equal (Key.Esc, Application.QuitKey);
  167. var settingsScope = new SettingsScope ();
  168. settingsScope.LoadHardCodedDefaults ();
  169. // Act
  170. settingsScope ["Application.QuitKey"].PropertyValue = Key.Q;
  171. settingsScope.Apply ();
  172. Assert.Equal (Key.Q, Application.QuitKey);
  173. settingsScope.LoadHardCodedDefaults ();
  174. settingsScope.Apply ();
  175. // Assert
  176. Assert.Equal (Key.Esc, Application.QuitKey);
  177. Disable (true);
  178. }
  179. private class ConfigPropertyMock
  180. {
  181. public object? PropertyValue { get; init; }
  182. public bool Immutable { get; init; }
  183. }
  184. private class SettingsScopeMock : Dictionary<string, ConfigPropertyMock>
  185. {
  186. public string? Theme { get; set; }
  187. }
  188. [Fact]
  189. public void SettingsScopeMockWithKey_CreatesDeepCopy ()
  190. {
  191. SettingsScopeMock? source = new ()
  192. {
  193. Theme = "Dark",
  194. ["KeyBinding"] = new () { PropertyValue = new Key (KeyCode.A) { Handled = true } },
  195. ["Counts"] = new () { PropertyValue = new Dictionary<string, int> { { "X", 1 } } }
  196. };
  197. SettingsScopeMock? result = DeepCloner.DeepClone (source);
  198. Assert.NotNull (result);
  199. Assert.NotSame (source, result);
  200. Assert.Equal (source.Theme, result!.Theme);
  201. Assert.NotSame (source ["KeyBinding"], result ["KeyBinding"]);
  202. Assert.NotSame (source ["Counts"], result ["Counts"]);
  203. ConfigPropertyMock clonedKeyProp = result ["KeyBinding"];
  204. var clonedKey = (Key)clonedKeyProp.PropertyValue!;
  205. Assert.NotSame (source ["KeyBinding"].PropertyValue, clonedKey);
  206. Assert.Equal (((Key)source ["KeyBinding"].PropertyValue!).KeyCode, clonedKey.KeyCode);
  207. Assert.Equal (((Key)source ["KeyBinding"].PropertyValue!).Handled, clonedKey.Handled);
  208. Assert.Equal ((Dictionary<string, int>)source ["Counts"].PropertyValue!, (Dictionary<string, int>)result ["Counts"].PropertyValue!);
  209. // Modify result, ensure source unchanged
  210. result.Theme = "Light";
  211. clonedKey.Handled = false;
  212. ((Dictionary<string, int>)result ["Counts"].PropertyValue!).Add ("Y", 2);
  213. Assert.Equal ("Dark", source.Theme);
  214. Assert.True (((Key)source ["KeyBinding"].PropertyValue!).Handled);
  215. Assert.Single ((Dictionary<string, int>)source ["Counts"].PropertyValue!);
  216. Disable (true);
  217. }
  218. [Fact /*(Skip = "This test randomly fails due to a concurrent change to something. Needs to be moved to non-parallel tests.")*/]
  219. public void ThemeScopeList_WithThemes_ClonesSuccessfully ()
  220. {
  221. // Arrange: Create a ThemeScope and verify a property exists
  222. var defaultThemeScope = new ThemeScope ();
  223. defaultThemeScope.LoadHardCodedDefaults ();
  224. Assert.True (defaultThemeScope.ContainsKey ("Button.DefaultHighlightStates"));
  225. var darkThemeScope = new ThemeScope ();
  226. darkThemeScope.LoadHardCodedDefaults ();
  227. Assert.True (darkThemeScope.ContainsKey ("Button.DefaultHighlightStates"));
  228. // Create a Themes list with two themes
  229. List<Dictionary<string, ThemeScope>> themesList =
  230. [
  231. new () { { "Default", defaultThemeScope } },
  232. new () { { "Dark", darkThemeScope } }
  233. ];
  234. // Create a SettingsScope and set the Themes property
  235. var settingsScope = new SettingsScope ();
  236. settingsScope.LoadHardCodedDefaults ();
  237. Assert.True (settingsScope.ContainsKey ("Themes"));
  238. settingsScope ["Themes"].PropertyValue = themesList;
  239. // Act
  240. SettingsScope? result = DeepCloner.DeepClone (settingsScope);
  241. // Assert
  242. Assert.NotNull (result);
  243. Assert.IsType<SettingsScope> (result);
  244. var resultScope = result;
  245. Assert.True (resultScope.ContainsKey ("Themes"));
  246. Assert.NotNull (resultScope ["Themes"].PropertyValue);
  247. List<Dictionary<string, ThemeScope>> clonedThemes = (List<Dictionary<string, ThemeScope>>)resultScope ["Themes"].PropertyValue!;
  248. Assert.Equal (2, clonedThemes.Count);
  249. Disable (true);
  250. }
  251. [Fact]
  252. public void Empty_SettingsScope_ClonesSuccessfully ()
  253. {
  254. // Arrange: Create a SettingsScope
  255. var settingsScope = new SettingsScope ();
  256. Assert.True (settingsScope.ContainsKey ("Themes"));
  257. // Act
  258. SettingsScope? result = DeepCloner.DeepClone (settingsScope);
  259. // Assert
  260. Assert.NotNull (result);
  261. Assert.IsType<SettingsScope> (result);
  262. Assert.True (result.ContainsKey ("Themes"));
  263. Disable (true);
  264. }
  265. [Fact]
  266. public void SettingsScope_With_Themes_Set_ClonesSuccessfully ()
  267. {
  268. // Arrange: Create a SettingsScope
  269. var settingsScope = new SettingsScope ();
  270. Assert.True (settingsScope.ContainsKey ("Themes"));
  271. settingsScope ["Themes"].PropertyValue = new List<Dictionary<string, ThemeScope>>
  272. {
  273. new () { { "Default", new () } },
  274. new () { { "Dark", new () } }
  275. };
  276. // Act
  277. SettingsScope? result = DeepCloner.DeepClone (settingsScope);
  278. // Assert
  279. Assert.NotNull (result);
  280. Assert.IsType<SettingsScope> (result);
  281. Assert.True (result.ContainsKey ("Themes"));
  282. Assert.NotNull (result ["Themes"].PropertyValue);
  283. Disable (true);
  284. }
  285. }