LineCanvasTests.cs 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921
  1. using NStack;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Text;
  5. using Xunit;
  6. using Xunit.Abstractions;
  7. namespace Terminal.Gui.DrawingTests {
  8. public class LineCanvasTests {
  9. readonly ITestOutputHelper output;
  10. public LineCanvasTests (ITestOutputHelper output)
  11. {
  12. this.output = output;
  13. }
  14. [InlineData (0, 0, 0,
  15. 0, 0, 1, 1)]
  16. [InlineData (0, 0, 1,
  17. 0, 0, 1, 1)]
  18. [InlineData (0, 0, 2,
  19. 0, 0, 2, 1)]
  20. [InlineData (0, 0, 3,
  21. 0, 0, 3, 1)]
  22. [InlineData (0, 0, -1,
  23. 0, 0, 1, 1)]
  24. [InlineData (0, 0, -2,
  25. -1, 0, 2, 1)]
  26. [InlineData (0, 0, -3,
  27. -2, 0, 3, 1)]
  28. [Theory, SetupFakeDriver]
  29. public void Bounds_H_Line (int x, int y, int length,
  30. int expectedX, int expectedY, int expectedWidth, int expectedHeight)
  31. {
  32. var canvas = new LineCanvas ();
  33. canvas.AddLine (new Point (x, y), length, Orientation.Horizontal, LineStyle.Single);
  34. Assert.Equal (new Rect (expectedX, expectedY, expectedWidth, expectedHeight), canvas.Bounds);
  35. }
  36. [InlineData (0, 0, 0,
  37. 0, 0, 1, 1)]
  38. [InlineData (0, 0, 1,
  39. 0, 0, 1, 1)]
  40. [InlineData (0, 0, 2,
  41. 0, 0, 2, 2)]
  42. [InlineData (0, 0, 3,
  43. 0, 0, 3, 3)]
  44. [InlineData (0, 0, -1,
  45. 0, 0, 1, 1)]
  46. [InlineData (0, 0, -2,
  47. -1, -1, 2, 2)]
  48. [InlineData (0, 0, -3,
  49. -2, -2, 3, 3)]
  50. [Theory, SetupFakeDriver]
  51. public void Bounds_H_And_V_Lines_Both_Positive (int x, int y, int length,
  52. int expectedX, int expectedY, int expectedWidth, int expectedHeight)
  53. {
  54. var canvas = new LineCanvas ();
  55. canvas.AddLine (new Point (x, y), length, Orientation.Horizontal, LineStyle.Single);
  56. canvas.AddLine (new Point (x, y), length, Orientation.Vertical, LineStyle.Single);
  57. Assert.Equal (new Rect (expectedX, expectedY, expectedWidth, expectedHeight), canvas.Bounds);
  58. }
  59. [Fact, SetupFakeDriver]
  60. public void Canvas_Updates_On_Changes ()
  61. {
  62. var lc = new LineCanvas ();
  63. Assert.Equal (Rect.Empty, lc.Bounds);
  64. lc.AddLine (new Point (0, 0), 2, Orientation.Horizontal, LineStyle.Double);
  65. Assert.NotEqual (Rect.Empty, lc.Bounds);
  66. lc.Clear ();
  67. Assert.Equal (Rect.Empty, lc.Bounds);
  68. }
  69. [Fact, SetupFakeDriver]
  70. public void Bounds_Specific ()
  71. {
  72. // Draw at 1,1 within client area of View (i.e. leave a top and left margin of 1)
  73. // This proves we aren't drawing excess above
  74. int x = 1;
  75. int y = 2;
  76. int width = 3;
  77. int height = 2;
  78. var lc = new LineCanvas ();
  79. // 01230
  80. // ╔╡╞╗1
  81. // ║ ║2
  82. // Add a short horiz line for ╔╡
  83. lc.AddLine (new Point (x, y), 2, Orientation.Horizontal, LineStyle.Double);
  84. Assert.Equal (new Rect (x, y, 2, 1), lc.Bounds);
  85. //LHS line down
  86. lc.AddLine (new Point (x, y), height, Orientation.Vertical, LineStyle.Double);
  87. Assert.Equal (new Rect (x, y, 2, 2), lc.Bounds);
  88. //Vertical line before Title, results in a ╡
  89. lc.AddLine (new Point (x + 1, y), 0, Orientation.Vertical, LineStyle.Single);
  90. Assert.Equal (new Rect (x, y, 2, 2), lc.Bounds);
  91. //Vertical line after Title, results in a ╞
  92. lc.AddLine (new Point (x + 2, y), 0, Orientation.Vertical, LineStyle.Single);
  93. Assert.Equal (new Rect (x, y, 3, 2), lc.Bounds);
  94. // remainder of top line
  95. lc.AddLine (new Point (x + 2, y), width - 1, Orientation.Horizontal, LineStyle.Double);
  96. Assert.Equal (new Rect (x, y, 4, 2), lc.Bounds);
  97. //RHS line down
  98. lc.AddLine (new Point (x + width, y), height, Orientation.Vertical, LineStyle.Double);
  99. Assert.Equal (new Rect (x, y, 4, 2), lc.Bounds);
  100. TestHelpers.AssertEqual (output, @"
  101. ╔╡╞╗
  102. ║ ║",
  103. $"{Environment.NewLine}{lc}");
  104. }
  105. [Fact, SetupFakeDriver]
  106. public void Bounds_Specific_With_Ustring ()
  107. {
  108. // Draw at 1,1 within client area of View (i.e. leave a top and left margin of 1)
  109. // This proves we aren't drawing excess above
  110. int x = 1;
  111. int y = 2;
  112. int width = 3;
  113. int height = 2;
  114. var lc = new LineCanvas ();
  115. // 01230
  116. // ╔╡╞╗1
  117. // ║ ║2
  118. // Add a short horiz line for ╔╡
  119. lc.AddLine (new Point (x, y), 2, Orientation.Horizontal, LineStyle.Double);
  120. Assert.Equal (new Rect (x, y, 2, 1), lc.Bounds);
  121. //LHS line down
  122. lc.AddLine (new Point (x, y), height, Orientation.Vertical, LineStyle.Double);
  123. Assert.Equal (new Rect (x, y, 2, 2), lc.Bounds);
  124. //Vertical line before Title, results in a ╡
  125. lc.AddLine (new Point (x + 1, y), 0, Orientation.Vertical, LineStyle.Single);
  126. Assert.Equal (new Rect (x, y, 2, 2), lc.Bounds);
  127. //Vertical line after Title, results in a ╞
  128. lc.AddLine (new Point (x + 2, y), 0, Orientation.Vertical, LineStyle.Single);
  129. Assert.Equal (new Rect (x, y, 3, 2), lc.Bounds);
  130. // remainder of top line
  131. lc.AddLine (new Point (x + 2, y), width - 1, Orientation.Horizontal, LineStyle.Double);
  132. Assert.Equal (new Rect (x, y, 4, 2), lc.Bounds);
  133. //RHS line down
  134. lc.AddLine (new Point (x + width, y), height, Orientation.Vertical, LineStyle.Double);
  135. Assert.Equal (new Rect (x, y, 4, 2), lc.Bounds);
  136. TestHelpers.AssertEqual (output, @"
  137. ╔╡╞╗
  138. ║ ║",
  139. ustring.Make ($"{Environment.NewLine}{lc}"));
  140. }
  141. [Fact, SetupFakeDriver]
  142. public void ToString_Empty ()
  143. {
  144. var lc = new LineCanvas ();
  145. TestHelpers.AssertEqual (output, string.Empty, lc.ToString ());
  146. }
  147. // 012
  148. [InlineData (0, 0, "═══")]
  149. [InlineData (1, 0, "═══")]
  150. [InlineData (0, 1, "═══")]
  151. [InlineData (1, 1, "═══")]
  152. [InlineData (2, 2, "═══")]
  153. [InlineData (-1, 0, "═══")]
  154. [InlineData (0, -1, "═══")]
  155. [InlineData (-1, -1, "═══")]
  156. [InlineData (-2, -2, "═══")]
  157. [Theory, SetupFakeDriver]
  158. public void ToString_Positive_Horizontal_1Line_Offset (int x, int y, string expected)
  159. {
  160. var lc = new LineCanvas ();
  161. lc.AddLine (new Point (x, y), 3, Orientation.Horizontal, LineStyle.Double);
  162. TestHelpers.AssertEqual (output, expected, $"{lc}");
  163. }
  164. [InlineData (0, 0, 0, 0, "═══")]
  165. [InlineData (1, 0, 1, 0, "═══")]
  166. [InlineData (-1, 0, -1, 0, "═══")]
  167. [InlineData (0, 0, 1, 0, "════")]
  168. [InlineData (1, 0, 3, 0, "═════")]
  169. [InlineData (1, 0, 4, 0, "══════")]
  170. [InlineData (1, 0, 5, 0, "═══ ═══")]
  171. [InlineData (0, 0, 0, 1, $"═══\r\n═══")]
  172. [InlineData (0, 0, 1, 1, "═══ \r\n ═══")]
  173. [InlineData (0, 0, 2, 1, "═══ \r\n ═══")]
  174. [InlineData (1, 0, 0, 1, " ═══\r\n═══ ")]
  175. [InlineData (0, 1, 0, 1, "═══")]
  176. [InlineData (1, 1, 0, 1, "════")]
  177. [InlineData (2, 2, 0, 1, "═══ \r\n ═══")]
  178. [Theory, SetupFakeDriver]
  179. public void ToString_Positive_Horizontal_2Line_Offset (int x1, int y1, int x2, int y2, string expected)
  180. {
  181. var lc = new LineCanvas ();
  182. lc.AddLine (new Point (x1, y1), 3, Orientation.Horizontal, LineStyle.Double);
  183. lc.AddLine (new Point (x2, y2), 3, Orientation.Horizontal, LineStyle.Double);
  184. TestHelpers.AssertEqual (output, expected, $"{lc}");
  185. }
  186. [InlineData (0, 0, Orientation.Horizontal, "─")]
  187. [InlineData (1, 0, Orientation.Horizontal, "─")]
  188. [InlineData (0, 1, Orientation.Horizontal, "─")]
  189. [InlineData (0, 0, Orientation.Vertical, "│")]
  190. [InlineData (1, 0, Orientation.Vertical, "│")]
  191. [InlineData (0, 1, Orientation.Vertical, "│")]
  192. [Theory, SetupFakeDriver]
  193. public void Length_Zero_Alone_Is_Line (int x, int y, Orientation orientation, string expected)
  194. {
  195. var lc = new LineCanvas ();
  196. // Add a line at 0, 0 that's has length of 0
  197. lc.AddLine (new Point (0, 0), 0, orientation, LineStyle.Single);
  198. TestHelpers.AssertEqual (output, expected, $"{lc}");
  199. }
  200. [InlineData (0, 0, Orientation.Horizontal, "┼")]
  201. [InlineData (1, 0, Orientation.Horizontal, "┼")]
  202. [InlineData (0, 1, Orientation.Horizontal, "┼")]
  203. [InlineData (0, 0, Orientation.Vertical, "┼")]
  204. [InlineData (1, 0, Orientation.Vertical, "┼")]
  205. [InlineData (0, 1, Orientation.Vertical, "┼")]
  206. [Theory, SetupFakeDriver]
  207. public void Length_Zero_Cross_Is_Cross (int x, int y, Orientation orientation, string expected)
  208. {
  209. var lc = new LineCanvas ();
  210. // Add point at opposite orientation
  211. lc.AddLine (new Point (0, 0), 0, orientation == Orientation.Horizontal ? Orientation.Vertical : Orientation.Horizontal, LineStyle.Single);
  212. // Add a line at 0, 0 that's has length of 0
  213. lc.AddLine (new Point (0, 0), 0, orientation, LineStyle.Single);
  214. TestHelpers.AssertEqual (output, expected, $"{lc}");
  215. }
  216. [InlineData (0, 0, Orientation.Horizontal, "╥")]
  217. [InlineData (1, 0, Orientation.Horizontal, "╥")]
  218. [InlineData (0, 1, Orientation.Horizontal, "╥")]
  219. [InlineData (0, 0, Orientation.Vertical, "╞")]
  220. [InlineData (1, 0, Orientation.Vertical, "╞")]
  221. [InlineData (0, 1, Orientation.Vertical, "╞")]
  222. [Theory, SetupFakeDriver]
  223. public void Length_Zero_NextTo_Opposite_Is_T (int x, int y, Orientation orientation, string expected)
  224. {
  225. var lc = new LineCanvas ();
  226. // Add line with length of 1 in opposite orientation starting at same location
  227. if (orientation == Orientation.Horizontal) {
  228. lc.AddLine (new Point (0, 0), 1, Orientation.Vertical, LineStyle.Double);
  229. } else {
  230. lc.AddLine (new Point (0, 0), 1, Orientation.Horizontal, LineStyle.Double);
  231. }
  232. // Add a line at 0, 0 that's has length of 0
  233. lc.AddLine (new Point (0, 0), 0, orientation, LineStyle.Single);
  234. TestHelpers.AssertEqual (output, expected, $"{lc}");
  235. }
  236. [InlineData (0, 0, Orientation.Horizontal, "─")]
  237. [InlineData (1, 0, Orientation.Horizontal, "─")]
  238. [InlineData (0, 1, Orientation.Horizontal, "─")]
  239. [InlineData (-1, 0, Orientation.Horizontal, "─")]
  240. [InlineData (0, -1, Orientation.Horizontal, "─")]
  241. [InlineData (-1, -1, Orientation.Horizontal, "─")]
  242. [InlineData (0, 0, Orientation.Vertical, "│")]
  243. [InlineData (1, 0, Orientation.Vertical, "│")]
  244. [InlineData (0, 1, Orientation.Vertical, "│")]
  245. [InlineData (0, -1, Orientation.Vertical, "│")]
  246. [InlineData (-1, 0, Orientation.Vertical, "│")]
  247. [InlineData (-1, -1, Orientation.Vertical, "│")]
  248. [Theory, SetupFakeDriver]
  249. public void Length_0_Is_1_Long (int x, int y, Orientation orientation, string expected)
  250. {
  251. var canvas = new LineCanvas ();
  252. // Add a line at 5, 5 that's has length of 1
  253. canvas.AddLine (new Point (x, y), 1, orientation, LineStyle.Single);
  254. TestHelpers.AssertEqual (output, $"{expected}", $"{canvas}");
  255. }
  256. // X is offset by 2
  257. [InlineData (0, 0, 1, Orientation.Horizontal, "─")]
  258. [InlineData (1, 0, 1, Orientation.Horizontal, "─")]
  259. [InlineData (0, 1, 1, Orientation.Horizontal, "─")]
  260. [InlineData (0, 0, 1, Orientation.Vertical, "│")]
  261. [InlineData (1, 0, 1, Orientation.Vertical, "│")]
  262. [InlineData (0, 1, 1, Orientation.Vertical, "│")]
  263. [InlineData (-1, 0, 1, Orientation.Horizontal, "─")]
  264. [InlineData (0, -1, 1, Orientation.Horizontal, "─")]
  265. [InlineData (-1, 0, 1, Orientation.Vertical, "│")]
  266. [InlineData (0, -1, 1, Orientation.Vertical, "│")]
  267. [InlineData (0, 0, -1, Orientation.Horizontal, "─")]
  268. [InlineData (1, 0, -1, Orientation.Horizontal, "─")]
  269. [InlineData (0, 1, -1, Orientation.Horizontal, "─")]
  270. [InlineData (0, 0, -1, Orientation.Vertical, "│")]
  271. [InlineData (1, 0, -1, Orientation.Vertical, "│")]
  272. [InlineData (0, 1, -1, Orientation.Vertical, "│")]
  273. [InlineData (-1, 0, -1, Orientation.Horizontal, "─")]
  274. [InlineData (0, -1, -1, Orientation.Horizontal, "─")]
  275. [InlineData (-1, 0, -1, Orientation.Vertical, "│")]
  276. [InlineData (0, -1, -1, Orientation.Vertical, "│")]
  277. [InlineData (0, 0, 2, Orientation.Horizontal, "──")]
  278. [InlineData (1, 0, 2, Orientation.Horizontal, "──")]
  279. [InlineData (0, 1, 2, Orientation.Horizontal, "──")]
  280. [InlineData (1, 1, 2, Orientation.Horizontal, "──")]
  281. [InlineData (0, 0, 2, Orientation.Vertical, "│\r\n│")]
  282. [InlineData (1, 0, 2, Orientation.Vertical, "│\r\n│")]
  283. [InlineData (0, 1, 2, Orientation.Vertical, "│\r\n│")]
  284. [InlineData (1, 1, 2, Orientation.Vertical, "│\r\n│")]
  285. [InlineData (-1, 0, 2, Orientation.Horizontal, "──")]
  286. [InlineData (0, -1, 2, Orientation.Horizontal, "──")]
  287. [InlineData (-1, 0, 2, Orientation.Vertical, "│\r\n│")]
  288. [InlineData (0, -1, 2, Orientation.Vertical, "│\r\n│")]
  289. [InlineData (-1, -1, 2, Orientation.Vertical, "│\r\n│")]
  290. [InlineData (0, 0, -2, Orientation.Horizontal, "──")]
  291. [InlineData (1, 0, -2, Orientation.Horizontal, "──")]
  292. [InlineData (0, 1, -2, Orientation.Horizontal, "──")]
  293. [InlineData (0, 0, -2, Orientation.Vertical, "│\r\n│")]
  294. [InlineData (1, 0, -2, Orientation.Vertical, "│\r\n│")]
  295. [InlineData (0, 1, -2, Orientation.Vertical, "│\r\n│")]
  296. [InlineData (1, 1, -2, Orientation.Vertical, "│\r\n│")]
  297. [InlineData (-1, 0, -2, Orientation.Horizontal, "──")]
  298. [InlineData (0, -1, -2, Orientation.Horizontal, "──")]
  299. [InlineData (-1, 0, -2, Orientation.Vertical, "│\r\n│")]
  300. [InlineData (0, -1, -2, Orientation.Vertical, "│\r\n│")]
  301. [InlineData (-1, -1, -2, Orientation.Vertical, "│\r\n│")]
  302. [Theory, SetupFakeDriver]
  303. public void Length_n_Is_n_Long (int x, int y, int length, Orientation orientation, string expected)
  304. {
  305. var canvas = new LineCanvas ();
  306. canvas.AddLine (new Point (x, y), length, orientation, LineStyle.Single);
  307. var result = canvas.ToString ();
  308. TestHelpers.AssertEqual (output, expected, result);
  309. }
  310. [Fact, SetupFakeDriver]
  311. public void Length_Negative ()
  312. {
  313. var offset = new Point (5, 5);
  314. var canvas = new LineCanvas ();
  315. canvas.AddLine (offset, -3, Orientation.Horizontal, LineStyle.Single);
  316. string looksLike = "───";
  317. Assert.Equal (looksLike, $"{canvas}");
  318. }
  319. [Fact, SetupFakeDriver]
  320. public void Zero_Length_Intersections ()
  321. {
  322. // Draw at 1,2 within client area of View (i.e. leave a top and left margin of 1)
  323. // This proves we aren't drawing excess above
  324. int x = 1;
  325. int y = 2;
  326. int width = 5;
  327. int height = 2;
  328. var lc = new LineCanvas ();
  329. // ╔╡╞═════╗
  330. // Add a short horiz line for ╔╡
  331. lc.AddLine (new Point (x, y), 2, Orientation.Horizontal, LineStyle.Double);
  332. //LHS line down
  333. lc.AddLine (new Point (x, y), height, Orientation.Vertical, LineStyle.Double);
  334. //Vertical line before Title, results in a ╡
  335. lc.AddLine (new Point (x + 1, y), 0, Orientation.Vertical, LineStyle.Single);
  336. //Vertical line after Title, results in a ╞
  337. lc.AddLine (new Point (x + 2, y), 0, Orientation.Vertical, LineStyle.Single);
  338. // remainder of top line
  339. lc.AddLine (new Point (x + 2, y), width - 1, Orientation.Horizontal, LineStyle.Double);
  340. //RHS line down
  341. lc.AddLine (new Point (x + width, y), height, Orientation.Vertical, LineStyle.Double);
  342. string looksLike = @"
  343. ╔╡╞══╗
  344. ║ ║";
  345. TestHelpers.AssertEqual (output, looksLike, $"{Environment.NewLine}{lc}");
  346. }
  347. [InlineData (LineStyle.Single)]
  348. [InlineData (LineStyle.Rounded)]
  349. [Theory, AutoInitShutdown]
  350. public void View_Draws_Horizontal (LineStyle style)
  351. {
  352. var v = GetCanvas (out var canvas);
  353. canvas.AddLine (new Point (0, 0), 2, Orientation.Horizontal, style);
  354. v.Redraw (v.Bounds);
  355. string looksLike =
  356. @"
  357. ──";
  358. TestHelpers.AssertDriverContentsAre (looksLike, output);
  359. }
  360. [Fact, AutoInitShutdown]
  361. public void View_Draws_Horizontal_Double ()
  362. {
  363. var v = GetCanvas (out var canvas);
  364. canvas.AddLine (new Point (0, 0), 2, Orientation.Horizontal, LineStyle.Double);
  365. v.Redraw (v.Bounds);
  366. string looksLike =
  367. @"
  368. ══";
  369. TestHelpers.AssertDriverContentsAre (looksLike, output);
  370. }
  371. [InlineData (LineStyle.Single)]
  372. [InlineData (LineStyle.Rounded)]
  373. [Theory, AutoInitShutdown]
  374. public void View_Draws_Vertical (LineStyle style)
  375. {
  376. var v = GetCanvas (out var canvas);
  377. canvas.AddLine (new Point (0, 0), 2, Orientation.Vertical, style);
  378. v.Redraw (v.Bounds);
  379. string looksLike =
  380. @"
  381. │";
  382. TestHelpers.AssertDriverContentsAre (looksLike, output);
  383. }
  384. [Fact, AutoInitShutdown]
  385. public void View_Draws_Vertical_Double ()
  386. {
  387. var v = GetCanvas (out var canvas);
  388. canvas.AddLine (new Point (0, 0), 2, Orientation.Vertical, LineStyle.Double);
  389. v.Redraw (v.Bounds);
  390. string looksLike =
  391. @"
  392. ║";
  393. TestHelpers.AssertDriverContentsAre (looksLike, output);
  394. }
  395. /// <summary>
  396. /// This test demonstrates that corners are only drawn when lines overlap.
  397. /// Not when they terminate adjacent to one another.
  398. /// </summary>
  399. [Fact, AutoInitShutdown]
  400. public void View_Draws_Corner_NoOverlap ()
  401. {
  402. var v = GetCanvas (out var canvas);
  403. canvas.AddLine (new Point (0, 0), 2, Orientation.Horizontal, LineStyle.Single);
  404. canvas.AddLine (new Point (0, 1), 2, Orientation.Vertical, LineStyle.Single);
  405. v.Redraw (v.Bounds);
  406. string looksLike =
  407. @"
  408. ──
  409. │";
  410. TestHelpers.AssertDriverContentsAre (looksLike, output);
  411. }
  412. /// <summary>
  413. /// This test demonstrates how to correctly trigger a corner. By
  414. /// overlapping the lines in the same cell
  415. /// </summary>
  416. [Fact, AutoInitShutdown]
  417. public void View_Draws_Corner_Correct ()
  418. {
  419. var v = GetCanvas (out var canvas);
  420. canvas.AddLine (new Point (0, 0), 2, Orientation.Horizontal, LineStyle.Single);
  421. canvas.AddLine (new Point (0, 0), 2, Orientation.Vertical, LineStyle.Single);
  422. v.Redraw (v.Bounds);
  423. string looksLike =
  424. @"
  425. ┌─
  426. │";
  427. TestHelpers.AssertDriverContentsAre (looksLike, output);
  428. }
  429. [Fact, SetupFakeDriver]
  430. public void Top_With_1Down ()
  431. {
  432. var canvas = new LineCanvas ();
  433. // Top ─
  434. canvas.AddLine (new Point (0, 0), 1, Orientation.Horizontal, LineStyle.Single);
  435. // Bottom ─
  436. canvas.AddLine (new Point (1, 1), -1, Orientation.Horizontal, LineStyle.Single);
  437. //// Right down
  438. //canvas.AddLine (new Point (9, 0), 3, Orientation.Vertical, LineStyle.Single);
  439. //// Bottom
  440. //canvas.AddLine (new Point (9, 3), -10, Orientation.Horizontal, LineStyle.Single);
  441. //// Left Up
  442. //canvas.AddLine (new Point (0, 3), -3, Orientation.Vertical, LineStyle.Single);
  443. Assert.Equal (new Rect (0, 0, 2, 2), canvas.Bounds);
  444. var map = canvas.GetMap ();
  445. Assert.Equal (2, map.Count);
  446. TestHelpers.AssertEqual (output, @"
  447. ─",
  448. $"{Environment.NewLine}{canvas}");
  449. }
  450. [Fact, SetupFakeDriver]
  451. public void Window ()
  452. {
  453. var canvas = new LineCanvas ();
  454. // Frame
  455. canvas.AddLine (new Point (0, 0), 10, Orientation.Horizontal, LineStyle.Single);
  456. canvas.AddLine (new Point (9, 0), 5, Orientation.Vertical, LineStyle.Single);
  457. canvas.AddLine (new Point (9, 4), -10, Orientation.Horizontal, LineStyle.Single);
  458. canvas.AddLine (new Point (0, 4), -5, Orientation.Vertical, LineStyle.Single);
  459. // Cross
  460. canvas.AddLine (new Point (5, 0), 5, Orientation.Vertical, LineStyle.Single);
  461. canvas.AddLine (new Point (0, 2), 10, Orientation.Horizontal, LineStyle.Single);
  462. string looksLike =
  463. @"
  464. ┌────┬───┐
  465. │ │ │
  466. ├────┼───┤
  467. │ │ │
  468. └────┴───┘";
  469. TestHelpers.AssertEqual (output, looksLike, $"{Environment.NewLine}{canvas}");
  470. }
  471. /// <summary>
  472. /// Demonstrates when <see cref="LineStyle.Rounded"/> corners are used. Notice how
  473. /// not all lines declare rounded. If there are 1+ lines intersecting and a corner is
  474. /// to be used then if any of them are rounded a rounded corner is used.
  475. /// </summary>
  476. [Fact, AutoInitShutdown]
  477. public void View_Draws_Window_Rounded ()
  478. {
  479. var v = GetCanvas (out var canvas);
  480. // outer box
  481. canvas.AddLine (new Point (0, 0), 10, Orientation.Horizontal, LineStyle.Rounded);
  482. // BorderStyle.Single is ignored because corner overlaps with the above line which is Rounded
  483. // this results in a rounded corner being used.
  484. canvas.AddLine (new Point (9, 0), 5, Orientation.Vertical, LineStyle.Single);
  485. canvas.AddLine (new Point (9, 4), -10, Orientation.Horizontal, LineStyle.Rounded);
  486. canvas.AddLine (new Point (0, 4), -5, Orientation.Vertical, LineStyle.Single);
  487. // These lines say rounded but they will result in the T sections which are never rounded.
  488. canvas.AddLine (new Point (5, 0), 5, Orientation.Vertical, LineStyle.Rounded);
  489. canvas.AddLine (new Point (0, 2), 10, Orientation.Horizontal, LineStyle.Rounded);
  490. v.Redraw (v.Bounds);
  491. string looksLike =
  492. @"
  493. ╭────┬───╮
  494. │ │ │
  495. ├────┼───┤
  496. │ │ │
  497. ╰────┴───╯";
  498. TestHelpers.AssertDriverContentsAre (looksLike, output);
  499. }
  500. [Fact, AutoInitShutdown]
  501. public void View_Draws_Window_Double ()
  502. {
  503. var v = GetCanvas (out var canvas);
  504. // outer box
  505. canvas.AddLine (new Point (0, 0), 10, Orientation.Horizontal, LineStyle.Double);
  506. canvas.AddLine (new Point (9, 0), 5, Orientation.Vertical, LineStyle.Double);
  507. canvas.AddLine (new Point (9, 4), -10, Orientation.Horizontal, LineStyle.Double);
  508. canvas.AddLine (new Point (0, 4), -5, Orientation.Vertical, LineStyle.Double);
  509. canvas.AddLine (new Point (5, 0), 5, Orientation.Vertical, LineStyle.Double);
  510. canvas.AddLine (new Point (0, 2), 10, Orientation.Horizontal, LineStyle.Double);
  511. v.Redraw (v.Bounds);
  512. string looksLike =
  513. @"
  514. ╔════╦═══╗
  515. ║ ║ ║
  516. ╠════╬═══╣
  517. ║ ║ ║
  518. ╚════╩═══╝";
  519. TestHelpers.AssertDriverContentsAre (looksLike, output);
  520. }
  521. [Theory, AutoInitShutdown]
  522. [InlineData (LineStyle.Single)]
  523. [InlineData (LineStyle.Rounded)]
  524. public void View_Draws_Window_DoubleTop_SingleSides (LineStyle thinStyle)
  525. {
  526. var v = GetCanvas (out var canvas);
  527. // outer box
  528. canvas.AddLine (new Point (0, 0), 10, Orientation.Horizontal, LineStyle.Double);
  529. canvas.AddLine (new Point (9, 0), 5, Orientation.Vertical, thinStyle);
  530. canvas.AddLine (new Point (9, 4), -10, Orientation.Horizontal, LineStyle.Double);
  531. canvas.AddLine (new Point (0, 4), -5, Orientation.Vertical, thinStyle);
  532. canvas.AddLine (new Point (5, 0), 5, Orientation.Vertical, thinStyle);
  533. canvas.AddLine (new Point (0, 2), 10, Orientation.Horizontal, LineStyle.Double);
  534. v.Redraw (v.Bounds);
  535. string looksLike =
  536. @"
  537. ╒════╤═══╕
  538. │ │ │
  539. ╞════╪═══╡
  540. │ │ │
  541. ╘════╧═══╛
  542. ";
  543. TestHelpers.AssertDriverContentsAre (looksLike, output);
  544. }
  545. [Theory, AutoInitShutdown]
  546. [InlineData (LineStyle.Single)]
  547. [InlineData (LineStyle.Rounded)]
  548. public void View_Draws_Window_SingleTop_DoubleSides (LineStyle thinStyle)
  549. {
  550. var v = GetCanvas (out var canvas);
  551. // outer box
  552. canvas.AddLine (new Point (0, 0), 10, Orientation.Horizontal, thinStyle);
  553. canvas.AddLine (new Point (9, 0), 5, Orientation.Vertical, LineStyle.Double);
  554. canvas.AddLine (new Point (9, 4), -10, Orientation.Horizontal, thinStyle);
  555. canvas.AddLine (new Point (0, 4), -5, Orientation.Vertical, LineStyle.Double);
  556. canvas.AddLine (new Point (5, 0), 5, Orientation.Vertical, LineStyle.Double);
  557. canvas.AddLine (new Point (0, 2), 10, Orientation.Horizontal, thinStyle);
  558. v.Redraw (v.Bounds);
  559. string looksLike =
  560. @"
  561. ╓────╥───╖
  562. ║ ║ ║
  563. ╟────╫───╢
  564. ║ ║ ║
  565. ╙────╨───╜
  566. ";
  567. TestHelpers.AssertDriverContentsAre (looksLike, output);
  568. }
  569. [Fact, SetupFakeDriver]
  570. public void Top_Left_From_TopRigth_TopDown ()
  571. {
  572. var canvas = new LineCanvas ();
  573. // Upper box
  574. canvas.AddLine (new Point (0, 0), 2, Orientation.Horizontal, LineStyle.Single);
  575. canvas.AddLine (new Point (0, 0), 2, Orientation.Vertical, LineStyle.Single);
  576. string looksLike =
  577. @"
  578. ┌─
  579. │ ";
  580. TestHelpers.AssertEqual (output, looksLike, $"{Environment.NewLine}{canvas}");
  581. }
  582. [Fact, SetupFakeDriver]
  583. public void Top_Left_From_TopRigth_LeftUp ()
  584. {
  585. var canvas = new LineCanvas ();
  586. // Upper box
  587. canvas.AddLine (new Point (0, 0), 2, Orientation.Horizontal, LineStyle.Single);
  588. canvas.AddLine (new Point (0, 1), -2, Orientation.Vertical, LineStyle.Single);
  589. string looksLike =
  590. @"
  591. ┌─
  592. │ ";
  593. TestHelpers.AssertEqual (output, looksLike, $"{Environment.NewLine}{canvas}");
  594. }
  595. // [Fact, SetupFakeDriver]
  596. // public void LeaveMargin_Top1_Left1 ()
  597. // {
  598. // var canvas = new LineCanvas ();
  599. // // Upper box
  600. // canvas.AddLine (new Point (0, 0), 9, Orientation.Horizontal, LineStyle.Single);
  601. // canvas.AddLine (new Point (8, 0), 3, Orientation.Vertical, LineStyle.Single);
  602. // canvas.AddLine (new Point (8, 3), -9, Orientation.Horizontal, LineStyle.Single);
  603. // canvas.AddLine (new Point (0, 2), -3, Orientation.Vertical, LineStyle.Single);
  604. // // Lower Box
  605. // canvas.AddLine (new Point (5, 0), 2, Orientation.Vertical, LineStyle.Single);
  606. // canvas.AddLine (new Point (0, 2), 9, Orientation.Horizontal, LineStyle.Single);
  607. // string looksLike =
  608. //@"
  609. //┌────┬──┐
  610. //│ │ │
  611. //├────┼──┤
  612. //└────┴──┘
  613. //";
  614. // Assert.Equal (looksLike, $"{Environment.NewLine}{canvas}");
  615. // }
  616. [InlineData (0, 0, 0, Orientation.Horizontal, LineStyle.Double, "═")]
  617. [InlineData (0, 0, 0, Orientation.Vertical, LineStyle.Double, "║")]
  618. [InlineData (0, 0, 0, Orientation.Horizontal, LineStyle.Single, "─")]
  619. [InlineData (0, 0, 0, Orientation.Vertical, LineStyle.Single, "│")]
  620. [InlineData (0, 0, 1, Orientation.Horizontal, LineStyle.Double, "═")]
  621. [InlineData (0, 0, 1, Orientation.Vertical, LineStyle.Double, "║")]
  622. [InlineData (0, 0, 1, Orientation.Horizontal, LineStyle.Single, "─")]
  623. [InlineData (0, 0, 1, Orientation.Vertical, LineStyle.Single, "│")]
  624. [InlineData (0, 0, 2, Orientation.Horizontal, LineStyle.Double, "══")]
  625. [InlineData (0, 0, 2, Orientation.Vertical, LineStyle.Double, "║\n║")]
  626. [InlineData (0, 0, 2, Orientation.Horizontal, LineStyle.Single, "──")]
  627. [InlineData (0, 0, 2, Orientation.Vertical, LineStyle.Single, "│\n│")]
  628. [AutoInitShutdown, Theory]
  629. public void View_Draws_1LineTests (
  630. int x1, int y1, int length, Orientation o1, LineStyle s1,
  631. string expected
  632. )
  633. {
  634. var v = GetCanvas (out var lc);
  635. v.Width = 10;
  636. v.Height = 10;
  637. v.Bounds = new Rect (0, 0, 10, 10);
  638. lc.AddLine (new Point (x1, y1), length, o1, s1);
  639. v.Redraw (v.Bounds);
  640. TestHelpers.AssertDriverContentsAre (expected, output);
  641. }
  642. [Theory, AutoInitShutdown]
  643. // Horizontal lines with a vertical zero-length
  644. [InlineData (
  645. 0, 0, 1, Orientation.Horizontal, LineStyle.Double,
  646. 0, 0, 0, Orientation.Vertical, LineStyle.Single, "╞"
  647. )]
  648. [InlineData (
  649. 0, 0, -1, Orientation.Horizontal, LineStyle.Double,
  650. 0, 0, 0, Orientation.Vertical, LineStyle.Single, "╡"
  651. )]
  652. [InlineData (
  653. 0, 0, 1, Orientation.Horizontal, LineStyle.Single,
  654. 0, 0, 0, Orientation.Vertical, LineStyle.Double, "╟"
  655. )]
  656. [InlineData (
  657. 0, 0, -1, Orientation.Horizontal, LineStyle.Single,
  658. 0, 0, 0, Orientation.Vertical, LineStyle.Double, "╢"
  659. )]
  660. [InlineData (
  661. 0, 0, 1, Orientation.Horizontal, LineStyle.Single,
  662. 0, 0, 0, Orientation.Vertical, LineStyle.Single, "├"
  663. )]
  664. [InlineData (
  665. 0, 0, -1, Orientation.Horizontal, LineStyle.Single,
  666. 0, 0, 0, Orientation.Vertical, LineStyle.Single, "┤"
  667. )]
  668. [InlineData (
  669. 0, 0, 1, Orientation.Horizontal, LineStyle.Double,
  670. 0, 0, 0, Orientation.Vertical, LineStyle.Double, "╠"
  671. )]
  672. [InlineData (
  673. 0, 0, -1, Orientation.Horizontal, LineStyle.Double,
  674. 0, 0, 0, Orientation.Vertical, LineStyle.Double, "╣"
  675. )]
  676. // Vertical lines with a horizontal zero-length
  677. [InlineData (
  678. 0, 0, 1, Orientation.Vertical, LineStyle.Double,
  679. 0, 0, 0, Orientation.Horizontal, LineStyle.Single, "╥"
  680. )]
  681. [InlineData (
  682. 0, 0, -1, Orientation.Vertical, LineStyle.Double,
  683. 0, 0, 0, Orientation.Horizontal, LineStyle.Single, "╨"
  684. )]
  685. [InlineData (
  686. 0, 0, 1, Orientation.Vertical, LineStyle.Single,
  687. 0, 0, 0, Orientation.Horizontal, LineStyle.Double, "╤"
  688. )]
  689. [InlineData (
  690. 0, 0, -1, Orientation.Vertical, LineStyle.Single,
  691. 0, 0, 0, Orientation.Horizontal, LineStyle.Double, "╧"
  692. )]
  693. [InlineData (
  694. 0, 0, 1, Orientation.Vertical, LineStyle.Single,
  695. 0, 0, 0, Orientation.Horizontal, LineStyle.Single, "┬"
  696. )]
  697. [InlineData (
  698. 0, 0, -1, Orientation.Vertical, LineStyle.Single,
  699. 0, 0, 0, Orientation.Horizontal, LineStyle.Single, "┴"
  700. )]
  701. [InlineData (
  702. 0, 0, 1, Orientation.Vertical, LineStyle.Double,
  703. 0, 0, 0, Orientation.Horizontal, LineStyle.Double, "╦"
  704. )]
  705. [InlineData (
  706. 0, 0, -1, Orientation.Vertical, LineStyle.Double,
  707. 0, 0, 0, Orientation.Horizontal, LineStyle.Double, "╩"
  708. )]
  709. // Crosses (two zero-length)
  710. [InlineData (
  711. 0, 0, 0, Orientation.Vertical, LineStyle.Double,
  712. 0, 0, 0, Orientation.Horizontal, LineStyle.Single, "╫"
  713. )]
  714. [InlineData (
  715. 0, 0, 0, Orientation.Vertical, LineStyle.Single,
  716. 0, 0, 0, Orientation.Horizontal, LineStyle.Double, "╪"
  717. )]
  718. [InlineData (
  719. 0, 0, 0, Orientation.Vertical, LineStyle.Single,
  720. 0, 0, 0, Orientation.Horizontal, LineStyle.Single, "┼"
  721. )]
  722. [InlineData (
  723. 0, 0, 0, Orientation.Vertical, LineStyle.Double,
  724. 0, 0, 0, Orientation.Horizontal, LineStyle.Double, "╬"
  725. )]
  726. public void Add_2_Lines (
  727. int x1, int y1, int len1, Orientation o1, LineStyle s1,
  728. int x2, int y2, int len2, Orientation o2, LineStyle s2,
  729. string expected
  730. )
  731. {
  732. var v = GetCanvas (out var lc);
  733. v.Width = 10;
  734. v.Height = 10;
  735. v.Bounds = new Rect (0, 0, 10, 10);
  736. lc.AddLine (new Point (x1, y1), len1, o1, s1);
  737. lc.AddLine (new Point (x2, y2), len2, o2, s2);
  738. TestHelpers.AssertEqual (output, expected, lc.ToString ());
  739. }
  740. // TODO: Remove this and make all LineCanvas tests independent of View
  741. /// <summary>
  742. /// Creates a new <see cref="View"/> into which a <see cref="LineCanvas"/> is rendered
  743. /// at <see cref="View.DrawContentComplete"/> time.
  744. /// </summary>
  745. /// <param name="canvas">The <see cref="LineCanvas"/> you can draw into.</param>
  746. /// <param name="offsetX">How far to offset drawing in X</param>
  747. /// <param name="offsetY">How far to offset drawing in Y</param>
  748. /// <returns></returns>
  749. private View GetCanvas (out LineCanvas canvas, int offsetX = 0, int offsetY = 0)
  750. {
  751. var v = new View {
  752. Width = 10,
  753. Height = 5,
  754. Bounds = new Rect (0, 0, 10, 5)
  755. };
  756. Application.Top.Add (v);
  757. Application.Begin (Application.Top);
  758. var canvasCopy = canvas = new LineCanvas ();
  759. v.DrawContentComplete += (s, e) => {
  760. v.Clear ();
  761. foreach (var p in canvasCopy.GetMap ()) {
  762. v.AddRune (
  763. offsetX + p.Key.X,
  764. offsetY + p.Key.Y,
  765. p.Value);
  766. }
  767. canvasCopy.Clear ();
  768. };
  769. return v;
  770. }
  771. }
  772. }