diff options
author | Michael Stapelberg <stapelberg@debian.org> | 2013-03-04 21:27:36 +0100 |
---|---|---|
committer | Michael Stapelberg <michael@stapelberg.de> | 2013-03-04 21:27:36 +0100 |
commit | 04b08da9af0c450d645ab7389d1467308cfc2db8 (patch) | |
tree | db247935fa4f2f94408edc3acd5d0d4f997aa0d8 /src/pkg/bufio/bufio_test.go | |
parent | 917c5fb8ec48e22459d77e3849e6d388f93d3260 (diff) | |
download | golang-upstream/1.1_hg20130304.tar.gz |
Imported Upstream version 1.1~hg20130304upstream/1.1_hg20130304
Diffstat (limited to 'src/pkg/bufio/bufio_test.go')
-rw-r--r-- | src/pkg/bufio/bufio_test.go | 310 |
1 files changed, 295 insertions, 15 deletions
diff --git a/src/pkg/bufio/bufio_test.go b/src/pkg/bufio/bufio_test.go index a43cbd23a..b0e811443 100644 --- a/src/pkg/bufio/bufio_test.go +++ b/src/pkg/bufio/bufio_test.go @@ -28,9 +28,9 @@ func newRot13Reader(r io.Reader) *rot13Reader { } func (r13 *rot13Reader) Read(p []byte) (int, error) { - n, e := r13.r.Read(p) - if e != nil { - return n, e + n, err := r13.r.Read(p) + if err != nil { + return n, err } for i := 0; i < n; i++ { c := p[i] | 0x20 // lowercase byte @@ -48,15 +48,15 @@ func readBytes(buf *Reader) string { var b [1000]byte nb := 0 for { - c, e := buf.ReadByte() - if e == io.EOF { + c, err := buf.ReadByte() + if err == io.EOF { break } - if e == nil { + if err == nil { b[nb] = c nb++ - } else if e != iotest.ErrTimeout { - panic("Data: " + e.Error()) + } else if err != iotest.ErrTimeout { + panic("Data: " + err.Error()) } } return string(b[0:nb]) @@ -93,12 +93,12 @@ var readMakers = []readMaker{ func readLines(b *Reader) string { s := "" for { - s1, e := b.ReadString('\n') - if e == io.EOF { + s1, err := b.ReadString('\n') + if err == io.EOF { break } - if e != nil && e != iotest.ErrTimeout { - panic("GetLines: " + e.Error()) + if err != nil && err != iotest.ErrTimeout { + panic("GetLines: " + err.Error()) } s += s1 } @@ -110,9 +110,9 @@ func reads(buf *Reader, m int) string { var b [1000]byte nb := 0 for { - n, e := buf.Read(b[nb : nb+m]) + n, err := buf.Read(b[nb : nb+m]) nb += n - if e == io.EOF { + if err == io.EOF { break } } @@ -748,7 +748,7 @@ 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.Compare(line, e.line) != 0 { + if !bytes.Equal(line, e.line) { t.Errorf("%q call %d, line == %q, want %q", input, i, line, e.line) return } @@ -762,3 +762,283 @@ func testReadLineNewlines(t *testing.T, input string, expect []readLineResult) { } } } + +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 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)) +} + +// 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 + for i := 0; i < b.N; i++ { + b.StopTimer() + src := NewReader(bytes.NewBuffer(make([]byte, 8192))) + dst := onlyWriter{new(bytes.Buffer)} + b.StartTimer() + io.Copy(dst, src) + } +} + +func BenchmarkReaderCopyUnoptimal(b *testing.B) { + // Unoptimal case is where the underlying reader doesn't implement io.WriterTo + for i := 0; i < b.N; i++ { + b.StopTimer() + src := NewReader(onlyReader{bytes.NewBuffer(make([]byte, 8192))}) + dst := onlyWriter{new(bytes.Buffer)} + b.StartTimer() + io.Copy(dst, src) + } +} + +func BenchmarkReaderCopyNoWriteTo(b *testing.B) { + for i := 0; i < b.N; i++ { + b.StopTimer() + src := onlyReader{NewReader(bytes.NewBuffer(make([]byte, 8192)))} + dst := onlyWriter{new(bytes.Buffer)} + b.StartTimer() + io.Copy(dst, src) + } +} + +func BenchmarkWriterCopyOptimal(b *testing.B) { + // Optimal case is where the underlying writer implements io.ReaderFrom + for i := 0; i < b.N; i++ { + b.StopTimer() + src := onlyReader{bytes.NewBuffer(make([]byte, 8192))} + dst := NewWriter(new(bytes.Buffer)) + b.StartTimer() + io.Copy(dst, src) + } +} + +func BenchmarkWriterCopyUnoptimal(b *testing.B) { + for i := 0; i < b.N; i++ { + b.StopTimer() + src := onlyReader{bytes.NewBuffer(make([]byte, 8192))} + dst := NewWriter(onlyWriter{new(bytes.Buffer)}) + b.StartTimer() + io.Copy(dst, src) + } +} + +func BenchmarkWriterCopyNoReadFrom(b *testing.B) { + for i := 0; i < b.N; i++ { + b.StopTimer() + src := onlyReader{bytes.NewBuffer(make([]byte, 8192))} + dst := onlyWriter{NewWriter(new(bytes.Buffer))} + b.StartTimer() + io.Copy(dst, src) + } +} |