1
0
buffer/readbytes_test.go
2026-02-17 16:58:00 +01:00

753 lines
15 KiB
Go

package buffer_test
import (
"testing"
"code.squareroundforest.org/arpio/buffer"
"bytes"
"errors"
"io"
)
func TestReadBytes(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 := buffer.ReaderFrom(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 := buffer.ReaderFrom(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),
ReadSize: 14,
}
r := buffer.ReaderFrom(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),
ReadSize: 8,
}
r := buffer.ReaderFrom(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),
ReadSize: 15,
}
r := buffer.ReaderFrom(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),
ReadSize: 15,
}
r := buffer.ReaderFrom(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 := buffer.ReaderFrom(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 := buffer.ReaderFrom(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 := buffer.ReaderFrom(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),
ReadSize: 8,
}
r := buffer.ReaderFrom(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),
ReadSize: 8,
}
r := buffer.ReaderFrom(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),
ReadSize: 8,
}
r := buffer.ReaderFrom(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),
ReadSize: 8,
}
r := buffer.ReaderFrom(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 := buffer.ReaderFrom(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 := buffer.ReaderFrom(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("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),
ReadSize: 8,
}
r := buffer.ReaderFrom(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),
ReadSize: 8,
}
r := buffer.ReaderFrom(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),
ReadSize: 8,
}
r := buffer.ReaderFrom(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 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 := buffer.ReaderFrom(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")
}
})
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),
ReadSize: 8,
}
r := buffer.ReaderFrom(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),
ReadSize: 256,
}
r := buffer.ReaderFrom(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")
}
})
t.Run("find not none consumed", func(t *testing.T) {
g := &gen{}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
r := buffer.ReaderFrom(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")
}
})
// conditions:
// - A0: error before read
// - A1: error during read
// - B0: allocation error
// - B1: read error
// - B2: eof
// - C0: error right before delimiter
// - C1: error during delimiter
// - C2: error right after full delimiter
// - D0: error before max
// - D1: error right at max
// - D2: error after max
// - E0: error at zero segment position
// - E1: error at not first segment boundary
// - E2: error in first segment
// - E3: error in subsequent segment
// - F0: error at zero data position
// - F1: error at not zero data position
// - F3: error at last data position
// - G0: less than max buffered
// - G1: exactly max buffered
// - G2: more than max buffered
// - H0: max at first segment start
// - H1: max at segment boundary
// - H2: max in first segment
// - H3: max in subsequent segment
// - I0: delimiter in newly read
// - I1: delimiter in buffered
// - J0: delimiter before max
// - J1: delimiter right up to max
// - J2: delimiter over max
// - J3: delimiter right after max
// - J4: delimiter after max
// - K0: delimiter at zero segment position
// - K1: delimiter at subsequent segment boundary
// - K2: delimiter within segment
// - L0: delimiter at zero data position
// - L1: delimiter at within buffered data
// - L2: delimiter at the end of buffered data
// - L3: partial delimiter at the end of buffered data
// - L4: delimiter right after buffered data
// - M0: delimiter found
// - M1: delimiter not found
//
// variations:
// - A0B0...
}