package door import ( "bytes" "fmt" "testing" "unicode" ) func TestLine(t *testing.T) { var textBuff *bytes.Buffer = &bytes.Buffer{} textBuff.WriteString("Test Me") var line *Line = &Line{Text: textBuff} var output string = string(line.Output()) var expect string = string("Test Me") if output != expect { t.Errorf("Line: Expected %#v, got %#v", expect, output) } if line.Update() { t.Error("Line: No updater, should return false") } line.DefaultColor = Color([]int{0}) line.Width = 8 output = string(line.Output()) expect = "\x1b[0mTest Me " if output != expect { t.Errorf("Line: Expected %#v, got %#v", expect, output) } // leave the default color, it is ignored when there's a render function // line.DefaultColor = "" line.RenderF = RenderBlueYellow output = string(line.Output()) var blue string = string(ColorText("BOLD BLUE")) var yellow string = string(ColorText("BOLD YELLOW")) expect = blue + "T" + yellow + "est " + blue + "M" + yellow + "e " if output != expect { t.Errorf("Line: Expected %#v, got %#v", expect, output) } } func TestLineUpdate(t *testing.T) { var counter int = 0 uf := func(u *bytes.Buffer) { u.Reset() fmt.Fprintf(u, "Count: %d", counter) } var line *Line = &Line{UpdateF: uf} line.Update() var output string = string(line.Output()) var expect string = "Count: 0" if output != expect { t.Errorf("LineUpdate: Expected %#v, got %#v", expect, output) } if line.Update() { t.Error("Unexpected Update: should have returned false. (no change)") } counter++ if !line.Update() { t.Error("Missing Update: value was changed, Text should have changed") } output = string(line.Output()) expect = "Count: 1" if output != expect { t.Errorf("LineUpdate: Expected %#v, got %#v", expect, output) } } func TestLineUnicode(t *testing.T) { Unicode = true // code point > FFFF, use \U00000000 (not \u). var lineBuff *bytes.Buffer = &bytes.Buffer{} lineBuff.WriteString("Howdy \U0001f920") var line *Line = &Line{Text: lineBuff} var output []byte = line.Output() var expect []byte = []byte("Howdy 🤠") if bytes.Compare(output, expect) != 0 { t.Errorf("LineUnicode: Expected %s, got %s", expect, output) } if StringLen(expect) != 8 { t.Errorf("LineUnicode Strlen: Expected 8, got %d", StringLen(expect)) } // 🤠 = 2 chars. line.Width = 9 output = line.Output() expect = []byte("Howdy 🤠 ") if bytes.Compare(output, expect) != 0 { t.Errorf("LineUnicode: Expected %#v, got %#v", expect, output) } } func TestLineCP437(t *testing.T) { Unicode = false var tests []string = []string{"\xdb TEXT \xdb", "\xf1 F1", "\xf2 F2", "\xf3 F3"} for _, test := range tests { var lineBuff *bytes.Buffer = &bytes.Buffer{} lineBuff.WriteString(test) var line *Line = &Line{Text: lineBuff} var output string = string(line.Output()) var expect string = test if output != expect { t.Errorf("LineCP437: Expected %#v, got %#v", expect, output) } } } // Benchmarks for profiling // go test -bench=BenchmarkLine -benchmem -memprofile memory.out -cpuprofile cpu.out // go tool pprof memory.out // go tool pprof cpu.out func BenchmarkLine(b *testing.B) { Unicode = false for n := 0; n < b.N; n++ { var lineBuff *bytes.Buffer = &bytes.Buffer{} fmt.Fprintf(lineBuff, "Line %d of %d", n, b.N) // lineBuff.WriteString(fmt.Sprintf("Line %d of %d", n, b.N)) var line *Line = &Line{Text: lineBuff} line.Output() } } func BenchmarkLineUnicode(b *testing.B) { Unicode = true for n := 0; n < b.N; n++ { var lineBuff *bytes.Buffer = &bytes.Buffer{} fmt.Fprintf(lineBuff, "Line %d of %d", n, b.N) // lineBuff.WriteString(fmt.Sprintf("Line %d of %d", n, b.N)) var line *Line = &Line{Text: lineBuff} line.Output() } } func BenchmarkLineColor(b *testing.B) { Unicode = false color := ColorText("BRI WHI ON BLUE") for n := 0; n < b.N; n++ { var lineBuff *bytes.Buffer = &bytes.Buffer{} fmt.Fprintf(lineBuff, "Line %d of %d", n, b.N) // lineBuff.WriteString(fmt.Sprintf("Line %d of %d", n, b.N)) var line *Line = &Line{Text: lineBuff, DefaultColor: color} line.Output() } } func BenchmarkLineColorUnicode(b *testing.B) { Unicode = true color := ColorText("BRI WHI ON BLUE") for n := 0; n < b.N; n++ { var lineBuff *bytes.Buffer = &bytes.Buffer{} fmt.Fprintf(lineBuff, "Line %d of %d", n, b.N) // lineBuff.WriteString(fmt.Sprintf("Line %d of %d", n, b.N)) var line *Line = &Line{Text: lineBuff, DefaultColor: color} line.Output() } } // Using RenderUppercase vs. RenderBlueYellow // BenchmarkLineRender-4 739462 1709 ns/op 376 B/op 13 allocs/op // BenchmarkLineRender-4 862102 1434 ns/op 648 B/op 9 allocs/op /* // This actually made BenchmarkLineUpdate worse. func printd(value int64, write io.Writer) { var buffer [32]byte var pos int if value == 0 { write.Write([]byte{'0'}) return } for value > 0 { buffer[pos] = byte(int64('0') + value%10) pos++ value = value / 10 } for pos != 0 { pos-- write.Write(buffer[pos : pos+1]) } } */ func BenchmarkLineUpdate(b *testing.B) { Unicode = false var line *Line = &Line{} var n int line.UpdateF = func(u *bytes.Buffer) { u.Reset() /* u.WriteString("Line ") printd(int64(n), u) u.WriteString(" of ") printd(int64(b.N), u) */ fmt.Fprintf(u, "Line # %d of %d", n, b.N) } line.Update() for n = 0; n < b.N; n++ { line.Update() line.Output() } } func BenchmarkLineUpdateUnicode(b *testing.B) { Unicode = true var line *Line = &Line{} var n int line.UpdateF = func(u *bytes.Buffer) { u.Reset() /* u.WriteString("Line ") printd(int64(n), u) u.WriteString(" of ") printd(int64(b.N), u) */ // № \u2116 fmt.Fprintf(u, "Line № %d of %d", n, b.N) } line.Update() for n = 0; n < b.N; n++ { line.Update() line.Output() } } func BenchmarkLineRender(b *testing.B) { Unicode = false var rf ColorRender = RenderUppercase("RED", "GREEN") var line *Line = NewLine("ThIs Is CrAzY TeXt HeRe") line.RenderF = rf var n int for n = 0; n < b.N; n++ { line.Output() } } func BenchmarkLineRenderUnicode(b *testing.B) { Unicode = true var rf ColorRender = RenderUppercase("RED", "GREEN") // ₿ /u208f var line *Line = NewLine("ThIs Is CrAzY ₿ TeXt HeRe") line.RenderF = rf var n int for n = 0; n < b.N; n++ { line.Output() } } // Benchmarks / profiling // BenchmarkLineColor-4 2868162 403.9 ns/op 8 B/op 0 allocs/op // BenchmarkLineColor-4 2944704 400.0 ns/op 8 B/op 0 allocs/op // No change making Color strings to []byte. (reverted change) func BenchmarkLineRenderUpdate(b *testing.B) { Unicode = false var Up = ColorText("BLUE") var Down = ColorText("BOLD BLUE") var Num = ColorText("BRI GREEN") var Sym = ColorText("CYAN") var render = func(output *bytes.Buffer, text []byte) { output.Reset() var last *[]byte // var r Render = Render{Line: text} for _, letter := range text { if unicode.IsUpper(rune(letter)) { if last != &Up { output.Write(Up) last = &Up } // r.Append(Up, 1) } else if unicode.IsLower(rune(letter)) { if last != &Down { output.Write(Down) last = &Down } // r.Append(Down, 1) } else if unicode.IsDigit(rune(letter)) { if last != &Num { output.Write(Num) last = &Num } // r.Append(Num, 1) } else { if last != &Sym { output.Write(Sym) last = &Sym } //r.Append(Sym, 1) } output.WriteByte(letter) // output.WriteString(string(letter)) } // return output.Bytes() // return r.Result } var up int var updater = func(u *bytes.Buffer) { u.Reset() fmt.Fprintf(u, "The Value: %d", up) // u.WriteString("The Value: ") // u.WriteString(strconv.Itoa(up)) up++ // return fmt.Sprintf("The Value: %d", up) } var line *Line = &Line{UpdateF: updater, RenderF: render, Width: 18} for i := 0; i < b.N; i++ { line.Update() line.Output() } } func BenchmarkLineRenderUpdateUnicode(b *testing.B) { Unicode = true var Up = ColorText("BLUE") var Down = ColorText("BOLD BLUE") var Num = ColorText("BRI GREEN") var Sym = ColorText("CYAN") // var unicodeBuff *bytes.Buffer = &bytes.Buffer{} var render = func(output *bytes.Buffer, text []byte) { output.Reset() var last *[]byte // var r Render = Render{Line: text} for _, letter := range text { if unicode.IsUpper(rune(letter)) { if last != &Up { output.Write(Up) last = &Up } // r.Append(Up, 1) } else if unicode.IsLower(rune(letter)) { if last != &Down { output.Write(Down) last = &Down } // r.Append(Down, 1) } else if unicode.IsDigit(rune(letter)) { if last != &Num { output.Write(Num) last = &Num } // r.Append(Num, 1) } else { if last != &Sym { output.Write(Sym) last = &Sym } //r.Append(Sym, 1) } output.WriteByte(letter) // output.WriteString(string(letter)) } // return output.Bytes() // return r.Result } var up int var updater = func(u *bytes.Buffer) { u.Reset() fmt.Fprintf(u, "The Value: %d", up) // u.WriteString("The Value: ") // u.WriteString(strconv.Itoa(up)) up++ // return fmt.Sprintf("The Value: %d", up) } var line *Line = &Line{UpdateF: updater, RenderF: render, Width: 18} for i := 0; i < b.N; i++ { line.Update() line.Output() } }