SettingsScopeTests.cs 12 KB

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