1
0
mirror of https://github.com/golang/go synced 2024-11-08 10:46:23 -07:00
go/src/pkg/bufio/bufio_test.go
Dmitriy Vyukov 0ad2cd004c bufio: fix benchmarks behavior
Currently the benchmarks lie to testing package by doing O(N)
work under StopTimer. And that hidden O(N) actually consitutes
the bulk of benchmark work (e.g includes GC per iteration).
This behavior accounts for windows-amd64-race builder hangs.

Before:
BenchmarkReaderCopyOptimal-4	 1000000	      1861 ns/op
BenchmarkReaderCopyUnoptimal-4	  500000	      3327 ns/op
BenchmarkReaderCopyNoWriteTo-4	   50000	     34549 ns/op
BenchmarkWriterCopyOptimal-4	  100000	     16849 ns/op
BenchmarkWriterCopyUnoptimal-4	  500000	      3126 ns/op
BenchmarkWriterCopyNoReadFrom-4	   50000	     34609 ns/op
ok  	bufio	65.273s

After:
BenchmarkReaderCopyOptimal-4	10000000	       172 ns/op
BenchmarkReaderCopyUnoptimal-4	10000000	       267 ns/op
BenchmarkReaderCopyNoWriteTo-4	  100000	     22905 ns/op
BenchmarkWriterCopyOptimal-4	10000000	       170 ns/op
BenchmarkWriterCopyUnoptimal-4	10000000	       226 ns/op
BenchmarkWriterCopyNoReadFrom-4	  100000	     20575 ns/op
ok  	bufio	14.074s

Note the change in total time.

LGTM=alex.brainman, rsc
R=golang-codereviews, alex.brainman, rsc
CC=golang-codereviews
https://golang.org/cl/51360046
2014-01-23 15:13:21 -05:00

