SchemeTests.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  1. #nullable enable
  2. using Xunit;
  3. namespace UnitTests_Parallelizable.ViewTests;
  4. [Trait ("Category", "View.Scheme")]
  5. public class SchemeTests
  6. {
  7. [Fact]
  8. public void GetScheme_Default_ReturnsBaseScheme ()
  9. {
  10. var view = new View ();
  11. var baseScheme = SchemeManager.GetHardCodedSchemes ()? ["Base"];
  12. Assert.Equal (baseScheme, view.GetScheme ());
  13. view.Dispose ();
  14. }
  15. [Fact]
  16. public void SetScheme_Explicitly_SetsSchemeCorrectly ()
  17. {
  18. var view = new View ();
  19. var dialogScheme = SchemeManager.GetHardCodedSchemes ()? ["Dialog"];
  20. view.SetScheme (dialogScheme);
  21. Assert.True (view.HasScheme);
  22. Assert.Equal (dialogScheme, view.GetScheme ());
  23. view.Dispose ();
  24. }
  25. [Fact]
  26. public void GetScheme_InheritsFromSuperView_WhenNotExplicitlySet ()
  27. {
  28. var superView = new View ();
  29. var subView = new View ();
  30. superView.Add (subView);
  31. var dialogScheme = SchemeManager.GetHardCodedSchemes ()? ["Dialog"];
  32. superView.SetScheme (dialogScheme);
  33. Assert.Equal (dialogScheme, subView.GetScheme ());
  34. Assert.False (subView.HasScheme);
  35. subView.Dispose ();
  36. superView.Dispose ();
  37. }
  38. [Fact]
  39. public void SetSchemeName_OverridesInheritedScheme ()
  40. {
  41. var view = new View ();
  42. view.SchemeName = "Dialog";
  43. var dialogScheme = SchemeManager.GetHardCodedSchemes ()? ["Dialog"];
  44. Assert.Equal (dialogScheme, view.GetScheme ());
  45. view.Dispose ();
  46. }
  47. [Fact]
  48. public void GetAttribute_ReturnsCorrectAttribute_Via_Mock ()
  49. {
  50. var view = new View { SchemeName = "Base" };
  51. view.Driver = new FakeDriver ();
  52. view.Driver.Init ();
  53. view.Driver.SetAttribute (new Attribute (Color.Red, Color.Green));
  54. // Act
  55. var attribute = view.GetCurrentAttribute ();
  56. // Assert
  57. Assert.Equal (new Attribute (Color.Red, Color.Green), attribute);
  58. }
  59. [Fact]
  60. public void GetAttributeForRole_ReturnsCorrectAttribute ()
  61. {
  62. var view = new View { SchemeName = "Base" };
  63. Assert.Equal (view.GetScheme ().Normal, view.GetAttributeForRole (VisualRole.Normal));
  64. Assert.Equal (view.GetScheme ().HotNormal, view.GetAttributeForRole (VisualRole.HotNormal));
  65. Assert.Equal (view.GetScheme ().Focus, view.GetAttributeForRole (VisualRole.Focus));
  66. Assert.Equal (view.GetScheme ().HotFocus, view.GetAttributeForRole (VisualRole.HotFocus));
  67. Assert.Equal (view.GetScheme ().Disabled, view.GetAttributeForRole (VisualRole.Disabled));
  68. view.Dispose ();
  69. }
  70. [Fact]
  71. public void GetAttributeForRole_DisabledView_ReturnsCorrectAttribute ()
  72. {
  73. var view = new View { SchemeName = "Base" };
  74. view.Enabled = false;
  75. Assert.Equal (view.GetScheme ().Disabled, view.GetAttributeForRole (VisualRole.Normal));
  76. Assert.Equal (view.GetScheme ().Disabled, view.GetAttributeForRole (VisualRole.HotNormal));
  77. view.Dispose ();
  78. }
  79. [Fact]
  80. public void SetAttributeForRole_SetsCorrectAttribute ()
  81. {
  82. var view = new View { SchemeName = "Base" };
  83. view.Driver = new FakeDriver ();
  84. view.Driver.Init ();
  85. view.Driver.SetAttribute (new Attribute (Color.Red, Color.Green));
  86. var previousAttribute = view.SetAttributeForRole (VisualRole.Focus);
  87. Assert.Equal (view.GetScheme ().Focus, view.GetCurrentAttribute ());
  88. Assert.NotEqual (previousAttribute, view.GetCurrentAttribute ());
  89. view.Dispose ();
  90. }
  91. [Fact]
  92. public void OnGettingScheme_Override_StopsDefaultBehavior ()
  93. {
  94. var view = new CustomView ();
  95. var customScheme = SchemeManager.GetHardCodedSchemes ()? ["Error"];
  96. Assert.Equal (customScheme, view.GetScheme ());
  97. view.Dispose ();
  98. }
  99. [Fact]
  100. public void OnSettingScheme_Override_PreventsSettingScheme ()
  101. {
  102. var view = new CustomView ();
  103. var dialogScheme = SchemeManager.GetHardCodedSchemes ()? ["Dialog"];
  104. view.SetScheme (dialogScheme);
  105. Assert.NotEqual (dialogScheme, view.GetScheme ());
  106. view.Dispose ();
  107. }
  108. [Fact]
  109. public void GettingScheme_Event_CanOverrideScheme ()
  110. {
  111. var view = new View ();
  112. var customScheme = SchemeManager.GetHardCodedSchemes ()? ["Error"]! with { Normal = Attribute.Default };
  113. Assert.NotEqual (Attribute.Default, view.GetScheme ().Normal);
  114. view.GettingScheme += (sender, args) =>
  115. {
  116. args.Result = customScheme;
  117. args.Handled = true;
  118. };
  119. Assert.Equal (customScheme, view.GetScheme ());
  120. Assert.Equal (Attribute.Default, view.GetScheme ().Normal);
  121. view.Dispose ();
  122. }
  123. [Fact]
  124. public void SettingScheme_Event_CanCancelSchemeChange ()
  125. {
  126. var view = new View ();
  127. var dialogScheme = SchemeManager.GetHardCodedSchemes ()? ["Dialog"];
  128. view.SchemeChanging += (sender, args) => args.Handled = true;
  129. view.SetScheme (dialogScheme);
  130. Assert.NotEqual (dialogScheme, view.GetScheme ());
  131. view.Dispose ();
  132. }
  133. [Fact]
  134. public void GetAttributeForRole_Event_CanOverrideAttribute ()
  135. {
  136. var view = new View { SchemeName = "Base" };
  137. var customAttribute = new Attribute (Color.BrightRed, Color.BrightYellow);
  138. view.GettingAttributeForRole += (sender, args) =>
  139. {
  140. if (args.Role == VisualRole.Focus)
  141. {
  142. args.Result = customAttribute;
  143. args.Handled = true;
  144. }
  145. };
  146. Assert.Equal (customAttribute, view.GetAttributeForRole (VisualRole.Focus));
  147. view.Dispose ();
  148. }
  149. [Fact]
  150. public void GetHardCodedSchemes_ReturnsExpectedSchemes ()
  151. {
  152. var schemes = Scheme.GetHardCodedSchemes ();
  153. Assert.NotNull (schemes);
  154. Assert.Contains ("Base", schemes.Keys);
  155. Assert.Contains ("Dialog", schemes.Keys);
  156. Assert.Contains ("Error", schemes.Keys);
  157. Assert.Contains ("Menu", schemes.Keys);
  158. Assert.Contains ("Toplevel", schemes.Keys);
  159. }
  160. [Fact]
  161. public void SchemeName_OverridesSuperViewScheme ()
  162. {
  163. var superView = new View ();
  164. var subView = new View ();
  165. superView.Add (subView);
  166. subView.SchemeName = "Error";
  167. var errorScheme = SchemeManager.GetHardCodedSchemes ()? ["Error"];
  168. Assert.Equal (errorScheme, subView.GetScheme ());
  169. subView.Dispose ();
  170. superView.Dispose ();
  171. }
  172. [Fact]
  173. public void Scheme_DefaultsToBase_WhenNotSet ()
  174. {
  175. var view = new View ();
  176. var baseScheme = SchemeManager.GetHardCodedSchemes ()? ["Base"];
  177. Assert.Equal (baseScheme, view.GetScheme ());
  178. view.Dispose ();
  179. }
  180. [Fact]
  181. public void Scheme_HandlesNullSuperViewGracefully ()
  182. {
  183. var view = new View ();
  184. view.SchemeName = "Dialog";
  185. var dialogScheme = SchemeManager.GetHardCodedSchemes ()? ["Dialog"];
  186. Assert.Equal (dialogScheme, view.GetScheme ());
  187. view.Dispose ();
  188. }
  189. private class CustomView : View
  190. {
  191. protected override bool OnGettingScheme (out Scheme? scheme)
  192. {
  193. scheme = SchemeManager.GetHardCodedSchemes ()? ["Error"];
  194. return true;
  195. }
  196. protected override bool OnSettingScheme (ValueChangingEventArgs<Scheme?> args)
  197. {
  198. return true; // Prevent setting the scheme
  199. }
  200. }
  201. [Fact]
  202. public void GetAttributeForRole_SubView_DefersToSuperView_WhenNoExplicitScheme ()
  203. {
  204. var parentView = new View { SchemeName = "Base" };
  205. var childView = new View ();
  206. parentView.Add (childView);
  207. // Parent customizes attribute resolution
  208. var customAttribute = new Attribute (Color.BrightMagenta, Color.BrightGreen);
  209. parentView.GettingAttributeForRole += (sender, args) =>
  210. {
  211. if (args.Role == VisualRole.Normal)
  212. {
  213. args.Result = customAttribute;
  214. args.Handled = true;
  215. }
  216. };
  217. // Child without explicit scheme should get customized attribute from parent
  218. Assert.Equal (customAttribute, childView.GetAttributeForRole (VisualRole.Normal));
  219. childView.Dispose ();
  220. parentView.Dispose ();
  221. }
  222. [Fact]
  223. public void GetAttributeForRole_SubView_UsesOwnScheme_WhenExplicitlySet ()
  224. {
  225. var parentView = new View { SchemeName = "Base" };
  226. var childView = new View ();
  227. parentView.Add (childView);
  228. // Set explicit scheme on child
  229. var childScheme = SchemeManager.GetHardCodedSchemes ()? ["Dialog"];
  230. childView.SetScheme (childScheme);
  231. // Parent customizes attribute resolution
  232. var customAttribute = new Attribute (Color.BrightMagenta, Color.BrightGreen);
  233. parentView.GettingAttributeForRole += (sender, args) =>
  234. {
  235. if (args.Role == VisualRole.Normal)
  236. {
  237. args.Result = customAttribute;
  238. args.Handled = true;
  239. }
  240. };
  241. // Child with explicit scheme should NOT get customized attribute from parent
  242. Assert.NotEqual (customAttribute, childView.GetAttributeForRole (VisualRole.Normal));
  243. Assert.Equal (childScheme!.Normal, childView.GetAttributeForRole (VisualRole.Normal));
  244. childView.Dispose ();
  245. parentView.Dispose ();
  246. }
  247. [Fact]
  248. public void GetAttributeForRole_Adornment_UsesParentScheme ()
  249. {
  250. // Border (an Adornment) doesn't have a SuperView but should use its Parent's scheme
  251. var view = new View { SchemeName = "Dialog" };
  252. var border = view.Border!;
  253. Assert.NotNull (border);
  254. Assert.Null (border.SuperView); // Adornments don't have SuperView
  255. Assert.NotNull (border.Parent);
  256. var dialogScheme = SchemeManager.GetHardCodedSchemes ()? ["Dialog"];
  257. // Border should use its Parent's scheme, not Base
  258. Assert.Equal (dialogScheme!.Normal, border.GetAttributeForRole (VisualRole.Normal));
  259. view.Dispose ();
  260. }
  261. [Fact]
  262. public void GetAttributeForRole_SubView_UsesSchemeName_WhenSet ()
  263. {
  264. var parentView = new View { SchemeName = "Base" };
  265. var childView = new View ();
  266. parentView.Add (childView);
  267. // Set SchemeName on child (not explicit scheme)
  268. childView.SchemeName = "Dialog";
  269. // Parent customizes attribute resolution
  270. var customAttribute = new Attribute (Color.BrightMagenta, Color.BrightGreen);
  271. parentView.GettingAttributeForRole += (sender, args) =>
  272. {
  273. if (args.Role == VisualRole.Normal)
  274. {
  275. args.Result = customAttribute;
  276. args.Handled = true;
  277. }
  278. };
  279. // Child with SchemeName should NOT get customized attribute from parent
  280. // It should use the Dialog scheme instead
  281. var dialogScheme = SchemeManager.GetHardCodedSchemes ()? ["Dialog"];
  282. Assert.NotEqual (customAttribute, childView.GetAttributeForRole (VisualRole.Normal));
  283. Assert.Equal (dialogScheme!.Normal, childView.GetAttributeForRole (VisualRole.Normal));
  284. childView.Dispose ();
  285. parentView.Dispose ();
  286. }
  287. [Fact]
  288. public void GetAttributeForRole_NestedHierarchy_DefersCorrectly ()
  289. {
  290. // Test: grandchild without explicit scheme defers through parent to grandparent
  291. // Would fail without the SuperView deferral fix (commit 154ac15)
  292. var grandparentView = new View { SchemeName = "Base" };
  293. var parentView = new View (); // No scheme or SchemeName
  294. var childView = new View (); // No scheme or SchemeName
  295. grandparentView.Add (parentView);
  296. parentView.Add (childView);
  297. // Grandparent customizes attributes
  298. var customAttribute = new Attribute (Color.BrightYellow, Color.BrightBlue);
  299. grandparentView.GettingAttributeForRole += (sender, args) =>
  300. {
  301. if (args.Role == VisualRole.Normal)
  302. {
  303. args.Result = customAttribute;
  304. args.Handled = true;
  305. }
  306. };
  307. // Child should get attribute from grandparent through parent
  308. Assert.Equal (customAttribute, childView.GetAttributeForRole (VisualRole.Normal));
  309. // Parent should also get attribute from grandparent
  310. Assert.Equal (customAttribute, parentView.GetAttributeForRole (VisualRole.Normal));
  311. childView.Dispose ();
  312. parentView.Dispose ();
  313. grandparentView.Dispose ();
  314. }
  315. [Fact]
  316. public void GetAttributeForRole_ParentWithSchemeNameBreaksChain ()
  317. {
  318. // Test: parent with SchemeName stops deferral chain
  319. // Would fail without the SchemeName check (commit 866e002)
  320. var grandparentView = new View { SchemeName = "Base" };
  321. var parentView = new View { SchemeName = "Dialog" }; // Sets SchemeName
  322. var childView = new View (); // No scheme or SchemeName
  323. grandparentView.Add (parentView);
  324. parentView.Add (childView);
  325. // Grandparent customizes attributes
  326. var customAttribute = new Attribute (Color.BrightYellow, Color.BrightBlue);
  327. grandparentView.GettingAttributeForRole += (sender, args) =>
  328. {
  329. if (args.Role == VisualRole.Normal)
  330. {
  331. args.Result = customAttribute;
  332. args.Handled = true;
  333. }
  334. };
  335. // Parent should NOT get grandparent's customization (it has SchemeName)
  336. var dialogScheme = SchemeManager.GetHardCodedSchemes ()? ["Dialog"];
  337. Assert.NotEqual (customAttribute, parentView.GetAttributeForRole (VisualRole.Normal));
  338. Assert.Equal (dialogScheme!.Normal, parentView.GetAttributeForRole (VisualRole.Normal));
  339. // Child should get parent's Dialog scheme (defers to parent, parent uses Dialog scheme)
  340. Assert.Equal (dialogScheme!.Normal, childView.GetAttributeForRole (VisualRole.Normal));
  341. childView.Dispose ();
  342. parentView.Dispose ();
  343. grandparentView.Dispose ();
  344. }
  345. [Fact]
  346. public void GetAttributeForRole_OnGettingAttributeForRole_TakesPrecedence ()
  347. {
  348. // Test: view's own OnGettingAttributeForRole takes precedence over parent
  349. // This should work with or without the fix, but validates precedence
  350. var parentView = new View { SchemeName = "Base" };
  351. var childView = new TestViewWithAttributeOverride ();
  352. parentView.Add (childView);
  353. // Parent customizes attributes
  354. var parentAttribute = new Attribute (Color.BrightYellow, Color.BrightBlue);
  355. parentView.GettingAttributeForRole += (sender, args) =>
  356. {
  357. if (args.Role == VisualRole.Normal)
  358. {
  359. args.Result = parentAttribute;
  360. args.Handled = true;
  361. }
  362. };
  363. // Child's own override should take precedence
  364. var childOverrideAttribute = new Attribute (Color.BrightRed, Color.BrightCyan);
  365. childView.OverrideAttribute = childOverrideAttribute;
  366. Assert.Equal (childOverrideAttribute, childView.GetAttributeForRole (VisualRole.Normal));
  367. childView.Dispose ();
  368. parentView.Dispose ();
  369. }
  370. [Fact]
  371. public void GetAttributeForRole_MultipleRoles_DeferCorrectly ()
  372. {
  373. // Test: multiple VisualRoles all defer correctly
  374. // Would fail without the SuperView deferral fix for any role
  375. var parentView = new View { SchemeName = "Base" };
  376. var childView = new View ();
  377. parentView.Add (childView);
  378. var normalAttr = new Attribute (Color.Red, Color.Blue);
  379. var focusAttr = new Attribute (Color.Green, Color.Yellow);
  380. var hotNormalAttr = new Attribute (Color.Magenta, Color.Cyan);
  381. parentView.GettingAttributeForRole += (sender, args) =>
  382. {
  383. switch (args.Role)
  384. {
  385. case VisualRole.Normal:
  386. args.Result = normalAttr;
  387. args.Handled = true;
  388. break;
  389. case VisualRole.Focus:
  390. args.Result = focusAttr;
  391. args.Handled = true;
  392. break;
  393. case VisualRole.HotNormal:
  394. args.Result = hotNormalAttr;
  395. args.Handled = true;
  396. break;
  397. }
  398. };
  399. // All roles should defer to parent
  400. Assert.Equal (normalAttr, childView.GetAttributeForRole (VisualRole.Normal));
  401. Assert.Equal (focusAttr, childView.GetAttributeForRole (VisualRole.Focus));
  402. Assert.Equal (hotNormalAttr, childView.GetAttributeForRole (VisualRole.HotNormal));
  403. childView.Dispose ();
  404. parentView.Dispose ();
  405. }
  406. private class TestViewWithAttributeOverride : View
  407. {
  408. public Attribute? OverrideAttribute { get; set; }
  409. protected override bool OnGettingAttributeForRole (in VisualRole role, ref Attribute currentAttribute)
  410. {
  411. if (OverrideAttribute.HasValue && role == VisualRole.Normal)
  412. {
  413. currentAttribute = OverrideAttribute.Value;
  414. return true;
  415. }
  416. return base.OnGettingAttributeForRole (role, ref currentAttribute);
  417. }
  418. }
  419. }