LineCanvasTests.cs 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511
  1. using System.Text;
  2. using UnitTests;
  3. using UnitTests.Parallelizable;
  4. using Xunit.Abstractions;
  5. namespace UnitTests_Parallelizable.DrawingTests;
  6. /// <summary>
  7. /// Pure unit tests for <see cref="LineCanvas"/> that don't require Application.Driver or View context.
  8. /// These tests focus on properties and behavior that don't depend on glyph rendering.
  9. /// Note: Tests that verify rendered output (ToString()) cannot be parallelized because LineCanvas
  10. /// depends on Application.Driver for glyph resolution and configuration. Those tests remain in UnitTests.
  11. /// </summary>
  12. public class LineCanvasTests (ITestOutputHelper output) : ParallelizableBase
  13. {
  14. #region Basic API Tests
  15. [Fact]
  16. public void Empty_Canvas_ToString_Returns_EmptyString ()
  17. {
  18. var canvas = new LineCanvas ();
  19. Assert.Equal (string.Empty, canvas.ToString ());
  20. }
  21. [Fact]
  22. public void Clear_Removes_All_Lines ()
  23. {
  24. var canvas = new LineCanvas ();
  25. canvas.AddLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single);
  26. canvas.AddLine (new (0, 0), 3, Orientation.Vertical, LineStyle.Single);
  27. canvas.Clear ();
  28. Assert.Empty (canvas.Lines);
  29. Assert.Equal (Rectangle.Empty, canvas.Bounds);
  30. Assert.Equal (string.Empty, canvas.ToString ());
  31. }
  32. [Fact]
  33. public void Lines_Property_Returns_ReadOnly_Collection ()
  34. {
  35. var canvas = new LineCanvas ();
  36. canvas.AddLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single);
  37. Assert.Single (canvas.Lines);
  38. Assert.IsAssignableFrom<IReadOnlyCollection<StraightLine>> (canvas.Lines);
  39. }
  40. [Fact]
  41. public void AddLine_Adds_Line_To_Collection ()
  42. {
  43. var canvas = new LineCanvas ();
  44. Assert.Empty (canvas.Lines);
  45. canvas.AddLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single);
  46. Assert.Single (canvas.Lines);
  47. canvas.AddLine (new (0, 0), 3, Orientation.Vertical, LineStyle.Single);
  48. Assert.Equal (2, canvas.Lines.Count);
  49. }
  50. [Fact]
  51. public void Constructor_With_Lines_Creates_Canvas_With_Lines ()
  52. {
  53. StraightLine [] lines = new []
  54. {
  55. new StraightLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single),
  56. new StraightLine (new (0, 0), 3, Orientation.Vertical, LineStyle.Single)
  57. };
  58. var canvas = new LineCanvas (lines);
  59. Assert.Equal (2, canvas.Lines.Count);
  60. }
  61. #endregion
  62. #region Bounds Tests - Tests for Bounds property
  63. [Theory]
  64. [InlineData (0, 0, 0, 0, 0, 1, 1)]
  65. [InlineData (0, 0, 1, 0, 0, 1, 1)]
  66. [InlineData (0, 0, 2, 0, 0, 2, 2)]
  67. [InlineData (0, 0, 3, 0, 0, 3, 3)]
  68. [InlineData (0, 0, -1, 0, 0, 1, 1)]
  69. [InlineData (0, 0, -2, -1, -1, 2, 2)]
  70. [InlineData (0, 0, -3, -2, -2, 3, 3)]
  71. public void Viewport_H_And_V_Lines_Both_Positive (
  72. int x,
  73. int y,
  74. int length,
  75. int expectedX,
  76. int expectedY,
  77. int expectedWidth,
  78. int expectedHeight
  79. )
  80. {
  81. var canvas = new LineCanvas ();
  82. canvas.AddLine (new (x, y), length, Orientation.Horizontal, LineStyle.Single);
  83. canvas.AddLine (new (x, y), length, Orientation.Vertical, LineStyle.Single);
  84. Assert.Equal (new (expectedX, expectedY, expectedWidth, expectedHeight), canvas.Bounds);
  85. }
  86. [Theory]
  87. [InlineData (0, 0, 0, 0, 0, 1, 1)]
  88. [InlineData (0, 0, 1, 0, 0, 1, 1)]
  89. [InlineData (0, 0, 2, 0, 0, 2, 1)]
  90. [InlineData (0, 0, 3, 0, 0, 3, 1)]
  91. [InlineData (0, 0, -1, 0, 0, 1, 1)]
  92. [InlineData (0, 0, -2, -1, 0, 2, 1)]
  93. [InlineData (0, 0, -3, -2, 0, 3, 1)]
  94. public void Viewport_H_Line (
  95. int x,
  96. int y,
  97. int length,
  98. int expectedX,
  99. int expectedY,
  100. int expectedWidth,
  101. int expectedHeight
  102. )
  103. {
  104. var canvas = new LineCanvas ();
  105. canvas.AddLine (new (x, y), length, Orientation.Horizontal, LineStyle.Single);
  106. Assert.Equal (new (expectedX, expectedY, expectedWidth, expectedHeight), canvas.Bounds);
  107. }
  108. [Fact]
  109. public void Bounds_Specific_Coordinates ()
  110. {
  111. var canvas = new LineCanvas ();
  112. canvas.AddLine (new (5, 5), 3, Orientation.Horizontal, LineStyle.Single);
  113. Assert.Equal (new (5, 5, 3, 1), canvas.Bounds);
  114. }
  115. [Fact]
  116. public void Bounds_Empty_Canvas_Returns_Empty_Rectangle ()
  117. {
  118. var canvas = new LineCanvas ();
  119. Assert.Equal (Rectangle.Empty, canvas.Bounds);
  120. }
  121. [Fact]
  122. public void Bounds_Single_Point_Zero_Length ()
  123. {
  124. var canvas = new LineCanvas ();
  125. canvas.AddLine (new (5, 5), 0, Orientation.Horizontal, LineStyle.Single);
  126. Assert.Equal (new (5, 5, 1, 1), canvas.Bounds);
  127. }
  128. [Fact]
  129. public void Bounds_Horizontal_Line ()
  130. {
  131. var canvas = new LineCanvas ();
  132. canvas.AddLine (new (2, 3), 5, Orientation.Horizontal, LineStyle.Single);
  133. Assert.Equal (new (2, 3, 5, 1), canvas.Bounds);
  134. }
  135. [Fact]
  136. public void Bounds_Vertical_Line ()
  137. {
  138. var canvas = new LineCanvas ();
  139. canvas.AddLine (new (2, 3), 5, Orientation.Vertical, LineStyle.Single);
  140. Assert.Equal (new (2, 3, 1, 5), canvas.Bounds);
  141. }
  142. [Fact]
  143. public void Bounds_Multiple_Lines_Returns_Union ()
  144. {
  145. var canvas = new LineCanvas ();
  146. canvas.AddLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single);
  147. canvas.AddLine (new (0, 0), 3, Orientation.Vertical, LineStyle.Single);
  148. Assert.Equal (new (0, 0, 5, 3), canvas.Bounds);
  149. }
  150. [Fact]
  151. public void Bounds_Negative_Length_Line ()
  152. {
  153. var canvas = new LineCanvas ();
  154. canvas.AddLine (new (5, 5), -3, Orientation.Horizontal, LineStyle.Single);
  155. // Line from (5,5) going left 3 positions: includes points 3, 4, 5 (width 3, X starts at 3)
  156. Assert.Equal (new (3, 5, 3, 1), canvas.Bounds);
  157. }
  158. [Fact]
  159. public void Bounds_Complex_Box ()
  160. {
  161. var canvas = new LineCanvas ();
  162. // top
  163. canvas.AddLine (new (0, 0), 3, Orientation.Horizontal, LineStyle.Single);
  164. // left
  165. canvas.AddLine (new (0, 0), 2, Orientation.Vertical, LineStyle.Single);
  166. // right
  167. canvas.AddLine (new (2, 0), 2, Orientation.Vertical, LineStyle.Single);
  168. // bottom
  169. canvas.AddLine (new (0, 2), 3, Orientation.Horizontal, LineStyle.Single);
  170. Assert.Equal (new (0, 0, 3, 3), canvas.Bounds);
  171. }
  172. #endregion
  173. #region Exclusion Tests
  174. [Fact]
  175. public void ClearExclusions_Clears_Exclusion_Region ()
  176. {
  177. var canvas = new LineCanvas ();
  178. canvas.AddLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single);
  179. var region = new Region (new (0, 0, 2, 1));
  180. canvas.Exclude (region);
  181. canvas.ClearExclusions ();
  182. // After clearing exclusions, GetMap should return all points
  183. Dictionary<Point, Rune> map = canvas.GetMap ();
  184. Assert.Equal (5, map.Count);
  185. }
  186. [Fact]
  187. public void Exclude_Removes_Points_From_Map ()
  188. {
  189. var canvas = new LineCanvas ();
  190. canvas.AddLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single);
  191. var region = new Region (new (0, 0, 2, 1));
  192. canvas.Exclude (region);
  193. Dictionary<Point, Rune> map = canvas.GetMap ();
  194. // Should have 5 - 2 = 3 points (excluding the first 2)
  195. Assert.Equal (3, map.Count);
  196. }
  197. #endregion
  198. #region Fill Property Tests
  199. [Fact]
  200. public void Fill_Property_Can_Be_Set ()
  201. {
  202. var foregroundFill = new SolidFill (new (255, 0));
  203. var backgroundFill = new SolidFill (new (0, 0));
  204. var fillPair = new FillPair (foregroundFill, backgroundFill);
  205. var canvas = new LineCanvas { Fill = fillPair };
  206. Assert.Equal (fillPair, canvas.Fill);
  207. }
  208. [Fact]
  209. public void Fill_Property_Defaults_To_Null ()
  210. {
  211. var canvas = new LineCanvas ();
  212. Assert.Null (canvas.Fill);
  213. }
  214. #endregion
  215. [Theory]
  216. // Horizontal lines with a vertical zero-length
  217. [InlineData (
  218. 0,
  219. 0,
  220. 1,
  221. Orientation.Horizontal,
  222. LineStyle.Double,
  223. 0,
  224. 0,
  225. 0,
  226. Orientation.Vertical,
  227. LineStyle.Single,
  228. "╞"
  229. )]
  230. [InlineData (
  231. 0,
  232. 0,
  233. -1,
  234. Orientation.Horizontal,
  235. LineStyle.Double,
  236. 0,
  237. 0,
  238. 0,
  239. Orientation.Vertical,
  240. LineStyle.Single,
  241. "╡"
  242. )]
  243. [InlineData (
  244. 0,
  245. 0,
  246. 1,
  247. Orientation.Horizontal,
  248. LineStyle.Single,
  249. 0,
  250. 0,
  251. 0,
  252. Orientation.Vertical,
  253. LineStyle.Double,
  254. "╟"
  255. )]
  256. [InlineData (
  257. 0,
  258. 0,
  259. -1,
  260. Orientation.Horizontal,
  261. LineStyle.Single,
  262. 0,
  263. 0,
  264. 0,
  265. Orientation.Vertical,
  266. LineStyle.Double,
  267. "╢"
  268. )]
  269. [InlineData (
  270. 0,
  271. 0,
  272. 1,
  273. Orientation.Horizontal,
  274. LineStyle.Single,
  275. 0,
  276. 0,
  277. 0,
  278. Orientation.Vertical,
  279. LineStyle.Single,
  280. "├"
  281. )]
  282. [InlineData (
  283. 0,
  284. 0,
  285. -1,
  286. Orientation.Horizontal,
  287. LineStyle.Single,
  288. 0,
  289. 0,
  290. 0,
  291. Orientation.Vertical,
  292. LineStyle.Single,
  293. "┤"
  294. )]
  295. [InlineData (
  296. 0,
  297. 0,
  298. 1,
  299. Orientation.Horizontal,
  300. LineStyle.Double,
  301. 0,
  302. 0,
  303. 0,
  304. Orientation.Vertical,
  305. LineStyle.Double,
  306. "╠"
  307. )]
  308. [InlineData (
  309. 0,
  310. 0,
  311. -1,
  312. Orientation.Horizontal,
  313. LineStyle.Double,
  314. 0,
  315. 0,
  316. 0,
  317. Orientation.Vertical,
  318. LineStyle.Double,
  319. "╣"
  320. )]
  321. // Vertical lines with a horizontal zero-length
  322. [InlineData (
  323. 0,
  324. 0,
  325. 1,
  326. Orientation.Vertical,
  327. LineStyle.Double,
  328. 0,
  329. 0,
  330. 0,
  331. Orientation.Horizontal,
  332. LineStyle.Single,
  333. "╥"
  334. )]
  335. [InlineData (
  336. 0,
  337. 0,
  338. -1,
  339. Orientation.Vertical,
  340. LineStyle.Double,
  341. 0,
  342. 0,
  343. 0,
  344. Orientation.Horizontal,
  345. LineStyle.Single,
  346. "╨"
  347. )]
  348. [InlineData (
  349. 0,
  350. 0,
  351. 1,
  352. Orientation.Vertical,
  353. LineStyle.Single,
  354. 0,
  355. 0,
  356. 0,
  357. Orientation.Horizontal,
  358. LineStyle.Double,
  359. "╤"
  360. )]
  361. [InlineData (
  362. 0,
  363. 0,
  364. -1,
  365. Orientation.Vertical,
  366. LineStyle.Single,
  367. 0,
  368. 0,
  369. 0,
  370. Orientation.Horizontal,
  371. LineStyle.Double,
  372. "╧"
  373. )]
  374. [InlineData (
  375. 0,
  376. 0,
  377. 1,
  378. Orientation.Vertical,
  379. LineStyle.Single,
  380. 0,
  381. 0,
  382. 0,
  383. Orientation.Horizontal,
  384. LineStyle.Single,
  385. "┬"
  386. )]
  387. [InlineData (
  388. 0,
  389. 0,
  390. -1,
  391. Orientation.Vertical,
  392. LineStyle.Single,
  393. 0,
  394. 0,
  395. 0,
  396. Orientation.Horizontal,
  397. LineStyle.Single,
  398. "┴"
  399. )]
  400. [InlineData (
  401. 0,
  402. 0,
  403. 1,
  404. Orientation.Vertical,
  405. LineStyle.Double,
  406. 0,
  407. 0,
  408. 0,
  409. Orientation.Horizontal,
  410. LineStyle.Double,
  411. "╦"
  412. )]
  413. [InlineData (
  414. 0,
  415. 0,
  416. -1,
  417. Orientation.Vertical,
  418. LineStyle.Double,
  419. 0,
  420. 0,
  421. 0,
  422. Orientation.Horizontal,
  423. LineStyle.Double,
  424. "╩"
  425. )]
  426. // Crosses (two zero-length)
  427. [InlineData (
  428. 0,
  429. 0,
  430. 0,
  431. Orientation.Vertical,
  432. LineStyle.Double,
  433. 0,
  434. 0,
  435. 0,
  436. Orientation.Horizontal,
  437. LineStyle.Single,
  438. "╫"
  439. )]
  440. [InlineData (
  441. 0,
  442. 0,
  443. 0,
  444. Orientation.Vertical,
  445. LineStyle.Single,
  446. 0,
  447. 0,
  448. 0,
  449. Orientation.Horizontal,
  450. LineStyle.Double,
  451. "╪"
  452. )]
  453. [InlineData (
  454. 0,
  455. 0,
  456. 0,
  457. Orientation.Vertical,
  458. LineStyle.Single,
  459. 0,
  460. 0,
  461. 0,
  462. Orientation.Horizontal,
  463. LineStyle.Single,
  464. "┼"
  465. )]
  466. [InlineData (
  467. 0,
  468. 0,
  469. 0,
  470. Orientation.Vertical,
  471. LineStyle.Double,
  472. 0,
  473. 0,
  474. 0,
  475. Orientation.Horizontal,
  476. LineStyle.Double,
  477. "╬"
  478. )]
  479. public void Add_2_Lines (
  480. int x1,
  481. int y1,
  482. int len1,
  483. Orientation o1,
  484. LineStyle s1,
  485. int x2,
  486. int y2,
  487. int len2,
  488. Orientation o2,
  489. LineStyle s2,
  490. string expected
  491. )
  492. {
  493. IConsoleDriver driver = CreateFakeDriver ();
  494. View v = GetCanvas (driver, out LineCanvas lc);
  495. v.Width = 10;
  496. v.Height = 10;
  497. v.Viewport = new (0, 0, 10, 10);
  498. lc.AddLine (new (x1, y1), len1, o1, s1);
  499. lc.AddLine (new (x2, y2), len2, o2, s2);
  500. OutputAssert.AssertEqual (output, expected, lc.ToString ());
  501. v.Dispose ();
  502. }
  503. [Fact]
  504. public void Viewport_Specific ()
  505. {
  506. // Draw at 1,1 within client area of View (i.e. leave a top and left margin of 1)
  507. // This proves we aren't drawing excess above
  508. var x = 1;
  509. var y = 2;
  510. var width = 3;
  511. var height = 2;
  512. var lc = new LineCanvas ();
  513. // 01230
  514. // ╔╡╞╗1
  515. // ║ ║2
  516. // Add a short horiz line for ╔╡
  517. lc.AddLine (new (x, y), 2, Orientation.Horizontal, LineStyle.Double);
  518. Assert.Equal (new (x, y, 2, 1), lc.Bounds);
  519. //LHS line down
  520. lc.AddLine (new (x, y), height, Orientation.Vertical, LineStyle.Double);
  521. Assert.Equal (new (x, y, 2, 2), lc.Bounds);
  522. //Vertical line before Title, results in a ╡
  523. lc.AddLine (new (x + 1, y), 0, Orientation.Vertical, LineStyle.Single);
  524. Assert.Equal (new (x, y, 2, 2), lc.Bounds);
  525. //Vertical line after Title, results in a ╞
  526. lc.AddLine (new (x + 2, y), 0, Orientation.Vertical, LineStyle.Single);
  527. Assert.Equal (new (x, y, 3, 2), lc.Bounds);
  528. // remainder of top line
  529. lc.AddLine (new (x + 2, y), width - 1, Orientation.Horizontal, LineStyle.Double);
  530. Assert.Equal (new (x, y, 4, 2), lc.Bounds);
  531. //RHS line down
  532. lc.AddLine (new (x + width, y), height, Orientation.Vertical, LineStyle.Double);
  533. Assert.Equal (new (x, y, 4, 2), lc.Bounds);
  534. OutputAssert.AssertEqual (
  535. output,
  536. @"
  537. ╔╡╞╗
  538. ║ ║",
  539. $"{Environment.NewLine}{lc}"
  540. );
  541. }
  542. [Fact]
  543. public void Viewport_Specific_With_Ustring ()
  544. {
  545. // Draw at 1,1 within client area of View (i.e. leave a top and left margin of 1)
  546. // This proves we aren't drawing excess above
  547. var x = 1;
  548. var y = 2;
  549. var width = 3;
  550. var height = 2;
  551. var lc = new LineCanvas ();
  552. // 01230
  553. // ╔╡╞╗1
  554. // ║ ║2
  555. // Add a short horiz line for ╔╡
  556. lc.AddLine (new (x, y), 2, Orientation.Horizontal, LineStyle.Double);
  557. Assert.Equal (new (x, y, 2, 1), lc.Bounds);
  558. //LHS line down
  559. lc.AddLine (new (x, y), height, Orientation.Vertical, LineStyle.Double);
  560. Assert.Equal (new (x, y, 2, 2), lc.Bounds);
  561. //Vertical line before Title, results in a ╡
  562. lc.AddLine (new (x + 1, y), 0, Orientation.Vertical, LineStyle.Single);
  563. Assert.Equal (new (x, y, 2, 2), lc.Bounds);
  564. //Vertical line after Title, results in a ╞
  565. lc.AddLine (new (x + 2, y), 0, Orientation.Vertical, LineStyle.Single);
  566. Assert.Equal (new (x, y, 3, 2), lc.Bounds);
  567. // remainder of top line
  568. lc.AddLine (new (x + 2, y), width - 1, Orientation.Horizontal, LineStyle.Double);
  569. Assert.Equal (new (x, y, 4, 2), lc.Bounds);
  570. //RHS line down
  571. lc.AddLine (new (x + width, y), height, Orientation.Vertical, LineStyle.Double);
  572. Assert.Equal (new (x, y, 4, 2), lc.Bounds);
  573. OutputAssert.AssertEqual (
  574. output,
  575. @"
  576. ╔╡╞╗
  577. ║ ║",
  578. $"{Environment.NewLine}{lc}"
  579. );
  580. }
  581. [Fact]
  582. public void Canvas_Updates_On_Changes ()
  583. {
  584. var lc = new LineCanvas ();
  585. Assert.Equal (Rectangle.Empty, lc.Bounds);
  586. lc.AddLine (Point.Empty, 2, Orientation.Horizontal, LineStyle.Double);
  587. Assert.NotEqual (Rectangle.Empty, lc.Bounds);
  588. lc.Clear ();
  589. Assert.Equal (Rectangle.Empty, lc.Bounds);
  590. }
  591. [InlineData (0, 0, Orientation.Horizontal, "─")]
  592. [InlineData (1, 0, Orientation.Horizontal, "─")]
  593. [InlineData (0, 1, Orientation.Horizontal, "─")]
  594. [InlineData (-1, 0, Orientation.Horizontal, "─")]
  595. [InlineData (0, -1, Orientation.Horizontal, "─")]
  596. [InlineData (-1, -1, Orientation.Horizontal, "─")]
  597. [InlineData (0, 0, Orientation.Vertical, "│")]
  598. [InlineData (1, 0, Orientation.Vertical, "│")]
  599. [InlineData (0, 1, Orientation.Vertical, "│")]
  600. [InlineData (0, -1, Orientation.Vertical, "│")]
  601. [InlineData (-1, 0, Orientation.Vertical, "│")]
  602. [InlineData (-1, -1, Orientation.Vertical, "│")]
  603. [Theory]
  604. public void Length_0_Is_1_Long (int x, int y, Orientation orientation, string expected)
  605. {
  606. var canvas = new LineCanvas ();
  607. // Add a line at 5, 5 that's has length of 1
  608. canvas.AddLine (new (x, y), 1, orientation, LineStyle.Single);
  609. OutputAssert.AssertEqual (output, $"{expected}", $"{canvas}");
  610. }
  611. // X is offset by 2
  612. [InlineData (0, 0, 1, Orientation.Horizontal, "─")]
  613. [InlineData (1, 0, 1, Orientation.Horizontal, "─")]
  614. [InlineData (0, 1, 1, Orientation.Horizontal, "─")]
  615. [InlineData (0, 0, 1, Orientation.Vertical, "│")]
  616. [InlineData (1, 0, 1, Orientation.Vertical, "│")]
  617. [InlineData (0, 1, 1, Orientation.Vertical, "│")]
  618. [InlineData (-1, 0, 1, Orientation.Horizontal, "─")]
  619. [InlineData (0, -1, 1, Orientation.Horizontal, "─")]
  620. [InlineData (-1, 0, 1, Orientation.Vertical, "│")]
  621. [InlineData (0, -1, 1, Orientation.Vertical, "│")]
  622. [InlineData (0, 0, -1, Orientation.Horizontal, "─")]
  623. [InlineData (1, 0, -1, Orientation.Horizontal, "─")]
  624. [InlineData (0, 1, -1, Orientation.Horizontal, "─")]
  625. [InlineData (0, 0, -1, Orientation.Vertical, "│")]
  626. [InlineData (1, 0, -1, Orientation.Vertical, "│")]
  627. [InlineData (0, 1, -1, Orientation.Vertical, "│")]
  628. [InlineData (-1, 0, -1, Orientation.Horizontal, "─")]
  629. [InlineData (0, -1, -1, Orientation.Horizontal, "─")]
  630. [InlineData (-1, 0, -1, Orientation.Vertical, "│")]
  631. [InlineData (0, -1, -1, Orientation.Vertical, "│")]
  632. [InlineData (0, 0, 2, Orientation.Horizontal, "──")]
  633. [InlineData (1, 0, 2, Orientation.Horizontal, "──")]
  634. [InlineData (0, 1, 2, Orientation.Horizontal, "──")]
  635. [InlineData (1, 1, 2, Orientation.Horizontal, "──")]
  636. [InlineData (0, 0, 2, Orientation.Vertical, "│\r\n│")]
  637. [InlineData (1, 0, 2, Orientation.Vertical, "│\r\n│")]
  638. [InlineData (0, 1, 2, Orientation.Vertical, "│\r\n│")]
  639. [InlineData (1, 1, 2, Orientation.Vertical, "│\r\n│")]
  640. [InlineData (-1, 0, 2, Orientation.Horizontal, "──")]
  641. [InlineData (0, -1, 2, Orientation.Horizontal, "──")]
  642. [InlineData (-1, 0, 2, Orientation.Vertical, "│\r\n│")]
  643. [InlineData (0, -1, 2, Orientation.Vertical, "│\r\n│")]
  644. [InlineData (-1, -1, 2, Orientation.Vertical, "│\r\n│")]
  645. [InlineData (0, 0, -2, Orientation.Horizontal, "──")]
  646. [InlineData (1, 0, -2, Orientation.Horizontal, "──")]
  647. [InlineData (0, 1, -2, Orientation.Horizontal, "──")]
  648. [InlineData (0, 0, -2, Orientation.Vertical, "│\r\n│")]
  649. [InlineData (1, 0, -2, Orientation.Vertical, "│\r\n│")]
  650. [InlineData (0, 1, -2, Orientation.Vertical, "│\r\n│")]
  651. [InlineData (1, 1, -2, Orientation.Vertical, "│\r\n│")]
  652. [InlineData (-1, 0, -2, Orientation.Horizontal, "──")]
  653. [InlineData (0, -1, -2, Orientation.Horizontal, "──")]
  654. [InlineData (-1, 0, -2, Orientation.Vertical, "│\r\n│")]
  655. [InlineData (0, -1, -2, Orientation.Vertical, "│\r\n│")]
  656. [InlineData (-1, -1, -2, Orientation.Vertical, "│\r\n│")]
  657. [Theory] public void Length_n_Is_n_Long (int x, int y, int length, Orientation orientation, string expected)
  658. {
  659. var canvas = new LineCanvas ();
  660. canvas.AddLine (new (x, y), length, orientation, LineStyle.Single);
  661. var result = canvas.ToString ();
  662. OutputAssert.AssertEqual (output, expected, result);
  663. }
  664. [Fact]
  665. public void Length_Negative ()
  666. {
  667. var offset = new Point (5, 5);
  668. var canvas = new LineCanvas ();
  669. canvas.AddLine (offset, -3, Orientation.Horizontal, LineStyle.Single);
  670. var looksLike = "───";
  671. Assert.Equal (looksLike, $"{canvas}");
  672. }
  673. [InlineData (Orientation.Horizontal, "─")]
  674. [InlineData (Orientation.Vertical, "│")]
  675. [Theory]
  676. public void Length_Zero_Alone_Is_Line (Orientation orientation, string expected)
  677. {
  678. var lc = new LineCanvas ();
  679. // Add a line at 0, 0 that's has length of 0
  680. lc.AddLine (Point.Empty, 0, orientation, LineStyle.Single);
  681. OutputAssert.AssertEqual (output, expected, $"{lc}");
  682. }
  683. [InlineData (Orientation.Horizontal, "┼")]
  684. [InlineData (Orientation.Vertical, "┼")]
  685. [Theory]
  686. public void Length_Zero_Cross_Is_Cross (Orientation orientation, string expected)
  687. {
  688. var lc = new LineCanvas ();
  689. // Add point at opposite orientation
  690. lc.AddLine (
  691. Point.Empty,
  692. 0,
  693. orientation == Orientation.Horizontal ? Orientation.Vertical : Orientation.Horizontal,
  694. LineStyle.Single
  695. );
  696. // Add a line at 0, 0 that's has length of 0
  697. lc.AddLine (Point.Empty, 0, orientation, LineStyle.Single);
  698. OutputAssert.AssertEqual (output, expected, $"{lc}");
  699. }
  700. [InlineData (Orientation.Horizontal, "╥")]
  701. [InlineData (Orientation.Vertical, "╞")]
  702. [Theory]
  703. public void Length_Zero_NextTo_Opposite_Is_T (Orientation orientation, string expected)
  704. {
  705. var lc = new LineCanvas ();
  706. // Add line with length of 1 in opposite orientation starting at same location
  707. if (orientation == Orientation.Horizontal)
  708. {
  709. lc.AddLine (Point.Empty, 1, Orientation.Vertical, LineStyle.Double);
  710. }
  711. else
  712. {
  713. lc.AddLine (Point.Empty, 1, Orientation.Horizontal, LineStyle.Double);
  714. }
  715. // Add a line at 0, 0 that's has length of 0
  716. lc.AddLine (Point.Empty, 0, orientation, LineStyle.Single);
  717. OutputAssert.AssertEqual (output, expected, $"{lc}");
  718. }
  719. [Fact]
  720. public void TestLineCanvas_LeaveMargin_Top1_Left1 ()
  721. {
  722. var canvas = new LineCanvas ();
  723. // Upper box
  724. canvas.AddLine (Point.Empty, 2, Orientation.Horizontal, LineStyle.Single);
  725. canvas.AddLine (Point.Empty, 2, Orientation.Vertical, LineStyle.Single);
  726. var looksLike =
  727. @"
  728. ┌─
  729. │ ";
  730. OutputAssert.AssertEqual (output, looksLike, $"{Environment.NewLine}{canvas}");
  731. }
  732. [Fact]
  733. public void TestLineCanvas_Window_Heavy ()
  734. {
  735. var driver = CreateFakeDriver ();
  736. View v = GetCanvas (driver, out LineCanvas canvas);
  737. // outer box
  738. canvas.AddLine (Point.Empty, 10, Orientation.Horizontal, LineStyle.Heavy);
  739. canvas.AddLine (new (9, 0), 5, Orientation.Vertical, LineStyle.Heavy);
  740. canvas.AddLine (new (9, 4), -10, Orientation.Horizontal, LineStyle.Heavy);
  741. canvas.AddLine (new (0, 4), -5, Orientation.Vertical, LineStyle.Heavy);
  742. canvas.AddLine (new (5, 0), 5, Orientation.Vertical, LineStyle.Heavy);
  743. canvas.AddLine (new (0, 2), 10, Orientation.Horizontal, LineStyle.Heavy);
  744. v.Draw ();
  745. var looksLike =
  746. @"
  747. ┏━━━━┳━━━┓
  748. ┃ ┃ ┃
  749. ┣━━━━╋━━━┫
  750. ┃ ┃ ┃
  751. ┗━━━━┻━━━┛";
  752. DriverAssert.AssertDriverContentsAre (looksLike, output, driver);
  753. v.Dispose ();
  754. }
  755. [Theory]
  756. [InlineData (LineStyle.Single)]
  757. [InlineData (LineStyle.Rounded)]
  758. public void TestLineCanvas_Window_HeavyTop_ThinSides (LineStyle thinStyle)
  759. {
  760. var driver = CreateFakeDriver ();
  761. View v = GetCanvas (driver, out LineCanvas canvas);
  762. // outer box
  763. canvas.AddLine (Point.Empty, 10, Orientation.Horizontal, LineStyle.Heavy);
  764. canvas.AddLine (new (9, 0), 5, Orientation.Vertical, thinStyle);
  765. canvas.AddLine (new (9, 4), -10, Orientation.Horizontal, LineStyle.Heavy);
  766. canvas.AddLine (new (0, 4), -5, Orientation.Vertical, thinStyle);
  767. canvas.AddLine (new (5, 0), 5, Orientation.Vertical, thinStyle);
  768. canvas.AddLine (new (0, 2), 10, Orientation.Horizontal, LineStyle.Heavy);
  769. v.Draw ();
  770. var looksLike =
  771. @"
  772. ┍━━━━┯━━━┑
  773. │ │ │
  774. ┝━━━━┿━━━┥
  775. │ │ │
  776. ┕━━━━┷━━━┙
  777. ";
  778. DriverAssert.AssertDriverContentsAre (looksLike, output, driver);
  779. v.Dispose ();
  780. }
  781. [Theory]
  782. [InlineData (LineStyle.Single)]
  783. [InlineData (LineStyle.Rounded)]
  784. public void TestLineCanvas_Window_ThinTop_HeavySides (LineStyle thinStyle)
  785. {
  786. var driver = CreateFakeDriver ();
  787. View v = GetCanvas (driver, out LineCanvas canvas);
  788. // outer box
  789. canvas.AddLine (Point.Empty, 10, Orientation.Horizontal, thinStyle);
  790. canvas.AddLine (new (9, 0), 5, Orientation.Vertical, LineStyle.Heavy);
  791. canvas.AddLine (new (9, 4), -10, Orientation.Horizontal, thinStyle);
  792. canvas.AddLine (new (0, 4), -5, Orientation.Vertical, LineStyle.Heavy);
  793. canvas.AddLine (new (5, 0), 5, Orientation.Vertical, LineStyle.Heavy);
  794. canvas.AddLine (new (0, 2), 10, Orientation.Horizontal, thinStyle);
  795. v.Draw ();
  796. var looksLike =
  797. @"
  798. ┎────┰───┒
  799. ┃ ┃ ┃
  800. ┠────╂───┨
  801. ┃ ┃ ┃
  802. ┖────┸───┚
  803. ";
  804. DriverAssert.AssertDriverContentsAre (looksLike, output, driver);
  805. v.Dispose ();
  806. }
  807. [Fact]
  808. public void Top_Left_From_TopRight_LeftUp ()
  809. {
  810. var canvas = new LineCanvas ();
  811. // Upper box
  812. canvas.AddLine (Point.Empty, 2, Orientation.Horizontal, LineStyle.Single);
  813. canvas.AddLine (new (0, 1), -2, Orientation.Vertical, LineStyle.Single);
  814. var looksLike =
  815. @"
  816. ┌─
  817. │ ";
  818. OutputAssert.AssertEqual (output, looksLike, $"{Environment.NewLine}{canvas}");
  819. }
  820. [Fact]
  821. public void Top_With_1Down ()
  822. {
  823. var canvas = new LineCanvas ();
  824. // Top ─
  825. canvas.AddLine (Point.Empty, 1, Orientation.Horizontal, LineStyle.Single);
  826. // Bottom ─
  827. canvas.AddLine (new (1, 1), -1, Orientation.Horizontal, LineStyle.Single);
  828. //// Right down
  829. //canvas.AddLine (new Point (9, 0), 3, Orientation.Vertical, LineStyle.Single);
  830. //// Bottom
  831. //canvas.AddLine (new Point (9, 3), -10, Orientation.Horizontal, LineStyle.Single);
  832. //// Left Up
  833. //canvas.AddLine (new Point (0, 3), -3, Orientation.Vertical, LineStyle.Single);
  834. Assert.Equal (new (0, 0, 2, 2), canvas.Bounds);
  835. Dictionary<Point, Rune> map = canvas.GetMap ();
  836. Assert.Equal (2, map.Count);
  837. OutputAssert.AssertEqual (
  838. output,
  839. @"
  840. ─",
  841. $"{Environment.NewLine}{canvas}"
  842. );
  843. }
  844. [Fact]
  845. public void ToString_Empty ()
  846. {
  847. var lc = new LineCanvas ();
  848. OutputAssert.AssertEqual (output, string.Empty, lc.ToString ());
  849. }
  850. // 012
  851. [InlineData (0, 0, "═══")]
  852. [InlineData (1, 0, "═══")]
  853. [InlineData (0, 1, "═══")]
  854. [InlineData (1, 1, "═══")]
  855. [InlineData (2, 2, "═══")]
  856. [InlineData (-1, 0, "═══")]
  857. [InlineData (0, -1, "═══")]
  858. [InlineData (-1, -1, "═══")]
  859. [InlineData (-2, -2, "═══")]
  860. [Theory]
  861. public void ToString_Positive_Horizontal_1Line_Offset (int x, int y, string expected)
  862. {
  863. var lc = new LineCanvas ();
  864. lc.AddLine (new (x, y), 3, Orientation.Horizontal, LineStyle.Double);
  865. OutputAssert.AssertEqual (output, expected, $"{lc}");
  866. }
  867. [InlineData (0, 0, 0, 0, "═══")]
  868. [InlineData (1, 0, 1, 0, "═══")]
  869. [InlineData (-1, 0, -1, 0, "═══")]
  870. [InlineData (0, 0, 1, 0, "════")]
  871. [InlineData (1, 0, 3, 0, "═════")]
  872. [InlineData (1, 0, 4, 0, "══════")]
  873. [InlineData (1, 0, 5, 0, "═══ ═══")]
  874. [InlineData (0, 0, 0, 1, "\u2550\u2550\u2550\r\n\u2550\u2550\u2550")]
  875. [InlineData (0, 0, 1, 1, "═══ \r\n ═══")]
  876. [InlineData (0, 0, 2, 1, "═══ \r\n ═══")]
  877. [InlineData (1, 0, 0, 1, " ═══\r\n═══ ")]
  878. [InlineData (0, 1, 0, 1, "═══")]
  879. [InlineData (1, 1, 0, 1, "════")]
  880. [InlineData (2, 2, 0, 1, "═══ \r\n ═══")]
  881. [Theory]
  882. public void ToString_Positive_Horizontal_2Line_Offset (int x1, int y1, int x2, int y2, string expected)
  883. {
  884. var lc = new LineCanvas ();
  885. lc.AddLine (new (x1, y1), 3, Orientation.Horizontal, LineStyle.Double);
  886. lc.AddLine (new (x2, y2), 3, Orientation.Horizontal, LineStyle.Double);
  887. OutputAssert.AssertEqual (output, expected, $"{lc}");
  888. }
  889. // [Fact, SetupFakeDriver]
  890. // public void LeaveMargin_Top1_Left1 ()
  891. // {
  892. // var canvas = new LineCanvas ();
  893. // // Upper box
  894. // canvas.AddLine (Point.Empty, 9, Orientation.Horizontal, LineStyle.Single);
  895. // canvas.AddLine (new Point (8, 0), 3, Orientation.Vertical, LineStyle.Single);
  896. // canvas.AddLine (new Point (8, 3), -9, Orientation.Horizontal, LineStyle.Single);
  897. // canvas.AddLine (new Point (0, 2), -3, Orientation.Vertical, LineStyle.Single);
  898. // // Lower Box
  899. // canvas.AddLine (new Point (5, 0), 2, Orientation.Vertical, LineStyle.Single);
  900. // canvas.AddLine (new Point (0, 2), 9, Orientation.Horizontal, LineStyle.Single);
  901. // string looksLike =
  902. //@"
  903. //┌────┬──┐
  904. //│ │ │
  905. //├────┼──┤
  906. //└────┴──┘
  907. //";
  908. // Assert.Equal (looksLike, $"{Environment.NewLine}{canvas}");
  909. // }
  910. [InlineData (0, 0, 0, Orientation.Horizontal, LineStyle.Double, "═")]
  911. [InlineData (0, 0, 0, Orientation.Vertical, LineStyle.Double, "║")]
  912. [InlineData (0, 0, 0, Orientation.Horizontal, LineStyle.Single, "─")]
  913. [InlineData (0, 0, 0, Orientation.Vertical, LineStyle.Single, "│")]
  914. [InlineData (0, 0, 1, Orientation.Horizontal, LineStyle.Double, "═")]
  915. [InlineData (0, 0, 1, Orientation.Vertical, LineStyle.Double, "║")]
  916. [InlineData (0, 0, 1, Orientation.Horizontal, LineStyle.Single, "─")]
  917. [InlineData (0, 0, 1, Orientation.Vertical, LineStyle.Single, "│")]
  918. [InlineData (0, 0, 2, Orientation.Horizontal, LineStyle.Double, "══")]
  919. [InlineData (0, 0, 2, Orientation.Vertical, LineStyle.Double, "║\n║")]
  920. [InlineData (0, 0, 2, Orientation.Horizontal, LineStyle.Single, "──")]
  921. [InlineData (0, 0, 2, Orientation.Vertical, LineStyle.Single, "│\n│")]
  922. [Theory]
  923. public void View_Draws_1LineTests (
  924. int x1,
  925. int y1,
  926. int length,
  927. Orientation o1,
  928. LineStyle s1,
  929. string expected
  930. )
  931. {
  932. var driver = CreateFakeDriver ();
  933. View v = GetCanvas (driver, out LineCanvas lc);
  934. v.Width = 10;
  935. v.Height = 10;
  936. v.Viewport = new (0, 0, 10, 10);
  937. lc.AddLine (new (x1, y1), length, o1, s1);
  938. v.Draw ();
  939. DriverAssert.AssertDriverContentsAre (expected, output, driver);
  940. v.Dispose ();
  941. }
  942. /// <summary>This test demonstrates how to correctly trigger a corner. By overlapping the lines in the same cell</summary>
  943. [Fact]
  944. public void View_Draws_Corner_Correct ()
  945. {
  946. var driver = CreateFakeDriver ();
  947. View v = GetCanvas (driver, out LineCanvas canvas);
  948. canvas.AddLine (Point.Empty, 2, Orientation.Horizontal, LineStyle.Single);
  949. canvas.AddLine (Point.Empty, 2, Orientation.Vertical, LineStyle.Single);
  950. v.Draw ();
  951. var looksLike =
  952. @"
  953. ┌─
  954. │";
  955. DriverAssert.AssertDriverContentsAre (looksLike, output, driver);
  956. v.Dispose ();
  957. }
  958. /// <summary>
  959. /// This test demonstrates that corners are only drawn when lines overlap. Not when they terminate adjacent to one
  960. /// another.
  961. /// </summary>
  962. [Fact]
  963. public void View_Draws_Corner_NoOverlap ()
  964. {
  965. var driver = CreateFakeDriver ();
  966. View v = GetCanvas (driver, out LineCanvas canvas);
  967. canvas.AddLine (Point.Empty, 2, Orientation.Horizontal, LineStyle.Single);
  968. canvas.AddLine (new (0, 1), 2, Orientation.Vertical, LineStyle.Single);
  969. v.Draw ();
  970. var looksLike =
  971. @"
  972. ──
  973. │";
  974. DriverAssert.AssertDriverContentsAre (looksLike, output, driver);
  975. v.Dispose ();
  976. }
  977. [InlineData (LineStyle.Single)]
  978. [InlineData (LineStyle.Rounded)]
  979. [Theory]
  980. public void View_Draws_Horizontal (LineStyle style)
  981. {
  982. var driver = CreateFakeDriver ();
  983. View v = GetCanvas (driver, out LineCanvas canvas);
  984. canvas.AddLine (Point.Empty, 2, Orientation.Horizontal, style);
  985. v.Draw ();
  986. var looksLike =
  987. @"
  988. ──";
  989. DriverAssert.AssertDriverContentsAre (looksLike, output, driver);
  990. v.Dispose ();
  991. }
  992. [Fact]
  993. public void View_Draws_Horizontal_Double ()
  994. {
  995. var driver = CreateFakeDriver ();
  996. View v = GetCanvas (driver, out LineCanvas canvas);
  997. canvas.AddLine (Point.Empty, 2, Orientation.Horizontal, LineStyle.Double);
  998. v.Draw ();
  999. var looksLike =
  1000. @"
  1001. ══";
  1002. DriverAssert.AssertDriverContentsAre (looksLike, output, driver);
  1003. v.Dispose ();
  1004. }
  1005. [InlineData (LineStyle.Single)]
  1006. [InlineData (LineStyle.Rounded)]
  1007. [Theory]
  1008. public void View_Draws_Vertical (LineStyle style)
  1009. {
  1010. var driver = CreateFakeDriver ();
  1011. View v = GetCanvas (driver, out LineCanvas canvas);
  1012. canvas.AddLine (Point.Empty, 2, Orientation.Vertical, style);
  1013. v.Draw ();
  1014. var looksLike =
  1015. @"
  1016. │";
  1017. DriverAssert.AssertDriverContentsAre (looksLike, output, driver);
  1018. v.Dispose ();
  1019. }
  1020. [Fact]
  1021. public void View_Draws_Vertical_Double ()
  1022. {
  1023. var driver = CreateFakeDriver ();
  1024. View v = GetCanvas (driver, out LineCanvas canvas);
  1025. canvas.AddLine (Point.Empty, 2, Orientation.Vertical, LineStyle.Double);
  1026. v.Draw ();
  1027. var looksLike =
  1028. @"
  1029. ║";
  1030. DriverAssert.AssertDriverContentsAre (looksLike, output, driver);
  1031. v.Dispose ();
  1032. }
  1033. [Fact]
  1034. public void View_Draws_Window_Double ()
  1035. {
  1036. var driver = CreateFakeDriver ();
  1037. View v = GetCanvas (driver, out LineCanvas canvas);
  1038. // outer box
  1039. canvas.AddLine (Point.Empty, 10, Orientation.Horizontal, LineStyle.Double);
  1040. canvas.AddLine (new (9, 0), 5, Orientation.Vertical, LineStyle.Double);
  1041. canvas.AddLine (new (9, 4), -10, Orientation.Horizontal, LineStyle.Double);
  1042. canvas.AddLine (new (0, 4), -5, Orientation.Vertical, LineStyle.Double);
  1043. canvas.AddLine (new (5, 0), 5, Orientation.Vertical, LineStyle.Double);
  1044. canvas.AddLine (new (0, 2), 10, Orientation.Horizontal, LineStyle.Double);
  1045. v.Draw ();
  1046. var looksLike =
  1047. @"
  1048. ╔════╦═══╗
  1049. ║ ║ ║
  1050. ╠════╬═══╣
  1051. ║ ║ ║
  1052. ╚════╩═══╝";
  1053. DriverAssert.AssertDriverContentsAre (looksLike, output, driver);
  1054. v.Dispose ();
  1055. }
  1056. [Theory]
  1057. [InlineData (LineStyle.Single)]
  1058. [InlineData (LineStyle.Rounded)]
  1059. public void View_Draws_Window_DoubleTop_SingleSides (LineStyle thinStyle)
  1060. {
  1061. var driver = CreateFakeDriver ();
  1062. View v = GetCanvas (driver, out LineCanvas canvas);
  1063. // outer box
  1064. canvas.AddLine (Point.Empty, 10, Orientation.Horizontal, LineStyle.Double);
  1065. canvas.AddLine (new (9, 0), 5, Orientation.Vertical, thinStyle);
  1066. canvas.AddLine (new (9, 4), -10, Orientation.Horizontal, LineStyle.Double);
  1067. canvas.AddLine (new (0, 4), -5, Orientation.Vertical, thinStyle);
  1068. canvas.AddLine (new (5, 0), 5, Orientation.Vertical, thinStyle);
  1069. canvas.AddLine (new (0, 2), 10, Orientation.Horizontal, LineStyle.Double);
  1070. v.Draw ();
  1071. var looksLike =
  1072. @"
  1073. ╒════╤═══╕
  1074. │ │ │
  1075. ╞════╪═══╡
  1076. │ │ │
  1077. ╘════╧═══╛
  1078. ";
  1079. DriverAssert.AssertDriverContentsAre (looksLike, output, driver);
  1080. v.Dispose ();
  1081. }
  1082. /// <summary>
  1083. /// Demonstrates when <see cref="LineStyle.Rounded"/> corners are used. Notice how not all lines declare rounded.
  1084. /// If there are 1+ lines intersecting and a corner is to be used then if any of them are rounded a rounded corner is
  1085. /// used.
  1086. /// </summary>
  1087. [Fact]
  1088. public void View_Draws_Window_Rounded ()
  1089. {
  1090. var driver = CreateFakeDriver ();
  1091. View v = GetCanvas (driver, out LineCanvas canvas);
  1092. // outer box
  1093. canvas.AddLine (Point.Empty, 10, Orientation.Horizontal, LineStyle.Rounded);
  1094. // LineStyle.Single is ignored because corner overlaps with the above line which is Rounded
  1095. // this results in a rounded corner being used.
  1096. canvas.AddLine (new (9, 0), 5, Orientation.Vertical, LineStyle.Single);
  1097. canvas.AddLine (new (9, 4), -10, Orientation.Horizontal, LineStyle.Rounded);
  1098. canvas.AddLine (new (0, 4), -5, Orientation.Vertical, LineStyle.Single);
  1099. // These lines say rounded but they will result in the T sections which are never rounded.
  1100. canvas.AddLine (new (5, 0), 5, Orientation.Vertical, LineStyle.Rounded);
  1101. canvas.AddLine (new (0, 2), 10, Orientation.Horizontal, LineStyle.Rounded);
  1102. v.Draw ();
  1103. var looksLike =
  1104. @"
  1105. ╭────┬───╮
  1106. │ │ │
  1107. ├────┼───┤
  1108. │ │ │
  1109. ╰────┴───╯";
  1110. DriverAssert.AssertDriverContentsAre (looksLike, output, driver);
  1111. v.Dispose ();
  1112. }
  1113. [Theory]
  1114. [InlineData (LineStyle.Single)]
  1115. [InlineData (LineStyle.Rounded)]
  1116. public void View_Draws_Window_SingleTop_DoubleSides (LineStyle thinStyle)
  1117. {
  1118. var driver = CreateFakeDriver ();
  1119. View v = GetCanvas (driver, out LineCanvas canvas);
  1120. // outer box
  1121. canvas.AddLine (Point.Empty, 10, Orientation.Horizontal, thinStyle);
  1122. canvas.AddLine (new (9, 0), 5, Orientation.Vertical, LineStyle.Double);
  1123. canvas.AddLine (new (9, 4), -10, Orientation.Horizontal, thinStyle);
  1124. canvas.AddLine (new (0, 4), -5, Orientation.Vertical, LineStyle.Double);
  1125. canvas.AddLine (new (5, 0), 5, Orientation.Vertical, LineStyle.Double);
  1126. canvas.AddLine (new (0, 2), 10, Orientation.Horizontal, thinStyle);
  1127. v.Draw ();
  1128. var looksLike =
  1129. @"
  1130. ╓────╥───╖
  1131. ║ ║ ║
  1132. ╟────╫───╢
  1133. ║ ║ ║
  1134. ╙────╨───╜
  1135. ";
  1136. DriverAssert.AssertDriverContentsAre (looksLike, output, driver);
  1137. v.Dispose ();
  1138. }
  1139. [Fact]
  1140. public void Window ()
  1141. {
  1142. var canvas = new LineCanvas ();
  1143. // Frame
  1144. canvas.AddLine (Point.Empty, 10, Orientation.Horizontal, LineStyle.Single);
  1145. canvas.AddLine (new (9, 0), 5, Orientation.Vertical, LineStyle.Single);
  1146. canvas.AddLine (new (9, 4), -10, Orientation.Horizontal, LineStyle.Single);
  1147. canvas.AddLine (new (0, 4), -5, Orientation.Vertical, LineStyle.Single);
  1148. // Cross
  1149. canvas.AddLine (new (5, 0), 5, Orientation.Vertical, LineStyle.Single);
  1150. canvas.AddLine (new (0, 2), 10, Orientation.Horizontal, LineStyle.Single);
  1151. var looksLike =
  1152. @"
  1153. ┌────┬───┐
  1154. │ │ │
  1155. ├────┼───┤
  1156. │ │ │
  1157. └────┴───┘";
  1158. OutputAssert.AssertEqual (output, looksLike, $"{Environment.NewLine}{canvas}");
  1159. }
  1160. [Fact]
  1161. public void Zero_Length_Intersections ()
  1162. {
  1163. // Draw at 1,2 within client area of View (i.e. leave a top and left margin of 1)
  1164. // This proves we aren't drawing excess above
  1165. var x = 1;
  1166. var y = 2;
  1167. var width = 5;
  1168. var height = 2;
  1169. var lc = new LineCanvas ();
  1170. // ╔╡╞═════╗
  1171. // Add a short horiz line for ╔╡
  1172. lc.AddLine (new (x, y), 2, Orientation.Horizontal, LineStyle.Double);
  1173. //LHS line down
  1174. lc.AddLine (new (x, y), height, Orientation.Vertical, LineStyle.Double);
  1175. //Vertical line before Title, results in a ╡
  1176. lc.AddLine (new (x + 1, y), 0, Orientation.Vertical, LineStyle.Single);
  1177. //Vertical line after Title, results in a ╞
  1178. lc.AddLine (new (x + 2, y), 0, Orientation.Vertical, LineStyle.Single);
  1179. // remainder of top line
  1180. lc.AddLine (new (x + 2, y), width - 1, Orientation.Horizontal, LineStyle.Double);
  1181. //RHS line down
  1182. lc.AddLine (new (x + width, y), height, Orientation.Vertical, LineStyle.Double);
  1183. var looksLike = @"
  1184. ╔╡╞══╗
  1185. ║ ║";
  1186. OutputAssert.AssertEqual (output, looksLike, $"{Environment.NewLine}{lc}");
  1187. }
  1188. [Fact]
  1189. public void LineCanvas_UsesFillCorrectly ()
  1190. {
  1191. // Arrange
  1192. var foregroundColor = new Color (255, 0); // Red
  1193. var backgroundColor = new Color (0, 0); // Black
  1194. var foregroundFill = new SolidFill (foregroundColor);
  1195. var backgroundFill = new SolidFill (backgroundColor);
  1196. var fillPair = new FillPair (foregroundFill, backgroundFill);
  1197. var lineCanvas = new LineCanvas
  1198. {
  1199. Fill = fillPair
  1200. };
  1201. // Act
  1202. lineCanvas.AddLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single);
  1203. Dictionary<Point, Cell?> cellMap = lineCanvas.GetCellMap ();
  1204. // Assert
  1205. foreach (Cell? cell in cellMap.Values)
  1206. {
  1207. Assert.NotNull (cell);
  1208. Assert.Equal (foregroundColor, cell.Value.Attribute.Value.Foreground);
  1209. Assert.Equal (backgroundColor, cell.Value.Attribute.Value.Background);
  1210. }
  1211. }
  1212. [Fact]
  1213. public void LineCanvas_LineColorIgnoredBecauseOfFill ()
  1214. {
  1215. // Arrange
  1216. var foregroundColor = new Color (255, 0); // Red
  1217. var backgroundColor = new Color (0, 0); // Black
  1218. var lineColor = new Attribute (new Color (0, 255), new Color (255, 255, 255)); // Green on White
  1219. var foregroundFill = new SolidFill (foregroundColor);
  1220. var backgroundFill = new SolidFill (backgroundColor);
  1221. var fillPair = new FillPair (foregroundFill, backgroundFill);
  1222. var lineCanvas = new LineCanvas
  1223. {
  1224. Fill = fillPair
  1225. };
  1226. // Act
  1227. lineCanvas.AddLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single, lineColor);
  1228. Dictionary<Point, Cell?> cellMap = lineCanvas.GetCellMap ();
  1229. // Assert
  1230. foreach (Cell? cell in cellMap.Values)
  1231. {
  1232. Assert.NotNull (cell);
  1233. Assert.Equal (foregroundColor, cell.Value.Attribute.Value.Foreground);
  1234. Assert.Equal (backgroundColor, cell.Value.Attribute.Value.Background);
  1235. }
  1236. }
  1237. [Fact]
  1238. public void LineCanvas_IntersectingLinesUseFillCorrectly ()
  1239. {
  1240. // Arrange
  1241. var foregroundColor = new Color (255, 0); // Red
  1242. var backgroundColor = new Color (0, 0); // Black
  1243. var foregroundFill = new SolidFill (foregroundColor);
  1244. var backgroundFill = new SolidFill (backgroundColor);
  1245. var fillPair = new FillPair (foregroundFill, backgroundFill);
  1246. var lineCanvas = new LineCanvas
  1247. {
  1248. Fill = fillPair
  1249. };
  1250. // Act
  1251. lineCanvas.AddLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single);
  1252. lineCanvas.AddLine (new (2, -2), 5, Orientation.Vertical, LineStyle.Single);
  1253. Dictionary<Point, Cell?> cellMap = lineCanvas.GetCellMap ();
  1254. // Assert
  1255. foreach (Cell? cell in cellMap.Values)
  1256. {
  1257. Assert.NotNull (cell);
  1258. Assert.Equal (foregroundColor, cell.Value.Attribute.Value.Foreground);
  1259. Assert.Equal (backgroundColor, cell.Value.Attribute.Value.Background);
  1260. }
  1261. }
  1262. // TODO: Remove this and make all LineCanvas tests independent of View
  1263. /// <summary>
  1264. /// Creates a new <see cref="View"/> into which a <see cref="LineCanvas"/> is rendered at
  1265. /// <see cref="View.DrawComplete"/> time.
  1266. /// </summary>
  1267. /// <param name="canvas">The <see cref="LineCanvas"/> you can draw into.</param>
  1268. /// <param name="offsetX">How far to offset drawing in X</param>
  1269. /// <param name="offsetY">How far to offset drawing in Y</param>
  1270. /// <returns></returns>
  1271. private View GetCanvas (IConsoleDriver driver, out LineCanvas canvas, int offsetX = 0, int offsetY = 0)
  1272. {
  1273. var v = new View { Width = 10, Height = 5, Viewport = new (0, 0, 10, 5) };
  1274. v.Driver = driver;
  1275. LineCanvas canvasCopy = canvas = new ();
  1276. v.DrawComplete += (s, e) =>
  1277. {
  1278. v.FillRect (v.Viewport);
  1279. foreach (KeyValuePair<Point, Rune> p in canvasCopy.GetMap ())
  1280. {
  1281. v.AddRune (
  1282. offsetX + p.Key.X,
  1283. offsetY + p.Key.Y,
  1284. p.Value
  1285. );
  1286. }
  1287. canvasCopy.Clear ();
  1288. };
  1289. return v;
  1290. }
  1291. }