2026-02-22 18:45:57 +01:00
|
|
|
package buffer_test
|
2026-02-22 16:30:37 +01:00
|
|
|
|
|
|
|
|
import (
|
2026-02-22 18:45:57 +01:00
|
|
|
"code.squareroundforest.org/arpio/buffer"
|
2026-02-22 16:30:37 +01:00
|
|
|
"errors"
|
2026-02-22 18:45:57 +01:00
|
|
|
"io"
|
|
|
|
|
"testing"
|
2026-02-22 16:30:37 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func TestContent(t *testing.T) {
|
|
|
|
|
t.Run("eof", func(t *testing.T) {
|
2026-02-22 18:45:57 +01:00
|
|
|
c := buffer.ContentFunc(func(w io.Writer) (int64, error) {
|
2026-02-22 16:30:37 +01:00
|
|
|
var n int64
|
|
|
|
|
for i := 0; i < 3; i++ {
|
2026-02-22 18:45:57 +01:00
|
|
|
ni, err := w.Write([]byte("123456789")[i*3 : i*3+3])
|
2026-02-22 16:30:37 +01:00
|
|
|
n += int64(ni)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return n, err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return n, nil
|
|
|
|
|
})
|
|
|
|
|
|
2026-02-28 14:12:34 +01:00
|
|
|
p := &fakePool{allocSize: 2}
|
2026-02-22 18:45:57 +01:00
|
|
|
o := buffer.Options{Pool: p}
|
|
|
|
|
r := buffer.BufferedContent(c, o)
|
2026-02-22 16:30:37 +01:00
|
|
|
b := make([]byte, 3)
|
|
|
|
|
for i := 0; i < 3; i++ {
|
|
|
|
|
n, err := r.Read(b)
|
|
|
|
|
if n != 3 || err != nil {
|
|
|
|
|
t.Fatal(n, err)
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-22 18:45:57 +01:00
|
|
|
if string(b) != "123456789"[i*3:i*3+3] {
|
2026-02-22 16:30:37 +01:00
|
|
|
t.Fatal(string(b))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
n, err := r.Read(b)
|
|
|
|
|
if n != 0 || !errors.Is(err, io.EOF) {
|
|
|
|
|
t.Fatal(n, err)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
t.Run("eof right away", func(t *testing.T) {
|
2026-02-22 18:45:57 +01:00
|
|
|
c := buffer.ContentFunc(func(w io.Writer) (int64, error) {
|
2026-02-22 16:30:37 +01:00
|
|
|
return 0, nil
|
|
|
|
|
})
|
|
|
|
|
|
2026-02-28 14:12:34 +01:00
|
|
|
p := &fakePool{allocSize: 2}
|
2026-02-22 18:45:57 +01:00
|
|
|
o := buffer.Options{Pool: p}
|
|
|
|
|
r := buffer.BufferedContent(c, o)
|
2026-02-22 16:30:37 +01:00
|
|
|
b := make([]byte, 3)
|
|
|
|
|
n, err := r.Read(b)
|
|
|
|
|
if n != 0 || !errors.Is(err, io.EOF) {
|
|
|
|
|
t.Fatal(n, err)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
t.Run("writer error", func(t *testing.T) {
|
2026-02-22 18:45:57 +01:00
|
|
|
c := buffer.ContentFunc(func(w io.Writer) (int64, error) {
|
2026-02-22 16:30:37 +01:00
|
|
|
var n int64
|
|
|
|
|
for i := 0; i < 3; i++ {
|
2026-02-22 18:45:57 +01:00
|
|
|
ni, err := w.Write([]byte("123456789")[i*3 : i*3+3])
|
2026-02-22 16:30:37 +01:00
|
|
|
n += int64(ni)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return n, err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return n, errTest
|
|
|
|
|
})
|
|
|
|
|
|
2026-02-28 14:12:34 +01:00
|
|
|
p := &fakePool{allocSize: 2}
|
2026-02-22 18:45:57 +01:00
|
|
|
o := buffer.Options{Pool: p}
|
|
|
|
|
r := buffer.BufferedContent(c, o)
|
2026-02-22 16:30:37 +01:00
|
|
|
b := make([]byte, 3)
|
|
|
|
|
for i := 0; i < 3; i++ {
|
|
|
|
|
n, err := r.Read(b)
|
|
|
|
|
if n != 3 || err != nil {
|
|
|
|
|
t.Fatal(n, err)
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-22 18:45:57 +01:00
|
|
|
if string(b) != "123456789"[i*3:i*3+3] {
|
2026-02-22 16:30:37 +01:00
|
|
|
t.Fatal(string(b))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
n, err := r.Read(b)
|
|
|
|
|
if n != 0 || !errors.Is(err, errTest) {
|
|
|
|
|
t.Fatal(n, err)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
t.Run("writer error right away", func(t *testing.T) {
|
2026-02-22 18:45:57 +01:00
|
|
|
c := buffer.ContentFunc(func(w io.Writer) (int64, error) {
|
2026-02-22 16:30:37 +01:00
|
|
|
return 0, errTest
|
|
|
|
|
})
|
|
|
|
|
|
2026-02-28 14:12:34 +01:00
|
|
|
p := &fakePool{allocSize: 2}
|
2026-02-22 18:45:57 +01:00
|
|
|
o := buffer.Options{Pool: p}
|
|
|
|
|
r := buffer.BufferedContent(c, o)
|
2026-02-22 16:30:37 +01:00
|
|
|
b := make([]byte, 3)
|
|
|
|
|
n, err := r.Read(b)
|
|
|
|
|
if n != 0 || !errors.Is(err, errTest) {
|
|
|
|
|
t.Fatal(n, err)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
t.Run("abort", func(t *testing.T) {
|
2026-02-22 18:45:57 +01:00
|
|
|
c := buffer.ContentFunc(func(w io.Writer) (int64, error) {
|
2026-02-22 16:30:37 +01:00
|
|
|
var n int64
|
|
|
|
|
for i := 0; i < 3; i++ {
|
2026-02-22 18:45:57 +01:00
|
|
|
ni, err := w.Write([]byte("123456789")[i*3 : i*3+3])
|
2026-02-22 16:30:37 +01:00
|
|
|
n += int64(ni)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return n, err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return n, nil
|
|
|
|
|
})
|
|
|
|
|
|
2026-02-28 14:12:34 +01:00
|
|
|
p := &fakePool{
|
2026-02-22 18:45:57 +01:00
|
|
|
allocSize: 2,
|
|
|
|
|
errAfter: []int{1},
|
2026-02-22 16:30:37 +01:00
|
|
|
}
|
|
|
|
|
|
2026-02-22 18:45:57 +01:00
|
|
|
o := buffer.Options{Pool: p}
|
|
|
|
|
r := buffer.BufferedContent(c, o)
|
2026-02-22 16:30:37 +01:00
|
|
|
b, ok, err := r.ReadBytes([]byte("67"), 12)
|
|
|
|
|
if string(b) != "12" /* segment size og 2 by the pool */ || ok || err != nil {
|
|
|
|
|
t.Fatal(string(b), ok, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
b, ok, err = r.ReadBytes([]byte("67"), 12)
|
|
|
|
|
if len(b) != 0 || ok || !errors.Is(err, errTest) {
|
|
|
|
|
t.Fatal(string(b), ok, err)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
t.Run("abort right away", func(t *testing.T) {
|
2026-02-22 18:45:57 +01:00
|
|
|
c := buffer.ContentFunc(func(w io.Writer) (int64, error) {
|
2026-02-22 16:30:37 +01:00
|
|
|
var n int64
|
|
|
|
|
for i := 0; i < 3; i++ {
|
2026-02-22 18:45:57 +01:00
|
|
|
ni, err := w.Write([]byte("123456789")[i*3 : i*3+3])
|
2026-02-22 16:30:37 +01:00
|
|
|
n += int64(ni)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return n, err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return n, nil
|
|
|
|
|
})
|
|
|
|
|
|
2026-02-28 14:12:34 +01:00
|
|
|
p := &fakePool{
|
2026-02-22 18:45:57 +01:00
|
|
|
allocSize: 2,
|
|
|
|
|
errAfter: []int{0},
|
2026-02-22 16:30:37 +01:00
|
|
|
}
|
|
|
|
|
|
2026-02-22 18:45:57 +01:00
|
|
|
o := buffer.Options{Pool: p}
|
|
|
|
|
r := buffer.BufferedContent(c, o)
|
2026-02-22 16:30:37 +01:00
|
|
|
b, ok, err := r.ReadBytes([]byte("67"), 12)
|
|
|
|
|
if len(b) != 0 || ok || !errors.Is(err, errTest) {
|
|
|
|
|
t.Fatal(string(b), ok, err)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
t.Run("close when implementation ignores writer errors", func(t *testing.T) {
|
2026-02-22 18:45:57 +01:00
|
|
|
c := buffer.ContentFunc(func(w io.Writer) (int64, error) {
|
2026-02-22 16:30:37 +01:00
|
|
|
w.Write([]byte("123"))
|
|
|
|
|
w.Write([]byte("456"))
|
|
|
|
|
w.Write([]byte("123"))
|
|
|
|
|
return 0, nil
|
|
|
|
|
})
|
|
|
|
|
|
2026-02-28 14:12:34 +01:00
|
|
|
p := &fakePool{
|
2026-02-22 18:45:57 +01:00
|
|
|
allocSize: 2,
|
|
|
|
|
errAfter: []int{1},
|
2026-02-22 16:30:37 +01:00
|
|
|
}
|
|
|
|
|
|
2026-02-22 18:45:57 +01:00
|
|
|
o := buffer.Options{Pool: p}
|
|
|
|
|
r := buffer.BufferedContent(c, o)
|
2026-02-22 16:30:37 +01:00
|
|
|
b, ok, err := r.ReadBytes([]byte("67"), 12)
|
|
|
|
|
if string(b) != "12" /* segment size og 2 by the pool */ || ok || err != nil {
|
|
|
|
|
t.Fatal(string(b), ok, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
b, ok, err = r.ReadBytes([]byte("67"), 12)
|
|
|
|
|
if len(b) != 0 || ok || !errors.Is(err, errTest) {
|
|
|
|
|
t.Fatal(string(b), ok, err)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
t.Run("zero write", func(t *testing.T) {
|
2026-02-22 18:45:57 +01:00
|
|
|
c := buffer.ContentFunc(func(w io.Writer) (int64, error) {
|
2026-02-22 16:30:37 +01:00
|
|
|
w.Write([]byte("123"))
|
|
|
|
|
w.Write(nil)
|
|
|
|
|
w.Write([]byte("456"))
|
|
|
|
|
w.Write([]byte("789"))
|
|
|
|
|
return 0, nil
|
|
|
|
|
})
|
|
|
|
|
|
2026-02-28 14:12:34 +01:00
|
|
|
p := &fakePool{allocSize: 2}
|
2026-02-22 18:45:57 +01:00
|
|
|
o := buffer.Options{Pool: p}
|
|
|
|
|
r := buffer.BufferedContent(c, o)
|
2026-02-22 16:30:37 +01:00
|
|
|
b := make([]byte, 3)
|
|
|
|
|
for i := 0; i < 3; i++ {
|
|
|
|
|
n, err := r.Read(b)
|
|
|
|
|
if n != 3 || err != nil {
|
|
|
|
|
t.Fatal(n, err)
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-22 18:45:57 +01:00
|
|
|
if string(b) != "123456789"[i*3:i*3+3] {
|
2026-02-22 16:30:37 +01:00
|
|
|
t.Fatal(string(b))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
n, err := r.Read(b)
|
|
|
|
|
if n != 0 || !errors.Is(err, io.EOF) {
|
|
|
|
|
t.Fatal(n, err)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
t.Run("zero write right away", func(t *testing.T) {
|
2026-02-22 18:45:57 +01:00
|
|
|
c := buffer.ContentFunc(func(w io.Writer) (int64, error) {
|
2026-02-22 16:30:37 +01:00
|
|
|
w.Write(nil)
|
|
|
|
|
w.Write([]byte("123"))
|
|
|
|
|
w.Write([]byte("456"))
|
|
|
|
|
w.Write([]byte("789"))
|
|
|
|
|
return 0, nil
|
|
|
|
|
})
|
|
|
|
|
|
2026-02-28 14:12:34 +01:00
|
|
|
p := &fakePool{allocSize: 2}
|
2026-02-22 18:45:57 +01:00
|
|
|
o := buffer.Options{Pool: p}
|
|
|
|
|
r := buffer.BufferedContent(c, o)
|
2026-02-22 16:30:37 +01:00
|
|
|
b := make([]byte, 3)
|
|
|
|
|
for i := 0; i < 3; i++ {
|
|
|
|
|
n, err := r.Read(b)
|
|
|
|
|
if n != 3 || err != nil {
|
|
|
|
|
t.Fatal(n, err)
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-22 18:45:57 +01:00
|
|
|
if string(b) != "123456789"[i*3:i*3+3] {
|
2026-02-22 16:30:37 +01:00
|
|
|
t.Fatal(string(b))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
n, err := r.Read(b)
|
|
|
|
|
if n != 0 || !errors.Is(err, io.EOF) {
|
|
|
|
|
t.Fatal(n, err)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
t.Run("custom error", func(t *testing.T) {
|
2026-02-22 18:45:57 +01:00
|
|
|
c := buffer.ContentFunc(func(w io.Writer) (int64, error) {
|
2026-02-22 16:30:37 +01:00
|
|
|
var n int64
|
|
|
|
|
for i := 0; i < 3; i++ {
|
2026-02-22 18:45:57 +01:00
|
|
|
ni, err := w.Write([]byte("123456789")[i*3 : i*3+3])
|
2026-02-22 16:30:37 +01:00
|
|
|
n += int64(ni)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return n, err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return n, errTest
|
|
|
|
|
})
|
|
|
|
|
|
2026-02-28 14:12:34 +01:00
|
|
|
p := &fakePool{allocSize: 3}
|
2026-02-22 18:45:57 +01:00
|
|
|
o := buffer.Options{Pool: p}
|
|
|
|
|
r := buffer.BufferedContent(c, o)
|
2026-02-22 16:30:37 +01:00
|
|
|
b := make([]byte, 3)
|
|
|
|
|
for i := 0; i < 3; i++ {
|
|
|
|
|
n, err := r.Read(b)
|
|
|
|
|
if n != 3 || err != nil {
|
|
|
|
|
t.Fatal(n, err)
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-22 18:45:57 +01:00
|
|
|
if string(b) != "123456789"[i*3:i*3+3] {
|
2026-02-22 16:30:37 +01:00
|
|
|
t.Fatal(string(b))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
n, err := r.Read(b)
|
|
|
|
|
if n != 0 || !errors.Is(err, errTest) {
|
|
|
|
|
t.Fatal(n, err)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
t.Run("custom error with pool error", func(t *testing.T) {
|
2026-02-22 18:45:57 +01:00
|
|
|
c := buffer.ContentFunc(func(w io.Writer) (int64, error) {
|
2026-02-22 16:30:37 +01:00
|
|
|
w.Write([]byte("123"))
|
|
|
|
|
w.Write([]byte("456"))
|
|
|
|
|
w.Write([]byte("123"))
|
|
|
|
|
return 0, errTest2
|
|
|
|
|
})
|
|
|
|
|
|
2026-02-28 14:12:34 +01:00
|
|
|
p := &fakePool{
|
2026-02-22 18:45:57 +01:00
|
|
|
allocSize: 2,
|
|
|
|
|
errAfter: []int{1},
|
2026-02-22 16:30:37 +01:00
|
|
|
}
|
|
|
|
|
|
2026-02-22 18:45:57 +01:00
|
|
|
o := buffer.Options{Pool: p}
|
|
|
|
|
r := buffer.BufferedContent(c, o)
|
2026-02-22 16:30:37 +01:00
|
|
|
b, ok, err := r.ReadBytes([]byte("67"), 12)
|
|
|
|
|
if string(b) != "12" /* segment size og 2 by the pool */ || ok || err != nil {
|
|
|
|
|
t.Fatal(string(b), ok, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
b, ok, err = r.ReadBytes([]byte("67"), 12)
|
|
|
|
|
if len(b) != 0 || ok || !errors.Is(err, errTest) || !errors.Is(err, errTest2) {
|
|
|
|
|
t.Fatal(string(b), ok, err)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|