DrawTests.cs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771
  1. #nullable enable
  2. using System.Text;
  3. using Xunit.Abstractions;
  4. namespace Terminal.Gui.ViewTests;
  5. [Trait("Category","Output")]
  6. public class DrawTests
  7. {
  8. private readonly ITestOutputHelper _output;
  9. public DrawTests (ITestOutputHelper output) { _output = output; }
  10. [Theory]
  11. [InlineData (0, 0, 1, 1)]
  12. [InlineData (0, 0, 2, 2)]
  13. [InlineData (-1, -1, 2, 2)]
  14. [SetupFakeDriver]
  15. public void Clear_Bounds_Clears_Only_Bounds (int x, int y, int width, int height)
  16. {
  17. var superView = new View { Width = Dim.Fill (), Height = Dim.Fill () };
  18. var view = new View
  19. {
  20. Text = "X",
  21. X = 1, Y = 1,
  22. Width = 3, Height = 3,
  23. BorderStyle = LineStyle.Single
  24. };
  25. superView.Add (view);
  26. superView.BeginInit ();
  27. superView.EndInit ();
  28. superView.LayoutSubviews ();
  29. superView.Draw ();
  30. TestHelpers.AssertDriverContentsWithFrameAre (
  31. @"
  32. ┌─┐
  33. │X│
  34. └─┘",
  35. _output);
  36. Rectangle boundsToClear = new (x, y, width, height);
  37. view.Clear (boundsToClear);
  38. TestHelpers.AssertDriverContentsWithFrameAre (
  39. @"
  40. ┌─┐
  41. │ │
  42. └─┘",
  43. _output);
  44. }
  45. [Fact]
  46. [AutoInitShutdown]
  47. [Trait("Category","Unicode")]
  48. public void CJK_Compatibility_Ideographs_ConsoleWidth_ColumnWidth_Equal_Two ()
  49. {
  50. const string us = "\U0000f900";
  51. var r = (Rune)0xf900;
  52. Assert.Equal ("豈", us);
  53. Assert.Equal ("豈", r.ToString ());
  54. Assert.Equal (us, r.ToString ());
  55. Assert.Equal (2, us.GetColumns ());
  56. Assert.Equal (2, r.GetColumns ());
  57. var win = new Window { Title = us };
  58. var view = new View { Text = r.ToString (), Height = Dim.Fill (), Width = Dim.Fill () };
  59. var tf = new TextField { Text = us, Y = 1, Width = 3 };
  60. win.Add (view, tf);
  61. Toplevel top = new ();
  62. top.Add (win);
  63. Application.Begin (top);
  64. ((FakeDriver)Application.Driver).SetBufferSize (10, 4);
  65. const string expectedOutput = """
  66. ┌┤豈├────┐
  67. │豈 │
  68. │豈 │
  69. └────────┘
  70. """;
  71. TestHelpers.AssertDriverContentsWithFrameAre (expectedOutput, _output);
  72. TestHelpers.AssertDriverContentsAre (expectedOutput, _output);
  73. Attribute [] expectedColors =
  74. {
  75. // 0
  76. Colors.ColorSchemes ["Base"].Normal,
  77. // 1
  78. Colors.ColorSchemes ["Base"].Focus,
  79. // 2
  80. Colors.ColorSchemes ["Base"].HotNormal
  81. };
  82. TestHelpers.AssertDriverAttributesAre (
  83. """
  84. 0011000000
  85. 0000000000
  86. 0111000000
  87. 0000000000
  88. """,
  89. Application.Driver,
  90. expectedColors
  91. );
  92. }
  93. // TODO: Refactor this test to not depend on TextView etc... Make it as primitive as possible
  94. [Fact]
  95. [AutoInitShutdown]
  96. [Trait("Category","Unicode")]
  97. public void Clipping_AddRune_Left_Or_Right_Replace_Previous_Or_Next_Wide_Rune_With_Space ()
  98. {
  99. var tv = new TextView
  100. {
  101. Width = Dim.Fill (),
  102. Height = Dim.Fill (),
  103. Text = """
  104. これは広いルーンラインです。
  105. これは広いルーンラインです。
  106. これは広いルーンラインです。
  107. これは広いルーンラインです。
  108. これは広いルーンラインです。
  109. これは広いルーンラインです。
  110. これは広いルーンラインです。
  111. これは広いルーンラインです。
  112. """
  113. };
  114. var win = new Window { Width = Dim.Fill (), Height = Dim.Fill () };
  115. win.Add (tv);
  116. var top = new Toplevel ();
  117. top.Add (win);
  118. // Don't use Label. It sets AutoSize = true which is not what we're testing here.
  119. var view = new View { Text = "ワイドルーン。", Height = Dim.Fill (), Width = Dim.Fill () };
  120. // Don't have unit tests use things that aren't absolutely critical for the test, like Dialog
  121. var dg = new Window { X = 2, Y = 2, Width = 14, Height = 3 };
  122. dg.Add (view);
  123. RunState rsTop = Application.Begin (top);
  124. RunState rsDiag = Application.Begin (dg);
  125. ((FakeDriver)Application.Driver).SetBufferSize (30, 10);
  126. const string expectedOutput = """
  127. ┌────────────────────────────┐
  128. │これは広いルーンラインです。│
  129. │�┌────────────┐�ラインです。│
  130. │�│ワイドルーン│�ラインです。│
  131. │�└────────────┘�ラインです。│
  132. │これは広いルーンラインです。│
  133. │これは広いルーンラインです。│
  134. │これは広いルーンラインです。│
  135. │これは広いルーンラインです。│
  136. └────────────────────────────┘
  137. """;
  138. Rectangle pos = TestHelpers.AssertDriverContentsWithFrameAre (expectedOutput, _output);
  139. Assert.Equal (new Rectangle (0, 0, 30, 10), pos);
  140. Application.End (rsDiag);
  141. Application.End (rsTop);
  142. }
  143. [Fact]
  144. [AutoInitShutdown]
  145. [Trait("Category","Output")]
  146. public void Colors_On_TextAlignment_Right_And_Bottom ()
  147. {
  148. var viewRight = new View
  149. {
  150. Text = "Test",
  151. Width = 6,
  152. Height = 1,
  153. TextAlignment = TextAlignment.Right,
  154. ColorScheme = Colors.ColorSchemes ["Base"]
  155. };
  156. var viewBottom = new View
  157. {
  158. Text = "Test",
  159. TextDirection = TextDirection.TopBottom_LeftRight,
  160. Y = 1,
  161. Width = 1,
  162. Height = 6,
  163. VerticalTextAlignment = VerticalTextAlignment.Bottom,
  164. ColorScheme = Colors.ColorSchemes ["Base"]
  165. };
  166. Toplevel top = new ();
  167. top.Add (viewRight, viewBottom);
  168. Application.Begin (top);
  169. ((FakeDriver)Application.Driver).SetBufferSize (7, 7);
  170. TestHelpers.AssertDriverContentsWithFrameAre (
  171. """
  172. Test
  173. T
  174. e
  175. s
  176. t
  177. """,
  178. _output
  179. );
  180. TestHelpers.AssertDriverAttributesAre (
  181. """
  182. 000000
  183. 0
  184. 0
  185. 0
  186. 0
  187. 0
  188. 0
  189. """,
  190. Application.Driver,
  191. Colors.ColorSchemes ["Base"].Normal
  192. );
  193. }
  194. [Fact]
  195. [SetupFakeDriver]
  196. public void Draw_Minimum_Full_Border_With_Empty_Bounds ()
  197. {
  198. var view = new View { Width = 2, Height = 2, BorderStyle = LineStyle.Single };
  199. view.BeginInit ();
  200. view.EndInit ();
  201. view.SetRelativeLayout (Application.Driver.Bounds);
  202. Assert.Equal (new (0,0,2,2), view.Frame);
  203. Assert.Equal (Rectangle.Empty, view.Bounds);
  204. view.Draw ();
  205. TestHelpers.AssertDriverContentsWithFrameAre (
  206. """
  207. ┌┐
  208. └┘
  209. """,
  210. _output
  211. );
  212. }
  213. [Fact]
  214. [SetupFakeDriver]
  215. public void Draw_Minimum_Full_Border_With_Empty_Bounds_Without_Bottom ()
  216. {
  217. var view = new View { Width = 2, Height = 1, BorderStyle = LineStyle.Single };
  218. view.Border.Thickness = new Thickness (1, 1, 1, 0);
  219. view.BeginInit ();
  220. view.EndInit ();
  221. view.SetRelativeLayout (Application.Driver.Bounds);
  222. Assert.Equal (new (0,0,2,1), view.Frame);
  223. Assert.Equal (Rectangle.Empty, view.Bounds);
  224. view.Draw ();
  225. TestHelpers.AssertDriverContentsWithFrameAre (string.Empty, _output);
  226. }
  227. [Fact]
  228. [SetupFakeDriver]
  229. public void Draw_Minimum_Full_Border_With_Empty_Bounds_Without_Left ()
  230. {
  231. var view = new View { Width = 1, Height = 2, BorderStyle = LineStyle.Single };
  232. view.Border.Thickness = new Thickness (0, 1, 1, 1);
  233. view.BeginInit ();
  234. view.EndInit ();
  235. view.SetRelativeLayout (Application.Driver.Bounds);
  236. Assert.Equal (new (0,0,1,2), view.Frame);
  237. Assert.Equal (Rectangle.Empty, view.Bounds);
  238. view.Draw ();
  239. TestHelpers.AssertDriverContentsWithFrameAre (
  240. """
  241. """,
  242. _output
  243. );
  244. }
  245. [Fact]
  246. [SetupFakeDriver]
  247. public void Draw_Minimum_Full_Border_With_Empty_Bounds_Without_Right ()
  248. {
  249. var view = new View { Width = 1, Height = 2, BorderStyle = LineStyle.Single };
  250. view.Border.Thickness = new Thickness (1, 1, 0, 1);
  251. view.BeginInit ();
  252. view.EndInit ();
  253. view.SetRelativeLayout (Application.Driver.Bounds);
  254. Assert.Equal (new (0,0,1,2), view.Frame);
  255. Assert.Equal (Rectangle.Empty, view.Bounds);
  256. view.Draw ();
  257. TestHelpers.AssertDriverContentsWithFrameAre (
  258. """
  259. """,
  260. _output
  261. );
  262. }
  263. [Fact]
  264. [SetupFakeDriver]
  265. public void Draw_Minimum_Full_Border_With_Empty_Bounds_Without_Top ()
  266. {
  267. var view = new View { Width = 2, Height = 1, BorderStyle = LineStyle.Single };
  268. view.Border.Thickness = new Thickness (1, 0, 1, 1);
  269. view.BeginInit ();
  270. view.EndInit ();
  271. view.SetRelativeLayout (Application.Driver.Bounds);
  272. Assert.Equal (new (0,0,2,1), view.Frame);
  273. Assert.Equal (Rectangle.Empty, view.Bounds);
  274. view.Draw ();
  275. // BUGBUG: Wha? Is this right? Shouldn't it be "└┘"???
  276. TestHelpers.AssertDriverContentsWithFrameAre (
  277. """
  278. ┌┐
  279. """,
  280. _output
  281. );
  282. }
  283. [Fact]
  284. [AutoInitShutdown]
  285. public void Draw_Negative_Bounds_Horizontal_With_New_Lines ()
  286. {
  287. var subView = new View
  288. {
  289. Id = "subView",
  290. X = 1,
  291. Width = 1,
  292. Height = 7,
  293. Text = """
  294. s
  295. u
  296. b
  297. V
  298. i
  299. e
  300. w
  301. """
  302. };
  303. var view = new View
  304. {
  305. Id = "view", Width = 2, Height = 20, Text = """
  306. 0
  307. 1
  308. 2
  309. 3
  310. 4
  311. 5
  312. 6
  313. 7
  314. 8
  315. 9
  316. 0
  317. 1
  318. 2
  319. 3
  320. 4
  321. 5
  322. 6
  323. 7
  324. 8
  325. 9
  326. """
  327. };
  328. view.Add (subView);
  329. var content = new View { Id = "content", Width = 20, Height = 20 };
  330. content.Add (view);
  331. var container = new View
  332. {
  333. Id = "container",
  334. X = 1,
  335. Y = 1,
  336. Width = 5,
  337. Height = 5
  338. };
  339. container.Add (content);
  340. Toplevel top = new ();
  341. top.Add (container);
  342. Application.Driver.Clip = container.Frame;
  343. Application.Begin (top);
  344. TestHelpers.AssertDriverContentsWithFrameAre (
  345. """
  346. 0s
  347. 1u
  348. 2b
  349. 3V
  350. 4i
  351. """,
  352. _output
  353. );
  354. content.X = -1;
  355. Application.Refresh ();
  356. TestHelpers.AssertDriverContentsWithFrameAre (
  357. """
  358. s
  359. u
  360. b
  361. V
  362. i
  363. """,
  364. _output
  365. );
  366. content.X = -2;
  367. Application.Refresh ();
  368. TestHelpers.AssertDriverContentsWithFrameAre (@"", _output);
  369. content.X = 0;
  370. content.Y = -1;
  371. Application.Refresh ();
  372. TestHelpers.AssertDriverContentsWithFrameAre (
  373. """
  374. 1u
  375. 2b
  376. 3V
  377. 4i
  378. 5e
  379. """,
  380. _output
  381. );
  382. content.Y = -6;
  383. Application.Refresh ();
  384. TestHelpers.AssertDriverContentsWithFrameAre (
  385. """
  386. 6w
  387. 7
  388. 8
  389. 9
  390. 0
  391. """,
  392. _output
  393. );
  394. content.Y = -19;
  395. Application.Refresh ();
  396. TestHelpers.AssertDriverContentsWithFrameAre (
  397. """
  398. 9
  399. """,
  400. _output
  401. );
  402. content.Y = -20;
  403. Application.Refresh ();
  404. TestHelpers.AssertDriverContentsWithFrameAre ("", _output);
  405. content.X = -2;
  406. content.Y = 0;
  407. Application.Refresh ();
  408. TestHelpers.AssertDriverContentsWithFrameAre ("", _output);
  409. }
  410. [Fact]
  411. [AutoInitShutdown]
  412. public void Draw_Negative_Bounds_Horizontal_Without_New_Lines ()
  413. {
  414. // BUGBUG: This previously assumed the default height of a View was 1.
  415. var subView = new View
  416. {
  417. Id = "subView",
  418. Y = 1,
  419. Width = 7,
  420. Height = 1,
  421. Text = "subView"
  422. };
  423. var view = new View { Id = "view", Width = 20, Height = 2, Text = "01234567890123456789" };
  424. view.Add (subView);
  425. var content = new View { Id = "content", Width = 20, Height = 20 };
  426. content.Add (view);
  427. var container = new View
  428. {
  429. Id = "container",
  430. X = 1,
  431. Y = 1,
  432. Width = 5,
  433. Height = 5
  434. };
  435. container.Add (content);
  436. Toplevel top = new ();
  437. top.Add (container);
  438. // BUGBUG: v2 - it's bogus to reference .Frame before BeginInit. And why is the clip being set anyway???
  439. void Top_LayoutComplete (object sender, LayoutEventArgs e) { Application.Driver.Clip = container.Frame; }
  440. top.LayoutComplete += Top_LayoutComplete;
  441. Application.Begin (top);
  442. TestHelpers.AssertDriverContentsWithFrameAre (
  443. """
  444. 01234
  445. subVi
  446. """,
  447. _output
  448. );
  449. content.X = -1;
  450. Application.Refresh ();
  451. TestHelpers.AssertDriverContentsWithFrameAre (
  452. """
  453. 12345
  454. ubVie
  455. """,
  456. _output
  457. );
  458. content.Y = -1;
  459. Application.Refresh ();
  460. TestHelpers.AssertDriverContentsWithFrameAre (
  461. """
  462. ubVie
  463. """,
  464. _output
  465. );
  466. content.Y = -2;
  467. Application.Refresh ();
  468. TestHelpers.AssertDriverContentsWithFrameAre ("", _output);
  469. content.X = -20;
  470. content.Y = 0;
  471. Application.Refresh ();
  472. TestHelpers.AssertDriverContentsWithFrameAre ("", _output);
  473. }
  474. [Fact]
  475. [AutoInitShutdown]
  476. public void Draw_Negative_Bounds_Vertical ()
  477. {
  478. var subView = new View
  479. {
  480. Id = "subView",
  481. X = 1,
  482. Width = 1,
  483. Height = 7,
  484. Text = "subView",
  485. TextDirection = TextDirection.TopBottom_LeftRight
  486. };
  487. var view = new View
  488. {
  489. Id = "view",
  490. Width = 2,
  491. Height = 20,
  492. Text = "01234567890123456789",
  493. TextDirection = TextDirection.TopBottom_LeftRight
  494. };
  495. view.Add (subView);
  496. var content = new View { Id = "content", Width = 20, Height = 20 };
  497. content.Add (view);
  498. var container = new View
  499. {
  500. Id = "container",
  501. X = 1,
  502. Y = 1,
  503. Width = 5,
  504. Height = 5
  505. };
  506. container.Add (content);
  507. Toplevel top = new ();
  508. top.Add (container);
  509. Application.Driver.Clip = container.Frame;
  510. Application.Begin (top);
  511. TestHelpers.AssertDriverContentsWithFrameAre (
  512. """
  513. 0s
  514. 1u
  515. 2b
  516. 3V
  517. 4i
  518. """,
  519. _output
  520. );
  521. content.X = -1;
  522. Application.Refresh ();
  523. TestHelpers.AssertDriverContentsWithFrameAre (
  524. """
  525. s
  526. u
  527. b
  528. V
  529. i
  530. """,
  531. _output
  532. );
  533. content.X = -2;
  534. Application.Refresh ();
  535. TestHelpers.AssertDriverContentsWithFrameAre (@"", _output);
  536. content.X = 0;
  537. content.Y = -1;
  538. Application.Refresh ();
  539. TestHelpers.AssertDriverContentsWithFrameAre (
  540. """
  541. 1u
  542. 2b
  543. 3V
  544. 4i
  545. 5e
  546. """,
  547. _output
  548. );
  549. content.Y = -6;
  550. Application.Refresh ();
  551. TestHelpers.AssertDriverContentsWithFrameAre (
  552. """
  553. 6w
  554. 7
  555. 8
  556. 9
  557. 0
  558. """,
  559. _output
  560. );
  561. content.Y = -19;
  562. Application.Refresh ();
  563. TestHelpers.AssertDriverContentsWithFrameAre (
  564. """
  565. 9
  566. """,
  567. _output
  568. );
  569. content.Y = -20;
  570. Application.Refresh ();
  571. TestHelpers.AssertDriverContentsWithFrameAre ("", _output);
  572. content.X = -2;
  573. content.Y = 0;
  574. Application.Refresh ();
  575. TestHelpers.AssertDriverContentsWithFrameAre ("", _output);
  576. }
  577. [Theory]
  578. [SetupFakeDriver]
  579. [InlineData ("𝔽𝕆𝕆𝔹𝔸R")]
  580. [InlineData ("a𐐀b")]
  581. public void DrawHotString_NonBmp (string expected)
  582. {
  583. var view = new View { Width = 10, Height = 1 };
  584. view.DrawHotString (expected, Attribute.Default, Attribute.Default);
  585. TestHelpers.AssertDriverContentsWithFrameAre (expected, _output);
  586. }
  587. // TODO: The tests below that use Label should use View instead.
  588. [Fact]
  589. [AutoInitShutdown]
  590. public void Non_Bmp_ConsoleWidth_ColumnWidth_Equal_Two ()
  591. {
  592. var us = "\U0001d539";
  593. var r = (Rune)0x1d539;
  594. Assert.Equal ("𝔹", us);
  595. Assert.Equal ("𝔹", r.ToString ());
  596. Assert.Equal (us, r.ToString ());
  597. Assert.Equal (1, us.GetColumns ());
  598. Assert.Equal (1, r.GetColumns ());
  599. var win = new Window { Title = us };
  600. var view = new Label { Text = r.ToString () };
  601. var tf = new TextField { Text = us, Y = 1, Width = 3 };
  602. win.Add (view, tf);
  603. Toplevel top = new ();
  604. top.Add (win);
  605. Application.Begin (top);
  606. ((FakeDriver)Application.Driver).SetBufferSize (10, 4);
  607. var expected = """
  608. ┌┤𝔹├─────┐
  609. │𝔹 │
  610. │𝔹 │
  611. └────────┘
  612. """;
  613. TestHelpers.AssertDriverContentsWithFrameAre (expected, _output);
  614. TestHelpers.AssertDriverContentsAre (expected, _output);
  615. Attribute [] expectedColors =
  616. {
  617. // 0
  618. Colors.ColorSchemes ["Base"].Normal,
  619. // 1
  620. Colors.ColorSchemes ["Base"].Focus,
  621. // 2
  622. Colors.ColorSchemes ["Base"].HotNormal
  623. };
  624. TestHelpers.AssertDriverAttributesAre (
  625. """
  626. 0010000000
  627. 0000000000
  628. 0111000000
  629. 0000000000
  630. """,
  631. Application.Driver,
  632. expectedColors
  633. );
  634. }
  635. }