1
0
buffer/readbytes_test.go

713 lines
15 KiB
Go
Raw Permalink Normal View History

2026-02-17 16:58:00 +01:00
package buffer_test
import (
"bytes"
2026-02-22 16:30:37 +01:00
"code.squareroundforest.org/arpio/buffer"
2026-02-17 16:58:00 +01:00
"errors"
"io"
2026-02-22 16:30:37 +01:00
"testing"
2026-02-17 16:58:00 +01:00
)
func TestReadBytes(t *testing.T) {
2026-02-22 16:30:37 +01:00
for title, cr := range map[string]createReader{"reader": buffer.BufferedReader, "content": testContent} {
t.Run(title, func(t *testing.T) {
t.Run("find", func(t *testing.T) {
g := &gen{
max: 1 << 15,
customContentAfter: []int{12},
customContent: map[int][]byte{12: []byte("123")},
}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 64)
if err != nil {
t.Fatal(err)
}
if !ok {
t.Fatal("delimiter")
}
if !bytes.Equal(b, append(generate(12), []byte("123")...)) {
t.Fatal("failed to generate right content")
}
})
t.Run("find not", func(t *testing.T) {
g := &gen{max: 1 << 15}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 64)
if err != nil {
t.Fatal(err)
}
if ok || len(b) != 0 {
t.Fatal("failed to not find delimiter")
}
})
t.Run("find across segments", func(t *testing.T) {
g := &gen{
max: 1 << 15,
customContentAfter: []int{12},
customContent: map[int][]byte{12: []byte("123")},
}
o := buffer.Options{Pool: buffer.NoPool(14)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 64)
if err != nil {
t.Fatal(err)
}
if !ok {
t.Fatal("failed to find delimiter")
}
if !bytes.Equal(b, append(generate(12), []byte("123")...)) {
t.Fatal("invalid content")
}
})
t.Run("find across multiple segments", func(t *testing.T) {
d := generateFrom([]byte("123"), 12)
g := &gen{
max: 1 << 15,
customContentAfter: []int{6},
customContent: map[int][]byte{6: d},
}
o := buffer.Options{Pool: buffer.NoPool(8)}
r := cr(g, o)
b, ok, err := r.ReadBytes(d, 64)
if err != nil {
t.Fatal(err)
}
if !ok {
t.Fatal("failed to find delimiter")
}
if !bytes.Equal(b, append(generate(6), d...)) {
t.Fatal("invalid content")
}
})
t.Run("find not across segments", func(t *testing.T) {
g := &gen{max: 1 << 15}
o := buffer.Options{Pool: buffer.NoPool(15)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 24)
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("invalid delimiter found")
}
if len(b) != 0 {
t.Fatal("invalid content", len(b), string(b))
}
})
t.Run("find not across multiple segments", func(t *testing.T) {
g := &gen{max: 1 << 15}
o := buffer.Options{Pool: buffer.NoPool(15)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 64)
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("invalid delimiter found")
}
if len(b) != 0 {
t.Fatal("invalid content", len(b), string(b))
}
})
t.Run("find not due to max", func(t *testing.T) {
g := &gen{
max: 1 << 15,
customContentAfter: []int{12},
customContent: map[int][]byte{12: []byte("123")},
}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("8"), 64)
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("invalid delimiter found")
}
if len(b) != 0 {
t.Fatal("invalid content")
}
})
t.Run("find partial", func(t *testing.T) {
g := &gen{
max: 1 << 15,
customContentAfter: []int{12},
customContent: map[int][]byte{12: []byte("12")},
}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 64)
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("invalid delimiter found")
}
if len(b) != 0 {
t.Fatal("invalid content")
}
})
t.Run("find partial and then full", func(t *testing.T) {
g := &gen{
max: 1 << 15,
customContentAfter: []int{12, 18},
customContent: map[int][]byte{
12: []byte("12"),
18: []byte("123"),
},
}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 16)
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("invalid delimiter found")
}
if len(b) != 0 {
t.Fatal("invalid content")
}
b, ok, err = r.ReadBytes([]byte("123"), 24)
if err != nil {
t.Fatal(err)
}
if !ok {
t.Fatal("delimiter")
}
check := append(
append(generate(12), append([]byte("12"), generate(18)[14:]...)...),
[]byte("123")...,
)
if !bytes.Equal(b, check) {
t.Fatal("invalid content", len(b), string(b), len(check), string(check))
}
})
t.Run("find partial across segments", func(t *testing.T) {
g := &gen{
max: 1 << 15,
customContentAfter: []int{7},
customContent: map[int][]byte{7: []byte("12")},
}
o := buffer.Options{Pool: buffer.NoPool(8)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 64)
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("invalid delimiter found")
}
if len(b) != 0 {
t.Fatal("invalid content")
}
})
t.Run("find partial across multiple segments", func(t *testing.T) {
g := &gen{
max: 1 << 15,
customContentAfter: []int{7},
customContent: map[int][]byte{7: []byte("1234567890")},
}
o := buffer.Options{Pool: buffer.NoPool(8)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("1234567890123"), 64)
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("invalid delimiter found")
}
if len(b) != 0 {
t.Fatal("invalid content")
}
})
t.Run("find partial across segments and then full", func(t *testing.T) {
g := &gen{
max: 1 << 15,
customContentAfter: []int{7, 15},
customContent: map[int][]byte{
7: []byte("12"),
15: []byte("123"),
},
}
o := buffer.Options{Pool: buffer.NoPool(8)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 10)
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("invalid delimiter found")
}
if len(b) != 0 {
t.Fatal("invalid content")
}
b, ok, err = r.ReadBytes([]byte("123"), 64)
if err != nil {
t.Fatal(err)
}
if !ok {
t.Fatal("delimiter")
}
check := append(
append(generate(7), append([]byte("12"), generate(15)[9:]...)...),
[]byte("123")...,
)
if !bytes.Equal(b, check) {
t.Fatal("invalid content", len(b), string(b), len(check), string(check))
}
})
t.Run("find partial across multiple segments and then full", func(t *testing.T) {
g := &gen{
max: 1 << 15,
customContentAfter: []int{7, 22},
customContent: map[int][]byte{
7: []byte("1234567890"),
22: []byte("1234567890123"),
},
}
o := buffer.Options{Pool: buffer.NoPool(8)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("1234567890123"), 20)
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("invalid delimiter found")
}
if len(b) != 0 {
t.Fatal("invalid content")
}
b, ok, err = r.ReadBytes([]byte("1234567890123"), 64)
if err != nil {
t.Fatal(err)
}
if !ok {
t.Fatal("delimiter")
}
check := append(
append(append(generate(7), []byte("1234567890")...), generate(22)[17:]...),
[]byte("1234567890123")...,
)
if !bytes.Equal(b, check) {
t.Fatal("invalid content", len(b), string(b), len(check), string(check))
}
})
t.Run("find partial due to max", func(t *testing.T) {
g := &gen{
max: 1 << 15,
customContentAfter: []int{12},
customContent: map[int][]byte{12: []byte("123")},
}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 14)
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("delimiter")
}
if len(b) != 0 {
t.Fatal("invalid content")
}
})
t.Run("find partial due to max and then full", func(t *testing.T) {
g := &gen{
max: 1 << 15,
customContentAfter: []int{12},
customContent: map[int][]byte{12: []byte("123")},
}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 14)
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("delimiter")
}
if len(b) != 0 {
t.Fatal("invalid content")
}
b, ok, err = r.ReadBytes([]byte("123"), 64)
if err != nil {
t.Fatal(err)
}
if !ok {
t.Fatal("delimiter")
}
if !bytes.Equal(b, append(generate(12), []byte("123")...)) {
t.Fatal("invalid content")
}
})
t.Run("null delimiter", func(t *testing.T) {
g := &gen{
max: 1 << 15,
customContentAfter: []int{12},
customContent: map[int][]byte{12: []byte("123")},
}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
r := cr(g, o)
b, ok, err := r.ReadBytes(nil, 64)
if err != nil {
t.Fatal(err)
}
if !ok {
t.Fatal("failed to find delimiter")
}
if len(b) != 0 {
t.Fatal("failed to generate right content")
}
})
2026-02-28 14:12:34 +01:00
t.Run("single char delimiter", func(t *testing.T) {
g := &gen{
max: 1 << 15,
customContentAfter: []int{12},
customContent: map[int][]byte{12: []byte("1")},
}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte{'1'}, 64)
if err != nil {
t.Fatal(err)
}
if !ok {
t.Fatal("delimiter")
}
if !bytes.Equal(b, append(generate(12), '1')) {
t.Fatal("failed to generate right content")
}
})
t.Run("large delimiter", func(t *testing.T) {
src := &gen{
max: 1 << 18,
customContentAfter: []int{1<<17 - 3},
customContent: map[int][]byte{1<<17 - 3: []byte("123")},
}
r := buffer.BufferedReader(src, buffer.Options{Pool: buffer.NoPool(1 << 12)})
r.ReadBytes([]byte{'1'}, 1<<17+1<<16)
})
t.Run("delimiter longer than content", func(t *testing.T) {
g := &gen{max: 1 << 9}
o := buffer.Options{Pool: buffer.NoPool(16)}
r := cr(g, o)
b, ok, err := r.ReadBytes(generate(1<<10), 1<<11)
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("delimiter")
}
if !bytes.Equal(b, generate(1<<9)) {
t.Fatal("content")
}
})
2026-02-22 16:30:37 +01:00
t.Run("find not none consumed", func(t *testing.T) {
g := &gen{}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 64)
if !errors.Is(err, io.EOF) {
t.Fatal(err)
}
if ok {
t.Fatal("failed to find delimiter")
}
if len(b) != 0 {
t.Fatal("invalid content")
}
})
})
}
t.Run("reader only", func(t *testing.T) {
cr := buffer.BufferedReader
t.Run("error before found", func(t *testing.T) {
g := &gen{
max: 1 << 15,
errAfter: []int{8},
customContentAfter: []int{12},
customContent: map[int][]byte{12: []byte("123")},
}
o := buffer.Options{Pool: buffer.NoPool(8)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 64)
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("delimiter")
}
if !bytes.Equal(b, generate(8)) {
t.Fatal("invalid content")
}
b, ok, err = r.ReadBytes([]byte("123"), 64)
if !errors.Is(err, errTest) {
t.Fatal("failed to fail", err)
}
if ok {
t.Fatal("delimiter")
}
if len(b) != 0 {
t.Fatal("invalid content")
}
})
t.Run("error when found", func(t *testing.T) {
g := &gen{
max: 1 << 15,
errAfter: []int{15},
fastErr: true,
customContentAfter: []int{12},
customContent: map[int][]byte{12: []byte("123")},
}
o := buffer.Options{Pool: buffer.NoPool(8)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 64)
if err != nil {
t.Fatal(err)
}
if !ok {
t.Fatal("delimiter", len(b), string(b))
}
if !bytes.Equal(b, append(generate(12), []byte("123")...)) {
t.Fatal("invalid content")
}
b, ok, err = r.ReadBytes([]byte("123"), 64)
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("unexpected delimiter")
}
if !bytes.Equal(b, generate(16)[15:]) {
t.Fatal("invalid content", len(b), string(b))
}
b, ok, err = r.ReadBytes([]byte("123"), 64)
if !errors.Is(err, errTest) {
t.Fatal(err)
}
if ok {
t.Fatal("unexpected delimiter")
}
if len(b) != 0 {
t.Fatal("invalid content", len(b), string(b))
}
})
t.Run("error after found", func(t *testing.T) {
g := &gen{
max: 1 << 15,
errAfter: []int{18},
fastErr: true,
customContentAfter: []int{12},
customContent: map[int][]byte{12: []byte("123")},
}
o := buffer.Options{Pool: buffer.NoPool(8)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 64)
if err != nil {
t.Fatal(err)
}
if !ok {
t.Fatal("delimiter", len(b), string(b))
}
if !bytes.Equal(b, append(generate(12), []byte("123")...)) {
t.Fatal("invalid content")
}
b, ok, err = r.ReadBytes([]byte("123"), 64)
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("unexpected delimiter")
}
if !bytes.Equal(b, generate(24)[15:]) {
t.Fatal("invalid content", len(b), string(b))
}
b, ok, err = r.ReadBytes([]byte("123"), 64)
if !errors.Is(err, errTest) {
t.Fatal(err)
}
if ok {
t.Fatal("unexpected delimiter")
}
if len(b) != 0 {
t.Fatal("invalid content", len(b), string(b))
}
})
t.Run("null read", func(t *testing.T) {
g := &gen{
max: 1 << 15,
nullReadAfter: []int{8, 8},
customContentAfter: []int{12},
customContent: map[int][]byte{12: []byte("123")},
}
o := buffer.Options{Pool: buffer.NoPool(8)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 64)
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("delimiter")
}
if len(b) != 0 {
t.Fatal("invalid content")
}
b, ok, err = r.ReadBytes([]byte("123"), 64)
if err != nil {
t.Fatal(err)
}
if !ok {
t.Fatal("delimiter")
}
if !bytes.Equal(b, append(generate(12), []byte("123")...)) {
t.Fatal("failed to generate right content")
}
})
t.Run("find not more than max", func(t *testing.T) {
g := &gen{
max: 256,
fastErr: true,
}
o := buffer.Options{Pool: buffer.NoPool(256)}
r := cr(g, o)
b, ok, err := r.ReadBytes([]byte("123"), 64)
if err != nil {
t.Fatal(err)
}
if ok {
t.Fatal("delimiter")
}
if !bytes.Equal(b, generate(64)) {
t.Fatal("failed to generate right content")
}
})
2026-02-17 16:58:00 +01:00
})
}