2008-09-12 16:42:53 -07:00
|
|
|
// 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.
|
|
|
|
|
2011-03-16 10:12:25 -07:00
|
|
|
package bufio_test
|
2008-09-12 16:42:53 -07:00
|
|
|
|
|
|
|
import (
|
2011-03-16 10:12:25 -07:00
|
|
|
. "bufio"
|
2009-12-15 15:33:31 -08:00
|
|
|
"bytes"
|
2013-03-21 19:59:49 -07:00
|
|
|
"errors"
|
2009-12-15 15:33:31 -08:00
|
|
|
"fmt"
|
all: skip and fix various tests with -asan and -msan
First, skip all the allocation count tests.
In some cases this aligns with existing skips for -race, but in others
we've got new issues. These are debug modes, so some performance loss is
expected, and this is clearly no worse than today where the tests fail.
Next, skip internal linking and static linking tests for msan and asan.
With asan we get an explicit failure that neither are supported by the C
and/or Go compilers. With msan, we only get the Go compiler telling us
internal linking is unavailable. With static linking, we segfault
instead. Filed #70080 to track that.
Next, skip some malloc tests with asan that don't quite work because of
the redzone.
This is because of some sizeclass assumptions that get broken with the
redzone and the fact that the tiny allocator is effectively disabled
(again, due to the redzone).
Next, skip some runtime/pprof tests with asan, because of extra
allocations.
Next, skip some malloc tests with asan that also fail because of extra
allocations.
Next, fix up memstats accounting for arenas when asan is enabled. There
is a bug where more is added to the stats than subtracted. This also
simplifies the accounting a little.
Next, skip race tests with msan or asan enabled; they're mutually
incompatible.
Fixes #70054.
Fixes #64256.
Fixes #64257.
For #70079.
For #70080.
Change-Id: I99c02a0b9d621e44f1f918b307aa4a4944c3ec60
Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-asan-clang15,gotip-linux-amd64-msan-clang15
Reviewed-on: https://go-review.googlesource.com/c/go/+/622855
Reviewed-by: Cherry Mui <cherryyz@google.com>
TryBot-Bypass: Michael Knyszek <mknyszek@google.com>
2024-10-28 17:23:40 +00:00
|
|
|
"internal/asan"
|
2009-12-15 15:33:31 -08:00
|
|
|
"io"
|
2021-08-04 01:22:45 -07:00
|
|
|
"math/rand"
|
|
|
|
"strconv"
|
2009-12-15 15:33:31 -08:00
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
"testing/iotest"
|
2014-04-10 21:46:00 -07:00
|
|
|
"time"
|
2011-11-08 15:40:58 -08:00
|
|
|
"unicode/utf8"
|
2008-09-12 16:42:53 -07:00
|
|
|
)
|
|
|
|
|
|
|
|
// Reads from a reader and rot13s the result.
|
2009-01-15 16:22:57 -08:00
|
|
|
type rot13Reader struct {
|
2009-12-15 15:33:31 -08:00
|
|
|
r io.Reader
|
2008-09-12 16:42:53 -07:00
|
|
|
}
|
|
|
|
|
2009-05-08 11:22:57 -07:00
|
|
|
func newRot13Reader(r io.Reader) *rot13Reader {
|
2009-12-15 15:33:31 -08:00
|
|
|
r13 := new(rot13Reader)
|
|
|
|
r13.r = r
|
|
|
|
return r13
|
2008-09-12 16:42:53 -07:00
|
|
|
}
|
|
|
|
|
2011-11-01 22:04:37 -04:00
|
|
|
func (r13 *rot13Reader) Read(p []byte) (int, error) {
|
2013-01-07 11:15:53 +11:00
|
|
|
n, err := r13.r.Read(p)
|
2008-09-12 16:42:53 -07:00
|
|
|
for i := 0; i < n; i++ {
|
2009-12-15 15:33:31 -08:00
|
|
|
c := p[i] | 0x20 // lowercase byte
|
2009-05-18 13:31:56 -07:00
|
|
|
if 'a' <= c && c <= 'm' {
|
2009-11-09 12:07:39 -08:00
|
|
|
p[i] += 13
|
2009-05-18 13:31:56 -07:00
|
|
|
} else if 'n' <= c && c <= 'z' {
|
2009-11-09 12:07:39 -08:00
|
|
|
p[i] -= 13
|
2008-09-12 16:42:53 -07:00
|
|
|
}
|
|
|
|
}
|
2014-07-29 17:56:28 -07:00
|
|
|
return n, err
|
2008-09-12 16:42:53 -07:00
|
|
|
}
|
|
|
|
|
2009-05-18 13:31:56 -07:00
|
|
|
// Call ReadByte to accumulate the text of a file
|
|
|
|
func readBytes(buf *Reader) string {
|
2009-12-15 15:33:31 -08:00
|
|
|
var b [1000]byte
|
|
|
|
nb := 0
|
2009-05-18 13:31:56 -07:00
|
|
|
for {
|
2013-01-07 11:15:53 +11:00
|
|
|
c, err := buf.ReadByte()
|
|
|
|
if err == io.EOF {
|
2009-11-09 12:07:39 -08:00
|
|
|
break
|
2009-05-18 13:31:56 -07:00
|
|
|
}
|
2013-01-07 11:15:53 +11:00
|
|
|
if err == nil {
|
2011-06-27 16:12:04 -04:00
|
|
|
b[nb] = c
|
|
|
|
nb++
|
2013-01-07 11:15:53 +11:00
|
|
|
} else if err != iotest.ErrTimeout {
|
|
|
|
panic("Data: " + err.Error())
|
2009-05-18 13:31:56 -07:00
|
|
|
}
|
|
|
|
}
|
2009-12-15 15:33:31 -08:00
|
|
|
return string(b[0:nb])
|
2009-05-18 13:31:56 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestReaderSimple(t *testing.T) {
|
2009-12-15 15:33:31 -08:00
|
|
|
data := "hello world"
|
2014-01-27 11:05:01 -08:00
|
|
|
b := NewReader(strings.NewReader(data))
|
2009-05-18 13:31:56 -07:00
|
|
|
if s := readBytes(b); s != "hello world" {
|
2009-11-09 12:07:39 -08:00
|
|
|
t.Errorf("simple hello world test failed: got %q", s)
|
2009-05-18 13:31:56 -07:00
|
|
|
}
|
|
|
|
|
2014-01-27 11:05:01 -08:00
|
|
|
b = NewReader(newRot13Reader(strings.NewReader(data)))
|
2009-05-18 13:31:56 -07:00
|
|
|
if s := readBytes(b); s != "uryyb jbeyq" {
|
2010-09-23 13:48:56 +10:00
|
|
|
t.Errorf("rot13 hello world test failed: got %q", s)
|
2009-05-18 13:31:56 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-01-15 16:22:57 -08:00
|
|
|
type readMaker struct {
|
2009-12-15 15:33:31 -08:00
|
|
|
name string
|
|
|
|
fn func(io.Reader) io.Reader
|
2008-09-12 16:42:53 -07:00
|
|
|
}
|
2009-11-04 17:04:21 -08:00
|
|
|
|
|
|
|
var readMakers = []readMaker{
|
2010-10-22 10:06:33 -07:00
|
|
|
{"full", func(r io.Reader) io.Reader { return r }},
|
|
|
|
{"byte", iotest.OneByteReader},
|
|
|
|
{"half", iotest.HalfReader},
|
|
|
|
{"data+err", iotest.DataErrReader},
|
2011-06-27 16:12:04 -04:00
|
|
|
{"timeout", iotest.TimeoutReader},
|
2009-03-03 08:39:12 -08:00
|
|
|
}
|
2008-09-12 16:42:53 -07:00
|
|
|
|
2009-08-27 11:20:15 -07:00
|
|
|
// Call ReadString (which ends up calling everything else)
|
2008-09-12 16:42:53 -07:00
|
|
|
// to accumulate the text of a file.
|
2009-05-08 11:52:39 -07:00
|
|
|
func readLines(b *Reader) string {
|
2009-12-15 15:33:31 -08:00
|
|
|
s := ""
|
2008-09-12 16:42:53 -07:00
|
|
|
for {
|
2013-01-07 11:15:53 +11:00
|
|
|
s1, err := b.ReadString('\n')
|
|
|
|
if err == io.EOF {
|
2009-11-09 12:07:39 -08:00
|
|
|
break
|
2008-09-12 16:42:53 -07:00
|
|
|
}
|
2013-01-07 11:15:53 +11:00
|
|
|
if err != nil && err != iotest.ErrTimeout {
|
|
|
|
panic("GetLines: " + err.Error())
|
2008-09-12 16:42:53 -07:00
|
|
|
}
|
2009-12-15 15:33:31 -08:00
|
|
|
s += s1
|
2008-09-12 16:42:53 -07:00
|
|
|
}
|
2009-12-15 15:33:31 -08:00
|
|
|
return s
|
2008-09-12 16:42:53 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Call Read to accumulate the text of a file
|
2009-05-08 11:52:39 -07:00
|
|
|
func reads(buf *Reader, m int) string {
|
2009-12-15 15:33:31 -08:00
|
|
|
var b [1000]byte
|
|
|
|
nb := 0
|
2008-09-12 16:42:53 -07:00
|
|
|
for {
|
2013-01-07 11:15:53 +11:00
|
|
|
n, err := buf.Read(b[nb : nb+m])
|
2009-12-15 15:33:31 -08:00
|
|
|
nb += n
|
2013-01-07 11:15:53 +11:00
|
|
|
if err == io.EOF {
|
2009-11-09 12:07:39 -08:00
|
|
|
break
|
2008-09-12 16:42:53 -07:00
|
|
|
}
|
|
|
|
}
|
2009-12-15 15:33:31 -08:00
|
|
|
return string(b[0:nb])
|
2008-09-12 16:42:53 -07:00
|
|
|
}
|
|
|
|
|
2009-01-15 16:22:57 -08:00
|
|
|
type bufReader struct {
|
2009-12-15 15:33:31 -08:00
|
|
|
name string
|
|
|
|
fn func(*Reader) string
|
2008-11-24 15:17:47 -08:00
|
|
|
}
|
2009-11-04 17:04:21 -08:00
|
|
|
|
|
|
|
var bufreaders = []bufReader{
|
2010-10-22 10:06:33 -07:00
|
|
|
{"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},
|
2009-03-03 08:39:12 -08:00
|
|
|
}
|
2008-09-12 16:42:53 -07:00
|
|
|
|
2011-12-13 15:07:17 -08:00
|
|
|
const minReadBufferSize = 16
|
|
|
|
|
2009-11-04 17:04:21 -08:00
|
|
|
var bufsizes = []int{
|
2014-01-01 22:26:22 +11:00
|
|
|
0, minReadBufferSize, 23, 32, 46, 64, 93, 128, 1024, 4096,
|
2009-03-03 08:39:12 -08:00
|
|
|
}
|
2008-09-12 16:42:53 -07:00
|
|
|
|
2009-05-08 11:52:39 -07:00
|
|
|
func TestReader(t *testing.T) {
|
2009-12-15 15:33:31 -08:00
|
|
|
var texts [31]string
|
|
|
|
str := ""
|
|
|
|
all := ""
|
2008-09-12 16:42:53 -07:00
|
|
|
for i := 0; i < len(texts)-1; i++ {
|
2009-12-15 15:33:31 -08:00
|
|
|
texts[i] = str + "\n"
|
|
|
|
all += texts[i]
|
2020-11-26 14:16:33 -05:00
|
|
|
str += string(rune(i%26 + 'a'))
|
2008-09-12 16:42:53 -07:00
|
|
|
}
|
2009-12-15 15:33:31 -08:00
|
|
|
texts[len(texts)-1] = all
|
2008-10-07 12:31:31 -07:00
|
|
|
|
2008-09-12 16:42:53 -07:00
|
|
|
for h := 0; h < len(texts); h++ {
|
2009-12-15 15:33:31 -08:00
|
|
|
text := texts[h]
|
2009-01-15 16:22:57 -08:00
|
|
|
for i := 0; i < len(readMakers); i++ {
|
2008-09-12 16:42:53 -07:00
|
|
|
for j := 0; j < len(bufreaders); j++ {
|
|
|
|
for k := 0; k < len(bufsizes); k++ {
|
2009-12-15 15:33:31 -08:00
|
|
|
readmaker := readMakers[i]
|
|
|
|
bufreader := bufreaders[j]
|
|
|
|
bufsize := bufsizes[k]
|
2014-01-27 11:05:01 -08:00
|
|
|
read := readmaker.fn(strings.NewReader(text))
|
2012-02-08 13:07:13 +11:00
|
|
|
buf := NewReaderSize(read, bufsize)
|
2009-12-15 15:33:31 -08:00
|
|
|
s := bufreader.fn(buf)
|
2008-09-12 16:42:53 -07:00
|
|
|
if s != text {
|
2008-11-24 15:17:47 -08:00
|
|
|
t.Errorf("reader=%s fn=%s bufsize=%d want=%q got=%q",
|
2009-11-09 12:07:39 -08:00
|
|
|
readmaker.name, bufreader.name, bufsize, text, s)
|
2008-09-12 16:42:53 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-10 21:46:00 -07:00
|
|
|
type zeroReader struct{}
|
|
|
|
|
|
|
|
func (zeroReader) Read(p []byte) (int, error) {
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestZeroReader(t *testing.T) {
|
|
|
|
var z zeroReader
|
|
|
|
r := NewReader(z)
|
|
|
|
|
|
|
|
c := make(chan error)
|
|
|
|
go func() {
|
|
|
|
_, err := r.ReadByte()
|
|
|
|
c <- err
|
|
|
|
}()
|
|
|
|
|
|
|
|
select {
|
|
|
|
case err := <-c:
|
|
|
|
if err == nil {
|
|
|
|
t.Error("error expected")
|
|
|
|
} else if err != io.ErrNoProgress {
|
|
|
|
t.Error("unexpected error:", err)
|
|
|
|
}
|
|
|
|
case <-time.After(time.Second):
|
|
|
|
t.Error("test timed out (endless loop in ReadByte?)")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-06 12:05:16 -07:00
|
|
|
// A StringReader delivers its data one string segment at a time via Read.
|
|
|
|
type StringReader struct {
|
2009-12-15 15:33:31 -08:00
|
|
|
data []string
|
|
|
|
step int
|
2009-08-06 12:05:16 -07:00
|
|
|
}
|
|
|
|
|
2011-11-01 22:04:37 -04:00
|
|
|
func (r *StringReader) Read(p []byte) (n int, err error) {
|
2009-08-06 12:05:16 -07:00
|
|
|
if r.step < len(r.data) {
|
2009-12-15 15:33:31 -08:00
|
|
|
s := r.data[r.step]
|
2010-10-26 21:52:54 -07:00
|
|
|
n = copy(p, s)
|
2009-12-15 15:33:31 -08:00
|
|
|
r.step++
|
2009-08-06 12:05:16 -07:00
|
|
|
} else {
|
2011-11-01 22:04:37 -04:00
|
|
|
err = io.EOF
|
2009-08-06 12:05:16 -07:00
|
|
|
}
|
2009-12-15 15:33:31 -08:00
|
|
|
return
|
2009-08-06 12:05:16 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func readRuneSegments(t *testing.T, segments []string) {
|
2009-12-15 15:33:31 -08:00
|
|
|
got := ""
|
|
|
|
want := strings.Join(segments, "")
|
|
|
|
r := NewReader(&StringReader{data: segments})
|
2009-08-06 12:05:16 -07:00
|
|
|
for {
|
2011-10-25 22:23:34 -07:00
|
|
|
r, _, err := r.ReadRune()
|
2009-08-06 12:05:16 -07:00
|
|
|
if err != nil {
|
2011-11-01 22:04:37 -04:00
|
|
|
if err != io.EOF {
|
2009-11-09 12:07:39 -08:00
|
|
|
return
|
2009-08-06 12:05:16 -07:00
|
|
|
}
|
2009-12-15 15:33:31 -08:00
|
|
|
break
|
2009-08-06 12:05:16 -07:00
|
|
|
}
|
2011-10-25 22:23:34 -07:00
|
|
|
got += string(r)
|
2009-08-06 12:05:16 -07:00
|
|
|
}
|
|
|
|
if got != want {
|
2009-11-09 12:07:39 -08:00
|
|
|
t.Errorf("segments=%v got=%s want=%s", segments, got, want)
|
2009-08-06 12:05:16 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-11-04 17:04:21 -08:00
|
|
|
var segmentList = [][]string{
|
2010-10-22 10:06:33 -07:00
|
|
|
{},
|
|
|
|
{""},
|
|
|
|
{"日", "本語"},
|
|
|
|
{"\u65e5", "\u672c", "\u8a9e"},
|
|
|
|
{"\U000065e5", "\U0000672c", "\U00008a9e"},
|
|
|
|
{"\xe6", "\x97\xa5\xe6", "\x9c\xac\xe8\xaa\x9e"},
|
|
|
|
{"Hello", ", ", "World", "!"},
|
|
|
|
{"Hello", ", ", "", "World", "!"},
|
2009-08-06 12:05:16 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestReadRune(t *testing.T) {
|
|
|
|
for _, s := range segmentList {
|
2009-11-09 12:07:39 -08:00
|
|
|
readRuneSegments(t, s)
|
2009-08-06 12:05:16 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-12 17:40:27 +10:00
|
|
|
func TestUnreadRune(t *testing.T) {
|
|
|
|
segments := []string{"Hello, world:", "日本語"}
|
|
|
|
r := NewReader(&StringReader{data: segments})
|
2014-04-09 14:19:13 -07:00
|
|
|
got := ""
|
|
|
|
want := strings.Join(segments, "")
|
2010-09-12 17:40:27 +10:00
|
|
|
// Normal execution.
|
|
|
|
for {
|
2011-10-25 22:23:34 -07:00
|
|
|
r1, _, err := r.ReadRune()
|
2010-09-12 17:40:27 +10:00
|
|
|
if err != nil {
|
2011-11-01 22:04:37 -04:00
|
|
|
if err != io.EOF {
|
2014-04-09 14:19:13 -07:00
|
|
|
t.Error("unexpected error on ReadRune:", err)
|
2010-09-12 17:40:27 +10:00
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
2011-10-25 22:23:34 -07:00
|
|
|
got += string(r1)
|
2014-04-09 14:19:13 -07:00
|
|
|
// Put it back and read it again.
|
2010-09-12 17:40:27 +10:00
|
|
|
if err = r.UnreadRune(); err != nil {
|
2014-04-09 14:19:13 -07:00
|
|
|
t.Fatal("unexpected error on UnreadRune:", err)
|
2010-09-12 17:40:27 +10:00
|
|
|
}
|
2011-10-25 22:23:34 -07:00
|
|
|
r2, _, err := r.ReadRune()
|
2010-09-12 17:40:27 +10:00
|
|
|
if err != nil {
|
2014-04-09 14:19:13 -07:00
|
|
|
t.Fatal("unexpected error reading after unreading:", err)
|
2010-09-12 17:40:27 +10:00
|
|
|
}
|
2011-10-25 22:23:34 -07:00
|
|
|
if r1 != r2 {
|
2014-04-09 14:19:13 -07:00
|
|
|
t.Fatalf("incorrect rune after unread: got %c, want %c", r1, r2)
|
2010-09-12 17:40:27 +10:00
|
|
|
}
|
|
|
|
}
|
2014-04-09 14:19:13 -07:00
|
|
|
if got != want {
|
|
|
|
t.Errorf("got %q, want %q", got, want)
|
2010-09-12 17:40:27 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-13 13:34:42 +00:00
|
|
|
func TestNoUnreadRuneAfterPeek(t *testing.T) {
|
|
|
|
br := NewReader(strings.NewReader("example"))
|
|
|
|
br.ReadRune()
|
|
|
|
br.Peek(1)
|
|
|
|
if err := br.UnreadRune(); err == nil {
|
|
|
|
t.Error("UnreadRune didn't fail after Peek")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestNoUnreadByteAfterPeek(t *testing.T) {
|
|
|
|
br := NewReader(strings.NewReader("example"))
|
|
|
|
br.ReadByte()
|
|
|
|
br.Peek(1)
|
|
|
|
if err := br.UnreadByte(); err == nil {
|
|
|
|
t.Error("UnreadByte didn't fail after Peek")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-23 11:56:16 -04:00
|
|
|
func TestNoUnreadRuneAfterDiscard(t *testing.T) {
|
|
|
|
br := NewReader(strings.NewReader("example"))
|
|
|
|
br.ReadRune()
|
|
|
|
br.Discard(1)
|
|
|
|
if err := br.UnreadRune(); err == nil {
|
|
|
|
t.Error("UnreadRune didn't fail after Discard")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestNoUnreadByteAfterDiscard(t *testing.T) {
|
|
|
|
br := NewReader(strings.NewReader("example"))
|
|
|
|
br.ReadByte()
|
|
|
|
br.Discard(1)
|
|
|
|
if err := br.UnreadByte(); err == nil {
|
|
|
|
t.Error("UnreadByte didn't fail after Discard")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestNoUnreadRuneAfterWriteTo(t *testing.T) {
|
|
|
|
br := NewReader(strings.NewReader("example"))
|
|
|
|
br.WriteTo(io.Discard)
|
|
|
|
if err := br.UnreadRune(); err == nil {
|
|
|
|
t.Error("UnreadRune didn't fail after WriteTo")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestNoUnreadByteAfterWriteTo(t *testing.T) {
|
|
|
|
br := NewReader(strings.NewReader("example"))
|
|
|
|
br.WriteTo(io.Discard)
|
|
|
|
if err := br.UnreadByte(); err == nil {
|
|
|
|
t.Error("UnreadByte didn't fail after WriteTo")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-01 22:26:22 +11:00
|
|
|
func TestUnreadByte(t *testing.T) {
|
|
|
|
segments := []string{"Hello, ", "world"}
|
|
|
|
r := NewReader(&StringReader{data: segments})
|
2014-04-09 14:19:13 -07:00
|
|
|
got := ""
|
|
|
|
want := strings.Join(segments, "")
|
2014-01-01 22:26:22 +11:00
|
|
|
// Normal execution.
|
|
|
|
for {
|
|
|
|
b1, err := r.ReadByte()
|
|
|
|
if err != nil {
|
|
|
|
if err != io.EOF {
|
2014-04-09 14:19:13 -07:00
|
|
|
t.Error("unexpected error on ReadByte:", err)
|
2014-01-01 22:26:22 +11:00
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
got += string(b1)
|
2014-04-09 14:19:13 -07:00
|
|
|
// Put it back and read it again.
|
2014-01-01 22:26:22 +11:00
|
|
|
if err = r.UnreadByte(); err != nil {
|
2014-04-09 14:19:13 -07:00
|
|
|
t.Fatal("unexpected error on UnreadByte:", err)
|
2014-01-01 22:26:22 +11:00
|
|
|
}
|
|
|
|
b2, err := r.ReadByte()
|
|
|
|
if err != nil {
|
2014-04-09 14:19:13 -07:00
|
|
|
t.Fatal("unexpected error reading after unreading:", err)
|
2014-01-01 22:26:22 +11:00
|
|
|
}
|
|
|
|
if b1 != b2 {
|
2014-04-09 14:19:13 -07:00
|
|
|
t.Fatalf("incorrect byte after unread: got %q, want %q", b1, b2)
|
2014-01-01 22:26:22 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if got != want {
|
2014-04-09 14:19:13 -07:00
|
|
|
t.Errorf("got %q, want %q", got, want)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestUnreadByteMultiple(t *testing.T) {
|
|
|
|
segments := []string{"Hello, ", "world"}
|
|
|
|
data := strings.Join(segments, "")
|
|
|
|
for n := 0; n <= len(data); n++ {
|
|
|
|
r := NewReader(&StringReader{data: segments})
|
|
|
|
// Read n bytes.
|
|
|
|
for i := 0; i < n; i++ {
|
|
|
|
b, err := r.ReadByte()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("n = %d: unexpected error on ReadByte: %v", n, err)
|
|
|
|
}
|
|
|
|
if b != data[i] {
|
|
|
|
t.Fatalf("n = %d: incorrect byte returned from ReadByte: got %q, want %q", n, b, data[i])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Unread one byte if there is one.
|
|
|
|
if n > 0 {
|
|
|
|
if err := r.UnreadByte(); err != nil {
|
|
|
|
t.Errorf("n = %d: unexpected error on UnreadByte: %v", n, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Test that we cannot unread any further.
|
|
|
|
if err := r.UnreadByte(); err == nil {
|
|
|
|
t.Errorf("n = %d: expected error on UnreadByte", n)
|
|
|
|
}
|
2014-01-01 22:26:22 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-04-25 09:46:07 -06:00
|
|
|
func TestUnreadByteOthers(t *testing.T) {
|
2014-04-29 12:44:40 -04:00
|
|
|
// A list of readers to use in conjunction with UnreadByte.
|
2014-04-25 09:46:07 -06:00
|
|
|
var readers = []func(*Reader, byte) ([]byte, error){
|
|
|
|
(*Reader).ReadBytes,
|
|
|
|
(*Reader).ReadSlice,
|
|
|
|
func(r *Reader, delim byte) ([]byte, error) {
|
|
|
|
data, err := r.ReadString(delim)
|
|
|
|
return []byte(data), err
|
|
|
|
},
|
|
|
|
// ReadLine doesn't fit the data/pattern easily
|
|
|
|
// so we leave it out. It should be covered via
|
|
|
|
// the ReadSlice test since ReadLine simply calls
|
|
|
|
// ReadSlice, and it's that function that handles
|
|
|
|
// the last byte.
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try all readers with UnreadByte.
|
|
|
|
for rno, read := range readers {
|
|
|
|
// Some input data that is longer than the minimum reader buffer size.
|
|
|
|
const n = 10
|
|
|
|
var buf bytes.Buffer
|
|
|
|
for i := 0; i < n; i++ {
|
|
|
|
buf.WriteString("abcdefg")
|
|
|
|
}
|
|
|
|
|
|
|
|
r := NewReaderSize(&buf, minReadBufferSize)
|
|
|
|
readTo := func(delim byte, want string) {
|
|
|
|
data, err := read(r, delim)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("#%d: unexpected error reading to %c: %v", rno, delim, err)
|
|
|
|
}
|
|
|
|
if got := string(data); got != want {
|
|
|
|
t.Fatalf("#%d: got %q, want %q", rno, got, want)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read the data with occasional UnreadByte calls.
|
|
|
|
for i := 0; i < n; i++ {
|
|
|
|
readTo('d', "abcd")
|
|
|
|
for j := 0; j < 3; j++ {
|
|
|
|
if err := r.UnreadByte(); err != nil {
|
|
|
|
t.Fatalf("#%d: unexpected error on UnreadByte: %v", rno, err)
|
|
|
|
}
|
|
|
|
readTo('d', "d")
|
|
|
|
}
|
|
|
|
readTo('g', "efg")
|
|
|
|
}
|
|
|
|
|
|
|
|
// All data should have been read.
|
|
|
|
_, err := r.ReadByte()
|
|
|
|
if err != io.EOF {
|
|
|
|
t.Errorf("#%d: got error %v; want EOF", rno, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-12 17:40:27 +10:00
|
|
|
// 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)
|
|
|
|
}
|
2014-07-16 16:29:51 -07:00
|
|
|
for range buf {
|
2010-09-12 17:40:27 +10:00
|
|
|
_, 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)")
|
|
|
|
}
|
2014-07-18 09:25:59 -07:00
|
|
|
// Test error after ReadSlice.
|
|
|
|
_, _, err = r.ReadRune() // reset state
|
|
|
|
if err != nil {
|
|
|
|
t.Error("unexpected error on ReadRune (4):", err)
|
|
|
|
}
|
|
|
|
_, err = r.ReadSlice(0)
|
|
|
|
if err != io.EOF {
|
|
|
|
t.Error("unexpected error on ReadSlice (4):", err)
|
|
|
|
}
|
|
|
|
if r.UnreadRune() == nil {
|
|
|
|
t.Error("expected error after ReadSlice (4)")
|
|
|
|
}
|
2010-09-12 17:40:27 +10:00
|
|
|
}
|
|
|
|
|
2010-09-24 12:28:14 +10:00
|
|
|
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")
|
2011-11-01 22:04:37 -04:00
|
|
|
} else if err != io.EOF {
|
2010-09-24 12:28:14 +10:00
|
|
|
t.Error("expected EOF; got", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-03-05 14:03:17 -08:00
|
|
|
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)
|
2011-10-25 22:23:34 -07:00
|
|
|
for r := rune(0); r < NRune; r++ {
|
|
|
|
size := utf8.EncodeRune(buf, r)
|
|
|
|
nbytes, err := w.WriteRune(r)
|
2010-03-05 14:03:17 -08:00
|
|
|
if err != nil {
|
2011-10-25 22:23:34 -07:00
|
|
|
t.Fatalf("WriteRune(0x%x) error: %s", r, err)
|
2010-03-05 14:03:17 -08:00
|
|
|
}
|
|
|
|
if nbytes != size {
|
2011-10-25 22:23:34 -07:00
|
|
|
t.Fatalf("WriteRune(0x%x) expected %d, got %d", r, size, nbytes)
|
2010-03-05 14:03:17 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
w.Flush()
|
|
|
|
|
|
|
|
r := NewReader(byteBuf)
|
|
|
|
// Read them back with ReadRune
|
2011-10-25 22:23:34 -07:00
|
|
|
for r1 := rune(0); r1 < NRune; r1++ {
|
|
|
|
size := utf8.EncodeRune(buf, r1)
|
2010-03-05 14:03:17 -08:00
|
|
|
nr, nbytes, err := r.ReadRune()
|
2011-10-25 22:23:34 -07:00
|
|
|
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)
|
2010-03-05 14:03:17 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-25 12:02:04 -05:00
|
|
|
func TestWriteInvalidRune(t *testing.T) {
|
|
|
|
// Invalid runes, including negative ones, should be written as the
|
|
|
|
// replacement character.
|
|
|
|
for _, r := range []rune{-1, utf8.MaxRune + 1} {
|
2022-09-04 17:26:48 +08:00
|
|
|
var buf strings.Builder
|
2020-12-25 12:02:04 -05:00
|
|
|
w := NewWriter(&buf)
|
|
|
|
w.WriteRune(r)
|
|
|
|
w.Flush()
|
|
|
|
if s := buf.String(); s != "\uFFFD" {
|
|
|
|
t.Errorf("WriteRune(%d) wrote %q, not replacement character", r, s)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-27 23:10:23 +00:00
|
|
|
func TestReadStringAllocs(t *testing.T) {
|
all: skip and fix various tests with -asan and -msan
First, skip all the allocation count tests.
In some cases this aligns with existing skips for -race, but in others
we've got new issues. These are debug modes, so some performance loss is
expected, and this is clearly no worse than today where the tests fail.
Next, skip internal linking and static linking tests for msan and asan.
With asan we get an explicit failure that neither are supported by the C
and/or Go compilers. With msan, we only get the Go compiler telling us
internal linking is unavailable. With static linking, we segfault
instead. Filed #70080 to track that.
Next, skip some malloc tests with asan that don't quite work because of
the redzone.
This is because of some sizeclass assumptions that get broken with the
redzone and the fact that the tiny allocator is effectively disabled
(again, due to the redzone).
Next, skip some runtime/pprof tests with asan, because of extra
allocations.
Next, skip some malloc tests with asan that also fail because of extra
allocations.
Next, fix up memstats accounting for arenas when asan is enabled. There
is a bug where more is added to the stats than subtracted. This also
simplifies the accounting a little.
Next, skip race tests with msan or asan enabled; they're mutually
incompatible.
Fixes #70054.
Fixes #64256.
Fixes #64257.
For #70079.
For #70080.
Change-Id: I99c02a0b9d621e44f1f918b307aa4a4944c3ec60
Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-asan-clang15,gotip-linux-amd64-msan-clang15
Reviewed-on: https://go-review.googlesource.com/c/go/+/622855
Reviewed-by: Cherry Mui <cherryyz@google.com>
TryBot-Bypass: Michael Knyszek <mknyszek@google.com>
2024-10-28 17:23:40 +00:00
|
|
|
if asan.Enabled {
|
|
|
|
t.Skip("test allocates more with -asan; see #70079")
|
|
|
|
}
|
2020-04-27 23:10:23 +00:00
|
|
|
r := strings.NewReader(" foo foo 42 42 42 42 42 42 42 42 4.2 4.2 4.2 4.2\n")
|
|
|
|
buf := NewReader(r)
|
|
|
|
allocs := testing.AllocsPerRun(100, func() {
|
|
|
|
r.Seek(0, io.SeekStart)
|
|
|
|
buf.Reset(r)
|
|
|
|
|
|
|
|
_, err := buf.ReadString('\n')
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
if allocs != 1 {
|
|
|
|
t.Errorf("Unexpected number of allocations, got %f, want 1", allocs)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-05-18 13:31:56 -07:00
|
|
|
func TestWriter(t *testing.T) {
|
2009-12-15 15:33:31 -08:00
|
|
|
var data [8192]byte
|
2008-09-12 16:42:53 -07:00
|
|
|
|
2009-05-18 13:31:56 -07:00
|
|
|
for i := 0; i < len(data); i++ {
|
2009-11-09 12:07:39 -08:00
|
|
|
data[i] = byte(' ' + i%('~'-' '))
|
2008-09-12 16:42:53 -07:00
|
|
|
}
|
2009-12-15 15:33:31 -08:00
|
|
|
w := new(bytes.Buffer)
|
2009-05-18 13:31:56 -07:00
|
|
|
for i := 0; i < len(bufsizes); i++ {
|
|
|
|
for j := 0; j < len(bufsizes); j++ {
|
2009-12-15 15:33:31 -08:00
|
|
|
nwrite := bufsizes[i]
|
|
|
|
bs := bufsizes[j]
|
2009-05-18 13:31:56 -07:00
|
|
|
|
|
|
|
// Write nwrite bytes using buffer size bs.
|
|
|
|
// Check that the right amount makes it out
|
|
|
|
// and that the data is correct.
|
|
|
|
|
2009-12-15 15:33:31 -08:00
|
|
|
w.Reset()
|
2012-02-08 13:07:13 +11:00
|
|
|
buf := NewWriterSize(w, bs)
|
2009-12-15 15:33:31 -08:00
|
|
|
context := fmt.Sprintf("nwrite=%d bufsize=%d", nwrite, bs)
|
|
|
|
n, e1 := buf.Write(data[0:nwrite])
|
2009-05-18 13:31:56 -07:00
|
|
|
if e1 != nil || n != nwrite {
|
2009-12-15 15:33:31 -08:00
|
|
|
t.Errorf("%s: buf.Write %d = %d, %v", context, nwrite, n, e1)
|
|
|
|
continue
|
2009-05-18 13:31:56 -07:00
|
|
|
}
|
2012-02-08 13:07:13 +11:00
|
|
|
if e := buf.Flush(); e != nil {
|
2009-11-09 12:07:39 -08:00
|
|
|
t.Errorf("%s: buf.Flush = %v", context, e)
|
2009-05-18 13:31:56 -07:00
|
|
|
}
|
2008-09-12 16:42:53 -07:00
|
|
|
|
2009-12-15 15:33:31 -08:00
|
|
|
written := w.Bytes()
|
2009-05-18 13:31:56 -07:00
|
|
|
if len(written) != nwrite {
|
2009-11-09 12:07:39 -08:00
|
|
|
t.Errorf("%s: %d bytes written", context, len(written))
|
2009-05-18 13:31:56 -07:00
|
|
|
}
|
|
|
|
for l := 0; l < len(written); l++ {
|
2018-11-09 18:13:52 +00:00
|
|
|
if written[l] != data[l] {
|
2010-12-07 16:42:54 -05:00
|
|
|
t.Errorf("wrong bytes written")
|
2024-08-30 19:05:07 +00:00
|
|
|
t.Errorf("want=%q", data[:len(written)])
|
2010-12-07 16:42:54 -05:00
|
|
|
t.Errorf("have=%q", written)
|
2009-05-18 13:31:56 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-09-12 16:42:53 -07:00
|
|
|
}
|
|
|
|
|
2021-08-04 01:22:45 -07:00
|
|
|
func TestWriterAppend(t *testing.T) {
|
|
|
|
got := new(bytes.Buffer)
|
|
|
|
var want []byte
|
|
|
|
rn := rand.New(rand.NewSource(0))
|
|
|
|
w := NewWriterSize(got, 64)
|
|
|
|
for i := 0; i < 100; i++ {
|
|
|
|
// Obtain a buffer to append to.
|
|
|
|
b := w.AvailableBuffer()
|
|
|
|
if w.Available() != cap(b) {
|
|
|
|
t.Fatalf("Available() = %v, want %v", w.Available(), cap(b))
|
|
|
|
}
|
|
|
|
|
|
|
|
// While not recommended, it is valid to append to a shifted buffer.
|
2021-12-15 18:52:21 +00:00
|
|
|
// This forces Write to copy the input.
|
2021-08-04 01:22:45 -07:00
|
|
|
if rn.Intn(8) == 0 && cap(b) > 0 {
|
|
|
|
b = b[1:1:cap(b)]
|
|
|
|
}
|
|
|
|
|
|
|
|
// Append a random integer of varying width.
|
|
|
|
n := int64(rn.Intn(1 << rn.Intn(30)))
|
|
|
|
want = append(strconv.AppendInt(want, n, 10), ' ')
|
|
|
|
b = append(strconv.AppendInt(b, n, 10), ' ')
|
|
|
|
w.Write(b)
|
|
|
|
}
|
|
|
|
w.Flush()
|
|
|
|
|
|
|
|
if !bytes.Equal(got.Bytes(), want) {
|
|
|
|
t.Errorf("output mismatch:\ngot %s\nwant %s", got.Bytes(), want)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-05-18 13:31:56 -07:00
|
|
|
// Check that write errors are returned properly.
|
2008-09-12 16:42:53 -07:00
|
|
|
|
2009-05-18 13:31:56 -07:00
|
|
|
type errorWriterTest struct {
|
2009-12-15 15:33:31 -08:00
|
|
|
n, m int
|
2011-11-01 22:04:37 -04:00
|
|
|
err error
|
|
|
|
expect error
|
2008-09-12 16:42:53 -07:00
|
|
|
}
|
|
|
|
|
2011-11-01 22:04:37 -04:00
|
|
|
func (w errorWriterTest) Write(p []byte) (int, error) {
|
2009-11-09 12:07:39 -08:00
|
|
|
return len(p) * w.n / w.m, w.err
|
2008-09-12 16:42:53 -07:00
|
|
|
}
|
|
|
|
|
2009-11-04 17:04:21 -08:00
|
|
|
var errorWriterTests = []errorWriterTest{
|
2010-10-22 10:06:33 -07:00
|
|
|
{0, 1, nil, io.ErrShortWrite},
|
|
|
|
{1, 2, nil, io.ErrShortWrite},
|
|
|
|
{1, 1, nil, nil},
|
2011-11-13 22:42:42 -05:00
|
|
|
{0, 1, io.ErrClosedPipe, io.ErrClosedPipe},
|
|
|
|
{1, 2, io.ErrClosedPipe, io.ErrClosedPipe},
|
|
|
|
{1, 1, io.ErrClosedPipe, io.ErrClosedPipe},
|
2008-11-24 15:17:47 -08:00
|
|
|
}
|
2008-10-07 12:31:31 -07:00
|
|
|
|
2009-05-18 13:31:56 -07:00
|
|
|
func TestWriteErrors(t *testing.T) {
|
2009-09-15 09:41:59 -07:00
|
|
|
for _, w := range errorWriterTests {
|
2009-12-15 15:33:31 -08:00
|
|
|
buf := NewWriter(w)
|
2010-02-25 16:01:29 -08:00
|
|
|
_, e := buf.Write([]byte("hello world"))
|
2009-05-18 13:31:56 -07:00
|
|
|
if e != nil {
|
2009-12-15 15:33:31 -08:00
|
|
|
t.Errorf("Write hello to %v: %v", w, e)
|
|
|
|
continue
|
2009-05-18 13:31:56 -07:00
|
|
|
}
|
2013-03-21 19:59:49 -07:00
|
|
|
// 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)
|
|
|
|
}
|
2008-09-12 16:42:53 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-05-08 11:52:39 -07:00
|
|
|
func TestNewReaderSizeIdempotent(t *testing.T) {
|
2009-12-15 15:33:31 -08:00
|
|
|
const BufSize = 1000
|
2014-01-27 11:05:01 -08:00
|
|
|
b := NewReaderSize(strings.NewReader("hello world"), BufSize)
|
2009-04-06 21:42:14 -07:00
|
|
|
// Does it recognize itself?
|
2012-02-08 13:07:13 +11:00
|
|
|
b1 := NewReaderSize(b, BufSize)
|
2009-04-06 21:42:14 -07:00
|
|
|
if b1 != b {
|
2009-11-09 12:07:39 -08:00
|
|
|
t.Error("NewReaderSize did not detect underlying Reader")
|
2009-04-06 21:42:14 -07:00
|
|
|
}
|
|
|
|
// Does it wrap if existing buffer is too small?
|
2012-02-08 13:07:13 +11:00
|
|
|
b2 := NewReaderSize(b, 2*BufSize)
|
2009-04-06 21:42:14 -07:00
|
|
|
if b2 == b {
|
2009-11-09 12:07:39 -08:00
|
|
|
t.Error("NewReaderSize did not enlarge buffer")
|
2009-04-06 21:42:14 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-05-08 11:52:39 -07:00
|
|
|
func TestNewWriterSizeIdempotent(t *testing.T) {
|
2009-12-15 15:33:31 -08:00
|
|
|
const BufSize = 1000
|
2012-02-08 13:07:13 +11:00
|
|
|
b := NewWriterSize(new(bytes.Buffer), BufSize)
|
2009-04-06 21:42:14 -07:00
|
|
|
// Does it recognize itself?
|
2012-02-08 13:07:13 +11:00
|
|
|
b1 := NewWriterSize(b, BufSize)
|
2009-04-06 21:42:14 -07:00
|
|
|
if b1 != b {
|
2009-11-09 12:07:39 -08:00
|
|
|
t.Error("NewWriterSize did not detect underlying Writer")
|
2009-04-06 21:42:14 -07:00
|
|
|
}
|
|
|
|
// Does it wrap if existing buffer is too small?
|
2012-02-08 13:07:13 +11:00
|
|
|
b2 := NewWriterSize(b, 2*BufSize)
|
2009-04-06 21:42:14 -07:00
|
|
|
if b2 == b {
|
2009-11-09 12:07:39 -08:00
|
|
|
t.Error("NewWriterSize did not enlarge buffer")
|
2009-04-06 21:42:14 -07:00
|
|
|
}
|
|
|
|
}
|
2009-08-03 18:28:05 -07:00
|
|
|
|
|
|
|
func TestWriteString(t *testing.T) {
|
2009-12-15 15:33:31 -08:00
|
|
|
const BufSize = 8
|
2022-09-28 21:17:34 +08:00
|
|
|
buf := new(strings.Builder)
|
2012-02-08 13:07:13 +11:00
|
|
|
b := NewWriterSize(buf, BufSize)
|
2009-12-15 15:33:31 -08:00
|
|
|
b.WriteString("0") // easy
|
|
|
|
b.WriteString("123456") // still easy
|
|
|
|
b.WriteString("7890") // easy after flush
|
|
|
|
b.WriteString("abcdefghijklmnopqrstuvwxy") // hard
|
|
|
|
b.WriteString("z")
|
2011-03-16 10:12:25 -07:00
|
|
|
if err := b.Flush(); err != nil {
|
|
|
|
t.Error("WriteString", err)
|
2009-08-03 18:28:05 -07:00
|
|
|
}
|
2009-12-15 15:33:31 -08:00
|
|
|
s := "01234567890abcdefghijklmnopqrstuvwxyz"
|
2022-09-28 21:17:34 +08:00
|
|
|
if buf.String() != s {
|
|
|
|
t.Errorf("WriteString wants %q gets %q", s, buf.String())
|
2009-08-03 18:28:05 -07:00
|
|
|
}
|
|
|
|
}
|
2010-06-02 16:17:18 -07:00
|
|
|
|
2022-01-21 14:21:38 +09:00
|
|
|
func TestWriteStringStringWriter(t *testing.T) {
|
|
|
|
const BufSize = 8
|
|
|
|
{
|
|
|
|
tw := &teststringwriter{}
|
|
|
|
b := NewWriterSize(tw, BufSize)
|
|
|
|
b.WriteString("1234")
|
|
|
|
tw.check(t, "", "")
|
|
|
|
b.WriteString("56789012") // longer than BufSize
|
|
|
|
tw.check(t, "12345678", "") // but not enough (after filling the partially-filled buffer)
|
|
|
|
b.Flush()
|
|
|
|
tw.check(t, "123456789012", "")
|
|
|
|
}
|
|
|
|
{
|
|
|
|
tw := &teststringwriter{}
|
|
|
|
b := NewWriterSize(tw, BufSize)
|
|
|
|
b.WriteString("123456789") // long string, empty buffer:
|
|
|
|
tw.check(t, "", "123456789") // use WriteString
|
|
|
|
}
|
|
|
|
{
|
|
|
|
tw := &teststringwriter{}
|
|
|
|
b := NewWriterSize(tw, BufSize)
|
|
|
|
b.WriteString("abc")
|
|
|
|
tw.check(t, "", "")
|
|
|
|
b.WriteString("123456789012345") // long string, non-empty buffer
|
|
|
|
tw.check(t, "abc12345", "6789012345") // use Write and then WriteString since the remaining part is still longer than BufSize
|
|
|
|
}
|
|
|
|
{
|
|
|
|
tw := &teststringwriter{}
|
|
|
|
b := NewWriterSize(tw, BufSize)
|
|
|
|
b.Write([]byte("abc")) // same as above, but use Write instead of WriteString
|
|
|
|
tw.check(t, "", "")
|
|
|
|
b.WriteString("123456789012345")
|
|
|
|
tw.check(t, "abc12345", "6789012345") // same as above
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type teststringwriter struct {
|
|
|
|
write string
|
|
|
|
writeString string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *teststringwriter) Write(b []byte) (int, error) {
|
|
|
|
w.write += string(b)
|
|
|
|
return len(b), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *teststringwriter) WriteString(s string) (int, error) {
|
|
|
|
w.writeString += s
|
|
|
|
return len(s), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *teststringwriter) check(t *testing.T, write, writeString string) {
|
|
|
|
t.Helper()
|
|
|
|
if w.write != write {
|
|
|
|
t.Errorf("write: expected %q, got %q", write, w.write)
|
|
|
|
}
|
|
|
|
if w.writeString != writeString {
|
|
|
|
t.Errorf("writeString: expected %q, got %q", writeString, w.writeString)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-06-02 16:17:18 -07:00
|
|
|
func TestBufferFull(t *testing.T) {
|
2011-12-13 15:07:17 -08:00
|
|
|
const longString = "And now, hello, world! It is the time for all good men to come to the aid of their party"
|
2012-02-08 13:07:13 +11:00
|
|
|
buf := NewReaderSize(strings.NewReader(longString), minReadBufferSize)
|
2011-12-13 15:07:17 -08:00
|
|
|
line, err := buf.ReadSlice('!')
|
|
|
|
if string(line) != "And now, hello, " || err != ErrBufferFull {
|
2010-06-02 16:17:18 -07:00
|
|
|
t.Errorf("first ReadSlice(,) = %q, %v", line, err)
|
|
|
|
}
|
2011-12-13 15:07:17 -08:00
|
|
|
line, err = buf.ReadSlice('!')
|
|
|
|
if string(line) != "world!" || err != nil {
|
2010-06-02 16:17:18 -07:00
|
|
|
t.Errorf("second ReadSlice(,) = %q, %v", line, err)
|
|
|
|
}
|
|
|
|
}
|
2010-08-04 09:44:02 +10:00
|
|
|
|
|
|
|
func TestPeek(t *testing.T) {
|
|
|
|
p := make([]byte, 10)
|
2011-12-13 15:07:17 -08:00
|
|
|
// string is 16 (minReadBufferSize) long.
|
2012-02-08 13:07:13 +11:00
|
|
|
buf := NewReaderSize(strings.NewReader("abcdefghijklmnop"), minReadBufferSize)
|
2010-08-04 09:44:02 +10:00
|
|
|
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)
|
|
|
|
}
|
2014-01-01 22:26:22 +11:00
|
|
|
if _, err := buf.Peek(-1); err != ErrNegativeCount {
|
|
|
|
t.Fatalf("want ErrNegativeCount got %v", err)
|
|
|
|
}
|
2016-01-29 00:32:00 -08:00
|
|
|
if s, err := buf.Peek(32); string(s) != "abcdefghijklmnop" || err != ErrBufferFull {
|
|
|
|
t.Fatalf("want %q, ErrBufFull got %q, err=%v", "abcdefghijklmnop", string(s), err)
|
2010-08-04 09:44:02 +10:00
|
|
|
}
|
|
|
|
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)
|
|
|
|
}
|
2011-12-13 15:07:17 -08:00
|
|
|
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)
|
2010-08-04 09:44:02 +10:00
|
|
|
}
|
|
|
|
if s, err := buf.Peek(0); string(s) != "" || err != nil {
|
|
|
|
t.Fatalf("want %q got %q, err=%v", "", string(s), err)
|
|
|
|
}
|
2011-11-01 22:04:37 -04:00
|
|
|
if _, err := buf.Peek(1); err != io.EOF {
|
2010-08-04 09:44:02 +10:00
|
|
|
t.Fatalf("want EOF got %v", err)
|
|
|
|
}
|
2012-02-16 10:15:36 +11:00
|
|
|
|
|
|
|
// 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
|
2010-08-04 09:44:02 +10:00
|
|
|
}
|
2010-10-13 17:12:43 -07:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|
2011-04-13 15:12:28 -04:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2011-11-01 22:04:37 -04:00
|
|
|
func (t *testReader) Read(buf []byte) (n int, err error) {
|
2011-04-13 15:12:28 -04:00
|
|
|
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 {
|
2011-11-01 22:04:37 -04:00
|
|
|
err = io.EOF
|
2011-04-13 15:12:28 -04:00
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func testReadLine(t *testing.T, input []byte) {
|
|
|
|
for stride := 1; stride < 2; stride++ {
|
|
|
|
done := 0
|
|
|
|
reader := testReader{input, stride}
|
2012-02-08 13:07:13 +11:00
|
|
|
l := NewReaderSize(&reader, len(input)+1)
|
2011-04-13 15:12:28 -04:00
|
|
|
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 {
|
2011-11-01 22:04:37 -04:00
|
|
|
if err != io.EOF {
|
2011-04-13 15:12:28 -04:00
|
|
|
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) {
|
2011-12-13 15:07:17 -08:00
|
|
|
data := make([]byte, 0)
|
|
|
|
for i := 0; i < minReadBufferSize*5/2; i++ {
|
|
|
|
data = append(data, '0'+byte(i%10))
|
|
|
|
}
|
2014-01-27 11:05:01 -08:00
|
|
|
buf := bytes.NewReader(data)
|
2012-02-08 13:07:13 +11:00
|
|
|
l := NewReaderSize(buf, minReadBufferSize)
|
2011-04-13 15:12:28 -04:00
|
|
|
line, isPrefix, err := l.ReadLine()
|
2011-12-13 15:07:17 -08:00
|
|
|
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)
|
2011-04-13 15:12:28 -04:00
|
|
|
}
|
2011-12-13 15:07:17 -08:00
|
|
|
data = data[len(line):]
|
2011-04-13 15:12:28 -04:00
|
|
|
line, isPrefix, err = l.ReadLine()
|
2011-12-13 15:07:17 -08:00
|
|
|
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)
|
2011-04-13 15:12:28 -04:00
|
|
|
}
|
2011-12-13 15:07:17 -08:00
|
|
|
data = data[len(line):]
|
2011-04-13 15:12:28 -04:00
|
|
|
line, isPrefix, err = l.ReadLine()
|
2011-12-13 15:07:17 -08:00
|
|
|
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)
|
2011-04-13 15:12:28 -04:00
|
|
|
}
|
|
|
|
line, isPrefix, err = l.ReadLine()
|
|
|
|
if isPrefix || err == nil {
|
|
|
|
t.Errorf("expected no more lines: %x %s", line, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestReadAfterLines(t *testing.T) {
|
2011-12-13 15:07:17 -08:00
|
|
|
line1 := "this is line1"
|
|
|
|
restData := "this is line2\nthis is line 3\n"
|
2014-01-27 11:05:01 -08:00
|
|
|
inbuf := bytes.NewReader([]byte(line1 + "\n" + restData))
|
2022-09-04 17:26:48 +08:00
|
|
|
outbuf := new(strings.Builder)
|
2011-04-13 15:12:28 -04:00
|
|
|
maxLineLength := len(line1) + len(restData)/2
|
2012-02-08 13:07:13 +11:00
|
|
|
l := NewReaderSize(inbuf, maxLineLength)
|
2011-04-13 15:12:28 -04:00
|
|
|
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) {
|
2012-02-08 13:07:13 +11:00
|
|
|
l := NewReaderSize(new(bytes.Buffer), minReadBufferSize)
|
2011-04-13 15:12:28 -04:00
|
|
|
line, isPrefix, err := l.ReadLine()
|
2011-11-01 22:04:37 -04:00
|
|
|
if err != io.EOF {
|
2011-04-13 15:12:28 -04:00
|
|
|
t.Errorf("expected EOF from ReadLine, got '%s' %t %s", line, isPrefix, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestLinesAfterRead(t *testing.T) {
|
2014-01-27 11:05:01 -08:00
|
|
|
l := NewReaderSize(bytes.NewReader([]byte("foo")), minReadBufferSize)
|
2020-10-16 00:49:02 -04:00
|
|
|
_, err := io.ReadAll(l)
|
2011-04-13 15:12:28 -04:00
|
|
|
if err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
line, isPrefix, err := l.ReadLine()
|
2011-11-01 22:04:37 -04:00
|
|
|
if err != io.EOF {
|
2011-04-13 15:12:28 -04:00
|
|
|
t.Errorf("expected EOF from ReadLine, got '%s' %t %s", line, isPrefix, err)
|
|
|
|
}
|
|
|
|
}
|
2011-08-25 08:44:12 +10:00
|
|
|
|
2011-11-02 08:30:50 -07:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-25 08:44:12 +10:00
|
|
|
type readLineResult struct {
|
|
|
|
line []byte
|
|
|
|
isPrefix bool
|
2011-11-01 22:04:37 -04:00
|
|
|
err error
|
2011-08-25 08:44:12 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
var readLineNewlinesTests = []struct {
|
2011-12-13 15:07:17 -08:00
|
|
|
input string
|
|
|
|
expect []readLineResult
|
2011-08-25 08:44:12 +10:00
|
|
|
}{
|
2011-12-13 15:07:17 -08:00
|
|
|
{"012345678901234\r\n012345678901234\r\n", []readLineResult{
|
|
|
|
{[]byte("012345678901234"), true, nil},
|
2011-08-25 08:44:12 +10:00
|
|
|
{nil, false, nil},
|
2011-12-13 15:07:17 -08:00
|
|
|
{[]byte("012345678901234"), true, nil},
|
2011-08-25 08:44:12 +10:00
|
|
|
{nil, false, nil},
|
2011-11-01 22:04:37 -04:00
|
|
|
{nil, false, io.EOF},
|
2011-08-25 08:44:12 +10:00
|
|
|
}},
|
2011-12-13 15:07:17 -08:00
|
|
|
{"0123456789012345\r012345678901234\r", []readLineResult{
|
|
|
|
{[]byte("0123456789012345"), true, nil},
|
|
|
|
{[]byte("\r012345678901234"), true, nil},
|
2011-08-25 08:44:12 +10:00
|
|
|
{[]byte("\r"), false, nil},
|
2011-11-01 22:04:37 -04:00
|
|
|
{nil, false, io.EOF},
|
2011-08-25 08:44:12 +10:00
|
|
|
}},
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestReadLineNewlines(t *testing.T) {
|
|
|
|
for _, e := range readLineNewlinesTests {
|
2011-12-13 15:07:17 -08:00
|
|
|
testReadLineNewlines(t, e.input, e.expect)
|
2011-08-25 08:44:12 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-13 15:07:17 -08:00
|
|
|
func testReadLineNewlines(t *testing.T, input string, expect []readLineResult) {
|
2012-02-08 13:07:13 +11:00
|
|
|
b := NewReaderSize(strings.NewReader(input), minReadBufferSize)
|
2011-08-25 08:44:12 +10:00
|
|
|
for i, e := range expect {
|
|
|
|
line, isPrefix, err := b.ReadLine()
|
2013-01-07 10:03:49 +11:00
|
|
|
if !bytes.Equal(line, e.line) {
|
2011-08-25 08:44:12 +10:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-09-27 16:31:03 +10:00
|
|
|
|
2012-10-19 11:22:51 +11:00
|
|
|
func createTestInput(n int) []byte {
|
|
|
|
input := make([]byte, n)
|
2012-09-27 16:31:03 +10:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
2012-10-19 11:22:51 +11:00
|
|
|
return input
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestReaderWriteTo(t *testing.T) {
|
|
|
|
input := createTestInput(8192)
|
2014-01-27 11:05:01 -08:00
|
|
|
r := NewReader(onlyReader{bytes.NewReader(input)})
|
2012-09-27 16:31:03 +10:00
|
|
|
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},
|
2025-01-26 21:52:58 -08:00
|
|
|
{0, 0, io.ErrUnexpectedEOF, io.ErrClosedPipe, io.ErrUnexpectedEOF},
|
2012-09-27 16:31:03 +10:00
|
|
|
{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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-19 11:22:51 +11:00
|
|
|
func TestWriterReadFrom(t *testing.T) {
|
|
|
|
ws := []func(io.Writer) io.Writer{
|
2012-10-20 13:02:29 +11:00
|
|
|
func(w io.Writer) io.Writer { return onlyWriter{w} },
|
2012-10-19 11:22:51 +11:00
|
|
|
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)
|
2022-09-04 17:26:48 +08:00
|
|
|
b := new(strings.Builder)
|
2012-10-19 11:22:51 +11:00
|
|
|
w := NewWriter(wfunc(b))
|
2014-01-27 11:05:01 -08:00
|
|
|
r := rfunc(bytes.NewReader(input))
|
2012-10-19 11:22:51 +11:00
|
|
|
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
|
|
|
|
}
|
2013-07-25 11:29:13 +10:00
|
|
|
if err := w.Flush(); err != nil {
|
|
|
|
t.Errorf("Flush returned %v", err)
|
|
|
|
continue
|
|
|
|
}
|
2012-10-19 11:22:51 +11:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-19 16:32:00 +11:00
|
|
|
// 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)
|
|
|
|
}
|
2012-10-20 13:02:29 +11:00
|
|
|
io.Copy(b0, onlyReader{strings.NewReader(strings.Repeat("x", 30))})
|
2012-10-19 16:32:00 +11:00
|
|
|
if w0 != 0 {
|
|
|
|
t.Fatalf("write 1230 'x's: got %d writes, want 0", w0)
|
|
|
|
}
|
2012-10-20 13:02:29 +11:00
|
|
|
io.Copy(b0, onlyReader{strings.NewReader(strings.Repeat("x", 9))})
|
2012-10-19 16:32:00 +11:00
|
|
|
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)
|
|
|
|
}
|
2012-10-20 13:02:29 +11:00
|
|
|
io.Copy(b1, onlyReader{strings.NewReader(strings.Repeat("x", 700))})
|
2012-10-19 16:32:00 +11:00
|
|
|
if w1 != 1 {
|
|
|
|
t.Fatalf("write 1200 + 789 'x's: got %d writes, want 1", w1)
|
|
|
|
}
|
2012-10-20 13:02:29 +11:00
|
|
|
io.Copy(b1, onlyReader{strings.NewReader(strings.Repeat("x", 600))})
|
2012-10-19 16:32:00 +11:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-16 00:49:02 -04:00
|
|
|
// A writeCountingDiscard is like io.Discard and counts the number of times
|
2012-10-19 16:32:00 +11:00
|
|
|
// Write is called on it.
|
|
|
|
type writeCountingDiscard int
|
|
|
|
|
|
|
|
func (w *writeCountingDiscard) Write(p []byte) (int, error) {
|
|
|
|
*w++
|
|
|
|
return len(p), nil
|
|
|
|
}
|
|
|
|
|
2012-12-10 17:25:31 -05:00
|
|
|
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") {
|
bufio: new Scanner interface
Add a new, simple interface for scanning (probably textual) data,
based on a new type called Scanner. It does its own internal buffering,
so should be plausibly efficient even without injecting a bufio.Reader.
The format of the input is defined by a "split function", by default
splitting into lines. Other implemented split functions include single
bytes, single runes, and space-separated words.
Here's the loop to scan stdin as a file of lines:
s := bufio.NewScanner(os.Stdin)
for s.Scan() {
fmt.Printf("%s\n", s.Bytes())
}
if s.Err() != nil {
log.Fatal(s.Err())
}
While we're dealing with spaces, define what space means to strings.Fields.
Fixes #4802.
R=adg, rogpeppe, bradfitz, rsc
CC=golang-dev
https://golang.org/cl/7322088
2013-02-20 12:14:31 -08:00
|
|
|
t.Fatalf("wrong panic: %v", err)
|
2012-12-10 17:25:31 -05:00
|
|
|
}
|
|
|
|
default:
|
|
|
|
t.Fatalf("unexpected panic value: %T(%v)", err, err)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
b.Read(make([]byte, 100))
|
|
|
|
}
|
|
|
|
|
2013-03-21 19:59:49 -07:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-25 11:29:13 +10:00
|
|
|
// 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 {
|
2014-03-22 17:40:17 -07:00
|
|
|
t.Fatalf("ReadFrom returned (%v, %v), want (6, nil)", n2, err)
|
2013-07-25 11:29:13 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-24 11:48:34 -07:00
|
|
|
type emptyThenNonEmptyReader struct {
|
|
|
|
r io.Reader
|
|
|
|
n int
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *emptyThenNonEmptyReader) Read(p []byte) (int, error) {
|
|
|
|
if r.n <= 0 {
|
|
|
|
return r.r.Read(p)
|
|
|
|
}
|
|
|
|
r.n--
|
|
|
|
return 0, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test for golang.org/issue/7611
|
|
|
|
func TestWriterReadFromUntilEOF(t *testing.T) {
|
|
|
|
buf := new(bytes.Buffer)
|
|
|
|
w := NewWriterSize(buf, 5)
|
|
|
|
|
|
|
|
// Partially fill buffer
|
|
|
|
n, err := w.Write([]byte("0123"))
|
|
|
|
if n != 4 || err != nil {
|
|
|
|
t.Fatalf("Write returned (%v, %v), want (4, nil)", n, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Use ReadFrom to read in some data.
|
|
|
|
r := &emptyThenNonEmptyReader{r: strings.NewReader("abcd"), n: 3}
|
|
|
|
n2, err := w.ReadFrom(r)
|
|
|
|
if n2 != 4 || err != nil {
|
|
|
|
t.Fatalf("ReadFrom returned (%v, %v), want (4, nil)", n2, err)
|
|
|
|
}
|
|
|
|
w.Flush()
|
2022-09-28 21:17:34 +08:00
|
|
|
if got, want := buf.String(), "0123abcd"; got != want {
|
2014-03-24 11:48:34 -07:00
|
|
|
t.Fatalf("buf.Bytes() returned %q, want %q", got, want)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestWriterReadFromErrNoProgress(t *testing.T) {
|
|
|
|
buf := new(bytes.Buffer)
|
|
|
|
w := NewWriterSize(buf, 5)
|
|
|
|
|
|
|
|
// Partially fill buffer
|
|
|
|
n, err := w.Write([]byte("0123"))
|
|
|
|
if n != 4 || err != nil {
|
|
|
|
t.Fatalf("Write returned (%v, %v), want (4, nil)", n, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Use ReadFrom to read in some data.
|
|
|
|
r := &emptyThenNonEmptyReader{r: strings.NewReader("abcd"), n: 100}
|
|
|
|
n2, err := w.ReadFrom(r)
|
|
|
|
if n2 != 0 || err != io.ErrNoProgress {
|
|
|
|
t.Fatalf("buf.Bytes() returned (%v, %v), want (0, io.ErrNoProgress)", n2, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-06 13:23:13 -07:00
|
|
|
type readFromWriter struct {
|
|
|
|
buf []byte
|
|
|
|
writeBytes int
|
|
|
|
readFromBytes int
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *readFromWriter) Write(p []byte) (int, error) {
|
|
|
|
w.buf = append(w.buf, p...)
|
|
|
|
w.writeBytes += len(p)
|
|
|
|
return len(p), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *readFromWriter) ReadFrom(r io.Reader) (int64, error) {
|
|
|
|
b, err := io.ReadAll(r)
|
|
|
|
w.buf = append(w.buf, b...)
|
|
|
|
w.readFromBytes += len(b)
|
|
|
|
return int64(len(b)), err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test that calling (*Writer).ReadFrom with a partially-filled buffer
|
|
|
|
// fills the buffer before switching over to ReadFrom.
|
|
|
|
func TestWriterReadFromWithBufferedData(t *testing.T) {
|
|
|
|
const bufsize = 16
|
|
|
|
|
|
|
|
input := createTestInput(64)
|
|
|
|
rfw := &readFromWriter{}
|
|
|
|
w := NewWriterSize(rfw, bufsize)
|
|
|
|
|
|
|
|
const writeSize = 8
|
|
|
|
if n, err := w.Write(input[:writeSize]); n != writeSize || err != nil {
|
|
|
|
t.Errorf("w.Write(%v bytes) = %v, %v; want %v, nil", writeSize, n, err, writeSize)
|
|
|
|
}
|
|
|
|
n, err := w.ReadFrom(bytes.NewReader(input[writeSize:]))
|
|
|
|
if wantn := len(input[writeSize:]); int(n) != wantn || err != nil {
|
|
|
|
t.Errorf("io.Copy(w, %v bytes) = %v, %v; want %v, nil", wantn, n, err, wantn)
|
|
|
|
}
|
|
|
|
if err := w.Flush(); err != nil {
|
|
|
|
t.Errorf("w.Flush() = %v, want nil", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if got, want := rfw.writeBytes, bufsize; got != want {
|
|
|
|
t.Errorf("wrote %v bytes with Write, want %v", got, want)
|
|
|
|
}
|
|
|
|
if got, want := rfw.readFromBytes, len(input)-bufsize; got != want {
|
|
|
|
t.Errorf("wrote %v bytes with ReadFrom, want %v", got, want)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-17 21:08:48 -04:00
|
|
|
func TestReadZero(t *testing.T) {
|
|
|
|
for _, size := range []int{100, 2} {
|
|
|
|
t.Run(fmt.Sprintf("bufsize=%d", size), func(t *testing.T) {
|
|
|
|
r := io.MultiReader(strings.NewReader("abc"), &emptyThenNonEmptyReader{r: strings.NewReader("def"), n: 1})
|
|
|
|
br := NewReaderSize(r, size)
|
|
|
|
want := func(s string, wantErr error) {
|
|
|
|
p := make([]byte, 50)
|
|
|
|
n, err := br.Read(p)
|
|
|
|
if err != wantErr || n != len(s) || string(p[:n]) != s {
|
|
|
|
t.Fatalf("read(%d) = %q, %v, want %q, %v", len(p), string(p[:n]), err, s, wantErr)
|
|
|
|
}
|
|
|
|
t.Logf("read(%d) = %q, %v", len(p), string(p[:n]), err)
|
|
|
|
}
|
|
|
|
want("abc", nil)
|
|
|
|
want("", nil)
|
|
|
|
want("def", nil)
|
|
|
|
want("", io.EOF)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-10 19:22:19 -07:00
|
|
|
func TestReaderReset(t *testing.T) {
|
2023-02-08 19:50:06 -08:00
|
|
|
checkAll := func(r *Reader, want string) {
|
|
|
|
t.Helper()
|
|
|
|
all, err := io.ReadAll(r)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if string(all) != want {
|
|
|
|
t.Errorf("ReadAll returned %q, want %q", all, want)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-10 19:22:19 -07:00
|
|
|
r := NewReader(strings.NewReader("foo foo"))
|
|
|
|
buf := make([]byte, 3)
|
|
|
|
r.Read(buf)
|
|
|
|
if string(buf) != "foo" {
|
|
|
|
t.Errorf("buf = %q; want foo", buf)
|
|
|
|
}
|
2021-08-04 00:57:07 -07:00
|
|
|
|
2013-08-10 19:22:19 -07:00
|
|
|
r.Reset(strings.NewReader("bar bar"))
|
2023-02-08 19:50:06 -08:00
|
|
|
checkAll(r, "bar bar")
|
2021-08-04 00:57:07 -07:00
|
|
|
|
|
|
|
*r = Reader{} // zero out the Reader
|
|
|
|
r.Reset(strings.NewReader("bar bar"))
|
2023-02-08 19:50:06 -08:00
|
|
|
checkAll(r, "bar bar")
|
|
|
|
|
|
|
|
// Wrap a reader and then Reset to that reader.
|
|
|
|
r.Reset(strings.NewReader("recur"))
|
|
|
|
r2 := NewReader(r)
|
|
|
|
checkAll(r2, "recur")
|
|
|
|
r.Reset(strings.NewReader("recur2"))
|
|
|
|
r2.Reset(r)
|
|
|
|
checkAll(r2, "recur2")
|
2013-08-10 19:22:19 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestWriterReset(t *testing.T) {
|
2023-02-08 19:50:06 -08:00
|
|
|
var buf1, buf2, buf3, buf4, buf5 strings.Builder
|
2013-08-10 19:22:19 -07:00
|
|
|
w := NewWriter(&buf1)
|
|
|
|
w.WriteString("foo")
|
2021-08-04 00:57:07 -07:00
|
|
|
|
2013-08-10 19:22:19 -07:00
|
|
|
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())
|
|
|
|
}
|
2021-08-04 00:57:07 -07:00
|
|
|
|
|
|
|
*w = Writer{} // zero out the Writer
|
|
|
|
w.Reset(&buf3) // and not flushed
|
|
|
|
w.WriteString("bar")
|
|
|
|
w.Flush()
|
|
|
|
if buf1.String() != "" {
|
|
|
|
t.Errorf("buf1 = %q; want empty", buf1.String())
|
|
|
|
}
|
|
|
|
if buf3.String() != "bar" {
|
|
|
|
t.Errorf("buf3 = %q; want bar", buf3.String())
|
|
|
|
}
|
2023-02-08 19:50:06 -08:00
|
|
|
|
|
|
|
// Wrap a writer and then Reset to that writer.
|
|
|
|
w.Reset(&buf4)
|
|
|
|
w2 := NewWriter(w)
|
|
|
|
w2.WriteString("recur")
|
|
|
|
w2.Flush()
|
|
|
|
if buf4.String() != "recur" {
|
|
|
|
t.Errorf("buf4 = %q, want %q", buf4.String(), "recur")
|
|
|
|
}
|
|
|
|
w.Reset(&buf5)
|
|
|
|
w2.Reset(w)
|
|
|
|
w2.WriteString("recur2")
|
|
|
|
w2.Flush()
|
|
|
|
if buf5.String() != "recur2" {
|
|
|
|
t.Errorf("buf5 = %q, want %q", buf5.String(), "recur2")
|
|
|
|
}
|
2013-08-10 19:22:19 -07:00
|
|
|
}
|
|
|
|
|
2015-01-02 10:02:15 -08:00
|
|
|
func TestReaderDiscard(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
r io.Reader
|
|
|
|
bufSize int // 0 means 16
|
|
|
|
peekSize int
|
|
|
|
|
|
|
|
n int // input to Discard
|
|
|
|
|
|
|
|
want int // from Discard
|
|
|
|
wantErr error // from Discard
|
|
|
|
|
|
|
|
wantBuffered int
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "normal case",
|
|
|
|
r: strings.NewReader("abcdefghijklmnopqrstuvwxyz"),
|
|
|
|
peekSize: 16,
|
|
|
|
n: 6,
|
|
|
|
want: 6,
|
|
|
|
wantBuffered: 10,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "discard causing read",
|
|
|
|
r: strings.NewReader("abcdefghijklmnopqrstuvwxyz"),
|
|
|
|
n: 6,
|
|
|
|
want: 6,
|
|
|
|
wantBuffered: 10,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "discard all without peek",
|
|
|
|
r: strings.NewReader("abcdefghijklmnopqrstuvwxyz"),
|
|
|
|
n: 26,
|
|
|
|
want: 26,
|
|
|
|
wantBuffered: 0,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "discard more than end",
|
|
|
|
r: strings.NewReader("abcdefghijklmnopqrstuvwxyz"),
|
|
|
|
n: 27,
|
|
|
|
want: 26,
|
|
|
|
wantErr: io.EOF,
|
|
|
|
wantBuffered: 0,
|
|
|
|
},
|
|
|
|
// Any error from filling shouldn't show up until we
|
2021-11-05 04:57:59 +00:00
|
|
|
// get past the valid bytes. Here we return 5 valid bytes at the same time
|
2015-01-02 10:02:15 -08:00
|
|
|
// as an error, but test that we don't see the error from Discard.
|
|
|
|
{
|
|
|
|
name: "fill error, discard less",
|
|
|
|
r: newScriptedReader(func(p []byte) (n int, err error) {
|
|
|
|
if len(p) < 5 {
|
|
|
|
panic("unexpected small read")
|
|
|
|
}
|
|
|
|
return 5, errors.New("5-then-error")
|
|
|
|
}),
|
|
|
|
n: 4,
|
|
|
|
want: 4,
|
|
|
|
wantErr: nil,
|
|
|
|
wantBuffered: 1,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "fill error, discard equal",
|
|
|
|
r: newScriptedReader(func(p []byte) (n int, err error) {
|
|
|
|
if len(p) < 5 {
|
|
|
|
panic("unexpected small read")
|
|
|
|
}
|
|
|
|
return 5, errors.New("5-then-error")
|
|
|
|
}),
|
|
|
|
n: 5,
|
|
|
|
want: 5,
|
|
|
|
wantErr: nil,
|
|
|
|
wantBuffered: 0,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "fill error, discard more",
|
|
|
|
r: newScriptedReader(func(p []byte) (n int, err error) {
|
|
|
|
if len(p) < 5 {
|
|
|
|
panic("unexpected small read")
|
|
|
|
}
|
|
|
|
return 5, errors.New("5-then-error")
|
|
|
|
}),
|
|
|
|
n: 6,
|
|
|
|
want: 5,
|
|
|
|
wantErr: errors.New("5-then-error"),
|
|
|
|
wantBuffered: 0,
|
|
|
|
},
|
|
|
|
// Discard of 0 shouldn't cause a read:
|
|
|
|
{
|
|
|
|
name: "discard zero",
|
|
|
|
r: newScriptedReader(), // will panic on Read
|
|
|
|
n: 0,
|
|
|
|
want: 0,
|
|
|
|
wantErr: nil,
|
|
|
|
wantBuffered: 0,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "discard negative",
|
|
|
|
r: newScriptedReader(), // will panic on Read
|
|
|
|
n: -1,
|
|
|
|
want: 0,
|
|
|
|
wantErr: ErrNegativeCount,
|
|
|
|
wantBuffered: 0,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
|
|
br := NewReaderSize(tt.r, tt.bufSize)
|
|
|
|
if tt.peekSize > 0 {
|
|
|
|
peekBuf, err := br.Peek(tt.peekSize)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("%s: Peek(%d): %v", tt.name, tt.peekSize, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if len(peekBuf) != tt.peekSize {
|
|
|
|
t.Errorf("%s: len(Peek(%d)) = %v; want %v", tt.name, tt.peekSize, len(peekBuf), tt.peekSize)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
discarded, err := br.Discard(tt.n)
|
|
|
|
if ge, we := fmt.Sprint(err), fmt.Sprint(tt.wantErr); discarded != tt.want || ge != we {
|
|
|
|
t.Errorf("%s: Discard(%d) = (%v, %v); want (%v, %v)", tt.name, tt.n, discarded, ge, tt.want, we)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if bn := br.Buffered(); bn != tt.wantBuffered {
|
|
|
|
t.Errorf("%s: after Discard, Buffered = %d; want %d", tt.name, bn, tt.wantBuffered)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2017-11-01 19:00:05 +00:00
|
|
|
func TestReaderSize(t *testing.T) {
|
|
|
|
if got, want := NewReader(nil).Size(), DefaultBufSize; got != want {
|
|
|
|
t.Errorf("NewReader's Reader.Size = %d; want %d", got, want)
|
|
|
|
}
|
|
|
|
if got, want := NewReaderSize(nil, 1234).Size(), 1234; got != want {
|
|
|
|
t.Errorf("NewReaderSize's Reader.Size = %d; want %d", got, want)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestWriterSize(t *testing.T) {
|
|
|
|
if got, want := NewWriter(nil).Size(), DefaultBufSize; got != want {
|
|
|
|
t.Errorf("NewWriter's Writer.Size = %d; want %d", got, want)
|
|
|
|
}
|
|
|
|
if got, want := NewWriterSize(nil, 1234).Size(), 1234; got != want {
|
|
|
|
t.Errorf("NewWriterSize's Writer.Size = %d; want %d", got, want)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-27 16:31:03 +10:00
|
|
|
// An onlyReader only implements io.Reader, no matter what other methods the underlying implementation may have.
|
|
|
|
type onlyReader struct {
|
2014-02-27 10:48:36 -08:00
|
|
|
io.Reader
|
2012-09-27 16:31:03 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
// An onlyWriter only implements io.Writer, no matter what other methods the underlying implementation may have.
|
|
|
|
type onlyWriter struct {
|
2014-02-27 10:48:36 -08:00
|
|
|
io.Writer
|
2012-09-27 16:31:03 +10:00
|
|
|
}
|
|
|
|
|
2015-01-02 10:02:15 -08:00
|
|
|
// A scriptedReader is an io.Reader that executes its steps sequentially.
|
|
|
|
type scriptedReader []func(p []byte) (n int, err error)
|
|
|
|
|
|
|
|
func (sr *scriptedReader) Read(p []byte) (n int, err error) {
|
|
|
|
if len(*sr) == 0 {
|
|
|
|
panic("too many Read calls on scripted Reader. No steps remain.")
|
|
|
|
}
|
|
|
|
step := (*sr)[0]
|
|
|
|
*sr = (*sr)[1:]
|
|
|
|
return step(p)
|
|
|
|
}
|
|
|
|
|
|
|
|
func newScriptedReader(steps ...func(p []byte) (n int, err error)) io.Reader {
|
|
|
|
sr := scriptedReader(steps)
|
|
|
|
return &sr
|
|
|
|
}
|
|
|
|
|
2019-06-19 12:32:30 -04:00
|
|
|
// eofReader returns the number of bytes read and io.EOF for the read that consumes the last of the content.
|
|
|
|
type eofReader struct {
|
|
|
|
buf []byte
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *eofReader) Read(p []byte) (int, error) {
|
|
|
|
read := copy(p, r.buf)
|
|
|
|
r.buf = r.buf[read:]
|
|
|
|
|
|
|
|
switch read {
|
|
|
|
case 0, len(r.buf):
|
|
|
|
// As allowed in the documentation, this will return io.EOF
|
|
|
|
// in the same call that consumes the last of the data.
|
|
|
|
// https://godoc.org/io#Reader
|
|
|
|
return read, io.EOF
|
|
|
|
}
|
|
|
|
|
|
|
|
return read, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestPartialReadEOF(t *testing.T) {
|
|
|
|
src := make([]byte, 10)
|
|
|
|
eofR := &eofReader{buf: src}
|
|
|
|
r := NewReader(eofR)
|
|
|
|
|
|
|
|
// Start by reading 5 of the 10 available bytes.
|
|
|
|
dest := make([]byte, 5)
|
|
|
|
read, err := r.Read(dest)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
}
|
|
|
|
if n := len(dest); read != n {
|
|
|
|
t.Fatalf("read %d bytes; wanted %d bytes", read, n)
|
|
|
|
}
|
|
|
|
|
|
|
|
// The Reader should have buffered all the content from the io.Reader.
|
|
|
|
if n := len(eofR.buf); n != 0 {
|
|
|
|
t.Fatalf("got %d bytes left in bufio.Reader source; want 0 bytes", n)
|
|
|
|
}
|
|
|
|
// To prove the point, check that there are still 5 bytes available to read.
|
|
|
|
if n := r.Buffered(); n != 5 {
|
|
|
|
t.Fatalf("got %d bytes buffered in bufio.Reader; want 5 bytes", n)
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is the second read of 0 bytes.
|
|
|
|
read, err = r.Read([]byte{})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
}
|
|
|
|
if read != 0 {
|
|
|
|
t.Fatalf("read %d bytes; want 0 bytes", read)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-29 13:45:40 +07:00
|
|
|
type writerWithReadFromError struct{}
|
|
|
|
|
|
|
|
func (w writerWithReadFromError) ReadFrom(r io.Reader) (int64, error) {
|
|
|
|
return 0, errors.New("writerWithReadFromError error")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w writerWithReadFromError) Write(b []byte) (n int, err error) {
|
|
|
|
return 10, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestWriterReadFromMustSetUnderlyingError(t *testing.T) {
|
|
|
|
var wr = NewWriter(writerWithReadFromError{})
|
|
|
|
if _, err := wr.ReadFrom(strings.NewReader("test2")); err == nil {
|
|
|
|
t.Fatal("expected ReadFrom returns error, got nil")
|
|
|
|
}
|
|
|
|
if _, err := wr.Write([]byte("123")); err == nil {
|
|
|
|
t.Fatal("expected Write returns error, got nil")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-29 13:27:00 +07:00
|
|
|
type writeErrorOnlyWriter struct{}
|
|
|
|
|
|
|
|
func (w writeErrorOnlyWriter) Write(p []byte) (n int, err error) {
|
|
|
|
return 0, errors.New("writeErrorOnlyWriter error")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure that previous Write errors are immediately returned
|
|
|
|
// on any ReadFrom. See golang.org/issue/35194.
|
|
|
|
func TestWriterReadFromMustReturnUnderlyingError(t *testing.T) {
|
|
|
|
var wr = NewWriter(writeErrorOnlyWriter{})
|
|
|
|
s := "test1"
|
|
|
|
wantBuffered := len(s)
|
|
|
|
if _, err := wr.WriteString(s); err != nil {
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
}
|
|
|
|
if err := wr.Flush(); err == nil {
|
|
|
|
t.Error("expected flush error, got nil")
|
|
|
|
}
|
|
|
|
if _, err := wr.ReadFrom(strings.NewReader("test2")); err == nil {
|
|
|
|
t.Fatal("expected error, got nil")
|
|
|
|
}
|
|
|
|
if buffered := wr.Buffered(); buffered != wantBuffered {
|
|
|
|
t.Fatalf("Buffered = %v; want %v", buffered, wantBuffered)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-27 16:31:03 +10:00
|
|
|
func BenchmarkReaderCopyOptimal(b *testing.B) {
|
|
|
|
// Optimal case is where the underlying reader implements io.WriterTo
|
2014-01-23 15:13:21 -05:00
|
|
|
srcBuf := bytes.NewBuffer(make([]byte, 8192))
|
|
|
|
src := NewReader(srcBuf)
|
|
|
|
dstBuf := new(bytes.Buffer)
|
|
|
|
dst := onlyWriter{dstBuf}
|
2012-09-27 16:31:03 +10:00
|
|
|
for i := 0; i < b.N; i++ {
|
2014-01-23 15:13:21 -05:00
|
|
|
srcBuf.Reset()
|
|
|
|
src.Reset(srcBuf)
|
|
|
|
dstBuf.Reset()
|
2012-09-27 16:31:03 +10:00
|
|
|
io.Copy(dst, src)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func BenchmarkReaderCopyUnoptimal(b *testing.B) {
|
|
|
|
// Unoptimal case is where the underlying reader doesn't implement io.WriterTo
|
2014-01-23 15:13:21 -05:00
|
|
|
srcBuf := bytes.NewBuffer(make([]byte, 8192))
|
|
|
|
src := NewReader(onlyReader{srcBuf})
|
|
|
|
dstBuf := new(bytes.Buffer)
|
|
|
|
dst := onlyWriter{dstBuf}
|
2012-09-27 16:31:03 +10:00
|
|
|
for i := 0; i < b.N; i++ {
|
2014-01-23 15:13:21 -05:00
|
|
|
srcBuf.Reset()
|
|
|
|
src.Reset(onlyReader{srcBuf})
|
|
|
|
dstBuf.Reset()
|
2012-09-27 16:31:03 +10:00
|
|
|
io.Copy(dst, src)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func BenchmarkReaderCopyNoWriteTo(b *testing.B) {
|
2014-01-23 15:13:21 -05:00
|
|
|
srcBuf := bytes.NewBuffer(make([]byte, 8192))
|
|
|
|
srcReader := NewReader(srcBuf)
|
|
|
|
src := onlyReader{srcReader}
|
|
|
|
dstBuf := new(bytes.Buffer)
|
|
|
|
dst := onlyWriter{dstBuf}
|
2012-09-27 16:31:03 +10:00
|
|
|
for i := 0; i < b.N; i++ {
|
2014-01-23 15:13:21 -05:00
|
|
|
srcBuf.Reset()
|
|
|
|
srcReader.Reset(srcBuf)
|
|
|
|
dstBuf.Reset()
|
2012-09-27 16:31:03 +10:00
|
|
|
io.Copy(dst, src)
|
|
|
|
}
|
|
|
|
}
|
2012-10-19 11:22:51 +11:00
|
|
|
|
2014-02-27 10:48:36 -08:00
|
|
|
func BenchmarkReaderWriteToOptimal(b *testing.B) {
|
|
|
|
const bufSize = 16 << 10
|
|
|
|
buf := make([]byte, bufSize)
|
|
|
|
r := bytes.NewReader(buf)
|
|
|
|
srcReader := NewReaderSize(onlyReader{r}, 1<<10)
|
2020-10-16 00:49:02 -04:00
|
|
|
if _, ok := io.Discard.(io.ReaderFrom); !ok {
|
|
|
|
b.Fatal("io.Discard doesn't support ReaderFrom")
|
2014-02-27 10:48:36 -08:00
|
|
|
}
|
|
|
|
for i := 0; i < b.N; i++ {
|
2016-04-05 11:22:53 -07:00
|
|
|
r.Seek(0, io.SeekStart)
|
2014-02-27 10:48:36 -08:00
|
|
|
srcReader.Reset(onlyReader{r})
|
2020-10-16 00:49:02 -04:00
|
|
|
n, err := srcReader.WriteTo(io.Discard)
|
2014-02-27 10:48:36 -08:00
|
|
|
if err != nil {
|
|
|
|
b.Fatal(err)
|
|
|
|
}
|
|
|
|
if n != bufSize {
|
|
|
|
b.Fatalf("n = %d; want %d", n, bufSize)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-27 23:10:23 +00:00
|
|
|
func BenchmarkReaderReadString(b *testing.B) {
|
|
|
|
r := strings.NewReader(" foo foo 42 42 42 42 42 42 42 42 4.2 4.2 4.2 4.2\n")
|
|
|
|
buf := NewReader(r)
|
|
|
|
b.ReportAllocs()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
r.Seek(0, io.SeekStart)
|
|
|
|
buf.Reset(r)
|
|
|
|
|
|
|
|
_, err := buf.ReadString('\n')
|
|
|
|
if err != nil {
|
|
|
|
b.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-19 11:22:51 +11:00
|
|
|
func BenchmarkWriterCopyOptimal(b *testing.B) {
|
|
|
|
// Optimal case is where the underlying writer implements io.ReaderFrom
|
2014-01-23 15:13:21 -05:00
|
|
|
srcBuf := bytes.NewBuffer(make([]byte, 8192))
|
|
|
|
src := onlyReader{srcBuf}
|
|
|
|
dstBuf := new(bytes.Buffer)
|
|
|
|
dst := NewWriter(dstBuf)
|
2012-10-19 11:22:51 +11:00
|
|
|
for i := 0; i < b.N; i++ {
|
2014-01-23 15:13:21 -05:00
|
|
|
srcBuf.Reset()
|
|
|
|
dstBuf.Reset()
|
|
|
|
dst.Reset(dstBuf)
|
2012-10-19 11:22:51 +11:00
|
|
|
io.Copy(dst, src)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func BenchmarkWriterCopyUnoptimal(b *testing.B) {
|
2014-01-23 15:13:21 -05:00
|
|
|
srcBuf := bytes.NewBuffer(make([]byte, 8192))
|
|
|
|
src := onlyReader{srcBuf}
|
|
|
|
dstBuf := new(bytes.Buffer)
|
|
|
|
dst := NewWriter(onlyWriter{dstBuf})
|
2012-10-19 11:22:51 +11:00
|
|
|
for i := 0; i < b.N; i++ {
|
2014-01-23 15:13:21 -05:00
|
|
|
srcBuf.Reset()
|
|
|
|
dstBuf.Reset()
|
|
|
|
dst.Reset(onlyWriter{dstBuf})
|
2012-10-19 11:22:51 +11:00
|
|
|
io.Copy(dst, src)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func BenchmarkWriterCopyNoReadFrom(b *testing.B) {
|
2014-01-23 15:13:21 -05:00
|
|
|
srcBuf := bytes.NewBuffer(make([]byte, 8192))
|
|
|
|
src := onlyReader{srcBuf}
|
|
|
|
dstBuf := new(bytes.Buffer)
|
|
|
|
dstWriter := NewWriter(dstBuf)
|
|
|
|
dst := onlyWriter{dstWriter}
|
2012-10-19 11:22:51 +11:00
|
|
|
for i := 0; i < b.N; i++ {
|
2014-01-23 15:13:21 -05:00
|
|
|
srcBuf.Reset()
|
|
|
|
dstBuf.Reset()
|
|
|
|
dstWriter.Reset(dstBuf)
|
2012-10-19 11:22:51 +11:00
|
|
|
io.Copy(dst, src)
|
|
|
|
}
|
|
|
|
}
|
2013-05-17 15:16:06 -07:00
|
|
|
|
|
|
|
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))
|
2020-10-16 00:49:02 -04:00
|
|
|
n, err := io.Copy(io.Discard, br)
|
2013-05-17 15:16:06 -07:00
|
|
|
if err != nil {
|
|
|
|
b.Fatal(err)
|
|
|
|
}
|
|
|
|
if n != int64(len(str)) {
|
|
|
|
b.Fatal("wrong length")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-05-21 15:51:49 -07:00
|
|
|
|
|
|
|
func BenchmarkWriterEmpty(b *testing.B) {
|
|
|
|
b.ReportAllocs()
|
|
|
|
str := strings.Repeat("x", 1<<10)
|
|
|
|
bs := []byte(str)
|
|
|
|
for i := 0; i < b.N; i++ {
|
2020-10-16 00:49:02 -04:00
|
|
|
bw := NewWriter(io.Discard)
|
2013-05-21 15:51:49 -07:00
|
|
|
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()
|
2020-10-16 00:49:02 -04:00
|
|
|
bw := NewWriter(io.Discard)
|
2013-05-21 15:51:49 -07:00
|
|
|
str := strings.Repeat("x", 50)
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
|
|
bw.WriteString(str)
|
|
|
|
bw.Flush()
|
|
|
|
}
|
|
|
|
}
|