Files
Terminal.Gui/Tests/UnitTestsParallelizable/Drawing/Lines/LineCanvasTests.cs
Copilot 6d53276be2 Fixes #4289 - Simplify Drawing/Color: unify named color handling under StandardColor and remove layered resolvers (#4432)
* Initial plan

* Delete AnsiColorNameResolver and MultiStandardColorNameResolver, add legacy 16-color names to StandardColor

Co-authored-by: tig <585482+tig@users.noreply.github.com>

* Refactor and enhance tests for Color, Region, and Lines

Refactored `Color` struct by removing unused methods and simplifying logic. Updated namespaces for better organization. Enhanced test coverage for `Color`, `Region`, and `LineCanvas` with new test cases, parameterized tests, and edge case handling.

Added `StraightLineExtensionsTests`, `StraightLineTests`, and `RegionClassTests` to validate behavior under various scenarios. Improved `MergeRectangles` stability and addressed crash patterns. Removed legacy features and unused code. Enhanced documentation and optimized performance in key methods.

* Improve Color struct and StandardColors functionality

Enhanced the Color struct to fully support the alpha channel for rendering intent while maintaining semantic color identity. Updated TryNameColor to ignore alpha when matching colors, ensuring transparency does not affect color resolution. Expanded XML documentation to clarify alpha channel usage and future alpha blending support.

Improved drawing documentation to explain the lifecycle, deferred rendering, and color support, including 24-bit true color and legacy 16-color compatibility. Added a new section on transparency and its role in rendering.

Revised StandardColors implementation to use modern C# features and ensure consistent ARGB mapping. Added comprehensive tests for StandardColors and Color, covering alpha handling, color parsing, thread safety, and aliased color resolution. Removed outdated tests relying on legacy behavior.

Enhanced code readability, maintainability, and test coverage to ensure correctness and backward compatibility.

* Code cleanup

* Code cleanup

* Fix warnings. Code cleanup

* Add comprehensive unit tests for ColorStrings class

Introduced a new test class `ColorStringsTests` under the
`DrawingTests.ColorTests` namespace to validate the functionality
of the `ColorStrings` class.

Key changes include:
- Added tests for `GetColorName` to verify behavior for standard
  and non-standard colors, ignoring alpha channels, and handling
  known colors.
- Added tests for `GetStandardColorNames` to ensure the method
  returns a non-empty, alphabetically sorted collection containing
  all `StandardColor` enum values.
- Implemented tests for `TryParseStandardColorName` to validate
  case-insensitive parsing, hex color support, handling invalid
  input, and `ReadOnlySpan<char>` compatibility.
- Added tests for `TryParseNamedColor` to verify parsing of named
  and hex colors, handling of aliases, and `ReadOnlySpan<char>`
  support.
- Added round-trip tests to ensure consistency between
  `GetColorName`, `TryParseNamedColor`, `GetStandardColorNames`,
  and `TryParseStandardColorName`.

These tests ensure robust validation of color parsing and naming
functionality.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: tig <585482+tig@users.noreply.github.com>
Co-authored-by: Tig <tig@users.noreply.github.com>
2025-12-03 11:09:02 -07:00

1511 lines
49 KiB
C#

