Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

canvas_test.gno

4.23 Kb · 141 lines
  1package art
  2
  3import (
  4	"strings"
  5	"testing"
  6
  7	"gno.land/p/nt/uassert/v0"
  8)
  9
 10func TestNewCanvasIsBlank(t *testing.T) {
 11	c := NewCanvas(3, 2)
 12	uassert.Equal(t, 6, len(c.Cells), "cell count")
 13	uassert.Equal(t, "\n", c.Text(), "two empty rows, both trimmed to nothing")
 14
 15	// Negative dimensions must not panic partway through a Render.
 16	empty := NewCanvas(-4, -4)
 17	uassert.Equal(t, 0, empty.W, "negative width clamps to 0")
 18	uassert.Equal(t, "", empty.Text(), "empty canvas is the empty string")
 19}
 20
 21func TestWriteAndClip(t *testing.T) {
 22	cases := []struct {
 23		name string
 24		x, y int
 25		in   string
 26		want string
 27	}{
 28		{"fits", 0, 0, "abc", "abc\n\n"},
 29		{"offset", 1, 1, "xy", "\n xy\n"},
 30		{"clipped at the right edge", 2, 0, "abcde", "  abc\n\n"},
 31		{"negative x drops the leading runes", -2, 0, "abcde", "cde\n\n"},
 32		{"newline returns to the start column", 1, 0, "ab\ncd", " ab\n cd\n"},
 33		{"off the bottom is dropped entirely", 0, 9, "abc", "\n\n"},
 34	}
 35	for _, tc := range cases {
 36		c := NewCanvas(5, 3)
 37		c.Write(tc.x, tc.y, tc.in)
 38		uassert.Equal(t, tc.want, c.Text(), tc.name)
 39	}
 40}
 41
 42func TestTextTrimsTrailingSpaceAndANSIDoesNot(t *testing.T) {
 43	c := NewCanvas(4, 1)
 44	c.Pen(Default, 0xff0000)
 45	c.Put(0, 0, 'x')
 46	c.Put(3, 0, ' ') // a trailing space that carries a background colour
 47
 48	uassert.Equal(t, "x", c.Text(), "text drops the coloured trailing space")
 49
 50	ansi := c.ANSI()
 51	uassert.True(t, strings.HasSuffix(ansi, reset), "a coloured row ends in a reset")
 52	uassert.True(t, strings.Contains(ansi, "48;2;255;0;0"), "the red background is emitted")
 53	// Trimming here would punch a hole in the picture, so the cell survives.
 54	uassert.Equal(t, "x   ", stripSGR(ansi), "all four cells survive in ansi")
 55}
 56
 57func TestANSIEmitsOneEscapePerChange(t *testing.T) {
 58	c := NewCanvas(4, 1)
 59	c.Pen(0x00ff00, Default)
 60	c.Write(0, 0, "aa")
 61	c.Pen(0x0000ff, Default)
 62	c.Write(2, 0, "bb")
 63
 64	got := c.ANSI()
 65	// Two colour changes plus the end-of-row reset, and not one per cell.
 66	uassert.Equal(t, 3, strings.Count(got, "\x1b["), "escape count")
 67	uassert.Equal(t, "aabb", stripSGR(got), "the runes survive intact")
 68}
 69
 70func TestANSIUncolouredRowHasNoEscapes(t *testing.T) {
 71	c := NewCanvas(3, 1)
 72	c.Write(0, 0, "abc")
 73	uassert.Equal(t, "abc", c.ANSI(), "no colour means no escapes and no reset")
 74}
 75
 76func TestBlit(t *testing.T) {
 77	src := NewCanvas(2, 2)
 78	src.Write(0, 0, "ab\ncd")
 79
 80	dst := NewCanvas(4, 4)
 81	dst.Blit(1, 1, src)
 82	uassert.Equal(t, "\n ab\n cd\n", dst.Text(), "placed at the offset")
 83
 84	// Partly off the edge keeps what fits and drops the rest.
 85	edge := NewCanvas(4, 4)
 86	edge.Blit(3, 3, src)
 87	uassert.Equal(t, "\n\n\n   a", edge.Text(), "only the top-left cell lands")
 88
 89	// A nil source is a no-op rather than a panic.
 90	edge.Blit(0, 0, nil)
 91}
 92
 93func TestBoxAndFrame(t *testing.T) {
 94	c := NewCanvas(5, 3)
 95	c.Box(0, 0, 5, 3, ASCII)
 96	uassert.Equal(t, "+---+\n|   |\n+---+", c.Text(), "ascii border")
 97
 98	// Degenerate boxes draw nothing: there is no border with no inside.
 99	small := NewCanvas(5, 3)
100	small.Box(0, 0, 1, 3, Light)
101	small.Box(0, 0, 5, 1, Light)
102	uassert.Equal(t, "\n\n", small.Text(), "width or height under 2 draws nothing")
103
104	inner := NewCanvas(3, 1)
105	inner.Write(0, 0, "hey")
106	framed := inner.Frame("", ASCII)
107	uassert.Equal(t, 5, framed.W, "frame adds two columns")
108	uassert.Equal(t, 3, framed.H, "frame adds two rows")
109	uassert.Equal(t, "+---+\n|hey|\n+---+", framed.Text(), "content centred inside")
110}
111
112func TestFrameTitleIsTruncatedNotOverrun(t *testing.T) {
113	inner := NewCanvas(4, 1)
114	titled := inner.Frame("settlers", ASCII)
115	// 6 columns wide: the corners take 2 and the title starts at x=2, so it
116	// gets 2 cells and must not eat the top-right corner.
117	uassert.True(t, strings.HasPrefix(titled.Text(), "+-se-+\n"), "truncated, corner survives")
118
119	// A canvas too narrow for any title still frames cleanly.
120	tiny := NewCanvas(1, 1)
121	uassert.Equal(t, "+-+\n| |\n+-+", tiny.Frame("nope", ASCII).Text(), "no room, no title")
122}
123
124// stripSGR removes every SGR sequence, so a test can assert on the runes and on
125// the escapes separately instead of pinning one long opaque string.
126func stripSGR(s string) string {
127	var b strings.Builder
128	in := false
129	for _, r := range s {
130		switch {
131		case r == Escape:
132			in = true
133		case in && r == 'm':
134			in = false
135		case in:
136		default:
137			b.WriteRune(r)
138		}
139	}
140	return b.String()
141}