package render import ( "bytes" "testing" ) const ( errFmt = "Failed: '%v'\nExpected %v, got %v" passFmt = "Passed: '%v'\n" ) type testTrimWrite struct { title string input Instruction n int // number of characters to trim d trimDir // FROM which side to trim expectedTrim Instruction expectedBuf string } var TrimWriteCases []testTrimWrite = []testTrimWrite{ { "Trim 2 characters from left", DrawInstruction{X: 5, Y: 5, Content: "hello"}, 2, TrimLeft, DrawInstruction{X: 7, Y: 5, Content: "llo"}, "\033[5;5Hhello", }, { "Trim 2 characters from the right of a bold Draw", DrawInstruction{ X: 43, Y: 6, Content: "hello", Decorators: []string{"\033[1m"}, }, 2, TrimRight, DrawInstruction{X: 43, Y: 6, Content: "hel"}, "\033[6;43H\033[1mhello", }, { "Trim 2 characters from the left of a Clear", ClearInstruction{X: 8, Y: 4, Size: 5}, 2, TrimLeft, ClearInstruction{X: 10, Y: 4, Size: 3}, "\033[4;8H\033[0m ", }, { "Trim 2 characters from the right of a Clear", ClearInstruction{X: 98, Y: 76, Size: 5}, 2, TrimRight, ClearInstruction{X: 98, Y: 76, Size: 3}, "\033[76;98H\033[0m ", }, { "Trim 10 characters from the left of a Clear of size 5", ClearInstruction{X: 42, Y: 69, Size: 5}, 10, TrimLeft, nil, "\033[69;42H\033[0m ", }, { "Trim 10 characters from the right of a Colored Draw of size 5", DrawInstruction{ X: 420, Y: 1337, Content: "hello", Decorators: []string{"\033[38;2;12;34;56m", "\033[48;2;98;76;54m"}, }, 10, TrimRight, nil, "\033[1337;420H\033[38;2;12;34;56m\033[48;2;98;76;54mhello", }, } 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"}, }, }, { "Clear split in half by a single character", DrawInstruction{X: 8, Y: 8, Content: "X"}, ClearInstruction{X: 0, Y: 8, Size: 17}, CoverWithin, []Instruction{ ClearInstruction{X: 0, Y: 8, Size: 8}, ClearInstruction{X: 9, Y: 8, Size: 8}, }, }, { "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 TrimWriteCases { result := tc.input.Trim(tc.n, tc.d) if !InstructionEquals(result, tc.expectedTrim) { t.Errorf(errFmt, tc.title, tc.expectedTrim, result) } 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) } } func TestWrite(t *testing.T) { for _, tt := range TrimWriteCases { var buf bytes.Buffer tt.input.Write(&buf) if buf.String() != tt.expectedBuf { t.Errorf(errFmt, tt.title, tt.expectedBuf, buf.String()) } } }