using System.Text;
using UnitTests;
using Xunit.Abstractions;
namespace DrawingTests.Lines;
/// <summary>
/// Pure unit tests for <see cref="LineCanvas"/> that don't require Application.Driver or View context.
/// These tests focus on properties and behavior that don't depend on glyph rendering.
/// Note: Tests that verify rendered output (ToString()) cannot be parallelized because LineCanvas
/// depends on Application.Driver for glyph resolution and configuration. Those tests remain in UnitTests.
/// </summary>
public class LineCanvasTests (ITestOutputHelper output) : FakeDriverBase
{
#region Basic API Tests
[Fact]
public void Empty_Canvas_ToString_Returns_EmptyString ()
{
var canvas = new LineCanvas ();
Assert.Equal (string.Empty, canvas.ToString ());
}
[Fact]
public void Clear_Removes_All_Lines ()
{
var canvas = new LineCanvas ();
canvas.AddLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single);
canvas.AddLine (new (0, 0), 3, Orientation.Vertical, LineStyle.Single);
canvas.Clear ();
Assert.Empty (canvas.Lines);
Assert.Equal (Rectangle.Empty, canvas.Bounds);
Assert.Equal (string.Empty, canvas.ToString ());
}
[Fact]
public void Lines_Property_Returns_ReadOnly_Collection ()
{
var canvas = new LineCanvas ();
canvas.AddLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single);
Assert.Single (canvas.Lines);
Assert.IsAssignableFrom<IReadOnlyCollection<StraightLine>> (canvas.Lines);
}
[Fact]
public void AddLine_Adds_Line_To_Collection ()
{
var canvas = new LineCanvas ();
Assert.Empty (canvas.Lines);
canvas.AddLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single);
Assert.Single (canvas.Lines);
canvas.AddLine (new (0, 0), 3, Orientation.Vertical, LineStyle.Single);
Assert.Equal (2, canvas.Lines.Count);
}
[Fact]
public void Constructor_With_Lines_Creates_Canvas_With_Lines ()
{
StraightLine [] lines = new []
{
new StraightLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single),
new StraightLine (new (0, 0), 3, Orientation.Vertical, LineStyle.Single)
};
var canvas = new LineCanvas (lines);
Assert.Equal (2, canvas.Lines.Count);
}
#endregion
#region Bounds Tests - Tests for Bounds property
[Theory]
[InlineData (0, 0, 0, 0, 0, 1, 1)]
[InlineData (0, 0, 1, 0, 0, 1, 1)]
[InlineData (0, 0, 2, 0, 0, 2, 2)]
[InlineData (0, 0, 3, 0, 0, 3, 3)]
[InlineData (0, 0, -1, 0, 0, 1, 1)]
[InlineData (0, 0, -2, -1, -1, 2, 2)]
[InlineData (0, 0, -3, -2, -2, 3, 3)]
public void Viewport_H_And_V_Lines_Both_Positive (
int x,
int y,
int length,
int expectedX,
int expectedY,
int expectedWidth,
int expectedHeight
)
{
var canvas = new LineCanvas ();
canvas.AddLine (new (x, y), length, Orientation.Horizontal, LineStyle.Single);
canvas.AddLine (new (x, y), length, Orientation.Vertical, LineStyle.Single);
Assert.Equal (new (expectedX, expectedY, expectedWidth, expectedHeight), canvas.Bounds);
}
[Theory]
[InlineData (0, 0, 0, 0, 0, 1, 1)]
[InlineData (0, 0, 1, 0, 0, 1, 1)]
[InlineData (0, 0, 2, 0, 0, 2, 1)]
[InlineData (0, 0, 3, 0, 0, 3, 1)]
[InlineData (0, 0, -1, 0, 0, 1, 1)]
[InlineData (0, 0, -2, -1, 0, 2, 1)]
[InlineData (0, 0, -3, -2, 0, 3, 1)]
public void Viewport_H_Line (
int x,
int y,
int length,
int expectedX,
int expectedY,
int expectedWidth,
int expectedHeight
)
{
var canvas = new LineCanvas ();
canvas.AddLine (new (x, y), length, Orientation.Horizontal, LineStyle.Single);
Assert.Equal (new (expectedX, expectedY, expectedWidth, expectedHeight), canvas.Bounds);
}
[Fact]
public void Bounds_Specific_Coordinates ()
{
var canvas = new LineCanvas ();
canvas.AddLine (new (5, 5), 3, Orientation.Horizontal, LineStyle.Single);
Assert.Equal (new (5, 5, 3, 1), canvas.Bounds);
}
[Fact]
public void Bounds_Empty_Canvas_Returns_Empty_Rectangle ()
{
var canvas = new LineCanvas ();
Assert.Equal (Rectangle.Empty, canvas.Bounds);
}
[Fact]
public void Bounds_Single_Point_Zero_Length ()
{
var canvas = new LineCanvas ();
canvas.AddLine (new (5, 5), 0, Orientation.Horizontal, LineStyle.Single);
Assert.Equal (new (5, 5, 1, 1), canvas.Bounds);
}
[Fact]
public void Bounds_Horizontal_Line ()
{
var canvas = new LineCanvas ();
canvas.AddLine (new (2, 3), 5, Orientation.Horizontal, LineStyle.Single);
Assert.Equal (new (2, 3, 5, 1), canvas.Bounds);
}
[Fact]
public void Bounds_Vertical_Line ()
{
var canvas = new LineCanvas ();
canvas.AddLine (new (2, 3), 5, Orientation.Vertical, LineStyle.Single);
Assert.Equal (new (2, 3, 1, 5), canvas.Bounds);
}
[Fact]
public void Bounds_Multiple_Lines_Returns_Union ()
{
var canvas = new LineCanvas ();
canvas.AddLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single);
canvas.AddLine (new (0, 0), 3, Orientation.Vertical, LineStyle.Single);
Assert.Equal (new (0, 0, 5, 3), canvas.Bounds);
}
[Fact]
public void Bounds_Negative_Length_Line ()
{
var canvas = new LineCanvas ();
canvas.AddLine (new (5, 5), -3, Orientation.Horizontal, LineStyle.Single);
// Line from (5,5) going left 3 positions: includes points 3, 4, 5 (width 3, X starts at 3)
Assert.Equal (new (3, 5, 3, 1), canvas.Bounds);
}
[Fact]
public void Bounds_Complex_Box ()
{
var canvas = new LineCanvas ();
// top
canvas.AddLine (new (0, 0), 3, Orientation.Horizontal, LineStyle.Single);
// left
canvas.AddLine (new (0, 0), 2, Orientation.Vertical, LineStyle.Single);
// right
canvas.AddLine (new (2, 0), 2, Orientation.Vertical, LineStyle.Single);
// bottom
canvas.AddLine (new (0, 2), 3, Orientation.Horizontal, LineStyle.Single);
Assert.Equal (new (0, 0, 3, 3), canvas.Bounds);
}
#endregion
#region Exclusion Tests
[Fact]
public void ClearExclusions_Clears_Exclusion_Region ()
{
var canvas = new LineCanvas ();
canvas.AddLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single);
var region = new Region (new (0, 0, 2, 1));
canvas.Exclude (region);
canvas.ClearExclusions ();
// After clearing exclusions, GetMap should return all points
Dictionary<Point, Rune> map = canvas.GetMap ();
Assert.Equal (5, map.Count);
}
[Fact]
public void Exclude_Removes_Points_From_Map ()
{
var canvas = new LineCanvas ();
canvas.AddLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single);
var region = new Region (new (0, 0, 2, 1));
canvas.Exclude (region);
Dictionary<Point, Rune> map = canvas.GetMap ();
// Should have 5 - 2 = 3 points (excluding the first 2)
Assert.Equal (3, map.Count);
}
#endregion
#region Fill Property Tests
[Fact]
public void Fill_Property_Can_Be_Set ()
{
var foregroundFill = new SolidFill (new (255, 0));
var backgroundFill = new SolidFill (new (0, 0));
var fillPair = new FillPair (foregroundFill, backgroundFill);
var canvas = new LineCanvas { Fill = fillPair };
Assert.Equal (fillPair, canvas.Fill);
}
[Fact]
public void Fill_Property_Defaults_To_Null ()
{
var canvas = new LineCanvas ();
Assert.Null (canvas.Fill);
}
#endregion
[Theory]
// Horizontal lines with a vertical zero-length
[InlineData (
0,
0,
1,
Orientation.Horizontal,
LineStyle.Double,
0,
0,
0,
Orientation.Vertical,
LineStyle.Single,
"╞"
)]
[InlineData (
0,
0,
-1,
Orientation.Horizontal,
LineStyle.Double,
0,
0,
0,
Orientation.Vertical,
LineStyle.Single,
"╡"
)]
[InlineData (
0,
0,
1,
Orientation.Horizontal,
LineStyle.Single,
0,
0,
0,
Orientation.Vertical,
LineStyle.Double,
"╟"
)]
[InlineData (
0,
0,
-1,
Orientation.Horizontal,
LineStyle.Single,
0,
0,
0,
Orientation.Vertical,
LineStyle.Double,
"╢"
)]
[InlineData (
0,
0,
1,
Orientation.Horizontal,
LineStyle.Single,
0,
0,
0,
Orientation.Vertical,
LineStyle.Single,
"├"
)]
[InlineData (
0,
0,
-1,
Orientation.Horizontal,
LineStyle.Single,
0,
0,
0,
Orientation.Vertical,
LineStyle.Single,
"┤"
)]
[InlineData (
0,
0,
1,
Orientation.Horizontal,
LineStyle.Double,
0,
0,
0,
Orientation.Vertical,
LineStyle.Double,
"╠"
)]
[InlineData (
0,
0,
-1,
Orientation.Horizontal,
LineStyle.Double,
0,
0,
0,
Orientation.Vertical,
LineStyle.Double,
"╣"
)]
// Vertical lines with a horizontal zero-length
[InlineData (
0,
0,
1,
Orientation.Vertical,
LineStyle.Double,
0,
0,
0,
Orientation.Horizontal,
LineStyle.Single,
"╥"
)]
[InlineData (
0,
0,
-1,
Orientation.Vertical,
LineStyle.Double,
0,
0,
0,
Orientation.Horizontal,
LineStyle.Single,
"╨"
)]
[InlineData (
0,
0,
1,
Orientation.Vertical,
LineStyle.Single,
0,
0,
0,
Orientation.Horizontal,
LineStyle.Double,
"╤"
)]
[InlineData (
0,
0,
-1,
Orientation.Vertical,
LineStyle.Single,
0,
0,
0,
Orientation.Horizontal,
LineStyle.Double,
"╧"
)]
[InlineData (
0,
0,
1,
Orientation.Vertical,
LineStyle.Single,
0,
0,
0,
Orientation.Horizontal,
LineStyle.Single,
"┬"
)]
[InlineData (
0,
0,
-1,
Orientation.Vertical,
LineStyle.Single,
0,
0,
0,
Orientation.Horizontal,
LineStyle.Single,
"┴"
)]
[InlineData (
0,
0,
1,
Orientation.Vertical,
LineStyle.Double,
0,
0,
0,
Orientation.Horizontal,
LineStyle.Double,
"╦"
)]
[InlineData (
0,
0,
-1,
Orientation.Vertical,
LineStyle.Double,
0,
0,
0,
Orientation.Horizontal,
LineStyle.Double,
"╩"
)]
// Crosses (two zero-length)
[InlineData (
0,
0,
0,
Orientation.Vertical,
LineStyle.Double,
0,
0,
0,
Orientation.Horizontal,
LineStyle.Single,
"╫"
)]
[InlineData (
0,
0,
0,
Orientation.Vertical,
LineStyle.Single,
0,
0,
0,
Orientation.Horizontal,
LineStyle.Double,
"╪"
)]
[InlineData (
0,
0,
0,
Orientation.Vertical,
LineStyle.Single,
0,
0,
0,
Orientation.Horizontal,
LineStyle.Single,
"┼"
)]
[InlineData (
0,
0,
0,
Orientation.Vertical,
LineStyle.Double,
0,
0,
0,
Orientation.Horizontal,
LineStyle.Double,
"╬"
)]
public void Add_2_Lines (
int x1,
int y1,
int len1,
Orientation o1,
LineStyle s1,
int x2,
int y2,
int len2,
Orientation o2,
LineStyle s2,
string expected
)
{
IDriver driver = CreateFakeDriver ();
View v = GetCanvas (driver, out LineCanvas lc);
v.Width = 10;
v.Height = 10;
v.Viewport = new (0, 0, 10, 10);
lc.AddLine (new (x1, y1), len1, o1, s1);
lc.AddLine (new (x2, y2), len2, o2, s2);
OutputAssert.AssertEqual (output, expected, lc.ToString ());
v.Dispose ();
}
[Fact]
public void Viewport_Specific ()
{
// Draw at 1,1 within client area of View (i.e. leave a top and left margin of 1)
// This proves we aren't drawing excess above
var x = 1;
var y = 2;
var width = 3;
var height = 2;
var lc = new LineCanvas ();
// 01230
// ╔╡╞╗1
// ║ ║2
// Add a short horiz line for ╔╡
lc.AddLine (new (x, y), 2, Orientation.Horizontal, LineStyle.Double);
Assert.Equal (new (x, y, 2, 1), lc.Bounds);
//LHS line down
lc.AddLine (new (x, y), height, Orientation.Vertical, LineStyle.Double);
Assert.Equal (new (x, y, 2, 2), lc.Bounds);
//Vertical line before Title, results in a ╡
lc.AddLine (new (x + 1, y), 0, Orientation.Vertical, LineStyle.Single);
Assert.Equal (new (x, y, 2, 2), lc.Bounds);
//Vertical line after Title, results in a ╞
lc.AddLine (new (x + 2, y), 0, Orientation.Vertical, LineStyle.Single);
Assert.Equal (new (x, y, 3, 2), lc.Bounds);
// remainder of top line
lc.AddLine (new (x + 2, y), width - 1, Orientation.Horizontal, LineStyle.Double);
Assert.Equal (new (x, y, 4, 2), lc.Bounds);
//RHS line down
lc.AddLine (new (x + width, y), height, Orientation.Vertical, LineStyle.Double);
Assert.Equal (new (x, y, 4, 2), lc.Bounds);
OutputAssert.AssertEqual (
output,
@"
╔╡╞╗
║ ║",
$"{Environment.NewLine}{lc}"
);
}
[Fact]
public void Viewport_Specific_With_Ustring ()
{
// Draw at 1,1 within client area of View (i.e. leave a top and left margin of 1)
// This proves we aren't drawing excess above
var x = 1;
var y = 2;
var width = 3;
var height = 2;
var lc = new LineCanvas ();
// 01230
// ╔╡╞╗1
// ║ ║2
// Add a short horiz line for ╔╡
lc.AddLine (new (x, y), 2, Orientation.Horizontal, LineStyle.Double);
Assert.Equal (new (x, y, 2, 1), lc.Bounds);
//LHS line down
lc.AddLine (new (x, y), height, Orientation.Vertical, LineStyle.Double);
Assert.Equal (new (x, y, 2, 2), lc.Bounds);
//Vertical line before Title, results in a ╡
lc.AddLine (new (x + 1, y), 0, Orientation.Vertical, LineStyle.Single);
Assert.Equal (new (x, y, 2, 2), lc.Bounds);
//Vertical line after Title, results in a ╞
lc.AddLine (new (x + 2, y), 0, Orientation.Vertical, LineStyle.Single);
Assert.Equal (new (x, y, 3, 2), lc.Bounds);
// remainder of top line
lc.AddLine (new (x + 2, y), width - 1, Orientation.Horizontal, LineStyle.Double);
Assert.Equal (new (x, y, 4, 2), lc.Bounds);
//RHS line down
lc.AddLine (new (x + width, y), height, Orientation.Vertical, LineStyle.Double);
Assert.Equal (new (x, y, 4, 2), lc.Bounds);
OutputAssert.AssertEqual (
output,
@"
╔╡╞╗
║ ║",
$"{Environment.NewLine}{lc}"
);
}
[Fact]
public void Canvas_Updates_On_Changes ()
{
var lc = new LineCanvas ();
Assert.Equal (Rectangle.Empty, lc.Bounds);
lc.AddLine (Point.Empty, 2, Orientation.Horizontal, LineStyle.Double);
Assert.NotEqual (Rectangle.Empty, lc.Bounds);
lc.Clear ();
Assert.Equal (Rectangle.Empty, lc.Bounds);
}
[InlineData (0, 0, Orientation.Horizontal, "─")]
[InlineData (1, 0, Orientation.Horizontal, "─")]
[InlineData (0, 1, Orientation.Horizontal, "─")]
[InlineData (-1, 0, Orientation.Horizontal, "─")]
[InlineData (0, -1, Orientation.Horizontal, "─")]
[InlineData (-1, -1, Orientation.Horizontal, "─")]
[InlineData (0, 0, Orientation.Vertical, "│")]
[InlineData (1, 0, Orientation.Vertical, "│")]
[InlineData (0, 1, Orientation.Vertical, "│")]
[InlineData (0, -1, Orientation.Vertical, "│")]
[InlineData (-1, 0, Orientation.Vertical, "│")]
[InlineData (-1, -1, Orientation.Vertical, "│")]
[Theory]
public void Length_0_Is_1_Long (int x, int y, Orientation orientation, string expected)
{
var canvas = new LineCanvas ();
// Add a line at 5, 5 that's has length of 1
canvas.AddLine (new (x, y), 1, orientation, LineStyle.Single);
OutputAssert.AssertEqual (output, $"{expected}", $"{canvas}");
}
// X is offset by 2
[InlineData (0, 0, 1, Orientation.Horizontal, "─")]
[InlineData (1, 0, 1, Orientation.Horizontal, "─")]
[InlineData (0, 1, 1, Orientation.Horizontal, "─")]
[InlineData (0, 0, 1, Orientation.Vertical, "│")]
[InlineData (1, 0, 1, Orientation.Vertical, "│")]
[InlineData (0, 1, 1, Orientation.Vertical, "│")]
[InlineData (-1, 0, 1, Orientation.Horizontal, "─")]
[InlineData (0, -1, 1, Orientation.Horizontal, "─")]
[InlineData (-1, 0, 1, Orientation.Vertical, "│")]
[InlineData (0, -1, 1, Orientation.Vertical, "│")]
[InlineData (0, 0, -1, Orientation.Horizontal, "─")]
[InlineData (1, 0, -1, Orientation.Horizontal, "─")]
[InlineData (0, 1, -1, Orientation.Horizontal, "─")]
[InlineData (0, 0, -1, Orientation.Vertical, "│")]
[InlineData (1, 0, -1, Orientation.Vertical, "│")]
[InlineData (0, 1, -1, Orientation.Vertical, "│")]
[InlineData (-1, 0, -1, Orientation.Horizontal, "─")]
[InlineData (0, -1, -1, Orientation.Horizontal, "─")]
[InlineData (-1, 0, -1, Orientation.Vertical, "│")]
[InlineData (0, -1, -1, Orientation.Vertical, "│")]
[InlineData (0, 0, 2, Orientation.Horizontal, "──")]
[InlineData (1, 0, 2, Orientation.Horizontal, "──")]
[InlineData (0, 1, 2, Orientation.Horizontal, "──")]
[InlineData (1, 1, 2, Orientation.Horizontal, "──")]
[InlineData (0, 0, 2, Orientation.Vertical, "│\r\n│")]
[InlineData (1, 0, 2, Orientation.Vertical, "│\r\n│")]
[InlineData (0, 1, 2, Orientation.Vertical, "│\r\n│")]
[InlineData (1, 1, 2, Orientation.Vertical, "│\r\n│")]
[InlineData (-1, 0, 2, Orientation.Horizontal, "──")]
[InlineData (0, -1, 2, Orientation.Horizontal, "──")]
[InlineData (-1, 0, 2, Orientation.Vertical, "│\r\n│")]
[InlineData (0, -1, 2, Orientation.Vertical, "│\r\n│")]
[InlineData (-1, -1, 2, Orientation.Vertical, "│\r\n│")]
[InlineData (0, 0, -2, Orientation.Horizontal, "──")]
[InlineData (1, 0, -2, Orientation.Horizontal, "──")]
[InlineData (0, 1, -2, Orientation.Horizontal, "──")]
[InlineData (0, 0, -2, Orientation.Vertical, "│\r\n│")]
[InlineData (1, 0, -2, Orientation.Vertical, "│\r\n│")]
[InlineData (0, 1, -2, Orientation.Vertical, "│\r\n│")]
[InlineData (1, 1, -2, Orientation.Vertical, "│\r\n│")]
[InlineData (-1, 0, -2, Orientation.Horizontal, "──")]
[InlineData (0, -1, -2, Orientation.Horizontal, "──")]
[InlineData (-1, 0, -2, Orientation.Vertical, "│\r\n│")]
[InlineData (0, -1, -2, Orientation.Vertical, "│\r\n│")]
[InlineData (-1, -1, -2, Orientation.Vertical, "│\r\n│")]
[Theory] public void Length_n_Is_n_Long (int x, int y, int length, Orientation orientation, string expected)
{
var canvas = new LineCanvas ();
canvas.AddLine (new (x, y), length, orientation, LineStyle.Single);
var result = canvas.ToString ();
OutputAssert.AssertEqual (output, expected, result);
}
[Fact]
public void Length_Negative ()
{
var offset = new Point (5, 5);
var canvas = new LineCanvas ();
canvas.AddLine (offset, -3, Orientation.Horizontal, LineStyle.Single);
var looksLike = "───";
Assert.Equal (looksLike, $"{canvas}");
}
[InlineData (Orientation.Horizontal, "─")]
[InlineData (Orientation.Vertical, "│")]
[Theory]
public void Length_Zero_Alone_Is_Line (Orientation orientation, string expected)
{
var lc = new LineCanvas ();
// Add a line at 0, 0 that's has length of 0
lc.AddLine (Point.Empty, 0, orientation, LineStyle.Single);
OutputAssert.AssertEqual (output, expected, $"{lc}");
}
[InlineData (Orientation.Horizontal, "┼")]
[InlineData (Orientation.Vertical, "┼")]
[Theory]
public void Length_Zero_Cross_Is_Cross (Orientation orientation, string expected)
{
var lc = new LineCanvas ();
// Add point at opposite orientation
lc.AddLine (
Point.Empty,
0,
orientation == Orientation.Horizontal ? Orientation.Vertical : Orientation.Horizontal,
LineStyle.Single
);
// Add a line at 0, 0 that's has length of 0
lc.AddLine (Point.Empty, 0, orientation, LineStyle.Single);
OutputAssert.AssertEqual (output, expected, $"{lc}");
}
[InlineData (Orientation.Horizontal, "╥")]
[InlineData (Orientation.Vertical, "╞")]
[Theory]
public void Length_Zero_NextTo_Opposite_Is_T (Orientation orientation, string expected)
{
var lc = new LineCanvas ();
// Add line with length of 1 in opposite orientation starting at same location
if (orientation == Orientation.Horizontal)
{
lc.AddLine (Point.Empty, 1, Orientation.Vertical, LineStyle.Double);
}
else
{
lc.AddLine (Point.Empty, 1, Orientation.Horizontal, LineStyle.Double);
}
// Add a line at 0, 0 that's has length of 0
lc.AddLine (Point.Empty, 0, orientation, LineStyle.Single);
OutputAssert.AssertEqual (output, expected, $"{lc}");
}
[Fact]
public void TestLineCanvas_LeaveMargin_Top1_Left1 ()
{
var canvas = new LineCanvas ();
// Upper box
canvas.AddLine (Point.Empty, 2, Orientation.Horizontal, LineStyle.Single);
canvas.AddLine (Point.Empty, 2, Orientation.Vertical, LineStyle.Single);
var looksLike =
@"
┌─
│ ";
OutputAssert.AssertEqual (output, looksLike, $"{Environment.NewLine}{canvas}");
}
[Fact]
public void TestLineCanvas_Window_Heavy ()
{
var driver = CreateFakeDriver ();
View v = GetCanvas (driver, out LineCanvas canvas);
// outer box
canvas.AddLine (Point.Empty, 10, Orientation.Horizontal, LineStyle.Heavy);
canvas.AddLine (new (9, 0), 5, Orientation.Vertical, LineStyle.Heavy);
canvas.AddLine (new (9, 4), -10, Orientation.Horizontal, LineStyle.Heavy);
canvas.AddLine (new (0, 4), -5, Orientation.Vertical, LineStyle.Heavy);
canvas.AddLine (new (5, 0), 5, Orientation.Vertical, LineStyle.Heavy);
canvas.AddLine (new (0, 2), 10, Orientation.Horizontal, LineStyle.Heavy);
v.Draw ();
var looksLike =
@"
┏━━━━┳━━━┓
┃ ┃ ┃
┣━━━━╋━━━┫
┃ ┃ ┃
┗━━━━┻━━━┛";
DriverAssert.AssertDriverContentsAre (looksLike, output, driver);
v.Dispose ();
}
[Theory]
[InlineData (LineStyle.Single)]
[InlineData (LineStyle.Rounded)]
public void TestLineCanvas_Window_HeavyTop_ThinSides (LineStyle thinStyle)
{
var driver = CreateFakeDriver ();
View v = GetCanvas (driver, out LineCanvas canvas);
// outer box
canvas.AddLine (Point.Empty, 10, Orientation.Horizontal, LineStyle.Heavy);
canvas.AddLine (new (9, 0), 5, Orientation.Vertical, thinStyle);
canvas.AddLine (new (9, 4), -10, Orientation.Horizontal, LineStyle.Heavy);
canvas.AddLine (new (0, 4), -5, Orientation.Vertical, thinStyle);
canvas.AddLine (new (5, 0), 5, Orientation.Vertical, thinStyle);
canvas.AddLine (new (0, 2), 10, Orientation.Horizontal, LineStyle.Heavy);
v.Draw ();
var looksLike =
@"
┍━━━━┯━━━┑
│ │ │
┝━━━━┿━━━┥
│ │ │
┕━━━━┷━━━┙
";
DriverAssert.AssertDriverContentsAre (looksLike, output, driver);
v.Dispose ();
}
[Theory]
[InlineData (LineStyle.Single)]
[InlineData (LineStyle.Rounded)]
public void TestLineCanvas_Window_ThinTop_HeavySides (LineStyle thinStyle)
{
var driver = CreateFakeDriver ();
View v = GetCanvas (driver, out LineCanvas canvas);
// outer box
canvas.AddLine (Point.Empty, 10, Orientation.Horizontal, thinStyle);
canvas.AddLine (new (9, 0), 5, Orientation.Vertical, LineStyle.Heavy);
canvas.AddLine (new (9, 4), -10, Orientation.Horizontal, thinStyle);
canvas.AddLine (new (0, 4), -5, Orientation.Vertical, LineStyle.Heavy);
canvas.AddLine (new (5, 0), 5, Orientation.Vertical, LineStyle.Heavy);
canvas.AddLine (new (0, 2), 10, Orientation.Horizontal, thinStyle);
v.Draw ();
var looksLike =
@"
┎────┰───┒
┃ ┃ ┃
┠────╂───┨
┃ ┃ ┃
┖────┸───┚
";
DriverAssert.AssertDriverContentsAre (looksLike, output, driver);
v.Dispose ();
}
[Fact]
public void Top_Left_From_TopRight_LeftUp ()
{
var canvas = new LineCanvas ();
// Upper box
canvas.AddLine (Point.Empty, 2, Orientation.Horizontal, LineStyle.Single);
canvas.AddLine (new (0, 1), -2, Orientation.Vertical, LineStyle.Single);
var looksLike =
@"
┌─
│ ";
OutputAssert.AssertEqual (output, looksLike, $"{Environment.NewLine}{canvas}");
}
[Fact]
public void Top_With_1Down ()
{
var canvas = new LineCanvas ();
// Top ─
canvas.AddLine (Point.Empty, 1, Orientation.Horizontal, LineStyle.Single);
// Bottom ─
canvas.AddLine (new (1, 1), -1, Orientation.Horizontal, LineStyle.Single);
//// Right down
//canvas.AddLine (new Point (9, 0), 3, Orientation.Vertical, LineStyle.Single);
//// Bottom
//canvas.AddLine (new Point (9, 3), -10, Orientation.Horizontal, LineStyle.Single);
//// Left Up
//canvas.AddLine (new Point (0, 3), -3, Orientation.Vertical, LineStyle.Single);
Assert.Equal (new (0, 0, 2, 2), canvas.Bounds);
Dictionary<Point, Rune> map = canvas.GetMap ();
Assert.Equal (2, map.Count);
OutputAssert.AssertEqual (
output,
@"
─",
$"{Environment.NewLine}{canvas}"
);
}
[Fact]
public void ToString_Empty ()
{
var lc = new LineCanvas ();
OutputAssert.AssertEqual (output, string.Empty, lc.ToString ());
}
// 012
[InlineData (0, 0, "═══")]
[InlineData (1, 0, "═══")]
[InlineData (0, 1, "═══")]
[InlineData (1, 1, "═══")]
[InlineData (2, 2, "═══")]
[InlineData (-1, 0, "═══")]
[InlineData (0, -1, "═══")]
[InlineData (-1, -1, "═══")]
[InlineData (-2, -2, "═══")]
[Theory]
public void ToString_Positive_Horizontal_1Line_Offset (int x, int y, string expected)
{
var lc = new LineCanvas ();
lc.AddLine (new (x, y), 3, Orientation.Horizontal, LineStyle.Double);
OutputAssert.AssertEqual (output, expected, $"{lc}");
}
[InlineData (0, 0, 0, 0, "═══")]
[InlineData (1, 0, 1, 0, "═══")]
[InlineData (-1, 0, -1, 0, "═══")]
[InlineData (0, 0, 1, 0, "════")]
[InlineData (1, 0, 3, 0, "═════")]
[InlineData (1, 0, 4, 0, "══════")]
[InlineData (1, 0, 5, 0, "═══ ═══")]
[InlineData (0, 0, 0, 1, "\u2550\u2550\u2550\r\n\u2550\u2550\u2550")]
[InlineData (0, 0, 1, 1, "═══ \r\n ═══")]
[InlineData (0, 0, 2, 1, "═══ \r\n ═══")]
[InlineData (1, 0, 0, 1, " ═══\r\n═══ ")]
[InlineData (0, 1, 0, 1, "═══")]
[InlineData (1, 1, 0, 1, "════")]
[InlineData (2, 2, 0, 1, "═══ \r\n ═══")]
[Theory]
public void ToString_Positive_Horizontal_2Line_Offset (int x1, int y1, int x2, int y2, string expected)
{
var lc = new LineCanvas ();
lc.AddLine (new (x1, y1), 3, Orientation.Horizontal, LineStyle.Double);
lc.AddLine (new (x2, y2), 3, Orientation.Horizontal, LineStyle.Double);
OutputAssert.AssertEqual (output, expected, $"{lc}");
}
// [Fact, SetupFakeDriver]
// public void LeaveMargin_Top1_Left1 ()
// {
// var canvas = new LineCanvas ();
// // Upper box
// canvas.AddLine (Point.Empty, 9, Orientation.Horizontal, LineStyle.Single);
// canvas.AddLine (new Point (8, 0), 3, Orientation.Vertical, LineStyle.Single);
// canvas.AddLine (new Point (8, 3), -9, Orientation.Horizontal, LineStyle.Single);
// canvas.AddLine (new Point (0, 2), -3, Orientation.Vertical, LineStyle.Single);
// // Lower Box
// canvas.AddLine (new Point (5, 0), 2, Orientation.Vertical, LineStyle.Single);
// canvas.AddLine (new Point (0, 2), 9, Orientation.Horizontal, LineStyle.Single);
// string looksLike =
//@"
//┌────┬──┐
//│ │ │
//├────┼──┤
//└────┴──┘
//";
// Assert.Equal (looksLike, $"{Environment.NewLine}{canvas}");
// }
[InlineData (0, 0, 0, Orientation.Horizontal, LineStyle.Double, "═")]
[InlineData (0, 0, 0, Orientation.Vertical, LineStyle.Double, "║")]
[InlineData (0, 0, 0, Orientation.Horizontal, LineStyle.Single, "─")]
[InlineData (0, 0, 0, Orientation.Vertical, LineStyle.Single, "│")]
[InlineData (0, 0, 1, Orientation.Horizontal, LineStyle.Double, "═")]
[InlineData (0, 0, 1, Orientation.Vertical, LineStyle.Double, "║")]
[InlineData (0, 0, 1, Orientation.Horizontal, LineStyle.Single, "─")]
[InlineData (0, 0, 1, Orientation.Vertical, LineStyle.Single, "│")]
[InlineData (0, 0, 2, Orientation.Horizontal, LineStyle.Double, "══")]
[InlineData (0, 0, 2, Orientation.Vertical, LineStyle.Double, "║\n║")]
[InlineData (0, 0, 2, Orientation.Horizontal, LineStyle.Single, "──")]
[InlineData (0, 0, 2, Orientation.Vertical, LineStyle.Single, "│\n│")]
[Theory]
public void View_Draws_1LineTests (
int x1,
int y1,
int length,
Orientation o1,
LineStyle s1,
string expected
)
{
var driver = CreateFakeDriver ();
View v = GetCanvas (driver, out LineCanvas lc);
v.Width = 10;
v.Height = 10;
v.Viewport = new (0, 0, 10, 10);
lc.AddLine (new (x1, y1), length, o1, s1);
v.Draw ();
DriverAssert.AssertDriverContentsAre (expected, output, driver);
v.Dispose ();
}
/// <summary>This test demonstrates how to correctly trigger a corner. By overlapping the lines in the same cell</summary>
[Fact]
public void View_Draws_Corner_Correct ()
{
var driver = CreateFakeDriver ();
View v = GetCanvas (driver, out LineCanvas canvas);
canvas.AddLine (Point.Empty, 2, Orientation.Horizontal, LineStyle.Single);
canvas.AddLine (Point.Empty, 2, Orientation.Vertical, LineStyle.Single);
v.Draw ();
var looksLike =
@"
┌─
│";
DriverAssert.AssertDriverContentsAre (looksLike, output, driver);
v.Dispose ();
}
/// <summary>
/// This test demonstrates that corners are only drawn when lines overlap. Not when they terminate adjacent to one
/// another.
/// </summary>
[Fact]
public void View_Draws_Corner_NoOverlap ()
{
var driver = CreateFakeDriver ();
View v = GetCanvas (driver, out LineCanvas canvas);
canvas.AddLine (Point.Empty, 2, Orientation.Horizontal, LineStyle.Single);
canvas.AddLine (new (0, 1), 2, Orientation.Vertical, LineStyle.Single);
v.Draw ();
var looksLike =
@"
──
│";
DriverAssert.AssertDriverContentsAre (looksLike, output, driver);
v.Dispose ();
}
[InlineData (LineStyle.Single)]
[InlineData (LineStyle.Rounded)]
[Theory]
public void View_Draws_Horizontal (LineStyle style)
{
var driver = CreateFakeDriver ();
View v = GetCanvas (driver, out LineCanvas canvas);
canvas.AddLine (Point.Empty, 2, Orientation.Horizontal, style);
v.Draw ();
var looksLike =
@"
──";
DriverAssert.AssertDriverContentsAre (looksLike, output, driver);
v.Dispose ();
}
[Fact]
public void View_Draws_Horizontal_Double ()
{
var driver = CreateFakeDriver ();
View v = GetCanvas (driver, out LineCanvas canvas);
canvas.AddLine (Point.Empty, 2, Orientation.Horizontal, LineStyle.Double);
v.Draw ();
var looksLike =
@"
══";
DriverAssert.AssertDriverContentsAre (looksLike, output, driver);
v.Dispose ();
}
[InlineData (LineStyle.Single)]
[InlineData (LineStyle.Rounded)]
[Theory]
public void View_Draws_Vertical (LineStyle style)
{
var driver = CreateFakeDriver ();
View v = GetCanvas (driver, out LineCanvas canvas);
canvas.AddLine (Point.Empty, 2, Orientation.Vertical, style);
v.Draw ();
var looksLike =
@"
│";
DriverAssert.AssertDriverContentsAre (looksLike, output, driver);
v.Dispose ();
}
[Fact]
public void View_Draws_Vertical_Double ()
{
var driver = CreateFakeDriver ();
View v = GetCanvas (driver, out LineCanvas canvas);
canvas.AddLine (Point.Empty, 2, Orientation.Vertical, LineStyle.Double);
v.Draw ();
var looksLike =
@"
║";
DriverAssert.AssertDriverContentsAre (looksLike, output, driver);
v.Dispose ();
}
[Fact]
public void View_Draws_Window_Double ()
{
var driver = CreateFakeDriver ();
View v = GetCanvas (driver, out LineCanvas canvas);
// outer box
canvas.AddLine (Point.Empty, 10, Orientation.Horizontal, LineStyle.Double);
canvas.AddLine (new (9, 0), 5, Orientation.Vertical, LineStyle.Double);
canvas.AddLine (new (9, 4), -10, Orientation.Horizontal, LineStyle.Double);
canvas.AddLine (new (0, 4), -5, Orientation.Vertical, LineStyle.Double);
canvas.AddLine (new (5, 0), 5, Orientation.Vertical, LineStyle.Double);
canvas.AddLine (new (0, 2), 10, Orientation.Horizontal, LineStyle.Double);
v.Draw ();
var looksLike =
@"
╔════╦═══╗
║ ║ ║
╠════╬═══╣
║ ║ ║
╚════╩═══╝";
DriverAssert.AssertDriverContentsAre (looksLike, output, driver);
v.Dispose ();
}
[Theory]
[InlineData (LineStyle.Single)]
[InlineData (LineStyle.Rounded)]
public void View_Draws_Window_DoubleTop_SingleSides (LineStyle thinStyle)
{
var driver = CreateFakeDriver ();
View v = GetCanvas (driver, out LineCanvas canvas);
// outer box
canvas.AddLine (Point.Empty, 10, Orientation.Horizontal, LineStyle.Double);
canvas.AddLine (new (9, 0), 5, Orientation.Vertical, thinStyle);
canvas.AddLine (new (9, 4), -10, Orientation.Horizontal, LineStyle.Double);
canvas.AddLine (new (0, 4), -5, Orientation.Vertical, thinStyle);
canvas.AddLine (new (5, 0), 5, Orientation.Vertical, thinStyle);
canvas.AddLine (new (0, 2), 10, Orientation.Horizontal, LineStyle.Double);
v.Draw ();
var looksLike =
@"
╒════╤═══╕
│ │ │
╞════╪═══╡
│ │ │
╘════╧═══╛
";
DriverAssert.AssertDriverContentsAre (looksLike, output, driver);
v.Dispose ();
}
/// <summary>
/// Demonstrates when <see cref="LineStyle.Rounded"/> corners are used. Notice how not all lines declare rounded.
/// If there are 1+ lines intersecting and a corner is to be used then if any of them are rounded a rounded corner is
/// used.
/// </summary>
[Fact]
public void View_Draws_Window_Rounded ()
{
var driver = CreateFakeDriver ();
View v = GetCanvas (driver, out LineCanvas canvas);
// outer box
canvas.AddLine (Point.Empty, 10, Orientation.Horizontal, LineStyle.Rounded);
// LineStyle.Single is ignored because corner overlaps with the above line which is Rounded
// this results in a rounded corner being used.
canvas.AddLine (new (9, 0), 5, Orientation.Vertical, LineStyle.Single);
canvas.AddLine (new (9, 4), -10, Orientation.Horizontal, LineStyle.Rounded);
canvas.AddLine (new (0, 4), -5, Orientation.Vertical, LineStyle.Single);
// These lines say rounded but they will result in the T sections which are never rounded.
canvas.AddLine (new (5, 0), 5, Orientation.Vertical, LineStyle.Rounded);
canvas.AddLine (new (0, 2), 10, Orientation.Horizontal, LineStyle.Rounded);
v.Draw ();
var looksLike =
@"
╭────┬───╮
│ │ │
├────┼───┤
│ │ │
╰────┴───╯";
DriverAssert.AssertDriverContentsAre (looksLike, output, driver);
v.Dispose ();
}
[Theory]
[InlineData (LineStyle.Single)]
[InlineData (LineStyle.Rounded)]
public void View_Draws_Window_SingleTop_DoubleSides (LineStyle thinStyle)
{
var driver = CreateFakeDriver ();
View v = GetCanvas (driver, out LineCanvas canvas);
// outer box
canvas.AddLine (Point.Empty, 10, Orientation.Horizontal, thinStyle);
canvas.AddLine (new (9, 0), 5, Orientation.Vertical, LineStyle.Double);
canvas.AddLine (new (9, 4), -10, Orientation.Horizontal, thinStyle);
canvas.AddLine (new (0, 4), -5, Orientation.Vertical, LineStyle.Double);
canvas.AddLine (new (5, 0), 5, Orientation.Vertical, LineStyle.Double);
canvas.AddLine (new (0, 2), 10, Orientation.Horizontal, thinStyle);
v.Draw ();
var looksLike =
@"
╓────╥───╖
║ ║ ║
╟────╫───╢
║ ║ ║
╙────╨───╜
";
DriverAssert.AssertDriverContentsAre (looksLike, output, driver);
v.Dispose ();
}
[Fact]
public void Window ()
{
var canvas = new LineCanvas ();
// Frame
canvas.AddLine (Point.Empty, 10, Orientation.Horizontal, LineStyle.Single);
canvas.AddLine (new (9, 0), 5, Orientation.Vertical, LineStyle.Single);
canvas.AddLine (new (9, 4), -10, Orientation.Horizontal, LineStyle.Single);
canvas.AddLine (new (0, 4), -5, Orientation.Vertical, LineStyle.Single);
// Cross
canvas.AddLine (new (5, 0), 5, Orientation.Vertical, LineStyle.Single);
canvas.AddLine (new (0, 2), 10, Orientation.Horizontal, LineStyle.Single);
var looksLike =
@"
┌────┬───┐
│ │ │
├────┼───┤
│ │ │
└────┴───┘";
OutputAssert.AssertEqual (output, looksLike, $"{Environment.NewLine}{canvas}");
}
[Fact]
public void Zero_Length_Intersections ()
{
// Draw at 1,2 within client area of View (i.e. leave a top and left margin of 1)
// This proves we aren't drawing excess above
var x = 1;
var y = 2;
var width = 5;
var height = 2;
var lc = new LineCanvas ();
// ╔╡╞═════╗
// Add a short horiz line for ╔╡
lc.AddLine (new (x, y), 2, Orientation.Horizontal, LineStyle.Double);
//LHS line down
lc.AddLine (new (x, y), height, Orientation.Vertical, LineStyle.Double);
//Vertical line before Title, results in a ╡
lc.AddLine (new (x + 1, y), 0, Orientation.Vertical, LineStyle.Single);
//Vertical line after Title, results in a ╞
lc.AddLine (new (x + 2, y), 0, Orientation.Vertical, LineStyle.Single);
// remainder of top line
lc.AddLine (new (x + 2, y), width - 1, Orientation.Horizontal, LineStyle.Double);
//RHS line down
lc.AddLine (new (x + width, y), height, Orientation.Vertical, LineStyle.Double);
var looksLike = @"
╔╡╞══╗
║ ║";
OutputAssert.AssertEqual (output, looksLike, $"{Environment.NewLine}{lc}");
}
[Fact]
public void LineCanvas_UsesFillCorrectly ()
{
// Arrange
var foregroundColor = new Color (255, 0); // Red
var backgroundColor = new Color (0, 0); // Black
var foregroundFill = new SolidFill (foregroundColor);
var backgroundFill = new SolidFill (backgroundColor);
var fillPair = new FillPair (foregroundFill, backgroundFill);
var lineCanvas = new LineCanvas
{
Fill = fillPair
};
// Act
lineCanvas.AddLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single);
Dictionary<Point, Cell?> cellMap = lineCanvas.GetCellMap ();
// Assert
foreach (Cell? cell in cellMap.Values)
{
Assert.NotNull (cell);
Assert.Equal (foregroundColor, cell.Value.Attribute!.Value.Foreground);
Assert.Equal (backgroundColor, cell.Value.Attribute.Value.Background);
}
}
[Fact]
public void LineCanvas_LineColorIgnoredBecauseOfFill ()
{
// Arrange
var foregroundColor = new Color (255, 0); // Red
var backgroundColor = new Color (0, 0); // Black
var lineColor = new Attribute (new Color (0, 255), new Color (255, 255, 255)); // Green on White
var foregroundFill = new SolidFill (foregroundColor);
var backgroundFill = new SolidFill (backgroundColor);
var fillPair = new FillPair (foregroundFill, backgroundFill);
var lineCanvas = new LineCanvas
{
Fill = fillPair
};
// Act
lineCanvas.AddLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single, lineColor);
Dictionary<Point, Cell?> cellMap = lineCanvas.GetCellMap ();
// Assert
foreach (Cell? cell in cellMap.Values)
{
Assert.NotNull (cell);
Assert.Equal (foregroundColor, cell.Value.Attribute!.Value.Foreground);
Assert.Equal (backgroundColor, cell.Value.Attribute.Value.Background);
}
}
[Fact]
public void LineCanvas_IntersectingLinesUseFillCorrectly ()
{
// Arrange
var foregroundColor = new Color (255, 0); // Red
var backgroundColor = new Color (0, 0); // Black
var foregroundFill = new SolidFill (foregroundColor);
var backgroundFill = new SolidFill (backgroundColor);
var fillPair = new FillPair (foregroundFill, backgroundFill);
var lineCanvas = new LineCanvas
{
Fill = fillPair
};
// Act
lineCanvas.AddLine (new (0, 0), 5, Orientation.Horizontal, LineStyle.Single);
lineCanvas.AddLine (new (2, -2), 5, Orientation.Vertical, LineStyle.Single);
Dictionary<Point, Cell?> cellMap = lineCanvas.GetCellMap ();
// Assert
foreach (Cell? cell in cellMap.Values)
{
Assert.NotNull (cell);
Assert.Equal (foregroundColor, cell.Value.Attribute!.Value.Foreground);
Assert.Equal (backgroundColor, cell.Value.Attribute.Value.Background);
}
}
// TODO: Remove this and make all LineCanvas tests independent of View
/// <summary>
/// Creates a new <see cref="View"/> into which a <see cref="LineCanvas"/> is rendered at
/// <see cref="View.DrawComplete"/> time.
/// </summary>
/// <param name="canvas">The <see cref="LineCanvas"/> you can draw into.</param>
/// <param name="offsetX">How far to offset drawing in X</param>
/// <param name="offsetY">How far to offset drawing in Y</param>
/// <returns></returns>
private View GetCanvas (IDriver driver, out LineCanvas canvas, int offsetX = 0, int offsetY = 0)
{
var v = new View { Width = 10, Height = 5, Viewport = new (0, 0, 10, 5) };
v.Driver = driver;
LineCanvas canvasCopy = canvas = new ();
v.DrawComplete += (s, e) =>
{
v.FillRect (v.Viewport);
foreach (KeyValuePair<Point, Rune> p in canvasCopy.GetMap ())
{
v.AddRune (
offsetX + p.Key.X,
offsetY + p.Key.Y,
p.Value
);
}
canvasCopy.Clear ();
};
return v;
}
}