187 lines
3.7 KiB
Go
187 lines
3.7 KiB
Go
package jstream
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/base64"
|
|
"io"
|
|
"io/ioutil"
|
|
. "testing"
|
|
|
|
"github.com/mediocregopher/mediocre-go-lib/mrand"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
type bbrTest struct {
|
|
wsSuffix []byte // whitespace
|
|
body []byte
|
|
shouldCancel bool
|
|
intoSize int
|
|
}
|
|
|
|
func randBBRTest(minBodySize, maxBodySize int) bbrTest {
|
|
var whitespace = []byte{' ', '\n', '\t', '\r'}
|
|
genWhitespace := func(n int) []byte {
|
|
ws := make([]byte, n)
|
|
for i := range ws {
|
|
ws[i] = whitespace[mrand.Intn(len(whitespace))]
|
|
}
|
|
return ws
|
|
}
|
|
|
|
body := mrand.Bytes(minBodySize + mrand.Intn(maxBodySize-minBodySize))
|
|
return bbrTest{
|
|
wsSuffix: genWhitespace(mrand.Intn(10)),
|
|
body: body,
|
|
intoSize: 1 + mrand.Intn(len(body)+1),
|
|
}
|
|
}
|
|
|
|
func (bt bbrTest) msgAndArgs() []interface{} {
|
|
return []interface{}{"bt:%#v len(body):%d", bt, len(bt.body)}
|
|
}
|
|
|
|
func (bt bbrTest) mkBytes() []byte {
|
|
buf := new(bytes.Buffer)
|
|
enc := base64.NewEncoder(base64.StdEncoding, buf)
|
|
|
|
if bt.shouldCancel {
|
|
enc.Write(bt.body[:len(bt.body)/2])
|
|
enc.Close()
|
|
buf.WriteByte(bbCancel)
|
|
} else {
|
|
enc.Write(bt.body)
|
|
enc.Close()
|
|
buf.WriteByte(bbEnd)
|
|
}
|
|
|
|
buf.Write(bt.wsSuffix)
|
|
return buf.Bytes()
|
|
}
|
|
|
|
func (bt bbrTest) do(t *T) bool {
|
|
buf := bytes.NewBuffer(bt.mkBytes())
|
|
bbr := newByteBlobReader(buf)
|
|
|
|
into := make([]byte, bt.intoSize)
|
|
outBuf := new(bytes.Buffer)
|
|
_, err := io.CopyBuffer(outBuf, bbr, into)
|
|
if bt.shouldCancel {
|
|
return assert.Equal(t, ErrCanceled, err, bt.msgAndArgs()...)
|
|
}
|
|
if !assert.NoError(t, err, bt.msgAndArgs()...) {
|
|
return false
|
|
}
|
|
if !assert.Equal(t, bt.body, outBuf.Bytes(), bt.msgAndArgs()...) {
|
|
return false
|
|
}
|
|
fullRest := append(bbr.dr.rest, buf.Bytes()...)
|
|
if len(bt.wsSuffix) == 0 {
|
|
return assert.Empty(t, fullRest, bt.msgAndArgs()...)
|
|
}
|
|
return assert.Equal(t, bt.wsSuffix, fullRest, bt.msgAndArgs()...)
|
|
}
|
|
|
|
func TestByteBlobReader(t *T) {
|
|
// some sanity tests
|
|
bbrTest{
|
|
body: []byte{2, 3, 4, 5},
|
|
intoSize: 4,
|
|
}.do(t)
|
|
bbrTest{
|
|
body: []byte{2, 3, 4, 5},
|
|
intoSize: 3,
|
|
}.do(t)
|
|
bbrTest{
|
|
body: []byte{2, 3, 4, 5},
|
|
shouldCancel: true,
|
|
intoSize: 3,
|
|
}.do(t)
|
|
|
|
// fuzz this bitch
|
|
for i := 0; i < 50000; i++ {
|
|
bt := randBBRTest(0, 1000)
|
|
if !bt.do(t) {
|
|
return
|
|
}
|
|
bt.shouldCancel = true
|
|
if !bt.do(t) {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkByteBlobReader(b *B) {
|
|
type bench struct {
|
|
bt bbrTest
|
|
body []byte
|
|
buf *bytes.Reader
|
|
cpBuf []byte
|
|
}
|
|
|
|
mkTestSet := func(minBodySize, maxBodySize int) []bench {
|
|
n := 100
|
|
benches := make([]bench, n)
|
|
for i := range benches {
|
|
bt := randBBRTest(minBodySize, maxBodySize)
|
|
body := bt.mkBytes()
|
|
benches[i] = bench{
|
|
bt: bt,
|
|
body: body,
|
|
buf: bytes.NewReader(nil),
|
|
cpBuf: make([]byte, bt.intoSize),
|
|
}
|
|
}
|
|
return benches
|
|
}
|
|
|
|
testRaw := func(b *B, benches []bench) {
|
|
j := 0
|
|
for i := 0; i < b.N; i++ {
|
|
if j >= len(benches) {
|
|
j = 0
|
|
}
|
|
benches[j].buf.Reset(benches[j].body)
|
|
io.CopyBuffer(ioutil.Discard, benches[j].buf, benches[j].cpBuf)
|
|
j++
|
|
}
|
|
}
|
|
|
|
testBBR := func(b *B, benches []bench) {
|
|
j := 0
|
|
for i := 0; i < b.N; i++ {
|
|
if j >= len(benches) {
|
|
j = 0
|
|
}
|
|
benches[j].buf.Reset(benches[j].body)
|
|
bbr := newByteBlobReader(benches[j].buf)
|
|
io.CopyBuffer(ioutil.Discard, bbr, benches[j].cpBuf)
|
|
j++
|
|
}
|
|
}
|
|
|
|
benches := []struct {
|
|
name string
|
|
minBodySize, maxBodySize int
|
|
}{
|
|
{"small", 0, 1000},
|
|
{"medium", 1000, 10000},
|
|
{"large", 10000, 100000},
|
|
{"xlarge", 100000, 1000000},
|
|
}
|
|
|
|
b.StopTimer()
|
|
for i := range benches {
|
|
b.Run(benches[i].name, func(b *B) {
|
|
set := mkTestSet(benches[i].minBodySize, benches[i].maxBodySize)
|
|
b.StartTimer()
|
|
b.Run("raw", func(b *B) {
|
|
testRaw(b, set)
|
|
})
|
|
b.Run("bbr", func(b *B) {
|
|
testBBR(b, set)
|
|
})
|
|
b.StopTimer()
|
|
})
|
|
}
|
|
}
|