1238 lines
31 KiB
Go

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package bufio_test
import (
. "bufio"
"bytes"
"errors"
"fmt"
"io"
"io/ioutil"
"strings"
"testing"
"testing/iotest"
"unicode/utf8"
)
// Reads from a reader and rot13s the result.
type rot13Reader struct {
r io.Reader
}
func newRot13Reader(r io.Reader) *rot13Reader {
r13 := new(rot13Reader)
r13.r = r
return r13
}
func (r13 *rot13Reader) Read(p []byte) (int, error) {
n, err := r13.r.Read(p)
if err != nil {
return n, err
}
for i := 0; i < n; i++ {
c := p[i] | 0x20 // lowercase byte
if 'a' <= c && c <= 'm' {
p[i] += 13
} else if 'n' <= c && c <= 'z' {
p[i] -= 13
}
}
return n, nil
}
// Call ReadByte to accumulate the text of a file
func readBytes(buf *Reader) string {
var b [1000]byte
nb := 0
for {
c, err := buf.ReadByte()
if err == io.EOF {
break
}
if err == nil {
b[nb] = c
nb++
} else if err != iotest.ErrTimeout {
panic("Data: " + err.Error())
}
}
return string(b[0:nb])
}
func TestReaderSimple(t *testing.T) {
data := "hello world"
b := NewReader(bytes.NewBufferString(data))
if s := readBytes(b); s != "hello world" {
t.Errorf("simple hello world test failed: got %q", s)
}
b = NewReader(newRot13Reader(bytes.NewBufferString(data)))
if s := readBytes(b); s != "uryyb jbeyq" {
t.Errorf("rot13 hello world test failed: got %q", s)
}
}
type readMaker struct {
name string
fn func(io.Reader) io.Reader
}
var readMakers = []readMaker{
{"full", func(r io.Reader) io.Reader { return r }},
{"byte", iotest.OneByteReader},
{"half", iotest.HalfReader},
{"data+err", iotest.DataErrReader},
{"timeout", iotest.TimeoutReader},
}
// Call ReadString (which ends up calling everything else)
// to accumulate the text of a file.
func readLines(b *Reader) string {
s := ""
for {
s1, err := b.ReadString('\n')
if err == io.EOF {
break
}
if err != nil && err != iotest.ErrTimeout {
panic("GetLines: " + err.Error())
}
s += s1
}
return s
}
// Call Read to accumulate the text of a file
func reads(buf *Reader, m int) string {
var b [1000]byte
nb := 0
for {
n, err := buf.Read(b[nb : nb+m])
nb += n
if err == io.EOF {
break
}
}
return string(b[0:nb])
}
type bufReader struct {
name string
fn func(*Reader) string
}
var bufreaders = []bufReader{
{"1", func(b *Reader) string { return reads(b, 1) }},
{"2", func(b *Reader) string { return reads(b, 2) }},
{"3", func(b *Reader) string { return reads(b, 3) }},
{"4", func(b *Reader) string { return reads(b, 4) }},
{"5", func(b *Reader) string { return reads(b, 5) }},
{"7", func(b *Reader) string { return reads(b, 7) }},
{"bytes", readBytes},
{"lines", readLines},
}
const minReadBufferSize = 16
var bufsizes = []int{
0, minReadBufferSize, 23, 32, 46, 64, 93, 128, 1024, 4096,
}
func TestReader(t *testing.T) {
var texts [31]string
str := ""
all := ""
for i := 0; i < len(texts)-1; i++ {
texts[i] = str + "\n"
all += texts[i]
str += string(i%26 + 'a')
}
texts[len(texts)-1] = all
for h := 0; h < len(texts); h++ {
text := texts[h]
for i := 0; i < len(readMakers); i++ {
for j := 0; j < len(bufreaders); j++ {
for k := 0; k < len(bufsizes); k++ {
readmaker := readMakers[i]
bufreader := bufreaders[j]
bufsize := bufsizes[k]
read := readmaker.fn(bytes.NewBufferString(text))
buf := NewReaderSize(read, bufsize)
s := bufreader.fn(buf)
if s != text {
t.Errorf("reader=%s fn=%s bufsize=%d want=%q got=%q",
readmaker.name, bufreader.name, bufsize, text, s)
}
}
}
}
}
}
// A StringReader delivers its data one string segment at a time via Read.
type StringReader struct {
data []string
step int
}
func (r *StringReader) Read(p []byte) (n int, err error) {
if r.step < len(r.data) {
s := r.data[r.step]
n = copy(p, s)
r.step++
} else {
err = io.EOF
}
return
}
func readRuneSegments(t *testing.T, segments []string) {
got := ""
want := strings.Join(segments, "")
r := NewReader(&StringReader{data: segments})
for {
r, _, err := r.ReadRune()
if err != nil {
if err != io.EOF {
return
}
break
}
got += string(r)
}
if got != want {
t.Errorf("segments=%v got=%s want=%s", segments, got, want)
}
}
var segmentList = [][]string{
{},
{""},
{"日", "本語"},
{"\u65e5", "\u672c", "\u8a9e"},
{"\U000065e5", "\U0000672c", "\U00008a9e"},
{"\xe6", "\x97\xa5\xe6", "\x9c\xac\xe8\xaa\x9e"},
{"Hello", ", ", "World", "!"},
{"Hello", ", ", "", "World", "!"},
}
func TestReadRune(t *testing.T) {
for _, s := range segmentList {
readRuneSegments(t, s)
}
}
func TestUnreadRune(t *testing.T) {
got := ""
segments := []string{"Hello, world:", "日本語"}
data := strings.Join(segments, "")
r := NewReader(&StringReader{data: segments})
// Normal execution.
for {
r1, _, err := r.ReadRune()
if err != nil {
if err != io.EOF {
t.Error("unexpected EOF")
}
break
}
got += string(r1)
// Put it back and read it again
if err = r.UnreadRune(); err != nil {
t.Error("unexpected error on UnreadRune:", err)
}
r2, _, err := r.ReadRune()
if err != nil {
t.Error("unexpected error reading after unreading:", err)
}
if r1 != r2 {
t.Errorf("incorrect rune after unread: got %c wanted %c", r1, r2)
}
}
if got != data {
t.Errorf("want=%q got=%q", data, got)
}
}
func TestUnreadByte(t *testing.T) {
want := "Hello, world"
got := ""
segments := []string{"Hello, ", "world"}
r := NewReader(&StringReader{data: segments})
// Normal execution.
for {
b1, err := r.ReadByte()
if err != nil {
if err != io.EOF {
t.Fatal("unexpected EOF")
}
break
}
got += string(b1)
// Put it back and read it again
if err = r.UnreadByte(); err != nil {
t.Fatalf("unexpected error on UnreadByte: %v", err)
}
b2, err := r.ReadByte()
if err != nil {
t.Fatalf("unexpected error reading after unreading: %v", err)
}
if b1 != b2 {
t.Fatalf("incorrect byte after unread: got %c wanted %c", b1, b2)
}
}
if got != want {
t.Errorf("got=%q want=%q", got, want)
}
}
// Test that UnreadRune fails if the preceding operation was not a ReadRune.
func TestUnreadRuneError(t *testing.T) {
buf := make([]byte, 3) // All runes in this test are 3 bytes long
r := NewReader(&StringReader{data: []string{"日本語日本語日本語"}})
if r.UnreadRune() == nil {
t.Error("expected error on UnreadRune from fresh buffer")
}
_, _, err := r.ReadRune()
if err != nil {
t.Error("unexpected error on ReadRune (1):", err)
}
if err = r.UnreadRune(); err != nil {
t.Error("unexpected error on UnreadRune (1):", err)
}
if r.UnreadRune() == nil {
t.Error("expected error after UnreadRune (1)")
}
// Test error after Read.
_, _, err = r.ReadRune() // reset state
if err != nil {
t.Error("unexpected error on ReadRune (2):", err)
}
_, err = r.Read(buf)
if err != nil {
t.Error("unexpected error on Read (2):", err)
}
if r.UnreadRune() == nil {
t.Error("expected error after Read (2)")
}
// Test error after ReadByte.
_, _, err = r.ReadRune() // reset state
if err != nil {
t.Error("unexpected error on ReadRune (2):", err)
}
for _ = range buf {
_, err = r.ReadByte()
if err != nil {
t.Error("unexpected error on ReadByte (2):", err)
}
}
if r.UnreadRune() == nil {
t.Error("expected error after ReadByte")
}
// Test error after UnreadByte.
_, _, err = r.ReadRune() // reset state
if err != nil {
t.Error("unexpected error on ReadRune (3):", err)
}
_, err = r.ReadByte()
if err != nil {
t.Error("unexpected error on ReadByte (3):", err)
}
err = r.UnreadByte()
if err != nil {
t.Error("unexpected error on UnreadByte (3):", err)
}
if r.UnreadRune() == nil {
t.Error("expected error after UnreadByte (3)")
}
}
func TestUnreadRuneAtEOF(t *testing.T) {
// UnreadRune/ReadRune should error at EOF (was a bug; used to panic)
r := NewReader(strings.NewReader("x"))
r.ReadRune()
r.ReadRune()
r.UnreadRune()
_, _, err := r.ReadRune()
if err == nil {
t.Error("expected error at EOF")
} else if err != io.EOF {
t.Error("expected EOF; got", err)
}
}
func TestReadWriteRune(t *testing.T) {
const NRune = 1000
byteBuf := new(bytes.Buffer)
w := NewWriter(byteBuf)
// Write the runes out using WriteRune
buf := make([]byte, utf8.UTFMax)
for r := rune(0); r < NRune; r++ {
size := utf8.EncodeRune(buf, r)
nbytes, err := w.WriteRune(r)
if err != nil {
t.Fatalf("WriteRune(0x%x) error: %s", r, err)
}
if nbytes != size {
t.Fatalf("WriteRune(0x%x) expected %d, got %d", r, size, nbytes)
}
}
w.Flush()
r := NewReader(byteBuf)
// Read them back with ReadRune
for r1 := rune(0); r1 < NRune; r1++ {
size := utf8.EncodeRune(buf, r1)
nr, nbytes, err := r.ReadRune()
if nr != r1 || nbytes != size || err != nil {
t.Fatalf("ReadRune(0x%x) got 0x%x,%d not 0x%x,%d (err=%s)", r1, nr, nbytes, r1, size, err)
}
}
}
func TestWriter(t *testing.T) {
var data [8192]byte
for i := 0; i < len(data); i++ {
data[i] = byte(' ' + i%('~'-' '))
}
w := new(bytes.Buffer)
for i := 0; i < len(bufsizes); i++ {
for j := 0; j < len(bufsizes); j++ {
nwrite := bufsizes[i]
bs := bufsizes[j]
// Write nwrite bytes using buffer size bs.
// Check that the right amount makes it out
// and that the data is correct.
w.Reset()
buf := NewWriterSize(w, bs)
context := fmt.Sprintf("nwrite=%d bufsize=%d", nwrite, bs)
n, e1 := buf.Write(data[0:nwrite])
if e1 != nil || n != nwrite {
t.Errorf("%s: buf.Write %d = %d, %v", context, nwrite, n, e1)
continue
}
if e := buf.Flush(); e != nil {
t.Errorf("%s: buf.Flush = %v", context, e)
}
written := w.Bytes()
if len(written) != nwrite {
t.Errorf("%s: %d bytes written", context, len(written))
}
for l := 0; l < len(written); l++ {
if written[i] != data[i] {
t.Errorf("wrong bytes written")
t.Errorf("want=%q", data[0:len(written)])
t.Errorf("have=%q", written)
}
}
}
}
}
// Check that write errors are returned properly.
type errorWriterTest struct {
n, m int
err error
expect error
}
func (w errorWriterTest) Write(p []byte) (int, error) {
return len(p) * w.n / w.m, w.err
}
var errorWriterTests = []errorWriterTest{
{0, 1, nil, io.ErrShortWrite},
{1, 2, nil, io.ErrShortWrite},
{1, 1, nil, nil},
{0, 1, io.ErrClosedPipe, io.ErrClosedPipe},
{1, 2, io.ErrClosedPipe, io.ErrClosedPipe},
{1, 1, io.ErrClosedPipe, io.ErrClosedPipe},
}
func TestWriteErrors(t *testing.T) {
for _, w := range errorWriterTests {
buf := NewWriter(w)
_, e := buf.Write([]byte("hello world"))
if e != nil {
t.Errorf("Write hello to %v: %v", w, e)
continue
}
// Two flushes, to verify the error is sticky.
for i := 0; i < 2; i++ {
e = buf.Flush()
if e != w.expect {
t.Errorf("Flush %d/2 %v: got %v, wanted %v", i+1, w, e, w.expect)
}
}
}
}
func TestNewReaderSizeIdempotent(t *testing.T) {
const BufSize = 1000
b := NewReaderSize(bytes.NewBufferString("hello world"), BufSize)
// Does it recognize itself?
b1 := NewReaderSize(b, BufSize)
if b1 != b {
t.Error("NewReaderSize did not detect underlying Reader")
}
// Does it wrap if existing buffer is too small?
b2 := NewReaderSize(b, 2*BufSize)
if b2 == b {
t.Error("NewReaderSize did not enlarge buffer")
}
}
func TestNewWriterSizeIdempotent(t *testing.T) {
const BufSize = 1000
b := NewWriterSize(new(bytes.Buffer), BufSize)
// Does it recognize itself?
b1 := NewWriterSize(b, BufSize)
if b1 != b {
t.Error("NewWriterSize did not detect underlying Writer")
}
// Does it wrap if existing buffer is too small?
b2 := NewWriterSize(b, 2*BufSize)
if b2 == b {
t.Error("NewWriterSize did not enlarge buffer")
}
}
func TestWriteString(t *testing.T) {
const BufSize = 8
buf := new(bytes.Buffer)
b := NewWriterSize(buf, BufSize)
b.WriteString("0") // easy
b.WriteString("123456") // still easy
b.WriteString("7890") // easy after flush
b.WriteString("abcdefghijklmnopqrstuvwxy") // hard
b.WriteString("z")
if err := b.Flush(); err != nil {
t.Error("WriteString", err)
}
s := "01234567890abcdefghijklmnopqrstuvwxyz"
if string(buf.Bytes()) != s {
t.Errorf("WriteString wants %q gets %q", s, string(buf.Bytes()))
}
}
func TestBufferFull(t *testing.T) {
const longString = "And now, hello, world! It is the time for all good men to come to the aid of their party"
buf := NewReaderSize(strings.NewReader(longString), minReadBufferSize)
line, err := buf.ReadSlice('!')
if string(line) != "And now, hello, " || err != ErrBufferFull {
t.Errorf("first ReadSlice(,) = %q, %v", line, err)
}
line, err = buf.ReadSlice('!')
if string(line) != "world!" || err != nil {
t.Errorf("second ReadSlice(,) = %q, %v", line, err)
}
}
func TestPeek(t *testing.T) {
p := make([]byte, 10)
// string is 16 (minReadBufferSize) long.
buf := NewReaderSize(strings.NewReader("abcdefghijklmnop"), minReadBufferSize)
if s, err := buf.Peek(1); string(s) != "a" || err != nil {
t.Fatalf("want %q got %q, err=%v", "a", string(s), err)
}
if s, err := buf.Peek(4); string(s) != "abcd" || err != nil {
t.Fatalf("want %q got %q, err=%v", "abcd", string(s), err)
}
if _, err := buf.Peek(-1); err != ErrNegativeCount {
t.Fatalf("want ErrNegativeCount got %v", err)
}
if _, err := buf.Peek(32); err != ErrBufferFull {
t.Fatalf("want ErrBufFull got %v", err)
}
if _, err := buf.Read(p[0:3]); string(p[0:3]) != "abc" || err != nil {
t.Fatalf("want %q got %q, err=%v", "abc", string(p[0:3]), err)
}
if s, err := buf.Peek(1); string(s) != "d" || err != nil {
t.Fatalf("want %q got %q, err=%v", "d", string(s), err)
}
if s, err := buf.Peek(2); string(s) != "de" || err != nil {
t.Fatalf("want %q got %q, err=%v", "de", string(s), err)
}
if _, err := buf.Read(p[0:3]); string(p[0:3]) != "def" || err != nil {
t.Fatalf("want %q got %q, err=%v", "def", string(p[0:3]), err)
}
if s, err := buf.Peek(4); string(s) != "ghij" || err != nil {
t.Fatalf("want %q got %q, err=%v", "ghij", string(s), err)
}
if _, err := buf.Read(p[0:]); string(p[0:]) != "ghijklmnop" || err != nil {
t.Fatalf("want %q got %q, err=%v", "ghijklmnop", string(p[0:minReadBufferSize]), err)
}
if s, err := buf.Peek(0); string(s) != "" || err != nil {
t.Fatalf("want %q got %q, err=%v", "", string(s), err)
}
if _, err := buf.Peek(1); err != io.EOF {
t.Fatalf("want EOF got %v", err)
}
// Test for issue 3022, not exposing a reader's error on a successful Peek.
buf = NewReaderSize(dataAndEOFReader("abcd"), 32)
if s, err := buf.Peek(2); string(s) != "ab" || err != nil {
t.Errorf(`Peek(2) on "abcd", EOF = %q, %v; want "ab", nil`, string(s), err)
}
if s, err := buf.Peek(4); string(s) != "abcd" || err != nil {
t.Errorf(`Peek(4) on "abcd", EOF = %q, %v; want "abcd", nil`, string(s), err)
}
if n, err := buf.Read(p[0:5]); string(p[0:n]) != "abcd" || err != nil {
t.Fatalf("Read after peek = %q, %v; want abcd, EOF", p[0:n], err)
}
if n, err := buf.Read(p[0:1]); string(p[0:n]) != "" || err != io.EOF {
t.Fatalf(`second Read after peek = %q, %v; want "", EOF`, p[0:n], err)
}
}
type dataAndEOFReader string
func (r dataAndEOFReader) Read(p []byte) (int, error) {
return copy(p, r), io.EOF
}
func TestPeekThenUnreadRune(t *testing.T) {
// This sequence used to cause a crash.
r := NewReader(strings.NewReader("x"))
r.ReadRune()
r.Peek(1)
r.UnreadRune()
r.ReadRune() // Used to panic here
}
var testOutput = []byte("0123456789abcdefghijklmnopqrstuvwxy")
var testInput = []byte("012\n345\n678\n9ab\ncde\nfgh\nijk\nlmn\nopq\nrst\nuvw\nxy")
var testInputrn = []byte("012\r\n345\r\n678\r\n9ab\r\ncde\r\nfgh\r\nijk\r\nlmn\r\nopq\r\nrst\r\nuvw\r\nxy\r\n\n\r\n")
// TestReader wraps a []byte and returns reads of a specific length.
type testReader struct {
data []byte
stride int
}
func (t *testReader) Read(buf []byte) (n int, err error) {
n = t.stride
if n > len(t.data) {
n = len(t.data)
}
if n > len(buf) {
n = len(buf)
}
copy(buf, t.data)
t.data = t.data[n:]
if len(t.data) == 0 {
err = io.EOF
}
return
}
func testReadLine(t *testing.T, input []byte) {
//for stride := 1; stride < len(input); stride++ {
for stride := 1; stride < 2; stride++ {
done := 0
reader := testReader{input, stride}
l := NewReaderSize(&reader, len(input)+1)
for {
line, isPrefix, err := l.ReadLine()
if len(line) > 0 && err != nil {
t.Errorf("ReadLine returned both data and error: %s", err)
}
if isPrefix {
t.Errorf("ReadLine returned prefix")
}
if err != nil {
if err != io.EOF {
t.Fatalf("Got unknown error: %s", err)
}
break
}
if want := testOutput[done : done+len(line)]; !bytes.Equal(want, line) {
t.Errorf("Bad line at stride %d: want: %x got: %x", stride, want, line)
}
done += len(line)
}
if done != len(testOutput) {
t.Errorf("ReadLine didn't return everything: got: %d, want: %d (stride: %d)", done, len(testOutput), stride)
}
}
}
func TestReadLine(t *testing.T) {
testReadLine(t, testInput)
testReadLine(t, testInputrn)
}
func TestLineTooLong(t *testing.T) {
data := make([]byte, 0)
for i := 0; i < minReadBufferSize*5/2; i++ {
data = append(data, '0'+byte(i%10))
}
buf := bytes.NewBuffer(data)
l := NewReaderSize(buf, minReadBufferSize)
line, isPrefix, err := l.ReadLine()
if !isPrefix || !bytes.Equal(line, data[:minReadBufferSize]) || err != nil {
t.Errorf("bad result for first line: got %q want %q %v", line, data[:minReadBufferSize], err)
}
data = data[len(line):]
line, isPrefix, err = l.ReadLine()
if !isPrefix || !bytes.Equal(line, data[:minReadBufferSize]) || err != nil {
t.Errorf("bad result for second line: got %q want %q %v", line, data[:minReadBufferSize], err)
}
data = data[len(line):]
line, isPrefix, err = l.ReadLine()
if isPrefix || !bytes.Equal(line, data[:minReadBufferSize/2]) || err != nil {
t.Errorf("bad result for third line: got %q want %q %v", line, data[:minReadBufferSize/2], err)
}
line, isPrefix, err = l.ReadLine()
if isPrefix || err == nil {
t.Errorf("expected no more lines: %x %s", line, err)
}
}
func TestReadAfterLines(t *testing.T) {
line1 := "this is line1"
restData := "this is line2\nthis is line 3\n"
inbuf := bytes.NewBuffer([]byte(line1 + "\n" + restData))
outbuf := new(bytes.Buffer)
maxLineLength := len(line1) + len(restData)/2
l := NewReaderSize(inbuf, maxLineLength)
line, isPrefix, err := l.ReadLine()
if isPrefix || err != nil || string(line) != line1 {
t.Errorf("bad result for first line: isPrefix=%v err=%v line=%q", isPrefix, err, string(line))
}
n, err := io.Copy(outbuf, l)
if int(n) != len(restData) || err != nil {
t.Errorf("bad result for Read: n=%d err=%v", n, err)
}
if outbuf.String() != restData {
t.Errorf("bad result for Read: got %q; expected %q", outbuf.String(), restData)
}
}
func TestReadEmptyBuffer(t *testing.T) {
l := NewReaderSize(new(bytes.Buffer), minReadBufferSize)
line, isPrefix, err := l.ReadLine()
if err != io.EOF {
t.Errorf("expected EOF from ReadLine, got '%s' %t %s", line, isPrefix, err)
}
}
func TestLinesAfterRead(t *testing.T) {
l := NewReaderSize(bytes.NewBuffer([]byte("foo")), minReadBufferSize)
_, err := ioutil.ReadAll(l)
if err != nil {
t.Error(err)
return
}
line, isPrefix, err := l.ReadLine()
if err != io.EOF {
t.Errorf("expected EOF from ReadLine, got '%s' %t %s", line, isPrefix, err)
}
}
func TestReadLineNonNilLineOrError(t *testing.T) {
r := NewReader(strings.NewReader("line 1\n"))
for i := 0; i < 2; i++ {
l, _, err := r.ReadLine()
if l != nil && err != nil {
t.Fatalf("on line %d/2; ReadLine=%#v, %v; want non-nil line or Error, but not both",
i+1, l, err)
}
}
}
type readLineResult struct {
line []byte
isPrefix bool
err error
}
var readLineNewlinesTests = []struct {
input string
expect []readLineResult
}{
{"012345678901234\r\n012345678901234\r\n", []readLineResult{
{[]byte("012345678901234"), true, nil},
{nil, false, nil},
{[]byte("012345678901234"), true, nil},
{nil, false, nil},
{nil, false, io.EOF},
}},
{"0123456789012345\r012345678901234\r", []readLineResult{
{[]byte("0123456789012345"), true, nil},
{[]byte("\r012345678901234"), true, nil},
{[]byte("\r"), false, nil},
{nil, false, io.EOF},
}},
}
func TestReadLineNewlines(t *testing.T) {
for _, e := range readLineNewlinesTests {
testReadLineNewlines(t, e.input, e.expect)
}
}
func testReadLineNewlines(t *testing.T, input string, expect []readLineResult) {
b := NewReaderSize(strings.NewReader(input), minReadBufferSize)
for i, e := range expect {
line, isPrefix, err := b.ReadLine()
if !bytes.Equal(line, e.line) {
t.Errorf("%q call %d, line == %q, want %q", input, i, line, e.line)
return
}
if isPrefix != e.isPrefix {
t.Errorf("%q call %d, isPrefix == %v, want %v", input, i, isPrefix, e.isPrefix)
return
}
if err != e.err {
t.Errorf("%q call %d, err == %v, want %v", input, i, err, e.err)
return
}
}
}
func createTestInput(n int) []byte {
input := make([]byte, n)
for i := range input {
// 101 and 251 are arbitrary prime numbers.
// The idea is to create an input sequence
// which doesn't repeat too frequently.
input[i] = byte(i % 251)
if i%101 == 0 {
input[i] ^= byte(i / 101)
}
}
return input
}
func TestReaderWriteTo(t *testing.T) {
input := createTestInput(8192)
r := NewReader(onlyReader{bytes.NewBuffer(input)})
w := new(bytes.Buffer)
if n, err := r.WriteTo(w); err != nil || n != int64(len(input)) {
t.Fatalf("r.WriteTo(w) = %d, %v, want %d, nil", n, err, len(input))
}
for i, val := range w.Bytes() {
if val != input[i] {
t.Errorf("after write: out[%d] = %#x, want %#x", i, val, input[i])
}
}
}
type errorWriterToTest struct {
rn, wn int
rerr, werr error
expected error
}
func (r errorWriterToTest) Read(p []byte) (int, error) {
return len(p) * r.rn, r.rerr
}
func (w errorWriterToTest) Write(p []byte) (int, error) {
return len(p) * w.wn, w.werr
}
var errorWriterToTests = []errorWriterToTest{
{1, 0, nil, io.ErrClosedPipe, io.ErrClosedPipe},
{0, 1, io.ErrClosedPipe, nil, io.ErrClosedPipe},
{0, 0, io.ErrUnexpectedEOF, io.ErrClosedPipe, io.ErrClosedPipe},
{0, 1, io.EOF, nil, nil},
}
func TestReaderWriteToErrors(t *testing.T) {
for i, rw := range errorWriterToTests {
r := NewReader(rw)
if _, err := r.WriteTo(rw); err != rw.expected {
t.Errorf("r.WriteTo(errorWriterToTests[%d]) = _, %v, want _,%v", i, err, rw.expected)
}
}
}
func TestWriterReadFrom(t *testing.T) {
ws := []func(io.Writer) io.Writer{
func(w io.Writer) io.Writer { return onlyWriter{w} },
func(w io.Writer) io.Writer { return w },
}
rs := []func(io.Reader) io.Reader{
iotest.DataErrReader,
func(r io.Reader) io.Reader { return r },
}
for ri, rfunc := range rs {
for wi, wfunc := range ws {
input := createTestInput(8192)
b := new(bytes.Buffer)
w := NewWriter(wfunc(b))
r := rfunc(bytes.NewBuffer(input))
if n, err := w.ReadFrom(r); err != nil || n != int64(len(input)) {
t.Errorf("ws[%d],rs[%d]: w.ReadFrom(r) = %d, %v, want %d, nil", wi, ri, n, err, len(input))
continue
}
if err := w.Flush(); err != nil {
t.Errorf("Flush returned %v", err)
continue
}
if got, want := b.String(), string(input); got != want {
t.Errorf("ws[%d], rs[%d]:\ngot %q\nwant %q\n", wi, ri, got, want)
}
}
}
}
type errorReaderFromTest struct {
rn, wn int
rerr, werr error
expected error
}
func (r errorReaderFromTest) Read(p []byte) (int, error) {
return len(p) * r.rn, r.rerr
}
func (w errorReaderFromTest) Write(p []byte) (int, error) {
return len(p) * w.wn, w.werr
}
var errorReaderFromTests = []errorReaderFromTest{
{0, 1, io.EOF, nil, nil},
{1, 1, io.EOF, nil, nil},
{0, 1, io.ErrClosedPipe, nil, io.ErrClosedPipe},
{0, 0, io.ErrClosedPipe, io.ErrShortWrite, io.ErrClosedPipe},
{1, 0, nil, io.ErrShortWrite, io.ErrShortWrite},
}
func TestWriterReadFromErrors(t *testing.T) {
for i, rw := range errorReaderFromTests {
w := NewWriter(rw)
if _, err := w.ReadFrom(rw); err != rw.expected {
t.Errorf("w.ReadFrom(errorReaderFromTests[%d]) = _, %v, want _,%v", i, err, rw.expected)
}
}
}
// TestWriterReadFromCounts tests that using io.Copy to copy into a
// bufio.Writer does not prematurely flush the buffer. For example, when
// buffering writes to a network socket, excessive network writes should be
// avoided.
func TestWriterReadFromCounts(t *testing.T) {
var w0 writeCountingDiscard
b0 := NewWriterSize(&w0, 1234)
b0.WriteString(strings.Repeat("x", 1000))
if w0 != 0 {
t.Fatalf("write 1000 'x's: got %d writes, want 0", w0)
}
b0.WriteString(strings.Repeat("x", 200))
if w0 != 0 {
t.Fatalf("write 1200 'x's: got %d writes, want 0", w0)
}
io.Copy(b0, onlyReader{strings.NewReader(strings.Repeat("x", 30))})
if w0 != 0 {
t.Fatalf("write 1230 'x's: got %d writes, want 0", w0)
}
io.Copy(b0, onlyReader{strings.NewReader(strings.Repeat("x", 9))})
if w0 != 1 {
t.Fatalf("write 1239 'x's: got %d writes, want 1", w0)
}
var w1 writeCountingDiscard
b1 := NewWriterSize(&w1, 1234)
b1.WriteString(strings.Repeat("x", 1200))
b1.Flush()
if w1 != 1 {
t.Fatalf("flush 1200 'x's: got %d writes, want 1", w1)
}
b1.WriteString(strings.Repeat("x", 89))
if w1 != 1 {
t.Fatalf("write 1200 + 89 'x's: got %d writes, want 1", w1)
}
io.Copy(b1, onlyReader{strings.NewReader(strings.Repeat("x", 700))})
if w1 != 1 {
t.Fatalf("write 1200 + 789 'x's: got %d writes, want 1", w1)
}
io.Copy(b1, onlyReader{strings.NewReader(strings.Repeat("x", 600))})
if w1 != 2 {
t.Fatalf("write 1200 + 1389 'x's: got %d writes, want 2", w1)
}
b1.Flush()
if w1 != 3 {
t.Fatalf("flush 1200 + 1389 'x's: got %d writes, want 3", w1)
}
}
// A writeCountingDiscard is like ioutil.Discard and counts the number of times
// Write is called on it.
type writeCountingDiscard int
func (w *writeCountingDiscard) Write(p []byte) (int, error) {
*w++
return len(p), nil
}
type negativeReader int
func (r *negativeReader) Read([]byte) (int, error) { return -1, nil }
func TestNegativeRead(t *testing.T) {
// should panic with a description pointing at the reader, not at itself.
// (should NOT panic with slice index error, for example.)
b := NewReader(new(negativeReader))
defer func() {
switch err := recover().(type) {
case nil:
t.Fatal("read did not panic")
case error:
if !strings.Contains(err.Error(), "reader returned negative count from Read") {
t.Fatalf("wrong panic: %v", err)
}
default:
t.Fatalf("unexpected panic value: %T(%v)", err, err)
}
}()
b.Read(make([]byte, 100))
}
var errFake = errors.New("fake error")
type errorThenGoodReader struct {
didErr bool
nread int
}
func (r *errorThenGoodReader) Read(p []byte) (int, error) {
r.nread++
if !r.didErr {
r.didErr = true
return 0, errFake
}
return len(p), nil
}
func TestReaderClearError(t *testing.T) {
r := &errorThenGoodReader{}
b := NewReader(r)
buf := make([]byte, 1)
if _, err := b.Read(nil); err != nil {
t.Fatalf("1st nil Read = %v; want nil", err)
}
if _, err := b.Read(buf); err != errFake {
t.Fatalf("1st Read = %v; want errFake", err)
}
if _, err := b.Read(nil); err != nil {
t.Fatalf("2nd nil Read = %v; want nil", err)
}
if _, err := b.Read(buf); err != nil {
t.Fatalf("3rd Read with buffer = %v; want nil", err)
}
if r.nread != 2 {
t.Errorf("num reads = %d; want 2", r.nread)
}
}
// Test for golang.org/issue/5947
func TestWriterReadFromWhileFull(t *testing.T) {
buf := new(bytes.Buffer)
w := NewWriterSize(buf, 10)
// Fill buffer exactly.
n, err := w.Write([]byte("0123456789"))
if n != 10 || err != nil {
t.Fatalf("Write returned (%v, %v), want (10, nil)", n, err)
}
// Use ReadFrom to read in some data.
n2, err := w.ReadFrom(strings.NewReader("abcdef"))
if n2 != 6 || err != nil {
t.Fatalf("ReadFrom returned (%v, %v), want (6, nil)", n, err)
}
}
func TestReaderReset(t *testing.T) {
r := NewReader(strings.NewReader("foo foo"))
buf := make([]byte, 3)
r.Read(buf)
if string(buf) != "foo" {
t.Errorf("buf = %q; want foo", buf)
}
r.Reset(strings.NewReader("bar bar"))
all, err := ioutil.ReadAll(r)
if err != nil {
t.Fatal(err)
}
if string(all) != "bar bar" {
t.Errorf("ReadAll = %q; want bar bar", all)
}
}
func TestWriterReset(t *testing.T) {
var buf1, buf2 bytes.Buffer
w := NewWriter(&buf1)
w.WriteString("foo")
w.Reset(&buf2) // and not flushed
w.WriteString("bar")
w.Flush()
if buf1.String() != "" {
t.Errorf("buf1 = %q; want empty", buf1.String())
}
if buf2.String() != "bar" {
t.Errorf("buf2 = %q; want bar", buf2.String())
}
}
// An onlyReader only implements io.Reader, no matter what other methods the underlying implementation may have.
type onlyReader struct {
r io.Reader
}
func (r onlyReader) Read(b []byte) (int, error) {
return r.r.Read(b)
}
// An onlyWriter only implements io.Writer, no matter what other methods the underlying implementation may have.
type onlyWriter struct {
w io.Writer
}
func (w onlyWriter) Write(b []byte) (int, error) {
return w.w.Write(b)
}
func BenchmarkReaderCopyOptimal(b *testing.B) {
// Optimal case is where the underlying reader implements io.WriterTo
srcBuf := bytes.NewBuffer(make([]byte, 8192))
src := NewReader(srcBuf)
dstBuf := new(bytes.Buffer)
dst := onlyWriter{dstBuf}
for i := 0; i < b.N; i++ {
srcBuf.Reset()
src.Reset(srcBuf)
dstBuf.Reset()
io.Copy(dst, src)
}
}
func BenchmarkReaderCopyUnoptimal(b *testing.B) {
// Unoptimal case is where the underlying reader doesn't implement io.WriterTo
srcBuf := bytes.NewBuffer(make([]byte, 8192))
src := NewReader(onlyReader{srcBuf})
dstBuf := new(bytes.Buffer)
dst := onlyWriter{dstBuf}
for i := 0; i < b.N; i++ {
srcBuf.Reset()
src.Reset(onlyReader{srcBuf})
dstBuf.Reset()
io.Copy(dst, src)
}
}
func BenchmarkReaderCopyNoWriteTo(b *testing.B) {
srcBuf := bytes.NewBuffer(make([]byte, 8192))
srcReader := NewReader(srcBuf)
src := onlyReader{srcReader}
dstBuf := new(bytes.Buffer)
dst := onlyWriter{dstBuf}
for i := 0; i < b.N; i++ {
srcBuf.Reset()
srcReader.Reset(srcBuf)
dstBuf.Reset()
io.Copy(dst, src)
}
}
func BenchmarkWriterCopyOptimal(b *testing.B) {
// Optimal case is where the underlying writer implements io.ReaderFrom
srcBuf := bytes.NewBuffer(make([]byte, 8192))
src := onlyReader{srcBuf}
dstBuf := new(bytes.Buffer)
dst := NewWriter(dstBuf)
for i := 0; i < b.N; i++ {
srcBuf.Reset()
dstBuf.Reset()
dst.Reset(dstBuf)
io.Copy(dst, src)
}
}
func BenchmarkWriterCopyUnoptimal(b *testing.B) {
srcBuf := bytes.NewBuffer(make([]byte, 8192))
src := onlyReader{srcBuf}
dstBuf := new(bytes.Buffer)
dst := NewWriter(onlyWriter{dstBuf})
for i := 0; i < b.N; i++ {
srcBuf.Reset()
dstBuf.Reset()
dst.Reset(onlyWriter{dstBuf})
io.Copy(dst, src)
}
}
func BenchmarkWriterCopyNoReadFrom(b *testing.B) {
srcBuf := bytes.NewBuffer(make([]byte, 8192))
src := onlyReader{srcBuf}
dstBuf := new(bytes.Buffer)
dstWriter := NewWriter(dstBuf)
dst := onlyWriter{dstWriter}
for i := 0; i < b.N; i++ {
srcBuf.Reset()
dstBuf.Reset()
dstWriter.Reset(dstBuf)
io.Copy(dst, src)
}
}
func BenchmarkReaderEmpty(b *testing.B) {
b.ReportAllocs()
str := strings.Repeat("x", 16<<10)
for i := 0; i < b.N; i++ {
br := NewReader(strings.NewReader(str))
n, err := io.Copy(ioutil.Discard, br)
if err != nil {
b.Fatal(err)
}
if n != int64(len(str)) {
b.Fatal("wrong length")
}
}
}
func BenchmarkWriterEmpty(b *testing.B) {
b.ReportAllocs()
str := strings.Repeat("x", 1<<10)
bs := []byte(str)
for i := 0; i < b.N; i++ {
bw := NewWriter(ioutil.Discard)
bw.Flush()
bw.WriteByte('a')
bw.Flush()
bw.WriteRune('B')
bw.Flush()
bw.Write(bs)
bw.Flush()
bw.WriteString(str)
bw.Flush()
}
}
func BenchmarkWriterFlush(b *testing.B) {
b.ReportAllocs()
bw := NewWriter(ioutil.Discard)
str := strings.Repeat("x", 50)
for i := 0; i < b.N; i++ {
bw.WriteString(str)
bw.Flush()
}
}