LineCanvasTests.cs 61 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867
  1. ๏ปฟusing System.Text;
  2. using UnitTests;
  3. using Xunit.Abstractions;
  4. namespace DrawingTests.Lines;
  5. /// <summary>
  6. /// Pure unit tests for <see cref="LineCanvas"/> that don't require Application.Driver or View context.
  7. /// These tests focus on properties and behavior that don't depend on glyph rendering.
  8. /// Note: Tests that verify rendered output (ToString()) cannot be parallelized because LineCanvas
  9. /// depends on Application.Driver for glyph resolution and configuration. Those tests remain in UnitTests.
  10. /// </summary>
  11. public class LineCanvasTests (ITestOutputHelper output) : FakeDriverBase
  12. {
  13. #region Basic API Tests
  14. [Fact]
  15. public void Empty_Canvas_ToString_Returns_EmptyString ()
  16. {
  17. LineCanvas canvas = new ();
  18. Assert.Equal (string.Empty, canvas.ToString ());
  19. }
  20. [Fact]
  21. public void Clear_Removes_All_Lines ()
  22. {
  23. LineCanvas canvas = new ();
  24. canvas.AddLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single);
  25. canvas.AddLine (new (0, 0), 3, Orientation.Vertical, LineStyle.Single);
  26. canvas.Clear ();
  27. Assert.Empty (canvas.Lines);
  28. Assert.Equal (Rectangle.Empty, canvas.Bounds);
  29. Assert.Equal (string.Empty, canvas.ToString ());
  30. }
  31. [Fact]
  32. public void Lines_Property_Returns_ReadOnly_Collection ()
  33. {
  34. LineCanvas canvas = new ();
  35. canvas.AddLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single);
  36. Assert.Single (canvas.Lines);
  37. Assert.IsAssignableFrom<IReadOnlyCollection<StraightLine>> (canvas.Lines);
  38. }
  39. [Fact]
  40. public void AddLine_Adds_Line_To_Collection ()
  41. {
  42. LineCanvas canvas = new ();
  43. Assert.Empty (canvas.Lines);
  44. canvas.AddLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single);
  45. Assert.Single (canvas.Lines);
  46. canvas.AddLine (new (0, 0), 3, Orientation.Vertical, LineStyle.Single);
  47. Assert.Equal (2, canvas.Lines.Count);
  48. }
  49. [Fact]
  50. public void Constructor_With_Lines_Creates_Canvas_With_Lines ()
  51. {
  52. StraightLine [] lines = new []
  53. {
  54. new StraightLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single),
  55. new StraightLine (new (0, 0), 3, Orientation.Vertical, LineStyle.Single)
  56. };
  57. var canvas = new LineCanvas (lines);
  58. Assert.Equal (2, canvas.Lines.Count);
  59. }
  60. #endregion
  61. #region Bounds Tests - Tests for Bounds property
  62. [Theory]
  63. [InlineData (0, 0, 0, 0, 0, 1, 1)]
  64. [InlineData (0, 0, 1, 0, 0, 1, 1)]
  65. [InlineData (0, 0, 2, 0, 0, 2, 2)]
  66. [InlineData (0, 0, 3, 0, 0, 3, 3)]
  67. [InlineData (0, 0, -1, 0, 0, 1, 1)]
  68. [InlineData (0, 0, -2, -1, -1, 2, 2)]
  69. [InlineData (0, 0, -3, -2, -2, 3, 3)]
  70. public void Viewport_H_And_V_Lines_Both_Positive (
  71. int x,
  72. int y,
  73. int length,
  74. int expectedX,
  75. int expectedY,
  76. int expectedWidth,
  77. int expectedHeight
  78. )
  79. {
  80. LineCanvas canvas = new ();
  81. canvas.AddLine (new (x, y), length, Orientation.Horizontal, LineStyle.Single);
  82. canvas.AddLine (new (x, y), length, Orientation.Vertical, LineStyle.Single);
  83. Assert.Equal (new (expectedX, expectedY, expectedWidth, expectedHeight), canvas.Bounds);
  84. }
  85. [Theory]
  86. [InlineData (0, 0, 0, 0, 0, 1, 1)]
  87. [InlineData (0, 0, 1, 0, 0, 1, 1)]
  88. [InlineData (0, 0, 2, 0, 0, 2, 1)]
  89. [InlineData (0, 0, 3, 0, 0, 3, 1)]
  90. [InlineData (0, 0, -1, 0, 0, 1, 1)]
  91. [InlineData (0, 0, -2, -1, 0, 2, 1)]
  92. [InlineData (0, 0, -3, -2, 0, 3, 1)]
  93. public void Viewport_H_Line (
  94. int x,
  95. int y,
  96. int length,
  97. int expectedX,
  98. int expectedY,
  99. int expectedWidth,
  100. int expectedHeight
  101. )
  102. {
  103. LineCanvas canvas = new ();
  104. canvas.AddLine (new (x, y), length, Orientation.Horizontal, LineStyle.Single);
  105. Assert.Equal (new (expectedX, expectedY, expectedWidth, expectedHeight), canvas.Bounds);
  106. }
  107. [Fact]
  108. public void Bounds_Specific_Coordinates ()
  109. {
  110. LineCanvas canvas = new ();
  111. canvas.AddLine (new (5, 5), 3, Orientation.Horizontal, LineStyle.Single);
  112. Assert.Equal (new (5, 5, 3, 1), canvas.Bounds);
  113. }
  114. [Fact]
  115. public void Bounds_Empty_Canvas_Returns_Empty_Rectangle ()
  116. {
  117. LineCanvas canvas = new ();
  118. Assert.Equal (Rectangle.Empty, canvas.Bounds);
  119. }
  120. [Fact]
  121. public void Bounds_Single_Point_Zero_Length ()
  122. {
  123. LineCanvas canvas = new ();
  124. canvas.AddLine (new (5, 5), 0, Orientation.Horizontal, LineStyle.Single);
  125. Assert.Equal (new (5, 5, 1, 1), canvas.Bounds);
  126. }
  127. [Fact]
  128. public void Bounds_Horizontal_Line ()
  129. {
  130. LineCanvas canvas = new ();
  131. canvas.AddLine (new (2, 3), 5, Orientation.Horizontal, LineStyle.Single);
  132. Assert.Equal (new (2, 3, 5, 1), canvas.Bounds);
  133. }
  134. [Fact]
  135. public void Bounds_Vertical_Line ()
  136. {
  137. LineCanvas canvas = new ();
  138. canvas.AddLine (new (2, 3), 5, Orientation.Vertical, LineStyle.Single);
  139. Assert.Equal (new (2, 3, 1, 5), canvas.Bounds);
  140. }
  141. [Fact]
  142. public void Bounds_Multiple_Lines_Returns_Union ()
  143. {
  144. LineCanvas canvas = new ();
  145. canvas.AddLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single);
  146. canvas.AddLine (new (0, 0), 3, Orientation.Vertical, LineStyle.Single);
  147. Assert.Equal (new (0, 0, 5, 3), canvas.Bounds);
  148. }
  149. [Fact]
  150. public void Bounds_Negative_Length_Line ()
  151. {
  152. LineCanvas canvas = new ();
  153. canvas.AddLine (new (5, 5), -3, Orientation.Horizontal, LineStyle.Single);
  154. // Line from (5,5) going left 3 positions: includes points 3, 4, 5 (width 3, X starts at 3)
  155. Assert.Equal (new (3, 5, 3, 1), canvas.Bounds);
  156. }
  157. [Fact]
  158. public void Bounds_Complex_Box ()
  159. {
  160. LineCanvas canvas = new ();
  161. // top
  162. canvas.AddLine (new (0, 0), 3, Orientation.Horizontal, LineStyle.Single);
  163. // left
  164. canvas.AddLine (new (0, 0), 2, Orientation.Vertical, LineStyle.Single);
  165. // right
  166. canvas.AddLine (new (2, 0), 2, Orientation.Vertical, LineStyle.Single);
  167. // bottom
  168. canvas.AddLine (new (0, 2), 3, Orientation.Horizontal, LineStyle.Single);
  169. Assert.Equal (new (0, 0, 3, 3), canvas.Bounds);
  170. }
  171. #endregion
  172. #region Exclusion Tests
  173. [Fact]
  174. public void ClearExclusions_Clears_Exclusion_Region ()
  175. {
  176. LineCanvas canvas = new ();
  177. canvas.AddLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single);
  178. var region = new Region (new (0, 0, 2, 1));
  179. canvas.Exclude (region);
  180. canvas.ClearExclusions ();
  181. // After clearing exclusions, GetMap should return all points
  182. Dictionary<Point, Rune> map = canvas.GetMap ();
  183. Assert.Equal (5, map.Count);
  184. }
  185. [Fact]
  186. public void Exclude_Removes_Points_From_Map ()
  187. {
  188. LineCanvas canvas = new ();
  189. canvas.AddLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single);
  190. var region = new Region (new (0, 0, 2, 1));
  191. canvas.Exclude (region);
  192. Dictionary<Point, Rune> map = canvas.GetMap ();
  193. // Should have 5 - 2 = 3 points (excluding the first 2)
  194. Assert.Equal (3, map.Count);
  195. }
  196. #endregion
  197. #region Fill Property Tests
  198. [Fact]
  199. public void Fill_Property_Can_Be_Set ()
  200. {
  201. var foregroundFill = new SolidFill (new (255, 0));
  202. var backgroundFill = new SolidFill (new (0, 0));
  203. var fillPair = new FillPair (foregroundFill, backgroundFill);
  204. var canvas = new LineCanvas { Fill = fillPair };
  205. Assert.Equal (fillPair, canvas.Fill);
  206. }
  207. [Fact]
  208. public void Fill_Property_Defaults_To_Null ()
  209. {
  210. LineCanvas canvas = new ();
  211. Assert.Null (canvas.Fill);
  212. }
  213. #endregion
  214. [Theory]
  215. // Horizontal lines with a vertical zero-length
  216. [InlineData (
  217. 0,
  218. 0,
  219. 1,
  220. Orientation.Horizontal,
  221. LineStyle.Double,
  222. 0,
  223. 0,
  224. 0,
  225. Orientation.Vertical,
  226. LineStyle.Single,
  227. "โ•ž"
  228. )]
  229. [InlineData (
  230. 0,
  231. 0,
  232. -1,
  233. Orientation.Horizontal,
  234. LineStyle.Double,
  235. 0,
  236. 0,
  237. 0,
  238. Orientation.Vertical,
  239. LineStyle.Single,
  240. "โ•ก"
  241. )]
  242. [InlineData (
  243. 0,
  244. 0,
  245. 1,
  246. Orientation.Horizontal,
  247. LineStyle.Single,
  248. 0,
  249. 0,
  250. 0,
  251. Orientation.Vertical,
  252. LineStyle.Double,
  253. "โ•Ÿ"
  254. )]
  255. [InlineData (
  256. 0,
  257. 0,
  258. -1,
  259. Orientation.Horizontal,
  260. LineStyle.Single,
  261. 0,
  262. 0,
  263. 0,
  264. Orientation.Vertical,
  265. LineStyle.Double,
  266. "โ•ข"
  267. )]
  268. [InlineData (
  269. 0,
  270. 0,
  271. 1,
  272. Orientation.Horizontal,
  273. LineStyle.Single,
  274. 0,
  275. 0,
  276. 0,
  277. Orientation.Vertical,
  278. LineStyle.Single,
  279. "โ”œ"
  280. )]
  281. [InlineData (
  282. 0,
  283. 0,
  284. -1,
  285. Orientation.Horizontal,
  286. LineStyle.Single,
  287. 0,
  288. 0,
  289. 0,
  290. Orientation.Vertical,
  291. LineStyle.Single,
  292. "โ”ค"
  293. )]
  294. [InlineData (
  295. 0,
  296. 0,
  297. 1,
  298. Orientation.Horizontal,
  299. LineStyle.Double,
  300. 0,
  301. 0,
  302. 0,
  303. Orientation.Vertical,
  304. LineStyle.Double,
  305. "โ• "
  306. )]
  307. [InlineData (
  308. 0,
  309. 0,
  310. -1,
  311. Orientation.Horizontal,
  312. LineStyle.Double,
  313. 0,
  314. 0,
  315. 0,
  316. Orientation.Vertical,
  317. LineStyle.Double,
  318. "โ•ฃ"
  319. )]
  320. // Vertical lines with a horizontal zero-length
  321. [InlineData (
  322. 0,
  323. 0,
  324. 1,
  325. Orientation.Vertical,
  326. LineStyle.Double,
  327. 0,
  328. 0,
  329. 0,
  330. Orientation.Horizontal,
  331. LineStyle.Single,
  332. "โ•ฅ"
  333. )]
  334. [InlineData (
  335. 0,
  336. 0,
  337. -1,
  338. Orientation.Vertical,
  339. LineStyle.Double,
  340. 0,
  341. 0,
  342. 0,
  343. Orientation.Horizontal,
  344. LineStyle.Single,
  345. "โ•จ"
  346. )]
  347. [InlineData (
  348. 0,
  349. 0,
  350. 1,
  351. Orientation.Vertical,
  352. LineStyle.Single,
  353. 0,
  354. 0,
  355. 0,
  356. Orientation.Horizontal,
  357. LineStyle.Double,
  358. "โ•ค"
  359. )]
  360. [InlineData (
  361. 0,
  362. 0,
  363. -1,
  364. Orientation.Vertical,
  365. LineStyle.Single,
  366. 0,
  367. 0,
  368. 0,
  369. Orientation.Horizontal,
  370. LineStyle.Double,
  371. "โ•ง"
  372. )]
  373. [InlineData (
  374. 0,
  375. 0,
  376. 1,
  377. Orientation.Vertical,
  378. LineStyle.Single,
  379. 0,
  380. 0,
  381. 0,
  382. Orientation.Horizontal,
  383. LineStyle.Single,
  384. "โ”ฌ"
  385. )]
  386. [InlineData (
  387. 0,
  388. 0,
  389. -1,
  390. Orientation.Vertical,
  391. LineStyle.Single,
  392. 0,
  393. 0,
  394. 0,
  395. Orientation.Horizontal,
  396. LineStyle.Single,
  397. "โ”ด"
  398. )]
  399. [InlineData (
  400. 0,
  401. 0,
  402. 1,
  403. Orientation.Vertical,
  404. LineStyle.Double,
  405. 0,
  406. 0,
  407. 0,
  408. Orientation.Horizontal,
  409. LineStyle.Double,
  410. "โ•ฆ"
  411. )]
  412. [InlineData (
  413. 0,
  414. 0,
  415. -1,
  416. Orientation.Vertical,
  417. LineStyle.Double,
  418. 0,
  419. 0,
  420. 0,
  421. Orientation.Horizontal,
  422. LineStyle.Double,
  423. "โ•ฉ"
  424. )]
  425. // Crosses (two zero-length)
  426. [InlineData (
  427. 0,
  428. 0,
  429. 0,
  430. Orientation.Vertical,
  431. LineStyle.Double,
  432. 0,
  433. 0,
  434. 0,
  435. Orientation.Horizontal,
  436. LineStyle.Single,
  437. "โ•ซ"
  438. )]
  439. [InlineData (
  440. 0,
  441. 0,
  442. 0,
  443. Orientation.Vertical,
  444. LineStyle.Single,
  445. 0,
  446. 0,
  447. 0,
  448. Orientation.Horizontal,
  449. LineStyle.Double,
  450. "โ•ช"
  451. )]
  452. [InlineData (
  453. 0,
  454. 0,
  455. 0,
  456. Orientation.Vertical,
  457. LineStyle.Single,
  458. 0,
  459. 0,
  460. 0,
  461. Orientation.Horizontal,
  462. LineStyle.Single,
  463. "โ”ผ"
  464. )]
  465. [InlineData (
  466. 0,
  467. 0,
  468. 0,
  469. Orientation.Vertical,
  470. LineStyle.Double,
  471. 0,
  472. 0,
  473. 0,
  474. Orientation.Horizontal,
  475. LineStyle.Double,
  476. "โ•ฌ"
  477. )]
  478. public void Add_2_Lines (
  479. int x1,
  480. int y1,
  481. int len1,
  482. Orientation o1,
  483. LineStyle s1,
  484. int x2,
  485. int y2,
  486. int len2,
  487. Orientation o2,
  488. LineStyle s2,
  489. string expected
  490. )
  491. {
  492. IDriver driver = CreateFakeDriver ();
  493. View v = GetCanvas (driver, out LineCanvas lc);
  494. v.Width = 10;
  495. v.Height = 10;
  496. v.Viewport = new (0, 0, 10, 10);
  497. lc.AddLine (new (x1, y1), len1, o1, s1);
  498. lc.AddLine (new (x2, y2), len2, o2, s2);
  499. OutputAssert.AssertEqual (output, expected, lc.ToString ());
  500. v.Dispose ();
  501. }
  502. [Fact]
  503. public void Viewport_Specific ()
  504. {
  505. // Draw at 1,1 within client area of View (i.e. leave a top and left margin of 1)
  506. // This proves we aren't drawing excess above
  507. var x = 1;
  508. var y = 2;
  509. var width = 3;
  510. var height = 2;
  511. var lc = new LineCanvas ();
  512. // 01230
  513. // โ•”โ•กโ•žโ•—1
  514. // โ•‘ โ•‘2
  515. // Add a short horiz line for โ•”โ•ก
  516. lc.AddLine (new (x, y), 2, Orientation.Horizontal, LineStyle.Double);
  517. Assert.Equal (new (x, y, 2, 1), lc.Bounds);
  518. //LHS line down
  519. lc.AddLine (new (x, y), height, Orientation.Vertical, LineStyle.Double);
  520. Assert.Equal (new (x, y, 2, 2), lc.Bounds);
  521. //Vertical line before Title, results in a โ•ก
  522. lc.AddLine (new (x + 1, y), 0, Orientation.Vertical, LineStyle.Single);
  523. Assert.Equal (new (x, y, 2, 2), lc.Bounds);
  524. //Vertical line after Title, results in a โ•ž
  525. lc.AddLine (new (x + 2, y), 0, Orientation.Vertical, LineStyle.Single);
  526. Assert.Equal (new (x, y, 3, 2), lc.Bounds);
  527. // remainder of top line
  528. lc.AddLine (new (x + 2, y), width - 1, Orientation.Horizontal, LineStyle.Double);
  529. Assert.Equal (new (x, y, 4, 2), lc.Bounds);
  530. //RHS line down
  531. lc.AddLine (new (x + width, y), height, Orientation.Vertical, LineStyle.Double);
  532. Assert.Equal (new (x, y, 4, 2), lc.Bounds);
  533. OutputAssert.AssertEqual (
  534. output,
  535. @"
  536. โ•”โ•กโ•žโ•—
  537. โ•‘ โ•‘",
  538. $"{Environment.NewLine}{lc}"
  539. );
  540. }
  541. [Fact]
  542. public void Viewport_Specific_With_Ustring ()
  543. {
  544. // Draw at 1,1 within client area of View (i.e. leave a top and left margin of 1)
  545. // This proves we aren't drawing excess above
  546. var x = 1;
  547. var y = 2;
  548. var width = 3;
  549. var height = 2;
  550. var lc = new LineCanvas ();
  551. // 01230
  552. // โ•”โ•กโ•žโ•—1
  553. // โ•‘ โ•‘2
  554. // Add a short horiz line for โ•”โ•ก
  555. lc.AddLine (new (x, y), 2, Orientation.Horizontal, LineStyle.Double);
  556. Assert.Equal (new (x, y, 2, 1), lc.Bounds);
  557. //LHS line down
  558. lc.AddLine (new (x, y), height, Orientation.Vertical, LineStyle.Double);
  559. Assert.Equal (new (x, y, 2, 2), lc.Bounds);
  560. //Vertical line before Title, results in a โ•ก
  561. lc.AddLine (new (x + 1, y), 0, Orientation.Vertical, LineStyle.Single);
  562. Assert.Equal (new (x, y, 2, 2), lc.Bounds);
  563. //Vertical line after Title, results in a โ•ž
  564. lc.AddLine (new (x + 2, y), 0, Orientation.Vertical, LineStyle.Single);
  565. Assert.Equal (new (x, y, 3, 2), lc.Bounds);
  566. // remainder of top line
  567. lc.AddLine (new (x + 2, y), width - 1, Orientation.Horizontal, LineStyle.Double);
  568. Assert.Equal (new (x, y, 4, 2), lc.Bounds);
  569. //RHS line down
  570. lc.AddLine (new (x + width, y), height, Orientation.Vertical, LineStyle.Double);
  571. Assert.Equal (new (x, y, 4, 2), lc.Bounds);
  572. OutputAssert.AssertEqual (
  573. output,
  574. @"
  575. โ•”โ•กโ•žโ•—
  576. โ•‘ โ•‘",
  577. $"{Environment.NewLine}{lc}"
  578. );
  579. }
  580. [Fact]
  581. public void Canvas_Updates_On_Changes ()
  582. {
  583. var lc = new LineCanvas ();
  584. Assert.Equal (Rectangle.Empty, lc.Bounds);
  585. lc.AddLine (Point.Empty, 2, Orientation.Horizontal, LineStyle.Double);
  586. Assert.NotEqual (Rectangle.Empty, lc.Bounds);
  587. lc.Clear ();
  588. Assert.Equal (Rectangle.Empty, lc.Bounds);
  589. }
  590. [InlineData (0, 0, Orientation.Horizontal, "โ”€")]
  591. [InlineData (1, 0, Orientation.Horizontal, "โ”€")]
  592. [InlineData (0, 1, Orientation.Horizontal, "โ”€")]
  593. [InlineData (-1, 0, Orientation.Horizontal, "โ”€")]
  594. [InlineData (0, -1, Orientation.Horizontal, "โ”€")]
  595. [InlineData (-1, -1, Orientation.Horizontal, "โ”€")]
  596. [InlineData (0, 0, Orientation.Vertical, "โ”‚")]
  597. [InlineData (1, 0, Orientation.Vertical, "โ”‚")]
  598. [InlineData (0, 1, Orientation.Vertical, "โ”‚")]
  599. [InlineData (0, -1, Orientation.Vertical, "โ”‚")]
  600. [InlineData (-1, 0, Orientation.Vertical, "โ”‚")]
  601. [InlineData (-1, -1, Orientation.Vertical, "โ”‚")]
  602. [Theory]
  603. public void Length_0_Is_1_Long (int x, int y, Orientation orientation, string expected)
  604. {
  605. LineCanvas canvas = new ();
  606. // Add a line at 5, 5 that's has length of 1
  607. canvas.AddLine (new (x, y), 1, orientation, LineStyle.Single);
  608. OutputAssert.AssertEqual (output, $"{expected}", $"{canvas}");
  609. }
  610. // X is offset by 2
  611. [InlineData (0, 0, 1, Orientation.Horizontal, "โ”€")]
  612. [InlineData (1, 0, 1, Orientation.Horizontal, "โ”€")]
  613. [InlineData (0, 1, 1, Orientation.Horizontal, "โ”€")]
  614. [InlineData (0, 0, 1, Orientation.Vertical, "โ”‚")]
  615. [InlineData (1, 0, 1, Orientation.Vertical, "โ”‚")]
  616. [InlineData (0, 1, 1, Orientation.Vertical, "โ”‚")]
  617. [InlineData (-1, 0, 1, Orientation.Horizontal, "โ”€")]
  618. [InlineData (0, -1, 1, Orientation.Horizontal, "โ”€")]
  619. [InlineData (-1, 0, 1, Orientation.Vertical, "โ”‚")]
  620. [InlineData (0, -1, 1, Orientation.Vertical, "โ”‚")]
  621. [InlineData (0, 0, -1, Orientation.Horizontal, "โ”€")]
  622. [InlineData (1, 0, -1, Orientation.Horizontal, "โ”€")]
  623. [InlineData (0, 1, -1, Orientation.Horizontal, "โ”€")]
  624. [InlineData (0, 0, -1, Orientation.Vertical, "โ”‚")]
  625. [InlineData (1, 0, -1, Orientation.Vertical, "โ”‚")]
  626. [InlineData (0, 1, -1, Orientation.Vertical, "โ”‚")]
  627. [InlineData (-1, 0, -1, Orientation.Horizontal, "โ”€")]
  628. [InlineData (0, -1, -1, Orientation.Horizontal, "โ”€")]
  629. [InlineData (-1, 0, -1, Orientation.Vertical, "โ”‚")]
  630. [InlineData (0, -1, -1, Orientation.Vertical, "โ”‚")]
  631. [InlineData (0, 0, 2, Orientation.Horizontal, "โ”€โ”€")]
  632. [InlineData (1, 0, 2, Orientation.Horizontal, "โ”€โ”€")]
  633. [InlineData (0, 1, 2, Orientation.Horizontal, "โ”€โ”€")]
  634. [InlineData (1, 1, 2, Orientation.Horizontal, "โ”€โ”€")]
  635. [InlineData (0, 0, 2, Orientation.Vertical, "โ”‚\r\nโ”‚")]
  636. [InlineData (1, 0, 2, Orientation.Vertical, "โ”‚\r\nโ”‚")]
  637. [InlineData (0, 1, 2, Orientation.Vertical, "โ”‚\r\nโ”‚")]
  638. [InlineData (1, 1, 2, Orientation.Vertical, "โ”‚\r\nโ”‚")]
  639. [InlineData (-1, 0, 2, Orientation.Horizontal, "โ”€โ”€")]
  640. [InlineData (0, -1, 2, Orientation.Horizontal, "โ”€โ”€")]
  641. [InlineData (-1, 0, 2, Orientation.Vertical, "โ”‚\r\nโ”‚")]
  642. [InlineData (0, -1, 2, Orientation.Vertical, "โ”‚\r\nโ”‚")]
  643. [InlineData (-1, -1, 2, Orientation.Vertical, "โ”‚\r\nโ”‚")]
  644. [InlineData (0, 0, -2, Orientation.Horizontal, "โ”€โ”€")]
  645. [InlineData (1, 0, -2, Orientation.Horizontal, "โ”€โ”€")]
  646. [InlineData (0, 1, -2, Orientation.Horizontal, "โ”€โ”€")]
  647. [InlineData (0, 0, -2, Orientation.Vertical, "โ”‚\r\nโ”‚")]
  648. [InlineData (1, 0, -2, Orientation.Vertical, "โ”‚\r\nโ”‚")]
  649. [InlineData (0, 1, -2, Orientation.Vertical, "โ”‚\r\nโ”‚")]
  650. [InlineData (1, 1, -2, Orientation.Vertical, "โ”‚\r\nโ”‚")]
  651. [InlineData (-1, 0, -2, Orientation.Horizontal, "โ”€โ”€")]
  652. [InlineData (0, -1, -2, Orientation.Horizontal, "โ”€โ”€")]
  653. [InlineData (-1, 0, -2, Orientation.Vertical, "โ”‚\r\nโ”‚")]
  654. [InlineData (0, -1, -2, Orientation.Vertical, "โ”‚\r\nโ”‚")]
  655. [InlineData (-1, -1, -2, Orientation.Vertical, "โ”‚\r\nโ”‚")]
  656. [Theory]
  657. public void Length_n_Is_n_Long (int x, int y, int length, Orientation orientation, string expected)
  658. {
  659. LineCanvas canvas = new ();
  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. LineCanvas canvas = new ();
  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. LineCanvas canvas = new ();
  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. LineCanvas canvas = new ();
  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. LineCanvas canvas = new ();
  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. โ”€",
  842. $"{Environment.NewLine}{canvas}"
  843. );
  844. }
  845. [Fact]
  846. public void ToString_Empty ()
  847. {
  848. var lc = new LineCanvas ();
  849. OutputAssert.AssertEqual (output, string.Empty, lc.ToString ());
  850. }
  851. // 012
  852. [InlineData (0, 0, "โ•โ•โ•")]
  853. [InlineData (1, 0, "โ•โ•โ•")]
  854. [InlineData (0, 1, "โ•โ•โ•")]
  855. [InlineData (1, 1, "โ•โ•โ•")]
  856. [InlineData (2, 2, "โ•โ•โ•")]
  857. [InlineData (-1, 0, "โ•โ•โ•")]
  858. [InlineData (0, -1, "โ•โ•โ•")]
  859. [InlineData (-1, -1, "โ•โ•โ•")]
  860. [InlineData (-2, -2, "โ•โ•โ•")]
  861. [Theory]
  862. public void ToString_Positive_Horizontal_1Line_Offset (int x, int y, string expected)
  863. {
  864. var lc = new LineCanvas ();
  865. lc.AddLine (new (x, y), 3, Orientation.Horizontal, LineStyle.Double);
  866. OutputAssert.AssertEqual (output, expected, $"{lc}");
  867. }
  868. [InlineData (0, 0, 0, 0, "โ•โ•โ•")]
  869. [InlineData (1, 0, 1, 0, "โ•โ•โ•")]
  870. [InlineData (-1, 0, -1, 0, "โ•โ•โ•")]
  871. [InlineData (0, 0, 1, 0, "โ•โ•โ•โ•")]
  872. [InlineData (1, 0, 3, 0, "โ•โ•โ•โ•โ•")]
  873. [InlineData (1, 0, 4, 0, "โ•โ•โ•โ•โ•โ•")]
  874. [InlineData (1, 0, 5, 0, "โ•โ•โ• โ•โ•โ•")]
  875. [InlineData (0, 0, 0, 1, "\u2550\u2550\u2550\r\n\u2550\u2550\u2550")]
  876. [InlineData (0, 0, 1, 1, "โ•โ•โ• \r\n โ•โ•โ•")]
  877. [InlineData (0, 0, 2, 1, "โ•โ•โ• \r\n โ•โ•โ•")]
  878. [InlineData (1, 0, 0, 1, " โ•โ•โ•\r\nโ•โ•โ• ")]
  879. [InlineData (0, 1, 0, 1, "โ•โ•โ•")]
  880. [InlineData (1, 1, 0, 1, "โ•โ•โ•โ•")]
  881. [InlineData (2, 2, 0, 1, "โ•โ•โ• \r\n โ•โ•โ•")]
  882. [Theory]
  883. public void ToString_Positive_Horizontal_2Line_Offset (int x1, int y1, int x2, int y2, string expected)
  884. {
  885. var lc = new LineCanvas ();
  886. lc.AddLine (new (x1, y1), 3, Orientation.Horizontal, LineStyle.Double);
  887. lc.AddLine (new (x2, y2), 3, Orientation.Horizontal, LineStyle.Double);
  888. OutputAssert.AssertEqual (output, expected, $"{lc}");
  889. }
  890. // [Fact, SetupFakeDriver]
  891. // public void LeaveMargin_Top1_Left1 ()
  892. // {
  893. // var canvas = new LineCanvas ();
  894. // // Upper box
  895. // canvas.AddLine (Point.Empty, 9, Orientation.Horizontal, LineStyle.Single);
  896. // canvas.AddLine (new Point (8, 0), 3, Orientation.Vertical, LineStyle.Single);
  897. // canvas.AddLine (new Point (8, 3), -9, Orientation.Horizontal, LineStyle.Single);
  898. // canvas.AddLine (new Point (0, 2), -3, Orientation.Vertical, LineStyle.Single);
  899. // // Lower Box
  900. // canvas.AddLine (new Point (5, 0), 2, Orientation.Vertical, LineStyle.Single);
  901. // canvas.AddLine (new Point (0, 2), 9, Orientation.Horizontal, LineStyle.Single);
  902. // string looksLike =
  903. //@"
  904. //โ”Œโ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”
  905. //โ”‚ โ”‚ โ”‚
  906. //โ”œโ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”ค
  907. //โ””โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”˜
  908. //";
  909. // Assert.Equal (looksLike, $"{Environment.NewLine}{canvas}");
  910. // }
  911. [InlineData (0, 0, 0, Orientation.Horizontal, LineStyle.Double, "โ•")]
  912. [InlineData (0, 0, 0, Orientation.Vertical, LineStyle.Double, "โ•‘")]
  913. [InlineData (0, 0, 0, Orientation.Horizontal, LineStyle.Single, "โ”€")]
  914. [InlineData (0, 0, 0, Orientation.Vertical, LineStyle.Single, "โ”‚")]
  915. [InlineData (0, 0, 1, Orientation.Horizontal, LineStyle.Double, "โ•")]
  916. [InlineData (0, 0, 1, Orientation.Vertical, LineStyle.Double, "โ•‘")]
  917. [InlineData (0, 0, 1, Orientation.Horizontal, LineStyle.Single, "โ”€")]
  918. [InlineData (0, 0, 1, Orientation.Vertical, LineStyle.Single, "โ”‚")]
  919. [InlineData (0, 0, 2, Orientation.Horizontal, LineStyle.Double, "โ•โ•")]
  920. [InlineData (0, 0, 2, Orientation.Vertical, LineStyle.Double, "โ•‘\nโ•‘")]
  921. [InlineData (0, 0, 2, Orientation.Horizontal, LineStyle.Single, "โ”€โ”€")]
  922. [InlineData (0, 0, 2, Orientation.Vertical, LineStyle.Single, "โ”‚\nโ”‚")]
  923. [Theory]
  924. public void View_Draws_1LineTests (
  925. int x1,
  926. int y1,
  927. int length,
  928. Orientation o1,
  929. LineStyle s1,
  930. string expected
  931. )
  932. {
  933. var driver = CreateFakeDriver ();
  934. View v = GetCanvas (driver, out LineCanvas lc);
  935. v.Width = 10;
  936. v.Height = 10;
  937. v.Viewport = new (0, 0, 10, 10);
  938. lc.AddLine (new (x1, y1), length, o1, s1);
  939. v.Draw ();
  940. DriverAssert.AssertDriverContentsAre (expected, output, driver);
  941. v.Dispose ();
  942. }
  943. /// <summary>This test demonstrates how to correctly trigger a corner. By overlapping the lines in the same cell</summary>
  944. [Fact]
  945. public void View_Draws_Corner_Correct ()
  946. {
  947. var driver = CreateFakeDriver ();
  948. View v = GetCanvas (driver, out LineCanvas canvas);
  949. canvas.AddLine (Point.Empty, 2, Orientation.Horizontal, LineStyle.Single);
  950. canvas.AddLine (Point.Empty, 2, Orientation.Vertical, LineStyle.Single);
  951. v.Draw ();
  952. var looksLike =
  953. @"
  954. โ”Œโ”€
  955. โ”‚";
  956. DriverAssert.AssertDriverContentsAre (looksLike, output, driver);
  957. v.Dispose ();
  958. }
  959. /// <summary>
  960. /// This test demonstrates that corners are only drawn when lines overlap. Not when they terminate adjacent to one
  961. /// another.
  962. /// </summary>
  963. [Fact]
  964. public void View_Draws_Corner_NoOverlap ()
  965. {
  966. var driver = CreateFakeDriver ();
  967. View v = GetCanvas (driver, out LineCanvas canvas);
  968. canvas.AddLine (Point.Empty, 2, Orientation.Horizontal, LineStyle.Single);
  969. canvas.AddLine (new (0, 1), 2, Orientation.Vertical, LineStyle.Single);
  970. v.Draw ();
  971. var looksLike =
  972. @"
  973. โ”€โ”€
  974. โ”‚
  975. โ”‚";
  976. DriverAssert.AssertDriverContentsAre (looksLike, output, driver);
  977. v.Dispose ();
  978. }
  979. [InlineData (LineStyle.Single)]
  980. [InlineData (LineStyle.Rounded)]
  981. [Theory]
  982. public void View_Draws_Horizontal (LineStyle style)
  983. {
  984. var driver = CreateFakeDriver ();
  985. View v = GetCanvas (driver, out LineCanvas canvas);
  986. canvas.AddLine (Point.Empty, 2, Orientation.Horizontal, style);
  987. v.Draw ();
  988. var looksLike =
  989. @"
  990. โ”€โ”€";
  991. DriverAssert.AssertDriverContentsAre (looksLike, output, driver);
  992. v.Dispose ();
  993. }
  994. [Fact]
  995. public void View_Draws_Horizontal_Double ()
  996. {
  997. var driver = CreateFakeDriver ();
  998. View v = GetCanvas (driver, out LineCanvas canvas);
  999. canvas.AddLine (Point.Empty, 2, Orientation.Horizontal, LineStyle.Double);
  1000. v.Draw ();
  1001. var looksLike =
  1002. @"
  1003. โ•โ•";
  1004. DriverAssert.AssertDriverContentsAre (looksLike, output, driver);
  1005. v.Dispose ();
  1006. }
  1007. [InlineData (LineStyle.Single)]
  1008. [InlineData (LineStyle.Rounded)]
  1009. [Theory]
  1010. public void View_Draws_Vertical (LineStyle style)
  1011. {
  1012. var driver = CreateFakeDriver ();
  1013. View v = GetCanvas (driver, out LineCanvas canvas);
  1014. canvas.AddLine (Point.Empty, 2, Orientation.Vertical, style);
  1015. v.Draw ();
  1016. var looksLike =
  1017. @"
  1018. โ”‚
  1019. โ”‚";
  1020. DriverAssert.AssertDriverContentsAre (looksLike, output, driver);
  1021. v.Dispose ();
  1022. }
  1023. [Fact]
  1024. public void View_Draws_Vertical_Double ()
  1025. {
  1026. var driver = CreateFakeDriver ();
  1027. View v = GetCanvas (driver, out LineCanvas canvas);
  1028. canvas.AddLine (Point.Empty, 2, Orientation.Vertical, LineStyle.Double);
  1029. v.Draw ();
  1030. var looksLike =
  1031. @"
  1032. โ•‘
  1033. โ•‘";
  1034. DriverAssert.AssertDriverContentsAre (looksLike, output, driver);
  1035. v.Dispose ();
  1036. }
  1037. [Fact]
  1038. public void View_Draws_Window_Double ()
  1039. {
  1040. var driver = CreateFakeDriver ();
  1041. View v = GetCanvas (driver, out LineCanvas canvas);
  1042. // outer box
  1043. canvas.AddLine (Point.Empty, 10, Orientation.Horizontal, LineStyle.Double);
  1044. canvas.AddLine (new (9, 0), 5, Orientation.Vertical, LineStyle.Double);
  1045. canvas.AddLine (new (9, 4), -10, Orientation.Horizontal, LineStyle.Double);
  1046. canvas.AddLine (new (0, 4), -5, Orientation.Vertical, LineStyle.Double);
  1047. canvas.AddLine (new (5, 0), 5, Orientation.Vertical, LineStyle.Double);
  1048. canvas.AddLine (new (0, 2), 10, Orientation.Horizontal, LineStyle.Double);
  1049. v.Draw ();
  1050. var looksLike =
  1051. @"
  1052. โ•”โ•โ•โ•โ•โ•ฆโ•โ•โ•โ•—
  1053. โ•‘ โ•‘ โ•‘
  1054. โ• โ•โ•โ•โ•โ•ฌโ•โ•โ•โ•ฃ
  1055. โ•‘ โ•‘ โ•‘
  1056. โ•šโ•โ•โ•โ•โ•ฉโ•โ•โ•โ•";
  1057. DriverAssert.AssertDriverContentsAre (looksLike, output, driver);
  1058. v.Dispose ();
  1059. }
  1060. [Theory]
  1061. [InlineData (LineStyle.Single)]
  1062. [InlineData (LineStyle.Rounded)]
  1063. public void View_Draws_Window_DoubleTop_SingleSides (LineStyle thinStyle)
  1064. {
  1065. var driver = CreateFakeDriver ();
  1066. View v = GetCanvas (driver, out LineCanvas canvas);
  1067. // outer box
  1068. canvas.AddLine (Point.Empty, 10, Orientation.Horizontal, LineStyle.Double);
  1069. canvas.AddLine (new (9, 0), 5, Orientation.Vertical, thinStyle);
  1070. canvas.AddLine (new (9, 4), -10, Orientation.Horizontal, LineStyle.Double);
  1071. canvas.AddLine (new (0, 4), -5, Orientation.Vertical, thinStyle);
  1072. canvas.AddLine (new (5, 0), 5, Orientation.Vertical, thinStyle);
  1073. canvas.AddLine (new (0, 2), 10, Orientation.Horizontal, LineStyle.Double);
  1074. v.Draw ();
  1075. var looksLike =
  1076. @"
  1077. โ•’โ•โ•โ•โ•โ•คโ•โ•โ•โ••
  1078. โ”‚ โ”‚ โ”‚
  1079. โ•žโ•โ•โ•โ•โ•ชโ•โ•โ•โ•ก
  1080. โ”‚ โ”‚ โ”‚
  1081. โ•˜โ•โ•โ•โ•โ•งโ•โ•โ•โ•›
  1082. ";
  1083. DriverAssert.AssertDriverContentsAre (looksLike, output, driver);
  1084. v.Dispose ();
  1085. }
  1086. /// <summary>
  1087. /// Demonstrates when <see cref="LineStyle.Rounded"/> corners are used. Notice how not all lines declare rounded.
  1088. /// If there are 1+ lines intersecting and a corner is to be used then if any of them are rounded a rounded corner is
  1089. /// used.
  1090. /// </summary>
  1091. [Fact]
  1092. public void View_Draws_Window_Rounded ()
  1093. {
  1094. var driver = CreateFakeDriver ();
  1095. View v = GetCanvas (driver, out LineCanvas canvas);
  1096. // outer box
  1097. canvas.AddLine (Point.Empty, 10, Orientation.Horizontal, LineStyle.Rounded);
  1098. // LineStyle.Single is ignored because corner overlaps with the above line which is Rounded
  1099. // this results in a rounded corner being used.
  1100. canvas.AddLine (new (9, 0), 5, Orientation.Vertical, LineStyle.Single);
  1101. canvas.AddLine (new (9, 4), -10, Orientation.Horizontal, LineStyle.Rounded);
  1102. canvas.AddLine (new (0, 4), -5, Orientation.Vertical, LineStyle.Single);
  1103. // These lines say rounded but they will result in the T sections which are never rounded.
  1104. canvas.AddLine (new (5, 0), 5, Orientation.Vertical, LineStyle.Rounded);
  1105. canvas.AddLine (new (0, 2), 10, Orientation.Horizontal, LineStyle.Rounded);
  1106. v.Draw ();
  1107. var looksLike =
  1108. @"
  1109. โ•ญโ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ•ฎ
  1110. โ”‚ โ”‚ โ”‚
  1111. โ”œโ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”ค
  1112. โ”‚ โ”‚ โ”‚
  1113. โ•ฐโ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ•ฏ";
  1114. DriverAssert.AssertDriverContentsAre (looksLike, output, driver);
  1115. v.Dispose ();
  1116. }
  1117. [Theory]
  1118. [InlineData (LineStyle.Single)]
  1119. [InlineData (LineStyle.Rounded)]
  1120. public void View_Draws_Window_SingleTop_DoubleSides (LineStyle thinStyle)
  1121. {
  1122. var driver = CreateFakeDriver ();
  1123. View v = GetCanvas (driver, out LineCanvas canvas);
  1124. // outer box
  1125. canvas.AddLine (Point.Empty, 10, Orientation.Horizontal, thinStyle);
  1126. canvas.AddLine (new (9, 0), 5, Orientation.Vertical, LineStyle.Double);
  1127. canvas.AddLine (new (9, 4), -10, Orientation.Horizontal, thinStyle);
  1128. canvas.AddLine (new (0, 4), -5, Orientation.Vertical, LineStyle.Double);
  1129. canvas.AddLine (new (5, 0), 5, Orientation.Vertical, LineStyle.Double);
  1130. canvas.AddLine (new (0, 2), 10, Orientation.Horizontal, thinStyle);
  1131. v.Draw ();
  1132. var looksLike =
  1133. @"
  1134. โ•“โ”€โ”€โ”€โ”€โ•ฅโ”€โ”€โ”€โ•–
  1135. โ•‘ โ•‘ โ•‘
  1136. โ•Ÿโ”€โ”€โ”€โ”€โ•ซโ”€โ”€โ”€โ•ข
  1137. โ•‘ โ•‘ โ•‘
  1138. โ•™โ”€โ”€โ”€โ”€โ•จโ”€โ”€โ”€โ•œ
  1139. ";
  1140. DriverAssert.AssertDriverContentsAre (looksLike, output, driver);
  1141. v.Dispose ();
  1142. }
  1143. [Fact]
  1144. public void Window ()
  1145. {
  1146. LineCanvas canvas = new ();
  1147. // Frame
  1148. canvas.AddLine (Point.Empty, 10, Orientation.Horizontal, LineStyle.Single);
  1149. canvas.AddLine (new (9, 0), 5, Orientation.Vertical, LineStyle.Single);
  1150. canvas.AddLine (new (9, 4), -10, Orientation.Horizontal, LineStyle.Single);
  1151. canvas.AddLine (new (0, 4), -5, Orientation.Vertical, LineStyle.Single);
  1152. // Cross
  1153. canvas.AddLine (new (5, 0), 5, Orientation.Vertical, LineStyle.Single);
  1154. canvas.AddLine (new (0, 2), 10, Orientation.Horizontal, LineStyle.Single);
  1155. var looksLike =
  1156. @"
  1157. โ”Œโ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”
  1158. โ”‚ โ”‚ โ”‚
  1159. โ”œโ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”ค
  1160. โ”‚ โ”‚ โ”‚
  1161. โ””โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”˜";
  1162. OutputAssert.AssertEqual (output, looksLike, $"{Environment.NewLine}{canvas}");
  1163. }
  1164. [Fact]
  1165. public void Zero_Length_Intersections ()
  1166. {
  1167. // Draw at 1,2 within client area of View (i.e. leave a top and left margin of 1)
  1168. // This proves we aren't drawing excess above
  1169. var x = 1;
  1170. var y = 2;
  1171. var width = 5;
  1172. var height = 2;
  1173. var lc = new LineCanvas ();
  1174. // โ•”โ•กโ•žโ•โ•โ•โ•โ•โ•—
  1175. // Add a short horiz line for โ•”โ•ก
  1176. lc.AddLine (new (x, y), 2, Orientation.Horizontal, LineStyle.Double);
  1177. //LHS line down
  1178. lc.AddLine (new (x, y), height, Orientation.Vertical, LineStyle.Double);
  1179. //Vertical line before Title, results in a โ•ก
  1180. lc.AddLine (new (x + 1, y), 0, Orientation.Vertical, LineStyle.Single);
  1181. //Vertical line after Title, results in a โ•ž
  1182. lc.AddLine (new (x + 2, y), 0, Orientation.Vertical, LineStyle.Single);
  1183. // remainder of top line
  1184. lc.AddLine (new (x + 2, y), width - 1, Orientation.Horizontal, LineStyle.Double);
  1185. //RHS line down
  1186. lc.AddLine (new (x + width, y), height, Orientation.Vertical, LineStyle.Double);
  1187. var looksLike = @"
  1188. โ•”โ•กโ•žโ•โ•โ•—
  1189. โ•‘ โ•‘";
  1190. OutputAssert.AssertEqual (output, looksLike, $"{Environment.NewLine}{lc}");
  1191. }
  1192. [Fact]
  1193. public void LineCanvas_UsesFillCorrectly ()
  1194. {
  1195. // Arrange
  1196. var foregroundColor = new Color (255, 0); // Red
  1197. var backgroundColor = new Color (0, 0); // Black
  1198. var foregroundFill = new SolidFill (foregroundColor);
  1199. var backgroundFill = new SolidFill (backgroundColor);
  1200. var fillPair = new FillPair (foregroundFill, backgroundFill);
  1201. var lineCanvas = new LineCanvas
  1202. {
  1203. Fill = fillPair
  1204. };
  1205. // Act
  1206. lineCanvas.AddLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single);
  1207. Dictionary<Point, Cell?> cellMap = lineCanvas.GetCellMap ();
  1208. // Assert
  1209. foreach (Cell? cell in cellMap.Values)
  1210. {
  1211. Assert.NotNull (cell);
  1212. Assert.Equal (foregroundColor, cell.Value.Attribute!.Value.Foreground);
  1213. Assert.Equal (backgroundColor, cell.Value.Attribute.Value.Background);
  1214. }
  1215. }
  1216. [Fact]
  1217. public void LineCanvas_LineColorIgnoredBecauseOfFill ()
  1218. {
  1219. // Arrange
  1220. var foregroundColor = new Color (255, 0); // Red
  1221. var backgroundColor = new Color (0, 0); // Black
  1222. var lineColor = new Attribute (new Color (0, 255), new Color (255, 255, 255)); // Green on White
  1223. var foregroundFill = new SolidFill (foregroundColor);
  1224. var backgroundFill = new SolidFill (backgroundColor);
  1225. var fillPair = new FillPair (foregroundFill, backgroundFill);
  1226. var lineCanvas = new LineCanvas
  1227. {
  1228. Fill = fillPair
  1229. };
  1230. // Act
  1231. lineCanvas.AddLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single, lineColor);
  1232. Dictionary<Point, Cell?> cellMap = lineCanvas.GetCellMap ();
  1233. // Assert
  1234. foreach (Cell? cell in cellMap.Values)
  1235. {
  1236. Assert.NotNull (cell);
  1237. Assert.Equal (foregroundColor, cell.Value.Attribute!.Value.Foreground);
  1238. Assert.Equal (backgroundColor, cell.Value.Attribute.Value.Background);
  1239. }
  1240. }
  1241. [Fact]
  1242. public void LineCanvas_IntersectingLinesUseFillCorrectly ()
  1243. {
  1244. // Arrange
  1245. var foregroundColor = new Color (255, 0); // Red
  1246. var backgroundColor = new Color (0, 0); // Black
  1247. var foregroundFill = new SolidFill (foregroundColor);
  1248. var backgroundFill = new SolidFill (backgroundColor);
  1249. var fillPair = new FillPair (foregroundFill, backgroundFill);
  1250. var lineCanvas = new LineCanvas
  1251. {
  1252. Fill = fillPair
  1253. };
  1254. // Act
  1255. lineCanvas.AddLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single);
  1256. lineCanvas.AddLine (new (2, -2), 5, Orientation.Vertical, LineStyle.Single);
  1257. Dictionary<Point, Cell?> cellMap = lineCanvas.GetCellMap ();
  1258. // Assert
  1259. foreach (Cell? cell in cellMap.Values)
  1260. {
  1261. Assert.NotNull (cell);
  1262. Assert.Equal (foregroundColor, cell.Value.Attribute!.Value.Foreground);
  1263. Assert.Equal (backgroundColor, cell.Value.Attribute.Value.Background);
  1264. }
  1265. }
  1266. // TODO: Remove this and make all LineCanvas tests independent of View
  1267. /// <summary>
  1268. /// Creates a new <see cref="View"/> into which a <see cref="LineCanvas"/> is rendered at
  1269. /// <see cref="View.DrawComplete"/> time.
  1270. /// </summary>
  1271. /// <param name="canvas">The <see cref="LineCanvas"/> you can draw into.</param>
  1272. /// <param name="offsetX">How far to offset drawing in X</param>
  1273. /// <param name="offsetY">How far to offset drawing in Y</param>
  1274. /// <returns></returns>
  1275. private View GetCanvas (IDriver driver, out LineCanvas canvas, int offsetX = 0, int offsetY = 0)
  1276. {
  1277. var v = new View { Width = 10, Height = 5, Viewport = new (0, 0, 10, 5) };
  1278. v.Driver = driver;
  1279. LineCanvas canvasCopy = canvas = new ();
  1280. v.DrawComplete += (s, e) =>
  1281. {
  1282. v.FillRect (v.Viewport);
  1283. foreach (KeyValuePair<Point, Rune> p in canvasCopy.GetMap ())
  1284. {
  1285. v.AddRune (
  1286. offsetX + p.Key.X,
  1287. offsetY + p.Key.Y,
  1288. p.Value
  1289. );
  1290. }
  1291. canvasCopy.Clear ();
  1292. };
  1293. return v;
  1294. }
  1295. #region GetRegion Tests
  1296. [Fact]
  1297. public void GetRegion_EmptyCellMap_ReturnsEmptyRegion ()
  1298. {
  1299. Dictionary<Point, Cell?> cellMap = new ();
  1300. Region region = LineCanvas.GetRegion (cellMap);
  1301. Assert.NotNull (region);
  1302. Assert.True (region.IsEmpty ());
  1303. }
  1304. [Fact]
  1305. public void GetRegion_SingleCell_ReturnsSingleRectangle ()
  1306. {
  1307. Dictionary<Point, Cell?> cellMap = new ()
  1308. {
  1309. { new Point (5, 10), new Cell { Grapheme = "X" } }
  1310. };
  1311. Region region = LineCanvas.GetRegion (cellMap);
  1312. Assert.NotNull (region);
  1313. Assert.False (region.IsEmpty ());
  1314. Assert.True (region.Contains (5, 10));
  1315. }
  1316. [Fact]
  1317. public void GetRegion_HorizontalLine_CreatesHorizontalSpan ()
  1318. {
  1319. Dictionary<Point, Cell?> cellMap = new ();
  1320. // Horizontal line from (5, 10) to (9, 10)
  1321. for (int x = 5; x <= 9; x++)
  1322. {
  1323. cellMap.Add (new Point (x, 10), new Cell { Grapheme = "โ”€" });
  1324. }
  1325. Region region = LineCanvas.GetRegion (cellMap);
  1326. Assert.NotNull (region);
  1327. // All cells in the horizontal span should be in the region
  1328. for (int x = 5; x <= 9; x++)
  1329. {
  1330. Assert.True (region.Contains (x, 10), $"Expected ({x}, 10) to be in region");
  1331. }
  1332. // Cells outside the span should not be in the region
  1333. Assert.False (region.Contains (4, 10));
  1334. Assert.False (region.Contains (10, 10));
  1335. Assert.False (region.Contains (7, 9));
  1336. Assert.False (region.Contains (7, 11));
  1337. }
  1338. [Fact]
  1339. public void GetRegion_VerticalLine_CreatesMultipleHorizontalSpans ()
  1340. {
  1341. Dictionary<Point, Cell?> cellMap = new ();
  1342. // Vertical line from (5, 10) to (5, 14)
  1343. for (int y = 10; y <= 14; y++)
  1344. {
  1345. cellMap.Add (new Point (5, y), new Cell { Grapheme = "โ”‚" });
  1346. }
  1347. Region region = LineCanvas.GetRegion (cellMap);
  1348. Assert.NotNull (region);
  1349. // All cells in the vertical line should be in the region
  1350. for (int y = 10; y <= 14; y++)
  1351. {
  1352. Assert.True (region.Contains (5, y), $"Expected (5, {y}) to be in region");
  1353. }
  1354. // Cells outside should not be in the region
  1355. Assert.False (region.Contains (4, 12));
  1356. Assert.False (region.Contains (6, 12));
  1357. }
  1358. [Fact]
  1359. public void GetRegion_LShape_CreatesCorrectSpans ()
  1360. {
  1361. Dictionary<Point, Cell?> cellMap = new ();
  1362. // L-shape: horizontal line from (0, 0) to (5, 0), then vertical to (5, 3)
  1363. for (int x = 0; x <= 5; x++)
  1364. {
  1365. cellMap.Add (new Point (x, 0), new Cell { Grapheme = "โ”€" });
  1366. }
  1367. for (int y = 1; y <= 3; y++)
  1368. {
  1369. cellMap.Add (new Point (5, y), new Cell { Grapheme = "โ”‚" });
  1370. }
  1371. Region region = LineCanvas.GetRegion (cellMap);
  1372. // Horizontal part
  1373. for (int x = 0; x <= 5; x++)
  1374. {
  1375. Assert.True (region.Contains (x, 0), $"Expected ({x}, 0) to be in region");
  1376. }
  1377. // Vertical part
  1378. for (int y = 1; y <= 3; y++)
  1379. {
  1380. Assert.True (region.Contains (5, y), $"Expected (5, {y}) to be in region");
  1381. }
  1382. // Empty cells should not be in region
  1383. Assert.False (region.Contains (1, 1));
  1384. Assert.False (region.Contains (4, 2));
  1385. }
  1386. [Fact]
  1387. public void GetRegion_DiscontiguousHorizontalCells_CreatesSeparateSpans ()
  1388. {
  1389. Dictionary<Point, Cell?> cellMap = new ()
  1390. {
  1391. { new Point (0, 5), new Cell { Grapheme = "X" } },
  1392. { new Point (1, 5), new Cell { Grapheme = "X" } },
  1393. // Gap at (2, 5)
  1394. { new Point (3, 5), new Cell { Grapheme = "X" } },
  1395. { new Point (4, 5), new Cell { Grapheme = "X" } }
  1396. };
  1397. Region region = LineCanvas.GetRegion (cellMap);
  1398. Assert.True (region.Contains (0, 5));
  1399. Assert.True (region.Contains (1, 5));
  1400. Assert.False (region.Contains (2, 5)); // Gap
  1401. Assert.True (region.Contains (3, 5));
  1402. Assert.True (region.Contains (4, 5));
  1403. }
  1404. [Fact]
  1405. public void GetRegion_IntersectingLines_CreatesCorrectRegion ()
  1406. {
  1407. Dictionary<Point, Cell?> cellMap = new ();
  1408. // Horizontal line
  1409. for (int x = 0; x <= 4; x++)
  1410. {
  1411. cellMap.Add (new Point (x, 2), new Cell { Grapheme = "โ”€" });
  1412. }
  1413. // Vertical line intersecting at (2, 2)
  1414. for (int y = 0; y <= 4; y++)
  1415. {
  1416. cellMap [new Point (2, y)] = new Cell { Grapheme = "โ”ผ" };
  1417. }
  1418. Region region = LineCanvas.GetRegion (cellMap);
  1419. // Horizontal line
  1420. for (int x = 0; x <= 4; x++)
  1421. {
  1422. Assert.True (region.Contains (x, 2), $"Expected ({x}, 2) to be in region");
  1423. }
  1424. // Vertical line
  1425. for (int y = 0; y <= 4; y++)
  1426. {
  1427. Assert.True (region.Contains (2, y), $"Expected (2, {y}) to be in region");
  1428. }
  1429. }
  1430. #endregion
  1431. #region GetCellMapWithRegion Tests
  1432. [Fact]
  1433. public void GetCellMapWithRegion_EmptyCanvas_ReturnsEmptyMapAndRegion ()
  1434. {
  1435. LineCanvas canvas = new ();
  1436. (Dictionary<Point, Cell?> cellMap, Region region) = canvas.GetCellMapWithRegion ();
  1437. Assert.NotNull (cellMap);
  1438. Assert.Empty (cellMap);
  1439. Assert.NotNull (region);
  1440. Assert.True (region.IsEmpty ());
  1441. }
  1442. [Fact]
  1443. public void GetCellMapWithRegion_SingleHorizontalLine_ReturnsCellMapAndRegion ()
  1444. {
  1445. LineCanvas canvas = new ();
  1446. canvas.AddLine (new Point (5, 10), 5, Orientation.Horizontal, LineStyle.Single);
  1447. (Dictionary<Point, Cell?> cellMap, Region region) = canvas.GetCellMapWithRegion ();
  1448. Assert.NotNull (cellMap);
  1449. Assert.NotEmpty (cellMap);
  1450. Assert.NotNull (region);
  1451. Assert.False (region.IsEmpty ());
  1452. // Both cellMap and region should contain the same cells
  1453. foreach (Point p in cellMap.Keys)
  1454. {
  1455. Assert.True (region.Contains (p.X, p.Y), $"Expected ({p.X}, {p.Y}) to be in region");
  1456. }
  1457. }
  1458. [Fact]
  1459. public void GetCellMapWithRegion_SingleVerticalLine_ReturnsCellMapAndRegion ()
  1460. {
  1461. LineCanvas canvas = new ();
  1462. canvas.AddLine (new Point (5, 10), 5, Orientation.Vertical, LineStyle.Single);
  1463. (Dictionary<Point, Cell?> cellMap, Region region) = canvas.GetCellMapWithRegion ();
  1464. Assert.NotNull (cellMap);
  1465. Assert.NotEmpty (cellMap);
  1466. Assert.NotNull (region);
  1467. Assert.False (region.IsEmpty ());
  1468. // Both cellMap and region should contain the same cells
  1469. foreach (Point p in cellMap.Keys)
  1470. {
  1471. Assert.True (region.Contains (p.X, p.Y), $"Expected ({p.X}, {p.Y}) to be in region");
  1472. }
  1473. }
  1474. [Fact]
  1475. public void GetCellMapWithRegion_IntersectingLines_CorrectlyHandlesIntersection ()
  1476. {
  1477. LineCanvas canvas = new ();
  1478. // Create a cross pattern
  1479. canvas.AddLine (new Point (0, 2), 5, Orientation.Horizontal, LineStyle.Single);
  1480. canvas.AddLine (new Point (2, 0), 5, Orientation.Vertical, LineStyle.Single);
  1481. (Dictionary<Point, Cell?> cellMap, Region region) = canvas.GetCellMapWithRegion ();
  1482. Assert.NotNull (cellMap);
  1483. Assert.NotEmpty (cellMap);
  1484. Assert.NotNull (region);
  1485. // Verify intersection point is in both
  1486. Assert.True (cellMap.ContainsKey (new Point (2, 2)), "Intersection should be in cellMap");
  1487. Assert.True (region.Contains (2, 2), "Intersection should be in region");
  1488. // All cells should be in both structures
  1489. foreach (Point p in cellMap.Keys)
  1490. {
  1491. Assert.True (region.Contains (p.X, p.Y), $"Expected ({p.X}, {p.Y}) to be in region");
  1492. }
  1493. }
  1494. [Fact]
  1495. public void GetCellMapWithRegion_ComplexShape_RegionMatchesCellMap ()
  1496. {
  1497. LineCanvas canvas = new ();
  1498. // Create a box
  1499. canvas.AddLine (new Point (0, 0), 5, Orientation.Horizontal, LineStyle.Single);
  1500. canvas.AddLine (new Point (0, 3), 5, Orientation.Horizontal, LineStyle.Single);
  1501. canvas.AddLine (new Point (0, 0), 4, Orientation.Vertical, LineStyle.Single);
  1502. canvas.AddLine (new Point (4, 0), 4, Orientation.Vertical, LineStyle.Single);
  1503. (Dictionary<Point, Cell?> cellMap, Region region) = canvas.GetCellMapWithRegion ();
  1504. Assert.NotNull (cellMap);
  1505. Assert.NotEmpty (cellMap);
  1506. Assert.NotNull (region);
  1507. // Every cell in the map should be in the region
  1508. foreach (Point p in cellMap.Keys)
  1509. {
  1510. Assert.True (region.Contains (p.X, p.Y), $"Expected ({p.X}, {p.Y}) to be in region");
  1511. }
  1512. // Cells not in the map should not be in the region (interior of box)
  1513. Assert.False (cellMap.ContainsKey (new Point (2, 1)));
  1514. // Note: Region might contain interior if it's filled, so we just verify consistency
  1515. }
  1516. [Fact]
  1517. public void GetCellMapWithRegion_ResultsMatchSeparateCalls ()
  1518. {
  1519. LineCanvas canvas = new ();
  1520. // Create a complex pattern
  1521. canvas.AddLine (new Point (0, 0), 10, Orientation.Horizontal, LineStyle.Single);
  1522. canvas.AddLine (new Point (5, 0), 10, Orientation.Vertical, LineStyle.Single);
  1523. canvas.AddLine (new Point (0, 5), 10, Orientation.Horizontal, LineStyle.Double);
  1524. // Get results from combined method
  1525. (Dictionary<Point, Cell?> combinedCellMap, Region combinedRegion) = canvas.GetCellMapWithRegion ();
  1526. // Get results from separate calls
  1527. Dictionary<Point, Cell?> separateCellMap = canvas.GetCellMap ();
  1528. Region separateRegion = LineCanvas.GetRegion (separateCellMap);
  1529. // Cell maps should be identical
  1530. Assert.Equal (separateCellMap.Count, combinedCellMap.Count);
  1531. foreach (KeyValuePair<Point, Cell?> kvp in separateCellMap)
  1532. {
  1533. Assert.True (combinedCellMap.ContainsKey (kvp.Key), $"Combined map missing key {kvp.Key}");
  1534. }
  1535. // Regions should contain the same points
  1536. foreach (Point p in combinedCellMap.Keys)
  1537. {
  1538. Assert.True (combinedRegion.Contains (p.X, p.Y), $"Combined region missing ({p.X}, {p.Y})");
  1539. Assert.True (separateRegion.Contains (p.X, p.Y), $"Separate region missing ({p.X}, {p.Y})");
  1540. }
  1541. }
  1542. [Fact]
  1543. public void GetCellMapWithRegion_NegativeCoordinates_HandlesCorrectly ()
  1544. {
  1545. LineCanvas canvas = new ();
  1546. canvas.AddLine (new Point (-5, -5), 10, Orientation.Horizontal, LineStyle.Single);
  1547. canvas.AddLine (new Point (0, -5), 10, Orientation.Vertical, LineStyle.Single);
  1548. (Dictionary<Point, Cell?> cellMap, Region region) = canvas.GetCellMapWithRegion ();
  1549. Assert.NotNull (cellMap);
  1550. Assert.NotEmpty (cellMap);
  1551. Assert.NotNull (region);
  1552. // Verify negative coordinates are handled
  1553. Assert.True (cellMap.Keys.Any (p => p.X < 0 || p.Y < 0), "Should have negative coordinates");
  1554. // All cells should be in region
  1555. foreach (Point p in cellMap.Keys)
  1556. {
  1557. Assert.True (region.Contains (p.X, p.Y), $"Expected ({p.X}, {p.Y}) to be in region");
  1558. }
  1559. }
  1560. [Fact]
  1561. public void GetCellMapWithRegion_WithExclusion_RegionExcludesExcludedCells ()
  1562. {
  1563. LineCanvas canvas = new ();
  1564. canvas.AddLine (new Point (0, 0), 10, Orientation.Horizontal, LineStyle.Single);
  1565. // Exclude middle section
  1566. Region exclusionRegion = new ();
  1567. exclusionRegion.Combine (new Rectangle (3, 0, 4, 1), RegionOp.Union);
  1568. canvas.Exclude (exclusionRegion);
  1569. (Dictionary<Point, Cell?> cellMap, Region region) = canvas.GetCellMapWithRegion ();
  1570. Assert.NotNull (cellMap);
  1571. Assert.NotEmpty (cellMap);
  1572. // Excluded cells should not be in cellMap
  1573. for (int x = 3; x < 7; x++)
  1574. {
  1575. Assert.False (cellMap.ContainsKey (new Point (x, 0)), $"({x}, 0) should be excluded from cellMap");
  1576. }
  1577. // Region should match cellMap
  1578. foreach (Point p in cellMap.Keys)
  1579. {
  1580. Assert.True (region.Contains (p.X, p.Y), $"Expected ({p.X}, {p.Y}) to be in region");
  1581. }
  1582. // Excluded points should not be in region
  1583. for (int x = 3; x < 7; x++)
  1584. {
  1585. Assert.False (region.Contains (x, 0), $"({x}, 0) should be excluded from region");
  1586. }
  1587. }
  1588. #endregion
  1589. }