Terminal-based user interface toolkit
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

438 lines
9.2 KiB

package cview
import (
"bytes"
"fmt"
"testing"
)
const (
// 512 bytes
randomDataSize = 512
// Write randomData 64 times (32768 bytes) before appending
appendSetupWriteCount = 64
)
var (
randomData = generateRandomData()
textViewTestCases = generateTestCases()
)
type textViewTestCase struct {
app bool
color bool
region bool
scroll bool
wrap bool
wordwrap bool
}
func (c *textViewTestCase) String() string {
return fmt.Sprintf("Append=%c/Color=%c/Region=%c/Scroll=%c/Wrap=%c/WordWrap=%c", cl(c.app), cl(c.color), cl(c.region), cl(c.scroll), cl(c.wrap), cl(c.wordwrap))
}
func TestTextViewWrite(t *testing.T) {
t.Parallel()
for _, c := range textViewTestCases {
c := c // Capture
t.Run(c.String(), func(t *testing.T) {
t.Parallel()
var (
tv = tvc(c)
expectedData []byte
n int
err error
)
if c.app {
expectedData, err = prepareAppendTextView(tv)
if err != nil {
t.Errorf("failed to prepare append TextView: %s", err)
}
expectedData = append(expectedData, randomData...)
} else {
expectedData = randomData
}
n, err = tv.Write(randomData)
if err != nil {
t.Errorf("failed to write (successfully wrote %d) bytes: %s", n, err)
} else if n != randomDataSize {
t.Errorf("failed to write: expected to write %d bytes, wrote %d", randomDataSize, n)
}
contents := tv.GetText(false)
if len(contents) != len(expectedData) {
t.Errorf("failed to write: incorrect contents: expected %d bytes, got %d", len(contents), len(expectedData))
} else if !bytes.Equal([]byte(contents), expectedData) {
t.Errorf("failed to write: incorrect contents: values do not match")
}
tv.Clear()
})
}
}
func BenchmarkTextViewWrite(b *testing.B) {
for _, c := range textViewTestCases {
c := c // Capture
b.Run(c.String(), func(b *testing.B) {
var (
tv = tvc(c)
n int
err error
)
if c.app {
_, err = prepareAppendTextView(tv)
if err != nil {
b.Errorf("failed to prepare append TextView: %s", err)
}
}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
n, err = tv.Write(randomData)
if err != nil {
b.Errorf("failed to write (successfully wrote %d) bytes: %s", n, err)
} else if n != randomDataSize {
b.Errorf("failed to write: expected to write %d bytes, wrote %d", randomDataSize, n)
}
if !c.app {
b.StopTimer()
tv.Clear()
b.StartTimer()
}
}
})
}
}
func BenchmarkTextViewIndex(b *testing.B) {
for _, c := range textViewTestCases {
c := c // Capture
b.Run(c.String(), func(b *testing.B) {
var (
tv = tvc(c)
n int
err error
)
_, err = prepareAppendTextView(tv)
if err != nil {
b.Errorf("failed to prepare append TextView: %s", err)
}
n, err = tv.Write(randomData)
if err != nil {
b.Errorf("failed to write (successfully wrote %d) bytes: %s", n, err)
} else if n != randomDataSize {
b.Errorf("failed to write: expected to write %d bytes, wrote %d", randomDataSize, n)
}
tv.index = nil
tv.reindexBuffer(80)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
tv.index = nil
tv.reindexBuffer(80)
}
})
}
}
func TestTextViewGetText(t *testing.T) {
t.Parallel()
tv := NewTextView()
tv.SetDynamicColors(true)
tv.SetRegions(true)
n, err := tv.Write(randomData)
if err != nil {
t.Errorf("failed to write (successfully wrote %d) bytes: %s", n, err)
} else if n != randomDataSize {
t.Errorf("failed to write: expected to write %d bytes, wrote %d", randomDataSize, n)
}
suffix := []byte(`["start"]outer[b]inner[-]outer[""]`)
suffixStripped := []byte("outerinnerouter")
n, err = tv.Write(suffix)
if err != nil {
t.Errorf("failed to write (successfully wrote %d) bytes: %s", n, err)
}
if !bytes.Equal(tv.GetBytes(false), append(randomData, suffix...)) {
t.Error("failed to get un-stripped text: unexpected suffix")
}
if !bytes.Equal(tv.GetBytes(true), append(randomData, suffixStripped...)) {
t.Error("failed to get text stripped text: unexpected suffix")
}
}
func BenchmarkTextViewGetText(b *testing.B) {
for _, c := range textViewTestCases {
c := c // Capture
if c.app {
continue // Skip for this benchmark
}
b.Run(c.String(), func(b *testing.B) {
var (
tv = tvc(c)
n int
err error
v []byte
)
_, err = prepareAppendTextView(tv)
if err != nil {
b.Errorf("failed to prepare append TextView: %s", err)
}
n, err = tv.Write(randomData)
if err != nil {
b.Errorf("failed to write (successfully wrote %d) bytes: %s", n, err)
} else if n != randomDataSize {
b.Errorf("failed to write: expected to write %d bytes, wrote %d", randomDataSize, n)
}
v = tv.GetBytes(true)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
v = tv.GetBytes(true)
}
_ = v
})
}
}
func TestTextViewDraw(t *testing.T) {
t.Parallel()
for _, c := range textViewTestCases {
c := c // Capture
t.Run(c.String(), func(t *testing.T) {
t.Parallel()
tv := tvc(c)
app, err := newTestApp(tv)
if err != nil {
t.Errorf("failed to initialize Application: %s", err)
}
if c.app {
_, err = prepareAppendTextView(tv)
if err != nil {
t.Errorf("failed to prepare append TextView: %s", err)
}
tv.Draw(app.screen)
}
n, err := tv.Write(randomData)
if err != nil {
t.Errorf("failed to write (successfully wrote %d) bytes: %s", n, err)
} else if n != randomDataSize {
t.Errorf("failed to write: expected to write %d bytes, wrote %d", randomDataSize, n)
}
tv.Draw(app.screen)
})
}
}
func BenchmarkTextViewDraw(b *testing.B) {
for _, c := range textViewTestCases {
c := c // Capture
b.Run(c.String(), func(b *testing.B) {
tv := tvc(c)
app, err := newTestApp(tv)
if err != nil {
b.Errorf("failed to initialize Application: %s", err)
}
if c.app {
_, err = prepareAppendTextView(tv)
if err != nil {
b.Errorf("failed to prepare append TextView: %s", err)
}
tv.Draw(app.screen)
}
n, err := tv.Write(randomData)
if err != nil {
b.Errorf("failed to write (successfully wrote %d) bytes: %s", n, err)
} else if n != randomDataSize {
b.Errorf("failed to write: expected to write %d bytes, wrote %d", randomDataSize, n)
}
tv.Draw(app.screen)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
tv.Draw(app.screen)
}
})
}
}
func TestTextViewMaxLines(t *testing.T) {
t.Parallel()
tv := NewTextView()
// append 100 lines with no limit set:
for i := 0; i < 100; i++ {
_, err := tv.Write([]byte(fmt.Sprintf("L%d\n", i)))
if err != nil {
t.Errorf("failed to write to TextView: %s", err)
}
}
// retrieve the total text and see we have the 100 lines:
count := bytes.Count(tv.GetBytes(true), []byte("\n"))
if count != 100 {
t.Errorf("expected 100 lines, got %d", count)
}
// now set the maximum lines to 20, this should clip the buffer:
tv.SetMaxLines(20)
// verify buffer was clipped:
count = len(bytes.Split(tv.GetBytes(true), []byte("\n")))
if count != 20 {
t.Errorf("expected 20 lines, got %d", count)
}
// append 100 more lines:
for i := 100; i < 200; i++ {
_, err := tv.Write([]byte(fmt.Sprintf("L%d\n", i)))
if err != nil {
t.Errorf("failed to write to TextView: %s", err)
}
}
// Sice max lines is set to 20, we should still get 20 lines:
txt := tv.GetBytes(true)
lines := bytes.Split(txt, []byte("\n"))
count = len(lines)
if count != 20 {
t.Errorf("expected 20 lines, got %d", count)
}
// and those 20 lines should be the last ones:
if !bytes.Equal(lines[0], []byte("L181")) {
t.Errorf("expected to get L181, got %s", lines[0])
}
}
func generateTestCases() []*textViewTestCase {
var cases []*textViewTestCase
for i := 0; i < 2; i++ {
app := i == 1
for i := 0; i < 2; i++ {
color := i == 1
for i := 0; i < 2; i++ {
region := i == 1
for i := 0; i < 2; i++ {
scroll := i == 1
for i := 0; i < 2; i++ {
wrap := i == 1
for i := 0; i < 2; i++ {
wordwrap := i == 1
if !wrap && wordwrap {
continue // WordWrap requires Wrap
}
cases = append(cases, &textViewTestCase{app, color, region, scroll, wrap, wordwrap})
}
}
}
}
}
}
return cases
}
func generateRandomData() []byte {
var (
b bytes.Buffer
r = 33
)
for i := 0; i < randomDataSize; i++ {
if i%80 == 0 && i <= 160 {
b.WriteRune('\n')
} else if i%7 == 0 {
b.WriteRune(' ')
} else {
b.WriteRune(rune(r))
}
r++
if r == 127 {
r = 33
}
}
return b.Bytes()
}
func tvc(c *textViewTestCase) *TextView {
tv := NewTextView()
tv.SetDynamicColors(c.color)
tv.SetRegions(c.region)
tv.SetScrollable(c.scroll)
tv.SetWrap(c.wrap)
tv.SetWordWrap(c.wordwrap)
return tv
}
func cl(v bool) rune {
if v {
return 'Y'
}
return 'N'
}
func prepareAppendTextView(t *TextView) ([]byte, error) {
var b []byte
for i := 0; i < appendSetupWriteCount; i++ {
b = append(b, randomData...)
n, err := t.Write(randomData)
if err != nil {
return nil, fmt.Errorf("failed to write (successfully wrote %d) bytes: %s", n, err)
} else if n != randomDataSize {
return nil, fmt.Errorf("failed to write: expected to write %d bytes, wrote %d", randomDataSize, n)
}
}
return b, nil
}