ipld-eth-server/vendor/github.com/philhofer/fwd/reader_test.go
2018-09-11 16:30:29 -05:00

399 lines
7.8 KiB
Go

package fwd
import (
"bytes"
"io"
"io/ioutil"
"math/rand"
"testing"
"unsafe"
)
// partialReader reads into only
// part of the supplied byte slice
// to the underlying reader
type partialReader struct {
r io.Reader
}
func (p partialReader) Read(b []byte) (int, error) {
n := max(1, rand.Intn(len(b)))
return p.r.Read(b[:n])
}
func randomBts(sz int) []byte {
o := make([]byte, sz)
for i := 0; i < len(o); i += 8 {
j := (*int64)(unsafe.Pointer(&o[i]))
*j = rand.Int63()
}
return o
}
func TestRead(t *testing.T) {
bts := randomBts(512)
// make the buffer much
// smaller than the underlying
// bytes to incur multiple fills
rd := NewReaderSize(partialReader{bytes.NewReader(bts)}, 128)
if rd.BufferSize() != cap(rd.data) {
t.Errorf("BufferSize() returned %d; should return %d", rd.BufferSize(), cap(rd.data))
}
// starting Buffered() should be 0
if rd.Buffered() != 0 {
t.Errorf("Buffered() should return 0 at initialization; got %d", rd.Buffered())
}
some := make([]byte, 32)
n, err := rd.Read(some)
if err != nil {
t.Fatal(err)
}
if n == 0 {
t.Fatal("read 0 bytes w/ a non-nil error!")
}
some = some[:n]
more := make([]byte, 64)
j, err := rd.Read(more)
if err != nil {
t.Fatal(err)
}
if j == 0 {
t.Fatal("read 0 bytes w/ a non-nil error")
}
more = more[:j]
out, err := ioutil.ReadAll(rd)
if err != nil {
t.Fatal(err)
}
all := append(some, more...)
all = append(all, out...)
if !bytes.Equal(bts, all) {
t.Errorf("bytes not equal; %d bytes in and %d bytes out", len(bts), len(out))
}
// test filling out of the underlying reader
big := randomBts(1 << 21)
rd = NewReaderSize(partialReader{bytes.NewReader(big)}, 2048)
buf := make([]byte, 3100)
n, err = rd.ReadFull(buf)
if err != nil {
t.Fatal(err)
}
if n != 3100 {
t.Errorf("expected 3100 bytes read by ReadFull; got %d", n)
}
if !bytes.Equal(buf[:n], big[:n]) {
t.Error("data parity")
}
rest := make([]byte, (1<<21)-3100)
n, err = io.ReadFull(rd, rest)
if err != nil {
t.Fatal(err)
}
if n != len(rest) {
t.Errorf("expected %d bytes read by io.ReadFull; got %d", len(rest), n)
}
if !bytes.Equal(append(buf, rest...), big) {
t.Fatal("data parity")
}
}
func TestReadByte(t *testing.T) {
bts := randomBts(512)
rd := NewReaderSize(partialReader{bytes.NewReader(bts)}, 98)
var (
err error
i int
b byte
)
// scan through the whole
// array byte-by-byte
for err != io.EOF {
b, err = rd.ReadByte()
if err == nil {
if b != bts[i] {
t.Fatalf("offset %d: %d in; %d out", i, b, bts[i])
}
}
i++
}
if err != io.EOF {
t.Fatal(err)
}
}
func TestSkipNoSeek(t *testing.T) {
bts := randomBts(1024)
rd := NewReaderSize(partialReader{bytes.NewReader(bts)}, 200)
n, err := rd.Skip(512)
if err != nil {
t.Fatal(err)
}
if n != 512 {
t.Fatalf("Skip() returned a nil error, but skipped %d bytes instead of %d", n, 512)
}
var b byte
b, err = rd.ReadByte()
if err != nil {
t.Fatal(err)
}
if b != bts[512] {
t.Fatalf("at index %d: %d in; %d out", 512, bts[512], b)
}
n, err = rd.Skip(10)
if err != nil {
t.Fatal(err)
}
if n != 10 {
t.Fatalf("Skip() returned a nil error, but skipped %d bytes instead of %d", n, 10)
}
// now try to skip past the end
rd = NewReaderSize(partialReader{bytes.NewReader(bts)}, 200)
n, err = rd.Skip(2000)
if err != io.ErrUnexpectedEOF {
t.Fatalf("expected error %q; got %q", io.EOF, err)
}
if n != 1024 {
t.Fatalf("expected to skip only 1024 bytes; skipped %d", n)
}
}
func TestSkipSeek(t *testing.T) {
bts := randomBts(1024)
// bytes.Reader implements io.Seeker
rd := NewReaderSize(bytes.NewReader(bts), 200)
n, err := rd.Skip(512)
if err != nil {
t.Fatal(err)
}
if n != 512 {
t.Fatalf("Skip() returned a nil error, but skipped %d bytes instead of %d", n, 512)
}
var b byte
b, err = rd.ReadByte()
if err != nil {
t.Fatal(err)
}
if b != bts[512] {
t.Fatalf("at index %d: %d in; %d out", 512, bts[512], b)
}
n, err = rd.Skip(10)
if err != nil {
t.Fatal(err)
}
if n != 10 {
t.Fatalf("Skip() returned a nil error, but skipped %d bytes instead of %d", n, 10)
}
// now try to skip past the end
rd.Reset(bytes.NewReader(bts))
// because of how bytes.Reader
// implements Seek, this should
// return (2000, nil)
n, err = rd.Skip(2000)
if err != nil {
t.Fatal(err)
}
if n != 2000 {
t.Fatalf("should have returned %d bytes; returned %d", 2000, n)
}
// the next call to Read()
// should return io.EOF
n, err = rd.Read([]byte{0, 0, 0})
if err != io.EOF {
t.Errorf("expected %q; got %q", io.EOF, err)
}
if n != 0 {
t.Errorf("expected 0 bytes read; got %d", n)
}
}
func TestPeek(t *testing.T) {
bts := randomBts(1024)
rd := NewReaderSize(partialReader{bytes.NewReader(bts)}, 200)
// first, a peek < buffer size
var (
peek []byte
err error
)
peek, err = rd.Peek(100)
if err != nil {
t.Fatal(err)
}
if len(peek) != 100 {
t.Fatalf("asked for %d bytes; got %d", 100, len(peek))
}
if !bytes.Equal(peek, bts[:100]) {
t.Fatal("peeked bytes not equal")
}
// now, a peek > buffer size
peek, err = rd.Peek(256)
if err != nil {
t.Fatal(err)
}
if len(peek) != 256 {
t.Fatalf("asked for %d bytes; got %d", 100, len(peek))
}
if !bytes.Equal(peek, bts[:256]) {
t.Fatal("peeked bytes not equal")
}
// now try to peek past EOF
peek, err = rd.Peek(2048)
if err != io.EOF {
t.Fatalf("expected error %q; got %q", io.EOF, err)
}
if len(peek) != 1024 {
t.Fatalf("expected %d bytes peek-able; got %d", 1024, len(peek))
}
}
func TestNext(t *testing.T) {
size := 1024
bts := randomBts(size)
rd := NewReaderSize(partialReader{bytes.NewReader(bts)}, 200)
chunksize := 256
chunks := size / chunksize
for i := 0; i < chunks; i++ {
out, err := rd.Next(chunksize)
if err != nil {
t.Fatal(err)
}
start := chunksize * i
if !bytes.Equal(bts[start:start+chunksize], out) {
t.Fatalf("chunk %d: chunks not equal", i+1)
}
}
}
func TestWriteTo(t *testing.T) {
bts := randomBts(2048)
rd := NewReaderSize(partialReader{bytes.NewReader(bts)}, 200)
// cause the buffer
// to fill a little, just
// to complicate things
rd.Peek(25)
var out bytes.Buffer
n, err := rd.WriteTo(&out)
if err != nil {
t.Fatal(err)
}
if n != 2048 {
t.Fatalf("should have written %d bytes; wrote %d", 2048, n)
}
if !bytes.Equal(out.Bytes(), bts) {
t.Fatal("bytes not equal")
}
}
func TestReadFull(t *testing.T) {
bts := randomBts(1024)
rd := NewReaderSize(partialReader{bytes.NewReader(bts)}, 256)
// try to ReadFull() the whole thing
out := make([]byte, 1024)
n, err := rd.ReadFull(out)
if err != nil {
t.Fatal(err)
}
if n != 1024 {
t.Fatalf("expected to read %d bytes; read %d", 1024, n)
}
if !bytes.Equal(bts, out) {
t.Fatal("bytes not equal")
}
// we've read everything; this should EOF
n, err = rd.Read(out)
if err != io.EOF {
t.Fatalf("expected %q; got %q", io.EOF, err)
}
rd.Reset(partialReader{bytes.NewReader(bts)})
// now try to read *past* EOF
out = make([]byte, 1500)
n, err = rd.ReadFull(out)
if err != io.ErrUnexpectedEOF {
t.Fatalf("expected error %q; got %q", io.EOF, err)
}
if n != 1024 {
t.Fatalf("expected to read %d bytes; read %d", 1024, n)
}
}
type readCounter struct {
r io.Reader
count int
}
func (r *readCounter) Read(p []byte) (int, error) {
r.count++
return r.r.Read(p)
}
func TestReadFullPerf(t *testing.T) {
const size = 1 << 22
data := randomBts(size)
c := readCounter{
r: &partialReader{
r: bytes.NewReader(data),
},
}
r := NewReader(&c)
const segments = 4
out := make([]byte, size/segments)
for i := 0; i < segments; i++ {
// force an unaligned read
_, err := r.Peek(5)
if err != nil {
t.Fatal(err)
}
n, err := r.ReadFull(out)
if err != nil {
t.Fatal(err)
}
if n != size/segments {
t.Fatalf("read %d bytes, not %d", n, size/segments)
}
}
t.Logf("called Read() on the underlying reader %d times to fill %d buffers", c.count, size/r.BufferSize())
}