blob: f2eea08255e3ec2df2ec4b77bdd7bea8b3407ed2 [file] [log] [blame]
package buffruneio
import (
"reflect"
"runtime/debug"
"strings"
"testing"
"unicode/utf8"
)
func assertNoError(t *testing.T, err error) {
if err != nil {
t.Log("unexpected error", err)
debug.PrintStack()
t.FailNow()
}
}
func assumeRunesArray(t *testing.T, expected []rune, got []rune) {
if len(expected) != len(got) {
t.Fatal("expected", len(expected), "runes, but got", len(got))
}
for i := 0; i < len(got); i++ {
if expected[i] != got[i] {
t.Fatal("expected rune", expected[i], "at index", i, "but got", got[i])
}
}
}
func assumeRune(t *testing.T, rd *Reader, r rune) {
gotRune, size, err := rd.ReadRune()
wantSize := utf8.RuneLen(r)
if wantSize < 0 {
wantSize = 0
}
if gotRune != r || size != wantSize || err != nil {
t.Fatalf("ReadRune() = %q, %d, %v, wanted %q, %d, nil", gotRune, size, err, r, wantSize)
}
}
func assumeBadRune(t *testing.T, rd *Reader) {
gotRune, size, err := rd.ReadRune()
if gotRune != utf8.RuneError || size != 1 || err != nil {
t.Fatalf("ReadRune() = %q, %d, %v, wanted %q, 1, nil", gotRune, size, err, utf8.RuneError)
}
}
func TestReadString(t *testing.T) {
s := "hello"
rd := NewReader(strings.NewReader(s))
assumeRune(t, rd, 'h')
assumeRune(t, rd, 'e')
assumeRune(t, rd, 'l')
assumeRune(t, rd, 'l')
assumeRune(t, rd, 'o')
assumeRune(t, rd, EOF)
}
func TestMultipleEOF(t *testing.T) {
s := ""
rd := NewReader(strings.NewReader(s))
assumeRune(t, rd, EOF)
assumeRune(t, rd, EOF)
}
func TestBadRunes(t *testing.T) {
s := "ab\xff\ufffd\xffcd"
rd := NewReader(strings.NewReader(s))
assumeRune(t, rd, 'a')
assumeRune(t, rd, 'b')
assumeBadRune(t, rd)
assumeRune(t, rd, utf8.RuneError)
assumeBadRune(t, rd)
assumeRune(t, rd, 'c')
assumeRune(t, rd, 'd')
for i := 0; i < 6; i++ {
assertNoError(t, rd.UnreadRune())
}
assumeRune(t, rd, 'b')
assumeBadRune(t, rd)
assumeRune(t, rd, utf8.RuneError)
assumeBadRune(t, rd)
assumeRune(t, rd, 'c')
assumeRune(t, rd, 'd')
}
func TestUnread(t *testing.T) {
s := "ab"
rd := NewReader(strings.NewReader(s))
assumeRune(t, rd, 'a')
assumeRune(t, rd, 'b')
assertNoError(t, rd.UnreadRune())
assumeRune(t, rd, 'b')
assumeRune(t, rd, EOF)
}
func TestUnreadEOF(t *testing.T) {
s := "x"
rd := NewReader(strings.NewReader(s))
_ = rd.UnreadRune()
assumeRune(t, rd, 'x')
assumeRune(t, rd, EOF)
assumeRune(t, rd, EOF)
assertNoError(t, rd.UnreadRune())
assumeRune(t, rd, EOF)
assertNoError(t, rd.UnreadRune())
assertNoError(t, rd.UnreadRune())
assumeRune(t, rd, EOF)
assumeRune(t, rd, EOF)
assertNoError(t, rd.UnreadRune())
assertNoError(t, rd.UnreadRune())
assertNoError(t, rd.UnreadRune())
assumeRune(t, rd, 'x')
assumeRune(t, rd, EOF)
assumeRune(t, rd, EOF)
}
func TestForget(t *testing.T) {
s := "helio"
rd := NewReader(strings.NewReader(s))
assumeRune(t, rd, 'h')
assumeRune(t, rd, 'e')
assumeRune(t, rd, 'l')
assumeRune(t, rd, 'i')
rd.Forget()
if rd.UnreadRune() != ErrNoRuneToUnread {
t.Fatal("no rune should be available")
}
assumeRune(t, rd, 'o')
}
func TestForgetAfterUnread(t *testing.T) {
s := "helio"
rd := NewReader(strings.NewReader(s))
assumeRune(t, rd, 'h')
assumeRune(t, rd, 'e')
assumeRune(t, rd, 'l')
assumeRune(t, rd, 'i')
assertNoError(t, rd.UnreadRune())
rd.Forget()
if rd.UnreadRune() != ErrNoRuneToUnread {
t.Fatal("no rune should be available")
}
assumeRune(t, rd, 'i')
assumeRune(t, rd, 'o')
}
func TestForgetEmpty(t *testing.T) {
s := ""
rd := NewReader(strings.NewReader(s))
rd.Forget()
assumeRune(t, rd, EOF)
rd.Forget()
}
func TestPeekEmpty(t *testing.T) {
s := ""
rd := NewReader(strings.NewReader(s))
runes := rd.PeekRunes(1)
if len(runes) != 1 {
t.Fatal("incorrect number of runes", len(runes))
}
if runes[0] != EOF {
t.Fatal("incorrect rune", runes[0])
}
}
func TestPeek(t *testing.T) {
s := "a"
rd := NewReader(strings.NewReader(s))
runes := rd.PeekRunes(1)
assumeRunesArray(t, []rune{'a'}, runes)
runes = rd.PeekRunes(1)
assumeRunesArray(t, []rune{'a'}, runes)
assumeRune(t, rd, 'a')
runes = rd.PeekRunes(1)
assumeRunesArray(t, []rune{EOF}, runes)
assumeRune(t, rd, EOF)
}
func TestPeekLarge(t *testing.T) {
s := "abcdefg☺\xff☹"
rd := NewReader(strings.NewReader(s))
runes := rd.PeekRunes(100)
want := []rune{'a', 'b', 'c', 'd', 'e', 'f', 'g', '☺', utf8.RuneError, '☹', EOF}
if !reflect.DeepEqual(runes, want) {
t.Fatalf("PeekRunes(100) = %q, want %q", runes, want)
}
}
var bigString = strings.Repeat("abcdefghi☺\xff☹", 1024) // 16 kB
const bigStringRunes = 12 * 1024 // 12k runes
func BenchmarkRead16K(b *testing.B) {
// Read 16K with no unread, no forget.
benchmarkRead(b, 1, false)
}
func BenchmarkReadForget16K(b *testing.B) {
// Read 16K, forgetting every 128 runes.
benchmarkRead(b, 1, true)
}
func BenchmarkReadRewind16K(b *testing.B) {
// Read 16K, unread all, read that 16K again.
benchmarkRead(b, 2, false)
}
func benchmarkRead(b *testing.B, count int, forget bool) {
if len(bigString) != 16*1024 {
b.Fatal("wrong length for bigString")
}
sr0 := strings.NewReader(bigString)
sr := new(strings.Reader)
b.SetBytes(int64(len(bigString)))
b.ReportAllocs()
for i := 0; i < b.N; i++ {
*sr = *sr0
rd := NewReader(sr)
for repeat := 0; repeat < count; repeat++ {
for j := 0; j < bigStringRunes; j++ {
r, _, err := rd.ReadRune()
if err != nil {
b.Fatal(err)
}
if r == EOF {
b.Fatal("unexpected EOF")
}
if forget && j%128 == 127 {
rd.Forget()
}
}
r, _, err := rd.ReadRune()
if err != nil {
b.Fatal(err)
}
if r != EOF {
b.Fatalf("missing EOF - %q", r)
}
if repeat == count-1 {
break
}
for rd.UnreadRune() == nil {
// keep unreading
}
}
}
}