399 lines
7.8 KiB
Go
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())
|
||
|
}
|