ConfigurationMangerTests.cs 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921
  1. using System.Diagnostics;
  2. using System.Reflection;
  3. using System.Text.Json;
  4. using Xunit.Abstractions;
  5. using static Terminal.Gui.ConfigurationManager;
  6. #pragma warning disable IDE1006
  7. namespace Terminal.Gui.ConfigurationTests;
  8. public class ConfigurationManagerTests
  9. {
  10. private readonly ITestOutputHelper _output;
  11. public ConfigurationManagerTests (ITestOutputHelper output)
  12. {
  13. _output = output;
  14. }
  15. public static readonly JsonSerializerOptions _jsonOptions = new ()
  16. {
  17. Converters = { new AttributeJsonConverter (), new ColorJsonConverter () }
  18. };
  19. [Fact]
  20. public void Apply_Raises_Applied ()
  21. {
  22. Reset ();
  23. Applied += ConfigurationManager_Applied;
  24. var fired = false;
  25. void ConfigurationManager_Applied (object sender, ConfigurationManagerEventArgs obj)
  26. {
  27. fired = true;
  28. // assert
  29. Assert.Equal (KeyCode.Q, Application.QuitKey.KeyCode);
  30. Assert.Equal (KeyCode.F, Application.NextTabGroupKey.KeyCode);
  31. Assert.Equal (KeyCode.B, Application.PrevTabGroupKey.KeyCode);
  32. }
  33. // act
  34. Settings! ["Application.QuitKey"].PropertyValue = Key.Q;
  35. Settings ["Application.NextTabGroupKey"].PropertyValue = Key.F;
  36. Settings ["Application.PrevTabGroupKey"].PropertyValue = Key.B;
  37. Apply ();
  38. // assert
  39. Assert.True (fired);
  40. Applied -= ConfigurationManager_Applied;
  41. Reset ();
  42. }
  43. [Fact]
  44. public void DeepMemberWiseCopyTest ()
  45. {
  46. // Value types
  47. var stringDest = "Destination";
  48. var stringSrc = "Source";
  49. object stringCopy = DeepMemberWiseCopy (stringSrc, stringDest);
  50. Assert.Equal (stringSrc, stringCopy);
  51. stringDest = "Destination";
  52. stringSrc = "Destination";
  53. stringCopy = DeepMemberWiseCopy (stringSrc, stringDest);
  54. Assert.Equal (stringSrc, stringCopy);
  55. stringDest = "Destination";
  56. stringSrc = null;
  57. stringCopy = DeepMemberWiseCopy (stringSrc, stringDest);
  58. Assert.Equal (stringSrc, stringCopy);
  59. stringDest = "Destination";
  60. stringSrc = string.Empty;
  61. stringCopy = DeepMemberWiseCopy (stringSrc, stringDest);
  62. Assert.Equal (stringSrc, stringCopy);
  63. var boolDest = true;
  64. var boolSrc = false;
  65. object boolCopy = DeepMemberWiseCopy (boolSrc, boolDest);
  66. Assert.Equal (boolSrc, boolCopy);
  67. boolDest = false;
  68. boolSrc = true;
  69. boolCopy = DeepMemberWiseCopy (boolSrc, boolDest);
  70. Assert.Equal (boolSrc, boolCopy);
  71. boolDest = true;
  72. boolSrc = true;
  73. boolCopy = DeepMemberWiseCopy (boolSrc, boolDest);
  74. Assert.Equal (boolSrc, boolCopy);
  75. boolDest = false;
  76. boolSrc = false;
  77. boolCopy = DeepMemberWiseCopy (boolSrc, boolDest);
  78. Assert.Equal (boolSrc, boolCopy);
  79. // Structs
  80. var attrDest = new Attribute (Color.Black);
  81. var attrSrc = new Attribute (Color.White);
  82. object attrCopy = DeepMemberWiseCopy (attrSrc, attrDest);
  83. Assert.Equal (attrSrc, attrCopy);
  84. // Classes
  85. var colorschemeDest = new ColorScheme { Disabled = new Attribute (Color.Black) };
  86. var colorschemeSrc = new ColorScheme { Disabled = new Attribute (Color.White) };
  87. object colorschemeCopy = DeepMemberWiseCopy (colorschemeSrc, colorschemeDest);
  88. Assert.Equal (colorschemeSrc, colorschemeCopy);
  89. // Dictionaries
  90. Dictionary<string, Attribute> dictDest = new () { { "Disabled", new Attribute (Color.Black) } };
  91. Dictionary<string, Attribute> dictSrc = new () { { "Disabled", new Attribute (Color.White) } };
  92. Dictionary<string, Attribute> dictCopy = (Dictionary<string, Attribute>)DeepMemberWiseCopy (dictSrc, dictDest);
  93. Assert.Equal (dictSrc, dictCopy);
  94. dictDest = new Dictionary<string, Attribute> { { "Disabled", new Attribute (Color.Black) } };
  95. dictSrc = new Dictionary<string, Attribute>
  96. {
  97. { "Disabled", new Attribute (Color.White) }, { "Normal", new Attribute (Color.Blue) }
  98. };
  99. dictCopy = (Dictionary<string, Attribute>)DeepMemberWiseCopy (dictSrc, dictDest);
  100. Assert.Equal (dictSrc, dictCopy);
  101. // src adds an item
  102. dictDest = new Dictionary<string, Attribute> { { "Disabled", new Attribute (Color.Black) } };
  103. dictSrc = new Dictionary<string, Attribute>
  104. {
  105. { "Disabled", new Attribute (Color.White) }, { "Normal", new Attribute (Color.Blue) }
  106. };
  107. dictCopy = (Dictionary<string, Attribute>)DeepMemberWiseCopy (dictSrc, dictDest);
  108. Assert.Equal (2, dictCopy!.Count);
  109. Assert.Equal (dictSrc ["Disabled"], dictCopy ["Disabled"]);
  110. Assert.Equal (dictSrc ["Normal"], dictCopy ["Normal"]);
  111. // src updates only one item
  112. dictDest = new Dictionary<string, Attribute>
  113. {
  114. { "Disabled", new Attribute (Color.Black) }, { "Normal", new Attribute (Color.White) }
  115. };
  116. dictSrc = new Dictionary<string, Attribute> { { "Disabled", new Attribute (Color.White) } };
  117. dictCopy = (Dictionary<string, Attribute>)DeepMemberWiseCopy (dictSrc, dictDest);
  118. Assert.Equal (2, dictCopy!.Count);
  119. Assert.Equal (dictSrc ["Disabled"], dictCopy ["Disabled"]);
  120. Assert.Equal (dictDest ["Normal"], dictCopy ["Normal"]);
  121. }
  122. public class DeepCopyTest ()
  123. {
  124. public static Key key = Key.Esc;
  125. }
  126. [Fact]
  127. public void Illustrate_DeepMemberWiseCopy_Breaks_Dictionary ()
  128. {
  129. Assert.Equal (Key.Esc, DeepCopyTest.key);
  130. Dictionary<Key, string> dict = new Dictionary<Key, string> (new KeyEqualityComparer ());
  131. dict.Add (new (DeepCopyTest.key), "Esc");
  132. Assert.Contains (Key.Esc, dict);
  133. DeepCopyTest.key = (Key)DeepMemberWiseCopy (Key.Q.WithCtrl, DeepCopyTest.key);
  134. Assert.Equal (Key.Q.WithCtrl, DeepCopyTest.key);
  135. Assert.Equal (Key.Esc, dict.Keys.ToArray () [0]);
  136. var eq = new KeyEqualityComparer ();
  137. Assert.True (eq.Equals (Key.Q.WithCtrl, DeepCopyTest.key));
  138. Assert.Equal (Key.Q.WithCtrl.GetHashCode (), DeepCopyTest.key.GetHashCode ());
  139. Assert.Equal (eq.GetHashCode (Key.Q.WithCtrl), eq.GetHashCode (DeepCopyTest.key));
  140. Assert.Equal (Key.Q.WithCtrl.GetHashCode (), eq.GetHashCode (DeepCopyTest.key));
  141. Assert.True (dict.ContainsKey (Key.Esc));
  142. dict.Remove (Key.Esc);
  143. dict.Add (new (DeepCopyTest.key), "Ctrl+Q");
  144. Assert.True (dict.ContainsKey (Key.Q.WithCtrl));
  145. }
  146. [Fact]
  147. public void Load_Raises_Updated ()
  148. {
  149. ThrowOnJsonErrors = true;
  150. Locations = ConfigLocations.All;
  151. Reset ();
  152. Assert.Equal (Key.Esc, (((Key)Settings! ["Application.QuitKey"].PropertyValue)!).KeyCode);
  153. Updated += ConfigurationManager_Updated;
  154. var fired = false;
  155. void ConfigurationManager_Updated (object sender, ConfigurationManagerEventArgs obj)
  156. {
  157. fired = true;
  158. }
  159. // Act
  160. // Reset to cause load to raise event
  161. Load (true);
  162. // assert
  163. Assert.True (fired);
  164. Updated -= ConfigurationManager_Updated;
  165. // clean up
  166. Locations = ConfigLocations.Default;
  167. Reset ();
  168. }
  169. [Fact]
  170. public void Load_Loads_Custom_Json ()
  171. {
  172. // arrange
  173. Locations = ConfigLocations.All;
  174. Reset ();
  175. ThrowOnJsonErrors = true;
  176. Assert.Equal (Key.Esc, (Key)Settings! ["Application.QuitKey"].PropertyValue);
  177. // act
  178. RuntimeConfig = """
  179. {
  180. "Application.QuitKey": "Ctrl-Q"
  181. }
  182. """;
  183. Load (false);
  184. // assert
  185. Assert.Equal (Key.Q.WithCtrl, (Key)Settings ["Application.QuitKey"].PropertyValue);
  186. // clean up
  187. Locations = ConfigLocations.Default;
  188. Reset ();
  189. }
  190. [Fact]
  191. [AutoInitShutdown]
  192. public void LoadConfigurationFromAllSources_ShouldLoadSettingsFromAllSources ()
  193. {
  194. //var _configFilename = "config.json";
  195. //// Arrange
  196. //// Create a mock of the configuration files in all sources
  197. //// Home directory
  198. //string homeDir = Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.UserProfile), ".tui");
  199. //if (!Directory.Exists (homeDir)) {
  200. // Directory.CreateDirectory (homeDir);
  201. //}
  202. //string globalConfigFile = Path.Combine (homeDir, _configFilename);
  203. //string appSpecificConfigFile = Path.Combine (homeDir, "appname.config.json");
  204. //File.WriteAllText (globalConfigFile, "{\"Settings\": {\"TestSetting\":\"Global\"}}");
  205. //File.WriteAllText (appSpecificConfigFile, "{\"Settings\": {\"TestSetting\":\"AppSpecific\"}}");
  206. //// App directory
  207. //string appDir = Directory.GetCurrentDirectory ();
  208. //string appDirGlobalConfigFile = Path.Combine (appDir, _configFilename);
  209. //string appDirAppSpecificConfigFile = Path.Combine (appDir, "appname.config.json");
  210. //File.WriteAllText (appDirGlobalConfigFile, "{\"Settings\": {\"TestSetting\":\"GlobalAppDir\"}}");
  211. //File.WriteAllText (appDirAppSpecificConfigFile, "{\"Settings\": {\"TestSetting\":\"AppSpecificAppDir\"}}");
  212. //// App resources
  213. //// ...
  214. //// Act
  215. //ConfigurationManager.Locations = ConfigurationManager.ConfigLocation.All;
  216. //ConfigurationManager.Load ();
  217. //// Assert
  218. //// Check that the settings from the highest precedence source are loaded
  219. //Assert.Equal ("AppSpecific", ConfigurationManager.Config.Settings.TestSetting);
  220. }
  221. [Fact]
  222. public void Reset_Raises_Updated ()
  223. {
  224. ConfigLocations savedLocations = Locations;
  225. Locations = ConfigLocations.All;
  226. Reset ();
  227. Settings! ["Application.QuitKey"].PropertyValue = Key.Q;
  228. Updated += ConfigurationManager_Updated;
  229. var fired = false;
  230. void ConfigurationManager_Updated (object sender, ConfigurationManagerEventArgs obj)
  231. {
  232. fired = true;
  233. }
  234. // Act
  235. Reset ();
  236. // assert
  237. Assert.True (fired);
  238. Updated -= ConfigurationManager_Updated;
  239. Reset ();
  240. Locations = savedLocations;
  241. }
  242. [Fact]
  243. public void Reset_and_ResetLoadWithLibraryResourcesOnly_are_same ()
  244. {
  245. Locations = ConfigLocations.Default;
  246. // arrange
  247. Reset ();
  248. Settings! ["Application.QuitKey"].PropertyValue = Key.Q;
  249. Settings ["Application.NextTabGroupKey"].PropertyValue = Key.F;
  250. Settings ["Application.PrevTabGroupKey"].PropertyValue = Key.B;
  251. Settings.Apply ();
  252. // assert apply worked
  253. Assert.Equal (KeyCode.Q, Application.QuitKey.KeyCode);
  254. Assert.Equal (KeyCode.F, Application.NextTabGroupKey.KeyCode);
  255. Assert.Equal (KeyCode.B, Application.PrevTabGroupKey.KeyCode);
  256. //act
  257. Reset ();
  258. // assert
  259. Assert.NotEmpty (Themes!);
  260. Assert.Equal ("Default", Themes.Theme);
  261. Assert.Equal (Key.Esc, Application.QuitKey);
  262. Assert.Equal (Key.F6, Application.NextTabGroupKey);
  263. Assert.Equal (Key.F6.WithShift, Application.PrevTabGroupKey);
  264. // arrange
  265. Settings ["Application.QuitKey"].PropertyValue = Key.Q;
  266. Settings ["Application.NextTabGroupKey"].PropertyValue = Key.F;
  267. Settings ["Application.PrevTabGroupKey"].PropertyValue = Key.B;
  268. Settings.Apply ();
  269. Locations = ConfigLocations.Default;
  270. // act
  271. Reset ();
  272. Load ();
  273. // assert
  274. Assert.NotEmpty (Themes);
  275. Assert.Equal ("Default", Themes.Theme);
  276. Assert.Equal (KeyCode.Esc, Application.QuitKey.KeyCode);
  277. Assert.Equal (Key.F6, Application.NextTabGroupKey);
  278. Assert.Equal (Key.F6.WithShift, Application.PrevTabGroupKey);
  279. Reset ();
  280. }
  281. [Fact]
  282. public void Reset_Resets ()
  283. {
  284. Locations = ConfigLocations.Default;
  285. Reset ();
  286. Assert.NotEmpty (Themes!);
  287. Assert.Equal ("Default", Themes.Theme);
  288. }
  289. //[Fact ()]
  290. //public void LoadFromJsonTest ()
  291. //{
  292. // Assert.True (false, "This test needs an implementation");
  293. //}
  294. //[Fact ()]
  295. //public void ToJsonTest ()
  296. //{
  297. // Assert.True (false, "This test needs an implementation");
  298. //}
  299. //[Fact ()]
  300. //public void UpdateConfigurationTest ()
  301. //{
  302. // Assert.True (false, "This test needs an implementation");
  303. //}
  304. //[Fact ()]
  305. //public void UpdateConfigurationFromFileTest ()
  306. //{
  307. // Assert.True (false, "This test needs an implementation");
  308. //}
  309. //[Fact ()]
  310. //public void SaveHardCodedDefaultsTest ()
  311. //{
  312. // Assert.True (false, "This test needs an implementation");
  313. //}
  314. //[Fact ()]
  315. //public void LoadGlobalFromLibraryResourceTest ()
  316. //{
  317. // Assert.True (false, "This test needs an implementation");
  318. //}
  319. //[Fact ()]
  320. //public void LoadGlobalFromAppDirectoryTest ()
  321. //{
  322. // Assert.True (false, "This test needs an implementation");
  323. //}
  324. //[Fact ()]
  325. //public void LoadGlobalFromHomeDirectoryTest ()
  326. //{
  327. // Assert.True (false, "This test needs an implementation");
  328. //}
  329. //[Fact ()]
  330. //public void LoadAppFromAppResourcesTest ()
  331. //{
  332. // Assert.True (false, "This test needs an implementation");
  333. //}
  334. //[Fact ()]
  335. //public void LoadAppFromAppDirectoryTest ()
  336. //{
  337. // Assert.True (false, "This test needs an implementation");
  338. //}
  339. //[Fact ()]
  340. //public void LoadAppFromHomeDirectoryTest ()
  341. //{
  342. // Assert.True (false, "This test needs an implementation");
  343. //}
  344. //[Fact ()]
  345. //public void LoadTest ()
  346. //{
  347. // Assert.True (false, "This test needs an implementation");
  348. //}
  349. /// <summary>Save the `config.json` file; this can be used to update the file in `Terminal.Gui.Resources.config.json'.</summary>
  350. /// <remarks>
  351. /// IMPORTANT: For the file generated to be valid, this must be the ONLY test run. Config Properties are all
  352. /// static and thus can be overwritten by other tests.
  353. /// </remarks>
  354. [Fact]
  355. public void SaveDefaults ()
  356. {
  357. Initialize ();
  358. Reset ();
  359. // Get the hard coded settings
  360. GetHardCodedDefaults ();
  361. // Serialize to a JSON string
  362. string json = ToJson ();
  363. // Write the JSON string to the file
  364. File.WriteAllText ("config.json", json);
  365. }
  366. [Fact]
  367. public void TestConfigProperties ()
  368. {
  369. Locations = ConfigLocations.All;
  370. Reset ();
  371. Assert.NotEmpty (Settings!);
  372. // test that all ConfigProperties have our attribute
  373. Assert.All (
  374. Settings,
  375. item => Assert.NotEmpty (
  376. item.Value.PropertyInfo!.CustomAttributes.Where (
  377. a => a.AttributeType == typeof (SerializableConfigurationProperty)
  378. )
  379. )
  380. );
  381. #pragma warning disable xUnit2029
  382. Assert.Empty (
  383. Settings.Where (
  384. cp => cp.Value.PropertyInfo!.GetCustomAttribute (
  385. typeof (SerializableConfigurationProperty)
  386. )
  387. == null
  388. )
  389. );
  390. #pragma warning restore xUnit2029
  391. // Application is a static class
  392. PropertyInfo pi = typeof (Application).GetProperty ("QuitKey");
  393. Assert.Equal (pi, Settings ["Application.QuitKey"].PropertyInfo);
  394. // FrameView is not a static class and DefaultBorderStyle is Scope.Scheme
  395. pi = typeof (FrameView).GetProperty ("DefaultBorderStyle");
  396. Assert.False (Settings.ContainsKey ("FrameView.DefaultBorderStyle"));
  397. Assert.True (Themes! ["Default"].ContainsKey ("FrameView.DefaultBorderStyle"));
  398. Assert.Equal (pi, Themes! ["Default"] ["FrameView.DefaultBorderStyle"].PropertyInfo);
  399. }
  400. [Fact]
  401. public void TestConfigPropertyOmitClassName ()
  402. {
  403. ConfigLocations savedLocations = Locations;
  404. Locations = ConfigLocations.All;
  405. // Color.ColorSchemes is serialized as "ColorSchemes", not "Colors.ColorSchemes"
  406. PropertyInfo pi = typeof (Colors).GetProperty ("ColorSchemes");
  407. var scp = (SerializableConfigurationProperty)pi!.GetCustomAttribute (typeof (SerializableConfigurationProperty));
  408. Assert.True (scp!.Scope == typeof (ThemeScope));
  409. Assert.True (scp.OmitClassName);
  410. Reset ();
  411. Assert.Equal (pi, Themes! ["Default"] ["ColorSchemes"].PropertyInfo);
  412. Locations = savedLocations;
  413. }
  414. [Fact]
  415. [AutoInitShutdown (configLocation: ConfigLocations.Default)]
  416. public void TestConfigurationManagerInitDriver ()
  417. {
  418. Assert.Equal ("Default", Themes!.Theme);
  419. Assert.Equal (new Color (Color.White), Colors.ColorSchemes ["Base"]!.Normal.Foreground);
  420. Assert.Equal (new Color (Color.Blue), Colors.ColorSchemes ["Base"].Normal.Background);
  421. // Change Base
  422. Stream json = ToStream ();
  423. Settings!.Update (json, "TestConfigurationManagerInitDriver", ConfigLocations.Runtime);
  424. Dictionary<string, ColorScheme> colorSchemes =
  425. (Dictionary<string, ColorScheme>)Themes [Themes.Theme] ["ColorSchemes"].PropertyValue;
  426. Assert.Equal (Colors.ColorSchemes ["Base"], colorSchemes! ["Base"]);
  427. Assert.Equal (Colors.ColorSchemes ["TopLevel"], colorSchemes ["TopLevel"]);
  428. Assert.Equal (Colors.ColorSchemes ["Error"], colorSchemes ["Error"]);
  429. Assert.Equal (Colors.ColorSchemes ["Dialog"], colorSchemes ["Dialog"]);
  430. Assert.Equal (Colors.ColorSchemes ["Menu"], colorSchemes ["Menu"]);
  431. Colors.ColorSchemes ["Base"] = colorSchemes ["Base"];
  432. Colors.ColorSchemes ["TopLevel"] = colorSchemes ["TopLevel"];
  433. Colors.ColorSchemes ["Error"] = colorSchemes ["Error"];
  434. Colors.ColorSchemes ["Dialog"] = colorSchemes ["Dialog"];
  435. Colors.ColorSchemes ["Menu"] = colorSchemes ["Menu"];
  436. Assert.Equal (colorSchemes ["Base"], Colors.ColorSchemes ["Base"]);
  437. Assert.Equal (colorSchemes ["TopLevel"], Colors.ColorSchemes ["TopLevel"]);
  438. Assert.Equal (colorSchemes ["Error"], Colors.ColorSchemes ["Error"]);
  439. Assert.Equal (colorSchemes ["Dialog"], Colors.ColorSchemes ["Dialog"]);
  440. Assert.Equal (colorSchemes ["Menu"], Colors.ColorSchemes ["Menu"]);
  441. }
  442. [Fact]
  443. [AutoInitShutdown (configLocation: ConfigLocations.None)]
  444. public void TestConfigurationManagerInitDriver_NoLocations ()
  445. {
  446. // TODO: Write this test
  447. }
  448. [Fact]
  449. public void TestConfigurationManagerInvalidJsonLogs ()
  450. {
  451. Application.Init (new FakeDriver ());
  452. ThrowOnJsonErrors = false;
  453. // "brown" is not a color
  454. var json = @"
  455. {
  456. ""Themes"" : [
  457. {
  458. ""Default"" : {
  459. ""ColorSchemes"": [
  460. {
  461. ""UserDefined"": {
  462. ""hotNormal"": {
  463. ""foreground"": ""brown"",
  464. ""background"": ""1234""
  465. }
  466. }
  467. }
  468. ]
  469. }
  470. }
  471. }
  472. }";
  473. Settings!.Update (json, "test", ConfigLocations.Runtime);
  474. // AbNormal is not a ColorScheme attribute
  475. json = @"
  476. {
  477. ""Themes"" : [
  478. {
  479. ""Default"" : {
  480. ""ColorSchemes"": [
  481. {
  482. ""UserDefined"": {
  483. ""AbNormal"": {
  484. ""foreground"": ""green"",
  485. ""background"": ""black""
  486. }
  487. }
  488. }
  489. ]
  490. }
  491. }
  492. }
  493. }";
  494. Settings.Update (json, "test", ConfigLocations.Runtime);
  495. // Modify hotNormal background only
  496. json = @"
  497. {
  498. ""Themes"" : [
  499. {
  500. ""Default"" : {
  501. ""ColorSchemes"": [
  502. {
  503. ""UserDefined"": {
  504. ""hotNormal"": {
  505. ""background"": ""cyan""
  506. }
  507. }
  508. }
  509. ]
  510. }
  511. }
  512. }
  513. }";
  514. Settings.Update (json, "test", ConfigLocations.Runtime);
  515. Settings.Update ("{}}", "test", ConfigLocations.Runtime);
  516. Assert.NotEqual (0, _jsonErrors.Length);
  517. Application.Shutdown ();
  518. ThrowOnJsonErrors = false;
  519. }
  520. [Fact]
  521. [AutoInitShutdown]
  522. public void TestConfigurationManagerInvalidJsonThrows ()
  523. {
  524. ThrowOnJsonErrors = true;
  525. // "yellow" is not a color
  526. var json = @"
  527. {
  528. ""Themes"" : [
  529. {
  530. ""Default"" : {
  531. ""ColorSchemes"": [
  532. {
  533. ""UserDefined"": {
  534. ""hotNormal"": {
  535. ""foreground"": ""brownish"",
  536. ""background"": ""1234""
  537. }
  538. }
  539. }
  540. ]
  541. }
  542. }
  543. ]
  544. }";
  545. var jsonException = Assert.Throws<JsonException> (() => Settings!.Update (json, "test", ConfigLocations.Runtime));
  546. Assert.Equal ("Unexpected color name: brownish.", jsonException.Message);
  547. // AbNormal is not a ColorScheme attribute
  548. json = @"
  549. {
  550. ""Themes"" : [
  551. {
  552. ""Default"" : {
  553. ""ColorSchemes"": [
  554. {
  555. ""UserDefined"": {
  556. ""AbNormal"": {
  557. ""foreground"": ""green"",
  558. ""background"": ""black""
  559. }
  560. }
  561. }
  562. ]
  563. }
  564. }
  565. ]
  566. }";
  567. jsonException = Assert.Throws<JsonException> (() => Settings!.Update (json, "test", ConfigLocations.Runtime));
  568. Assert.Equal ("Unrecognized ColorScheme Attribute name: AbNormal.", jsonException.Message);
  569. // Modify hotNormal background only
  570. json = @"
  571. {
  572. ""Themes"" : [
  573. {
  574. ""Default"" : {
  575. ""ColorSchemes"": [
  576. {
  577. ""UserDefined"": {
  578. ""hotNormal"": {
  579. ""background"": ""cyan""
  580. }
  581. }
  582. }
  583. ]
  584. }
  585. }
  586. ]
  587. }";
  588. jsonException = Assert.Throws<JsonException> (() => Settings!.Update (json, "test", ConfigLocations.Runtime));
  589. Assert.Equal ("Both Foreground and Background colors must be provided.", jsonException.Message);
  590. // Unknown property
  591. json = @"
  592. {
  593. ""Unknown"" : ""Not known""
  594. }";
  595. jsonException = Assert.Throws<JsonException> (() => Settings!.Update (json, "test", ConfigLocations.Runtime));
  596. Assert.StartsWith ("Unknown property", jsonException.Message);
  597. Assert.Equal (0, _jsonErrors.Length);
  598. ThrowOnJsonErrors = false;
  599. }
  600. [Fact]
  601. [AutoInitShutdown]
  602. public void TestConfigurationManagerToJson ()
  603. {
  604. Reset ();
  605. GetHardCodedDefaults ();
  606. Stream stream = ToStream ();
  607. Settings!.Update (stream, "TestConfigurationManagerToJson", ConfigLocations.Runtime);
  608. }
  609. [Fact]
  610. public void TestConfigurationManagerUpdateFromJson ()
  611. {
  612. ConfigLocations savedLocations = Locations;
  613. Locations = ConfigLocations.All;
  614. // Arrange
  615. var json = @"
  616. {
  617. ""$schema"": ""https://gui-cs.github.io/Terminal.GuiV2Docs/schemas/tui-config-schema.json"",
  618. ""Application.QuitKey"": ""Alt-Z"",
  619. ""Theme"": ""Default"",
  620. ""Themes"": [
  621. {
  622. ""Default"": {
  623. ""ColorSchemes"": [
  624. {
  625. ""TopLevel"": {
  626. ""Normal"": {
  627. ""Foreground"": ""BrightGreen"",
  628. ""Background"": ""Black""
  629. },
  630. ""Focus"": {
  631. ""Foreground"": ""White"",
  632. ""Background"": ""Cyan""
  633. },
  634. ""HotNormal"": {
  635. ""Foreground"": ""Yellow"",
  636. ""Background"": ""Black""
  637. },
  638. ""HotFocus"": {
  639. ""Foreground"": ""Blue"",
  640. ""Background"": ""Cyan""
  641. },
  642. ""Disabled"": {
  643. ""Foreground"": ""DarkGray"",
  644. ""Background"": ""Black""
  645. }
  646. }
  647. },
  648. {
  649. ""Base"": {
  650. ""Normal"": {
  651. ""Foreground"": ""White"",
  652. ""Background"": ""Blue""
  653. },
  654. ""Focus"": {
  655. ""Foreground"": ""Black"",
  656. ""Background"": ""Gray""
  657. },
  658. ""HotNormal"": {
  659. ""Foreground"": ""BrightCyan"",
  660. ""Background"": ""Blue""
  661. },
  662. ""HotFocus"": {
  663. ""Foreground"": ""BrightBlue"",
  664. ""Background"": ""Gray""
  665. },
  666. ""Disabled"": {
  667. ""Foreground"": ""DarkGray"",
  668. ""Background"": ""Blue""
  669. }
  670. }
  671. },
  672. {
  673. ""Dialog"": {
  674. ""Normal"": {
  675. ""Foreground"": ""Black"",
  676. ""Background"": ""Gray""
  677. },
  678. ""Focus"": {
  679. ""Foreground"": ""White"",
  680. ""Background"": ""DarkGray""
  681. },
  682. ""HotNormal"": {
  683. ""Foreground"": ""Blue"",
  684. ""Background"": ""Gray""
  685. },
  686. ""HotFocus"": {
  687. ""Foreground"": ""BrightYellow"",
  688. ""Background"": ""DarkGray""
  689. },
  690. ""Disabled"": {
  691. ""Foreground"": ""Gray"",
  692. ""Background"": ""DarkGray""
  693. }
  694. }
  695. },
  696. {
  697. ""Menu"": {
  698. ""Normal"": {
  699. ""Foreground"": ""White"",
  700. ""Background"": ""DarkGray""
  701. },
  702. ""Focus"": {
  703. ""Foreground"": ""White"",
  704. ""Background"": ""Black""
  705. },
  706. ""HotNormal"": {
  707. ""Foreground"": ""BrightYellow"",
  708. ""Background"": ""DarkGray""
  709. },
  710. ""HotFocus"": {
  711. ""Foreground"": ""BrightYellow"",
  712. ""Background"": ""Black""
  713. },
  714. ""Disabled"": {
  715. ""Foreground"": ""Gray"",
  716. ""Background"": ""DarkGray""
  717. }
  718. }
  719. },
  720. {
  721. ""Error"": {
  722. ""Normal"": {
  723. ""Foreground"": ""Red"",
  724. ""Background"": ""White""
  725. },
  726. ""Focus"": {
  727. ""Foreground"": ""Black"",
  728. ""Background"": ""BrightRed""
  729. },
  730. ""HotNormal"": {
  731. ""Foreground"": ""Black"",
  732. ""Background"": ""White""
  733. },
  734. ""HotFocus"": {
  735. ""Foreground"": ""White"",
  736. ""Background"": ""BrightRed""
  737. },
  738. ""Disabled"": {
  739. ""Foreground"": ""DarkGray"",
  740. ""Background"": ""White""
  741. }
  742. }
  743. }
  744. ]
  745. }
  746. }
  747. ]
  748. }
  749. ";
  750. Reset ();
  751. ThrowOnJsonErrors = true;
  752. Settings!.Update (json, "TestConfigurationManagerUpdateFromJson", ConfigLocations.Runtime);
  753. Assert.Equal (KeyCode.Esc, Application.QuitKey.KeyCode);
  754. Assert.Equal (KeyCode.Z | KeyCode.AltMask, ((Key)Settings ["Application.QuitKey"].PropertyValue)!.KeyCode);
  755. Assert.Equal ("Default", Themes!.Theme);
  756. Assert.Equal (new Color (Color.White), Colors.ColorSchemes ["Base"]!.Normal.Foreground);
  757. Assert.Equal (new Color (Color.Blue), Colors.ColorSchemes ["Base"].Normal.Background);
  758. Dictionary<string, ColorScheme> colorSchemes =
  759. (Dictionary<string, ColorScheme>)Themes.First ().Value ["ColorSchemes"].PropertyValue;
  760. Assert.Equal (new Color (Color.White), colorSchemes! ["Base"].Normal.Foreground);
  761. Assert.Equal (new Color (Color.Blue), colorSchemes ["Base"].Normal.Background);
  762. // Now re-apply
  763. Apply ();
  764. Assert.Equal (KeyCode.Z | KeyCode.AltMask, Application.QuitKey.KeyCode);
  765. Assert.Equal ("Default", Themes.Theme);
  766. Assert.Equal (new Color (Color.White), Colors.ColorSchemes ["Base"].Normal.Foreground);
  767. Assert.Equal (new Color (Color.Blue), Colors.ColorSchemes ["Base"].Normal.Background);
  768. Reset ();
  769. Locations = savedLocations;
  770. }
  771. [Fact]
  772. public void UseWithoutResetDoesNotThrow ()
  773. {
  774. Initialize ();
  775. _ = Settings;
  776. }
  777. }