1
0
buffer/read_test.go

281 lines
5.8 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"
2026-02-22 16:30:37 +01:00
"io"
"testing"
2026-02-17 16:58:00 +01:00
)
func TestRead(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("small", func(t *testing.T) {
g := &gen{max: 3}
r := cr(g, buffer.Options{Pool: buffer.NoPool(1 << 12)})
b, err := io.ReadAll(r)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(b, generate(3)) {
t.Fatal("output does not match", len(b))
}
})
t.Run("large", func(t *testing.T) {
g := &gen{max: 1 << 18}
r := cr(g, buffer.Options{Pool: buffer.NoPool(1 << 12)})
b, err := io.ReadAll(r)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(b, generate(1<<18)) {
t.Fatal("output does not match", len(b))
}
})
t.Run("zero first", func(t *testing.T) {
g := &gen{max: 1 << 15}
o := buffer.Options{Pool: buffer.NoPool(1 << 12)}
r := cr(g, o)
var p []byte
n, err := r.Read(p)
if err != nil {
t.Fatal(err)
}
if n != 0 {
t.Fatal("invalid length")
}
p = make([]byte, 256)
n, err = r.Read(p)
if err != nil {
t.Fatal(err)
}
if n != 256 {
t.Fatal("invalid length")
}
if !bytes.Equal(p, generate(256)) {
t.Fatal("invalid content")
}
})
t.Run("large with non-divisible fragments", func(t *testing.T) {
g := &gen{max: 1 << 15}
o := buffer.Options{Pool: buffer.NoPool(1 << 12 / 2)}
r := cr(g, o)
b, err := io.ReadAll(r)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(b, generate(1<<15)) {
t.Fatal("output does not match", len(b))
}
})
t.Run("partial segment", func(t *testing.T) {
g := &gen{max: 1 << 15}
o := buffer.Options{Pool: buffer.NoPool(128)}
r := cr(g, o)
r.Peek(30)
p := make([]byte, 60)
n, err := r.Read(p)
if n != 60 {
t.Fatal("invalid read length", n)
}
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(p, generate(60)) {
t.Fatal("invalid content")
}
})
t.Run("partial segment nth", func(t *testing.T) {
g := &gen{max: 1 << 15}
o := buffer.Options{Pool: buffer.NoPool(128)}
r := cr(g, o)
r.Peek(2*128 + 30)
p := make([]byte, 2*128+60)
n, err := r.Read(p)
if n != 2*128+60 {
t.Fatal("invalid read length", n)
}
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(p, generate(2*128+60)) {
t.Fatal("invalid content")
}
})
t.Run("read buffer larger than read size", func(t *testing.T) {
g := &gen{max: 1 << 15}
o := buffer.Options{Pool: buffer.NoPool(128)}
r := cr(g, o)
p := make([]byte, 12)
n, err := r.Read(p)
if n != 12 {
t.Fatal("invalid read size")
}
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(p, generate(12)) {
t.Fatal("invalid content")
}
})
t.Run("read buffer larger than segment", func(t *testing.T) {
g := &gen{max: 1 << 15}
o := buffer.Options{Pool: buffer.NoPool(128)}
r := cr(g, o)
p := make([]byte, 192)
n, err := r.Read(p)
if n != 192 {
t.Fatal("invalid read size")
}
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(p, generate(192)) {
t.Fatal("invalid content")
}
})
t.Run("read buffer larger than available data", func(t *testing.T) {
g := &gen{max: 256}
o := buffer.Options{Pool: buffer.NoPool(128)}
r := cr(g, o)
p := make([]byte, 384)
n, err := r.Read(p)
if n != 256 {
t.Fatal("invalid read size")
}
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(p[:n], generate(256)) {
t.Fatal("invalid content")
}
n, err = r.Read(p)
if n != 0 || !errors.Is(err, io.EOF) {
t.Fatal("invalid post read", n, err)
}
})
})
}
t.Run("reader only", func(t *testing.T) {
cr := buffer.BufferedReader
t.Run("null read on empty", func(t *testing.T) {
g := &gen{
max: 1 << 15,
nullReadAfter: []int{0, 0},
}
2026-02-17 16:58:00 +01:00
2026-02-22 16:30:37 +01:00
o := buffer.Options{Pool: buffer.NoPool(128)}
r := cr(g, o)
p := make([]byte, 64)
n, err := r.Read(p)
if n != 0 || err != nil {
t.Fatal("failed to handle null read", n, err)
}
2026-02-17 16:58:00 +01:00
2026-02-22 16:30:37 +01:00
n, err = r.Read(p)
if n != 64 || err != nil || !bytes.Equal(p, generate(64)) {
t.Fatal("failed to recover after null read", n, err)
}
})
2026-02-17 16:58:00 +01:00
2026-02-22 16:30:37 +01:00
t.Run("read error without content", func(t *testing.T) {
g := &gen{
max: 1 << 15,
errAfter: []int{32},
}
2026-02-17 16:58:00 +01:00
2026-02-22 16:30:37 +01:00
o := buffer.Options{Pool: buffer.NoPool(32)}
r := cr(g, o)
p := make([]byte, 64)
n, err := r.Read(p)
if n != 32 || err != nil {
t.Fatal("failed to read", n, err)
}
2026-02-17 16:58:00 +01:00
2026-02-22 16:30:37 +01:00
n, err = r.Read(p)
if n != 0 || !errors.Is(err, errTest) {
t.Fatal("failed to process read error", n, err)
}
})
2026-02-17 16:58:00 +01:00
2026-02-22 16:30:37 +01:00
t.Run("read error with content", func(t *testing.T) {
g := &gen{
max: 1 << 15,
errAfter: []int{32},
fastErr: true,
}
2026-02-17 16:58:00 +01:00
2026-02-22 16:30:37 +01:00
o := buffer.Options{Pool: buffer.NoPool(32)}
r := cr(g, o)
p := make([]byte, 64)
n, err := r.Read(p)
if n != 32 || err != nil {
t.Fatal("failed to read", n, err)
}
2026-02-17 16:58:00 +01:00
2026-02-22 16:30:37 +01:00
n, err = r.Read(p)
if n != 0 || !errors.Is(err, errTest) {
t.Fatal("failed to process read error", n, err)
}
})
2026-02-17 16:58:00 +01:00
2026-02-22 16:30:37 +01:00
t.Run("read after error", func(t *testing.T) {
g := &gen{
max: 1 << 15,
errAfter: []int{32},
fastErr: true,
}
2026-02-17 16:58:00 +01:00
2026-02-22 16:30:37 +01:00
o := buffer.Options{Pool: buffer.NoPool(32)}
var result []byte
r := cr(g, o)
p := make([]byte, 9)
for i := 0; i < 3; i++ {
if n, err := r.Read(p); n != 9 || err != nil {
t.Fatal(n, err)
}
2026-02-17 16:58:00 +01:00
2026-02-22 16:30:37 +01:00
result = append(result, p...)
}
2026-02-17 16:58:00 +01:00
2026-02-22 16:30:37 +01:00
if n, err := r.Read(p); n != 5 || err != nil {
2026-02-17 16:58:00 +01:00
t.Fatal(n, err)
}
2026-02-22 16:30:37 +01:00
result = append(result, p[:5]...)
if !bytes.Equal(result, generate(32)) {
t.Fatal("invalid content")
}
})
2026-02-17 16:58:00 +01:00
})
}