From c55207fe63006ed6e4a1151b91b9bfe5b9c3ff1c Mon Sep 17 00:00:00 2001 From: Benjamin Chausse Date: Thu, 11 Jul 2024 22:04:29 -0400 Subject: Render Test for Render Instructions --- internal/render/instructions.go | 62 ++++----- internal/render/instructions_test.go | 254 +++++++++++++++++++++++++++++++++++ internal/render/renderer.go | 10 +- main.go | 4 +- 4 files changed, 293 insertions(+), 37 deletions(-) create mode 100644 internal/render/instructions_test.go diff --git a/internal/render/instructions.go b/internal/render/instructions.go index 685f3f8..b4823f2 100644 --- a/internal/render/instructions.go +++ b/internal/render/instructions.go @@ -12,13 +12,13 @@ type ( ) const ( - ToLeft trimDir = iota - ToRight - None overlapType = iota - Covers - Within - Left - Right + TrimLeft trimDir = iota + TrimRight + CoverNone overlapType = iota + CoverTotal + CoverWithin + CoverLeft + CoverRight ) type Instruction interface { @@ -28,7 +28,7 @@ type Instruction interface { Pos() (int, int) // Len returns the length of the instructions content. Len() int - // Trim removes n characters from one side of the instruction. + // Trim removes n characters FROM one side of the instruction. Trim(n int, d trimDir) Instruction // Copy creates a new instruction with the same content and position. Copy() Instruction @@ -41,31 +41,31 @@ func overlap(over, under Instruction) overlapType { // wrong row: if oy != uy { - return None + return CoverNone } // totally covers it if ox <= ux && ox+os >= ux+us { - return Covers + return CoverTotal } // Overlap on the left: if ox <= ux && ox+os > ux { - return Left + return CoverLeft } // Overlap on the right: if ox < ux+us && ox+os >= ux+us { - return Right + return CoverRight } // centered inside: if ox > ux && ox+os < ux+us { - return Within + return CoverWithin } // Only other option is not touching - return None + return CoverNone } // Squash creates new instruction from overlapping instructions. @@ -82,22 +82,22 @@ func squash(over Instruction, under Instruction, ot overlapType) []Instruction { ux, _ := under.Pos() os, us := over.Len(), under.Len() switch ot { - case Covers: + case CoverTotal: return []Instruction{} - case Left: + case CoverLeft: overlap := ox + os - ux - under.Trim(overlap, ToLeft) + under = under.Trim(overlap, TrimLeft) return []Instruction{under} - case Right: + case CoverRight: overlap := ux + us - ox - under.Trim(overlap, ToRight) + under = under.Trim(overlap, TrimRight) return []Instruction{under} - case Within: - overlapL := ox - ux - overlapR := ux + us - (ox + os) - left := under.Copy().Trim(overlapL, ToLeft) - under.Trim(overlapR, ToRight) - return []Instruction{left, under} + case CoverWithin: + overlapR := ox - ux + os + overlapL := ux + us - ox + right := under.Copy().Trim(overlapR, TrimLeft) + left := under.Trim(overlapL, TrimRight) + return []Instruction{left, right} default: return []Instruction{under} } @@ -143,13 +143,13 @@ func (d DrawInstruction) Trim(n int, from trimDir) Instruction { return nil } switch from { - case ToLeft: + case TrimLeft: d.Content = d.Content[n:] d.X += n - case ToRight: + case TrimRight: d.Content = d.Content[:len(d.Content)-n] } - return &d + return d } func (d DrawInstruction) Copy() Instruction { @@ -200,13 +200,13 @@ func (c ClearInstruction) Trim(n int, from trimDir) Instruction { return nil } switch from { - case ToLeft: + case TrimLeft: c.Size -= n c.X += n - case ToRight: + case TrimRight: c.Size -= n } - return &c + return c } func (c ClearInstruction) Copy() Instruction { diff --git a/internal/render/instructions_test.go b/internal/render/instructions_test.go new file mode 100644 index 0000000..837a143 --- /dev/null +++ b/internal/render/instructions_test.go @@ -0,0 +1,254 @@ +package render + +import ( + "testing" +) + +const ( + errFmt = "Failed: '%v'\nExpected %v, got %v" + passFmt = "Passed: '%v'\n" +) + +type testTrim struct { + title string + input Instruction + expectedReach string + n int // number of characters to trim + d trimDir // FROM which side to trim + expected Instruction +} + +var TrimCases []testTrim = []testTrim{ + { + "Trim 2 characters from the left", + DrawInstruction{X: 5, Y: 5, Content: "hello"}, + "\033[5;5H", + 2, + TrimLeft, + DrawInstruction{X: 7, Y: 5, Content: "llo"}, + }, + { + "Trim 2 characters from the right", + DrawInstruction{X: 43, Y: 6, Content: "hello"}, + "\033[6;43H", + 2, + TrimRight, + DrawInstruction{X: 43, Y: 6, Content: "hel"}, + }, + { + "Trim 2 characters from the left of a Clear", + ClearInstruction{X: 8, Y: 4, Size: 5}, + "\033[4;8H", + 2, + TrimLeft, + ClearInstruction{X: 10, Y: 4, Size: 3}, + }, + { + "Trim 2 characters from the right of a Clear", + ClearInstruction{X: 98, Y: 76, Size: 5}, + "\033[76;98H", + 2, + TrimRight, + ClearInstruction{X: 98, Y: 76, Size: 3}, + }, + { + "Trim 10 characters from the left of a Clear of size 5", + ClearInstruction{X: 42, Y: 69, Size: 5}, + "\033[69;42H", + 10, + TrimLeft, + nil, + }, + { + "Trim 10 characters from the right of a Draw of size 5", + DrawInstruction{X: 420, Y: 1337, Content: "hello"}, + "\033[1337;420H", + 10, + TrimRight, + nil, + }, +} + +type testOverlap struct { + title string + over, under Instruction + overlap overlapType + expected []Instruction +} + +var OverlapCases []testOverlap = []testOverlap{ + { + "Two draws that perfectly overlap", + DrawInstruction{X: 5, Y: 5, Content: "hello"}, + DrawInstruction{X: 5, Y: 5, Content: "wadup"}, + CoverTotal, + []Instruction{}, + }, + { + "Draw + Clear that perfectly overlap", + DrawInstruction{X: 5, Y: 5, Content: "hello"}, + ClearInstruction{X: 5, Y: 5, Size: 5}, + CoverTotal, + []Instruction{}, + }, + { + "Two Clears that perfectly overlap", + ClearInstruction{X: 5, Y: 5, Size: 5}, + ClearInstruction{X: 5, Y: 5, Size: 5}, + CoverTotal, + []Instruction{}, + }, + { + "Draw 'More than covers' Clear", + DrawInstruction{X: 5, Y: 5, Content: "hello"}, + ClearInstruction{X: 7, Y: 5, Size: 3}, + CoverTotal, + []Instruction{}, + }, + { + "Clear Squashes Draw on the left", + ClearInstruction{X: 2, Y: 5, Size: 5}, + DrawInstruction{X: 5, Y: 5, Content: "qwertyuiop"}, + CoverLeft, + []Instruction{ + DrawInstruction{X: 7, Y: 5, Content: "ertyuiop"}, + }, + }, + { + "Clear Squashes Draw on the right", + ClearInstruction{X: 5, Y: 5, Size: 10}, + DrawInstruction{X: 2, Y: 5, Content: "qwertyuiop"}, + CoverRight, + []Instruction{ + DrawInstruction{X: 2, Y: 5, Content: "qwe"}, + }, + }, + { + "Draw is within another Draw", + DrawInstruction{X: 7, Y: 5, Content: "yo"}, + DrawInstruction{X: 5, Y: 5, Content: "hello"}, + CoverWithin, + []Instruction{ + DrawInstruction{X: 5, Y: 5, Content: "he"}, + DrawInstruction{X: 9, Y: 5, Content: "o"}, + }, + }, + { + "Clear is within another Draw", + ClearInstruction{X: 10, Y: 5, Size: 5}, + DrawInstruction{X: 5, Y: 5, Content: "TheQuickBrownFoxJumpsOverTheLazyDog"}, + CoverWithin, + []Instruction{ + DrawInstruction{X: 5, Y: 5, Content: "TheQu"}, + DrawInstruction{X: 15, Y: 5, Content: "ownFoxJumpsOverTheLazyDog"}, + }, + }, + + { + "Two Draws that don't overlap but are on the same row", + DrawInstruction{X: 5, Y: 5, Content: "hello"}, + DrawInstruction{X: 55, Y: 5, Content: "greetings"}, + CoverNone, + []Instruction{ + DrawInstruction{X: 55, Y: 5, Content: "greetings"}, + }, + }, + { + "Two Clears on different rows", + ClearInstruction{X: 5, Y: 5, Size: 5}, + ClearInstruction{X: 5, Y: 12, Size: 5}, + CoverNone, + []Instruction{ + ClearInstruction{X: 5, Y: 12, Size: 5}, + }, + }, +} + +func InstructionEquals(a, b Instruction) bool { + var drwA, drwB DrawInstruction + drwAOk, clrAOk := false, false + var clrA, clrB ClearInstruction + switch a := a.(type) { + case DrawInstruction: + drwA = a + drwAOk = true + case ClearInstruction: + clrA = a + clrAOk = true + + } + switch b := b.(type) { + case DrawInstruction: + drwB = b + if clrAOk { // A is of opposite type (clear) + return false + } + case ClearInstruction: + clrB = b + if drwAOk { // A is of opposite type (draw) + return false + } + } + // If both are draws + if drwAOk { + if drwA.Content != drwB.Content { + return false + } + if drwA.X != drwB.X || drwA.Y != drwB.Y { + return false + } + } + // If both are clears + if clrAOk { + if clrA.Size != clrB.Size { + return false + } + if clrA.X != clrB.X || clrA.Y != clrB.Y { + return false + } + } + return true +} + +func TestTrim(t *testing.T) { + for _, tc := range TrimCases { + result := tc.input.Trim(tc.n, tc.d) + if !InstructionEquals(result, tc.expected) { + t.Errorf(errFmt, tc.title, tc.expected, result) + } + t.Logf(passFmt, tc.title) + } +} + +func TestReach(t *testing.T) { + for _, tc := range TrimCases { + if r := reach(tc.input); r != tc.expectedReach { + t.Errorf(errFmt, tc.title, tc.expectedReach, r) + } + t.Logf(passFmt, tc.title) + } +} + +func TestOverlap(t *testing.T) { + for _, tc := range OverlapCases { + if ot := overlap(tc.over, tc.under); ot != tc.overlap { + t.Errorf(errFmt, tc.title, tc.overlap, ot) + } + t.Logf(passFmt, tc.title) + } +} + +func TestSquash(t *testing.T) { + for _, tc := range OverlapCases { + res := squash(tc.over, tc.under, tc.overlap) + if len(res) != len(tc.expected) { + t.Errorf(errFmt, tc.title, len(tc.expected), len(res)) + } + for i, r := range res { + if !InstructionEquals(r, tc.expected[i]) { + t.Errorf(errFmt, tc.title, tc.expected[i], r) + } + } + t.Logf(passFmt, tc.title) + } +} diff --git a/internal/render/renderer.go b/internal/render/renderer.go index c527413..b851e1e 100644 --- a/internal/render/renderer.go +++ b/internal/render/renderer.go @@ -125,7 +125,7 @@ func (r *Renderer) DrawFrame() { // squash overlapping clear instructions for _, drw := range drwBuf { for j, clr := range clrBuf { - if ot := overlap(drw, clr); ot != None { + if ot := overlap(drw, clr); ot != CoverNone { newClr := squash(drw, clr, ot) switch len(newClr) { case 0: // Complete overlap -> delete the one under @@ -143,7 +143,7 @@ func (r *Renderer) DrawFrame() { for i := 0; i < len(drwBuf); i++ { for j := i + 1; j < len(drwBuf); j++ { older, newer := drwBuf[i], drwBuf[j] - if ot := overlap(older, newer); ot != None { + if ot := overlap(older, newer); ot != CoverNone { newDrw := squash(newer, older, ot) switch len(newDrw) { case 0: // Complete overlap -> delete the one under @@ -158,10 +158,10 @@ func (r *Renderer) DrawFrame() { } } // Draw - for _, clr := range clrBuf { - clr.Write(r.stream) - } for _, drw := range drwBuf { drw.Write(r.stream) } + for _, clr := range clrBuf { + clr.Write(r.stream) + } } diff --git a/main.go b/main.go index b5173eb..fb2aada 100644 --- a/main.go +++ b/main.go @@ -13,5 +13,7 @@ func main() { pm := pacman.NewPacman(3, 3, rdr) pm.Start() rdr.Start() - time.Sleep(15 * time.Second) + time.Sleep(5 * time.Second) + pm.Redirect(pacman.DOWN) + time.Sleep(5 * time.Second) } -- cgit v1.2.3