Chromium Code Reviews| Index: common/logdog/frame/reader_test.go |
| diff --git a/common/logdog/frame/reader_test.go b/common/logdog/frame/reader_test.go |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..16d763febdae2c367d80f112c8e94aedfa615350 |
| --- /dev/null |
| +++ b/common/logdog/frame/reader_test.go |
| @@ -0,0 +1,208 @@ |
| +// Copyright 2015 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +package frame |
| + |
| +import ( |
| + "bytes" |
| + "encoding/binary" |
| + "errors" |
| + "io" |
| + "io/ioutil" |
| + "testing" |
| + |
| + . "github.com/smartystreets/goconvey/convey" |
| +) |
| + |
| +// plainReader implements the io.Reader interface on top of a bytes.Buffer; |
| +// however, it does not also implement io.ByteReader. |
| +type plainReader struct { |
| + buf bytes.Buffer |
| + err error |
| +} |
| + |
| +func (r *plainReader) loadFrames(data ...[]byte) { |
| + for _, d := range data { |
|
iannucci
2015/08/04 17:13:50
s/d/chunk
maybe?
dnj
2015/08/04 17:51:42
Done.
|
| + _, err := WriteFrame(&r.buf, d) |
| + if err != nil { |
| + panic(err) |
| + } |
| + } |
| +} |
| + |
| +func (r *plainReader) Read(data []byte) (int, error) { |
| + if r.err != nil { |
| + return 0, r.err |
| + } |
| + return r.buf.Read(data) |
| +} |
| + |
| +type testByteReader struct { |
| + plainReader |
| + readBytes int |
| + nativeReadByte bool |
|
iannucci
2015/08/04 17:13:51
why not just have 2 struct types (e.g. testByteRea
dnj
2015/08/04 17:51:41
Opted to just have a separate error field for Read
|
| + byteBuf [1]byte |
| +} |
| + |
| +func (r *testByteReader) ReadByte() (b byte, err error) { |
| + if r.nativeReadByte { |
| + b, err = r.buf.ReadByte() |
| + } else { |
| + _, err = r.Read(r.byteBuf[:]) |
| + b = r.byteBuf[0] |
| + } |
| + |
| + if err == nil { |
| + r.readBytes++ |
| + } |
| + return |
| +} |
| + |
| +// TestReader tests the default Reader implementation, "reader". |
| +func TestReader(t *testing.T) { |
| + t.Parallel() |
| + |
| + Convey(`A frame reader with max size 1MB using a plain io.Reader`, t, func() { |
| + maxSize := int64(1024 * 1024) |
| + tr := plainReader{} |
| + r := NewReader(&tr, maxSize) |
| + |
| + Convey(`Will return io.EOF with an empty reader.`, func() { |
| + _, err := r.ReadFrameAll() |
| + So(err, ShouldEqual, io.EOF) |
| + }) |
| + |
| + Convey(`Can successfully read a frame.`, func() { |
| + data := []byte{0x13, 0x37, 0xd0, 0x65} |
| + tr.loadFrames(data) |
| + |
| + f, err := r.ReadFrameAll() |
| + So(err, ShouldBeNil) |
| + So(f, ShouldResemble, data) |
| + }) |
| + |
| + Convey(`Can successfully read two frames.`, func() { |
| + data := [][]byte{ |
| + {0x13, 0x37, 0xd0, 0x65}, |
| + {0xd0, 0x06, 0xea, 0x15, 0xf0, 0x0d}, |
| + } |
| + tr.loadFrames(data...) |
| + |
| + c, fr, err := r.ReadFrame() |
| + So(err, ShouldBeNil) |
| + So(c, ShouldEqual, 4) |
| + |
| + d, err := ioutil.ReadAll(fr) |
| + So(err, ShouldBeNil) |
| + So(d, ShouldResemble, data[0]) |
| + |
| + c, fr, err = r.ReadFrame() |
| + So(err, ShouldBeNil) |
| + So(c, ShouldEqual, 6) |
| + |
| + d, err = ioutil.ReadAll(fr) |
| + So(err, ShouldBeNil) |
| + So(d, ShouldResemble, data[1]) |
| + }) |
| + |
| + Convey(`When reading a frame, will return EOF if the frame is exceeded.`, func() { |
| + data := []byte{0x13, 0x37, 0xd0, 0x65} |
| + tr.loadFrames(data) |
| + |
| + count, fr, err := r.ReadFrame() |
| + So(err, ShouldBeNil) |
| + So(count, ShouldEqual, 4) |
| + |
| + buf := make([]byte, 5) |
| + c, err := fr.Read(make([]byte, 5)) |
| + So(c, ShouldEqual, 4) |
| + So(err, ShouldBeNil) |
| + |
| + buf = buf[count:] |
| + _, err = fr.Read(buf) |
| + So(err, ShouldEqual, io.EOF) |
| + }) |
| + |
| + Convey(`Will fail if the underlying frame exceeds the maximum size.`, func() { |
| + var sizeBuf [binary.MaxVarintLen64]byte |
| + tr.buf.Write(sizeBuf[:binary.PutUvarint(sizeBuf[:], uint64(maxSize+1))]) |
| + |
| + _, err := r.ReadFrameAll() |
| + So(err, ShouldEqual, ErrFrameTooLarge) |
| + }) |
| + |
| + Convey(`Will fail if the frame contains an invalid size header.`, func() { |
| + tr.buf.Write([]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}) |
| + _, err := r.ReadFrameAll() |
| + So(err, ShouldNotBeNil) |
| + }) |
| + |
| + Convey(`Can read conscutive frames, then io.EOF.`, func() { |
| + data := [][]byte{} |
| + for _, size := range []int{ |
| + 0, |
| + 14, |
| + 1024 * 1024, |
| + 0, |
| + 511, |
| + } { |
| + data = append(data, bytes.Repeat([]byte{0x5A}, size)) |
| + tr.loadFrames(data[len(data)-1]) |
| + } |
| + |
| + for _, expected := range data { |
| + f, err := r.ReadFrameAll() |
| + So(err, ShouldBeNil) |
| + |
| + if len(expected) == 0 { |
| + expected = nil |
| + } |
| + So(f, ShouldResemble, expected) |
| + } |
| + |
| + _, err := r.ReadFrameAll() |
| + So(err, ShouldEqual, io.EOF) |
| + }) |
| + }) |
| + |
| + Convey(`A frame reader with max size 1MB using an io.Reader+io.ByteReader`, t, func() { |
| + tr := testByteReader{} |
| + r := NewReader(&tr, 1024*1024) |
| + |
| + Convey(`Will return io.EOF with an empty reader.`, func() { |
| + _, err := r.ReadFrameAll() |
| + So(err, ShouldEqual, io.EOF) |
| + }) |
| + |
| + Convey(`Will use io.ByteReader to read the frame header.`, func() { |
| + data := []byte{0x13, 0x37, 0xd0, 0x65} |
| + tr.loadFrames(data) |
| + |
| + f, err := r.ReadFrameAll() |
| + So(err, ShouldBeNil) |
| + So(f, ShouldResemble, data) |
| + So(tr.readBytes, ShouldEqual, 1) |
| + }) |
| + |
| + Convey(`Will fail if the underlying io.Reader returns an error.`, func() { |
| + tr.loadFrames([]byte{}) |
| + tr.err = errors.New("test: test-induced error") |
| + _, err := r.ReadFrameAll() |
| + So(err, ShouldEqual, tr.err) |
| + }) |
| + |
| + Convey(`Will fail if an error is returned while reading frame's data.`, func() { |
| + data := []byte{0x13, 0x37, 0xd0, 0x65} |
| + tr.loadFrames(data) |
| + |
| + // Have "ReadByte()" calls ignore the configured error. This will cause |
| + // the frame size to be read without incident, but the frame data to still |
| + // return an error. |
| + tr.err = errors.New("test: test-induced error") |
| + tr.nativeReadByte = true |
| + data, err := r.ReadFrameAll() |
| + So(err, ShouldEqual, tr.err) |
| + }) |
| + }) |
| +} |