SchemeTests.cs 17 KB

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