ShadowTests.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487
  1. ๏ปฟusing System.Text;
  2. using UnitTests;
  3. using Xunit.Abstractions;
  4. namespace ViewBaseTests.Adornments;
  5. [Collection ("Global Test Setup")]
  6. public class ShadowTests (ITestOutputHelper output)
  7. {
  8. private readonly ITestOutputHelper _output = output;
  9. [Fact]
  10. public void Default_None ()
  11. {
  12. var view = new View ();
  13. Assert.Equal (ShadowStyle.None, view.ShadowStyle);
  14. Assert.Equal (ShadowStyle.None, view.Margin!.ShadowStyle);
  15. view.Dispose ();
  16. }
  17. [Theory]
  18. [InlineData (ShadowStyle.None)]
  19. [InlineData (ShadowStyle.Opaque)]
  20. [InlineData (ShadowStyle.Transparent)]
  21. public void Set_View_Sets_Margin (ShadowStyle style)
  22. {
  23. var view = new View ();
  24. view.ShadowStyle = style;
  25. Assert.Equal (style, view.ShadowStyle);
  26. Assert.Equal (style, view.Margin!.ShadowStyle);
  27. view.Dispose ();
  28. }
  29. [Theory]
  30. [InlineData (ShadowStyle.None, 0, 0, 0, 0)]
  31. [InlineData (ShadowStyle.Opaque, 0, 0, 1, 1)]
  32. [InlineData (ShadowStyle.Transparent, 0, 0, 1, 1)]
  33. public void ShadowStyle_Margin_Thickness (ShadowStyle style, int expectedLeft, int expectedTop, int expectedRight, int expectedBottom)
  34. {
  35. var superView = new View
  36. {
  37. Height = 10, Width = 10
  38. };
  39. View view = new ()
  40. {
  41. Width = Dim.Auto (),
  42. Height = Dim.Auto (),
  43. Text = "0123",
  44. HighlightStates = MouseState.Pressed,
  45. ShadowStyle = style,
  46. CanFocus = true
  47. };
  48. superView.Add (view);
  49. superView.BeginInit ();
  50. superView.EndInit ();
  51. Assert.Equal (new (expectedLeft, expectedTop, expectedRight, expectedBottom), view.Margin!.Thickness);
  52. }
  53. [Theory]
  54. [InlineData (ShadowStyle.None, 3)]
  55. [InlineData (ShadowStyle.Opaque, 4)]
  56. [InlineData (ShadowStyle.Transparent, 4)]
  57. public void Style_Changes_Margin_Thickness (ShadowStyle style, int expected)
  58. {
  59. var view = new View ();
  60. view.Margin!.Thickness = new (3);
  61. view.ShadowStyle = style;
  62. Assert.Equal (new (3, 3, expected, expected), view.Margin.Thickness);
  63. view.ShadowStyle = ShadowStyle.None;
  64. Assert.Equal (new (3), view.Margin.Thickness);
  65. view.Dispose ();
  66. }
  67. [Theory]
  68. [InlineData (ShadowStyle.Opaque)]
  69. [InlineData (ShadowStyle.Transparent)]
  70. public void ShadowWidth_ShadowHeight_Defaults_To_One (ShadowStyle style)
  71. {
  72. View view = new () { ShadowStyle = style };
  73. Assert.Equal (new (1, 1), view.Margin!.ShadowSize);
  74. }
  75. [Theory]
  76. [InlineData (ShadowStyle.None, 0)]
  77. [InlineData (ShadowStyle.Opaque, 1)]
  78. [InlineData (ShadowStyle.Transparent, 1)]
  79. public void Margin_ShadowWidth_ShadowHeight_Cannot_Be_Set_Less_Than_One (ShadowStyle style, int expectedLength)
  80. {
  81. View view = new () { ShadowStyle = style };
  82. view.Margin!.ShadowSize = new (-1, -1);
  83. Assert.Equal (expectedLength, view.Margin!.ShadowSize.Width);
  84. Assert.Equal (expectedLength, view.Margin!.ShadowSize.Height);
  85. }
  86. [Fact]
  87. public void Changing_ShadowStyle_Correctly_Set_ShadowWidth_ShadowHeight_Thickness ()
  88. {
  89. View view = new () { ShadowStyle = ShadowStyle.Transparent };
  90. view.Margin!.ShadowSize = new (2, 2);
  91. Assert.Equal (new (2, 2), view.Margin!.ShadowSize);
  92. Assert.Equal (new (0, 0, 2, 2), view.Margin.Thickness);
  93. view.ShadowStyle = ShadowStyle.None;
  94. Assert.Equal (new (2, 2), view.Margin!.ShadowSize);
  95. Assert.Equal (new (0, 0, 0, 0), view.Margin.Thickness);
  96. view.ShadowStyle = ShadowStyle.Opaque;
  97. Assert.Equal (new (1, 1), view.Margin!.ShadowSize);
  98. Assert.Equal (new (0, 0, 1, 1), view.Margin.Thickness);
  99. }
  100. [Fact]
  101. public void ShadowStyle_Transparent_Handles_Wide_Glyphs_Correctly ()
  102. {
  103. IApplication app = Application.Create ();
  104. app.Init ("fake");
  105. app.Driver?.SetScreenSize (6, 5);
  106. app.Driver?.GetOutputBuffer ().SetWideGlyphReplacement (Rune.ReplacementChar);
  107. Runnable superview = new () { Width = Dim.Fill (), Height = Dim.Fill () };
  108. superview.Text = """
  109. ๐ŸŽ๐ŸŽ๐ŸŽ
  110. ๐ŸŽ๐ŸŽ๐ŸŽ
  111. ๐ŸŽ๐ŸŽ๐ŸŽ
  112. ๐ŸŽ๐ŸŽ๐ŸŽ
  113. ๐ŸŽ๐ŸŽ๐ŸŽ
  114. """;
  115. View view = new () { Width = Dim.Fill (), Height = Dim.Fill (), BorderStyle = LineStyle.Single, ShadowStyle = ShadowStyle.Transparent };
  116. view.Margin!.ShadowSize = view.Margin!.ShadowSize with { Width = 2 };
  117. superview.Add (view);
  118. app.Begin (superview);
  119. DriverAssert.AssertDriverContentsAre (
  120. """
  121. โ”Œโ”€โ”€โ”๐ŸŽ
  122. โ”‚ โ”‚๐ŸŽ
  123. โ”‚ โ”‚๐ŸŽ
  124. โ””โ”€โ”€โ”˜๐ŸŽ
  125. ๏ฟฝ ๐ŸŽ๐ŸŽ
  126. """,
  127. output,
  128. app.Driver);
  129. view.Margin!.ShadowSize = new (1, 2);
  130. app.LayoutAndDraw ();
  131. DriverAssert.AssertDriverContentsAre (
  132. """
  133. โ”Œโ”€โ”€โ”๐ŸŽ
  134. โ”‚ โ”‚๏ฟฝ
  135. โ””โ”€โ”€โ”˜๏ฟฝ
  136. ๏ฟฝ ๐ŸŽ๐ŸŽ
  137. ๏ฟฝ ๐ŸŽ๐ŸŽ
  138. """,
  139. output,
  140. app.Driver);
  141. }
  142. [Fact]
  143. public void ShadowStyle_Opaque_Change_Thickness_On_Mouse_Pressed_Released ()
  144. {
  145. IApplication app = Application.Create ();
  146. app.Init ("fake");
  147. app.Driver?.SetScreenSize (10, 4);
  148. Runnable superview = new () { Width = Dim.Fill (), Height = Dim.Fill () };
  149. View view = new () { Width = 7, Height = 2, ShadowStyle = ShadowStyle.Opaque, Text = "| Hi |", HighlightStates = MouseState.Pressed };
  150. superview.Add (view);
  151. app.Begin (superview);
  152. DriverAssert.AssertDriverContentsAre (
  153. """
  154. | Hi |โ––
  155. โ–โ–€โ–€โ–€โ–€โ–€โ–˜
  156. """,
  157. output,
  158. app.Driver);
  159. app.Mouse.RaiseMouseEvent (new () { ScreenPosition = new (2, 0), Flags = MouseFlags.Button1Pressed });
  160. app.LayoutAndDraw ();
  161. DriverAssert.AssertDriverContentsAre (
  162. """
  163. | Hi |
  164. """,
  165. output,
  166. app.Driver);
  167. app.Mouse.RaiseMouseEvent (new () { ScreenPosition = new (2, 0), Flags = MouseFlags.Button1Released });
  168. app.LayoutAndDraw ();
  169. DriverAssert.AssertDriverContentsAre (
  170. """
  171. | Hi |โ––
  172. โ–โ–€โ–€โ–€โ–€โ–€โ–˜
  173. """,
  174. output,
  175. app.Driver);
  176. }
  177. [Fact]
  178. public void ShadowStyle_Transparent_Never_Throws_Navigating_Outside_Bounds ()
  179. {
  180. IApplication app = Application.Create ();
  181. app.Init ("fake");
  182. app.Driver?.SetScreenSize (6, 5);
  183. Runnable superview = new () { Width = Dim.Fill (), Height = Dim.Fill () };
  184. superview.Text = """
  185. ๐ŸŽ๐ŸŽ๐ŸŽ
  186. ๐ŸŽ๐ŸŽ๐ŸŽ
  187. ๐ŸŽ๐ŸŽ๐ŸŽ
  188. ๐ŸŽ๐ŸŽ๐ŸŽ
  189. ๐ŸŽ๐ŸŽ๐ŸŽ
  190. """;
  191. View view = new ()
  192. {
  193. Width = Dim.Fill (), Height = Dim.Fill (), BorderStyle = LineStyle.Single, ShadowStyle = ShadowStyle.Transparent,
  194. Arrangement = ViewArrangement.Movable, CanFocus = true
  195. };
  196. view.Margin!.ShadowSize = view.Margin!.ShadowSize with { Width = 2 };
  197. superview.Add (view);
  198. app.Begin (superview);
  199. Assert.Equal (new (0, 0), view.Frame.Location);
  200. Assert.True (app.Keyboard.RaiseKeyDownEvent (Key.F5.WithCtrl));
  201. int i = 0;
  202. DecrementValue (-10, Key.CursorLeft);
  203. Assert.Equal (-10, i);
  204. IncrementValue (0, Key.CursorRight);
  205. Assert.Equal (0, i);
  206. DecrementValue (-10, Key.CursorUp);
  207. Assert.Equal (-10, i);
  208. IncrementValue (20, Key.CursorDown);
  209. Assert.Equal (20, i);
  210. DecrementValue (0, Key.CursorUp);
  211. Assert.Equal (0, i);
  212. IncrementValue (20, Key.CursorRight);
  213. Assert.Equal (20, i);
  214. return;
  215. void DecrementValue (int count, Key key)
  216. {
  217. for (; i > count; i--)
  218. {
  219. Assert.True (app.Keyboard.RaiseKeyDownEvent (key));
  220. app.LayoutAndDraw ();
  221. CheckAssertion (new (i - 1, 0), new (0, i - 1), key);
  222. }
  223. }
  224. void IncrementValue (int count, Key key)
  225. {
  226. for (; i < count; i++)
  227. {
  228. Assert.True (app.Keyboard.RaiseKeyDownEvent (key));
  229. app.LayoutAndDraw ();
  230. CheckAssertion (new (i + 1, 0), new (0, i + 1), key);
  231. }
  232. }
  233. bool? IsColumn (Key key)
  234. {
  235. if (key == Key.CursorLeft || key == Key.CursorRight)
  236. {
  237. return true;
  238. }
  239. if (key == Key.CursorUp || key == Key.CursorDown)
  240. {
  241. return false;
  242. }
  243. return null;
  244. }
  245. void CheckAssertion (Point colLocation, Point rowLocation, Key key)
  246. {
  247. bool? isCol = IsColumn (key);
  248. switch (isCol)
  249. {
  250. case true:
  251. Assert.Equal (colLocation, view.Frame.Location);
  252. break;
  253. case false:
  254. Assert.Equal (rowLocation, view.Frame.Location);
  255. break;
  256. default:
  257. throw new InvalidOperationException ();
  258. }
  259. }
  260. }
  261. [Theory]
  262. [InlineData (ShadowStyle.None, 3)]
  263. [InlineData (ShadowStyle.Opaque, 4)]
  264. [InlineData (ShadowStyle.Transparent, 4)]
  265. public void Margin_Thickness_Changes_Adjust_Correctly (ShadowStyle style, int expected)
  266. {
  267. var view = new View ();
  268. view.Margin!.Thickness = new (3);
  269. view.ShadowStyle = style;
  270. Assert.Equal (new (3, 3, expected, expected), view.Margin.Thickness);
  271. view.Margin.Thickness = new (3, 3, expected + 1, expected + 1);
  272. Assert.Equal (new (3, 3, expected + 1, expected + 1), view.Margin.Thickness);
  273. view.ShadowStyle = ShadowStyle.None;
  274. Assert.Equal (new (3, 3, 4, 4), view.Margin.Thickness);
  275. view.Dispose ();
  276. }
  277. [Fact]
  278. public void Runnable_View_Overlap_Other_Runnables ()
  279. {
  280. IApplication app = Application.Create ();
  281. app.Init ("fake");
  282. app.Driver?.SetScreenSize (10, 5);
  283. Runnable superview = new () { Width = Dim.Fill (), Height = Dim.Fill (), Text = "๐ŸŽ".Repeat (25)! };
  284. View view = new () { Width = 7, Height = 2, ShadowStyle = ShadowStyle.Opaque, Text = "| Hi |" };
  285. superview.Add (view);
  286. app.Begin (superview);
  287. DriverAssert.AssertDriverContentsAre (
  288. """
  289. | Hi |โ–– ๐ŸŽ
  290. โ–โ–€โ–€โ–€โ–€โ–€โ–˜ ๐ŸŽ
  291. ๐ŸŽ๐ŸŽ๐ŸŽ๐ŸŽ๐ŸŽ
  292. ๐ŸŽ๐ŸŽ๐ŸŽ๐ŸŽ๐ŸŽ
  293. ๐ŸŽ๐ŸŽ๐ŸŽ๐ŸŽ๐ŸŽ
  294. """,
  295. output,
  296. app.Driver);
  297. Runnable modalSuperview = new () { Y = 1, Width = Dim.Fill (), Height = 4, BorderStyle = LineStyle.Single };
  298. View view1 = new () { Width = 8, Height = 2, ShadowStyle = ShadowStyle.Opaque, Text = "| Hey |" };
  299. modalSuperview.Add (view1);
  300. app.Begin (modalSuperview);
  301. Assert.True (modalSuperview.IsModal);
  302. DriverAssert.AssertDriverContentsAre (
  303. """
  304. | Hi |โ–– ๐ŸŽ
  305. โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  306. โ”‚| Hey |โ––โ”‚
  307. โ”‚โ–โ–€โ–€โ–€โ–€โ–€โ–€โ–˜โ”‚
  308. โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
  309. """,
  310. output,
  311. app.Driver);
  312. app.Dispose ();
  313. }
  314. [Fact]
  315. public void TransparentShadow_Draws_Transparent_At_Driver_Output ()
  316. {
  317. // Arrange
  318. using IApplication app = Application.Create ();
  319. app.Init ("fake");
  320. app.Driver!.SetScreenSize (2, 1);
  321. app.Driver.Force16Colors = true;
  322. using Runnable superView = new ();
  323. superView.Width = Dim.Fill ();
  324. superView.Height = Dim.Fill ();
  325. superView.Text = "AB";
  326. superView.TextFormatter.WordWrap = true;
  327. superView.SetScheme (new (new Attribute (Color.Black, Color.White)));
  328. // Create view with transparent shadow
  329. View viewWithShadow = new ()
  330. {
  331. Width = Dim.Auto (),
  332. Height = Dim.Auto (),
  333. Text = "*",
  334. ShadowStyle = ShadowStyle.Transparent
  335. };
  336. // Make it so the margin is only on the right for simplicity
  337. viewWithShadow.Margin!.Thickness = new (0, 0, 1, 0);
  338. viewWithShadow.SetScheme (new (new Attribute (Color.Black, Color.White)));
  339. superView.Add (viewWithShadow);
  340. // Act
  341. app.Begin (superView);
  342. app.LayoutAndDraw ();
  343. app.Driver.Refresh ();
  344. // Assert
  345. _output.WriteLine ("Actual driver contents:");
  346. _output.WriteLine (app.Driver.ToString ());
  347. _output.WriteLine ("\nActual driver output:");
  348. string? output = app.Driver.GetOutput ().GetLastOutput ();
  349. _output.WriteLine (output);
  350. DriverAssert.AssertDriverOutputIs ("""
  351. \x1b[30m\x1b[107m*\x1b[90m\x1b[100mB
  352. """, _output, app.Driver);
  353. }
  354. [Fact]
  355. public void TransparentShadow_OverWide_Draws_Transparent_At_Driver_Output ()
  356. {
  357. // Arrange
  358. using IApplication app = Application.Create ();
  359. app.Init ("fake");
  360. app.Driver!.SetScreenSize (2, 3);
  361. app.Driver.Force16Colors = true;
  362. using Runnable superView = new ();
  363. superView.Width = Dim.Fill ();
  364. superView.Height = Dim.Fill ();
  365. superView.Text = "๐ŸŽ๐ŸŽ๐ŸŽ๐ŸŽ";
  366. superView.TextFormatter.WordWrap = true;
  367. superView.SetScheme (new (new Attribute (Color.Black, Color.White)));
  368. // Create view with transparent shadow
  369. View viewWithShadow = new ()
  370. {
  371. Width = Dim.Auto (),
  372. Height = Dim.Auto (),
  373. Text = "*",
  374. ShadowStyle = ShadowStyle.Transparent
  375. };
  376. // Make it so the margin is only on the bottom for simplicity
  377. viewWithShadow.Margin!.Thickness = new (0, 0, 0, 1);
  378. viewWithShadow.SetScheme (new (new Attribute (Color.Black, Color.White)));
  379. superView.Add (viewWithShadow);
  380. // Act
  381. app.Begin (superView);
  382. app.LayoutAndDraw ();
  383. app.Driver.Refresh ();
  384. // Assert
  385. _output.WriteLine ("Actual driver contents:");
  386. _output.WriteLine (app.Driver.ToString ());
  387. _output.WriteLine ("\nActual driver output:");
  388. string? output = app.Driver.GetOutput ().GetLastOutput ();
  389. _output.WriteLine (output);
  390. DriverAssert.AssertDriverOutputIs ("""
  391. \x1b[30m\x1b[107m*\x1b[90m\x1b[103m \x1b[97m\x1b[40m \x1b[90m\x1b[100m \x1b[97m\x1b[40m๐ŸŽ
  392. """, _output, app.Driver);
  393. }
  394. }