1
0
pool/pool_test.go

327 lines
6.7 KiB
Go
Raw Permalink Normal View History

2026-03-03 19:50:09 +01:00
package pool_test
2026-03-05 12:34:35 +01:00
import (
"code.squareroundforest.org/arpio/pool"
"code.squareroundforest.org/arpio/syncbus"
"code.squareroundforest.org/arpio/times"
"errors"
"testing"
"time"
)
var errTest = errors.New("test error")
2026-03-03 19:50:09 +01:00
func TestPool(t *testing.T) {
2026-03-05 12:34:35 +01:00
t.Run("initial stats", func(t *testing.T) {
alloc := func() ([]byte, error) { return make([]byte, 1<<9), nil }
p := pool.Make(alloc, nil, pool.Options{Algo: pool.NoShrink()})
s := p.Stats()
e := pool.Stats{}
if s != e {
t.Fatal(s)
}
})
t.Run("get when empty", func(t *testing.T) {
alloc := func() ([]byte, error) { return make([]byte, 1<<9), nil }
p := pool.Make(alloc, nil, pool.Options{Algo: pool.NoShrink()})
b, err := p.Get()
if err != nil {
t.Fatal(err)
}
if len(b) != 1<<9 {
t.Fatal(len(b))
}
s := p.Stats()
e := pool.Stats{Alloc: 1, Get: 1, Active: 1}
if s != e {
t.Fatal(s)
}
})
t.Run("get pooled own", func(t *testing.T) {
alloc := func() ([]byte, error) { return make([]byte, 1<<9), nil }
p := pool.Make(alloc, nil, pool.Options{Algo: pool.NoShrink()})
b, err := p.Get()
if err != nil {
t.Fatal(err)
}
if len(b) != 1<<9 {
t.Fatal(len(b))
}
p.Put(b)
b, err = p.Get()
if err != nil {
t.Fatal(err)
}
if len(b) != 1<<9 {
t.Fatal(len(b))
}
s := p.Stats()
e := pool.Stats{Alloc: 1, Get: 2, Put: 1, Active: 1}
if s != e {
t.Fatal(s)
}
})
t.Run("get pooled foreign", func(t *testing.T) {
alloc := func() ([]byte, error) { return make([]byte, 1<<9), nil }
p := pool.Make(alloc, nil, pool.Options{Algo: pool.NoShrink()})
p.Put(make([]byte, 1<<6))
b, err := p.Get()
if err != nil {
t.Fatal(err)
}
if len(b) != 1<<6 {
t.Fatal(len(b))
}
s := p.Stats()
e := pool.Stats{Get: 1, Put: 1, Active: 1}
if s != e {
t.Fatal(s)
}
})
t.Run("get own alloc not available", func(t *testing.T) {
p := pool.Make[[]byte](nil, nil, pool.Options{Algo: pool.NoShrink()})
_, err := p.Get()
if !errors.Is(err, pool.ErrEmpty) {
t.Fatal(err)
}
s := p.Stats()
e := pool.Stats{Alloc: 1, Get: 1}
if s != e {
t.Fatal(s)
}
})
t.Run("get foreign no alloc", func(t *testing.T) {
p := pool.Make[[]byte](nil, nil, pool.Options{Algo: pool.NoShrink()})
p.Put(make([]byte, 1<<6))
b, err := p.Get()
if err != nil {
t.Fatal(err)
}
if len(b) != 1<<6 {
t.Fatal(len(b))
}
s := p.Stats()
e := pool.Stats{Get: 1, Put: 1, Active: 1}
if s != e {
t.Fatal(s)
}
})
t.Run("allocation error", func(t *testing.T) {
alloc := func() ([]byte, error) { return nil, errTest }
p := pool.Make(alloc, nil, pool.Options{Algo: pool.NoShrink()})
_, err := p.Get()
if !errors.Is(err, errTest) {
t.Fatal(err)
}
})
t.Run("put foreign not empty", func(t *testing.T) {
p := pool.Make[[]byte](nil, nil, pool.Options{Algo: pool.NoShrink()})
p.Put(make([]byte, 1<<6))
p.Put(make([]byte, 1<<6))
b, err := p.Get()
if err != nil {
t.Fatal(err)
}
if len(b) != 1<<6 {
t.Fatal(len(b))
}
s := p.Stats()
e := pool.Stats{Get: 1, Put: 2, Active: 1, Idle: 1}
if s != e {
t.Fatal(s)
}
})
2026-03-14 19:03:18 +01:00
t.Run("load items", func(t *testing.T) {
p := pool.Make[[]byte](nil, nil, pool.Options{Algo: pool.NoShrink()})
l := make([][]byte, 9)
for i := 0; i < len(l); i++ {
l[i] = make([]byte, 1<<9)
}
p.Load(l)
s := p.Stats()
if s.Idle != 9 {
t.Fatal(s)
}
})
2026-03-05 12:34:35 +01:00
t.Run("release on put no free", func(t *testing.T) {
p := pool.Make[[]byte](nil, nil, pool.Options{Algo: pool.Max(2)})
p.Put(make([]byte, 1<<9))
p.Put(make([]byte, 1<<9))
p.Put(make([]byte, 1<<9))
s := p.Stats()
e := pool.Stats{Put: 3, Idle: 2, Free: 1}
if s != e {
t.Fatal(s)
}
})
t.Run("release on put with free", func(t *testing.T) {
var freeCount int
f := func([]byte) { freeCount++ }
p := pool.Make(nil, f, pool.Options{Algo: pool.Max(2)})
p.Put(make([]byte, 1<<9))
p.Put(make([]byte, 1<<9))
p.Put(make([]byte, 1<<9))
if freeCount != 1 {
t.Fatal(freeCount)
}
s := p.Stats()
e := pool.Stats{Put: 3, Idle: 2, Free: 1}
if s != e {
t.Fatal(s)
}
})
t.Run("release all no free", func(t *testing.T) {
p := pool.Make[[]byte](nil, nil, pool.Options{Algo: pool.NoShrink()})
p.Put(make([]byte, 1<<9))
p.Put(make([]byte, 1<<9))
p.Put(make([]byte, 1<<9))
p.Free()
s := p.Stats()
e := pool.Stats{Put: 3, Idle: 0, Free: 3}
if s != e {
t.Fatal(s)
}
})
t.Run("release all with free", func(t *testing.T) {
var freeCount int
f := func([]byte) { freeCount++ }
p := pool.Make[[]byte](nil, f, pool.Options{Algo: pool.NoShrink()})
p.Put(make([]byte, 1<<9))
p.Put(make([]byte, 1<<9))
p.Put(make([]byte, 1<<9))
p.Free()
if freeCount != 3 {
t.Fatal(freeCount)
}
s := p.Stats()
e := pool.Stats{Put: 3, Idle: 0, Free: 3}
if s != e {
t.Fatal(s)
}
})
t.Run("release all when empty", func(t *testing.T) {
p := pool.Make[[]byte](nil, nil, pool.Options{Algo: pool.NoShrink()})
p.Free()
s := p.Stats()
var e pool.Stats
if s != e {
t.Fatal(s)
}
})
t.Run("release on timeout no free", func(t *testing.T) {
c := times.Test()
b := syncbus.New(time.Second)
o := pool.Options{
Algo: pool.Timeout(3 * time.Millisecond),
Clock: c,
TestBus: b,
}
p := pool.Make[[]byte](nil, nil, o)
p.Put(make([]byte, 1<<9))
2026-03-14 19:03:18 +01:00
if err := b.Wait("background-job-waiting"); err != nil {
2026-03-05 12:34:35 +01:00
t.Fatal(err)
}
c.Pass(2 * time.Millisecond)
p.Put(make([]byte, 1<<9))
2026-03-14 19:03:18 +01:00
c.Pass(2 * time.Millisecond)
if err := b.Wait("free-idle-done"); err != nil {
2026-03-05 12:34:35 +01:00
t.Fatal(err)
}
s := p.Stats()
e := pool.Stats{Put: 2, Idle: 1, Free: 1}
2026-03-14 19:03:18 +01:00
if s != e {
t.Fatal(s)
2026-03-05 12:34:35 +01:00
}
})
t.Run("release on timeout with free", func(t *testing.T) {
var freeCount int
f := func([]byte) { freeCount++ }
c := times.Test()
b := syncbus.New(time.Second)
o := pool.Options{
Algo: pool.Timeout(3 * time.Millisecond),
Clock: c,
TestBus: b,
}
p := pool.Make[[]byte](nil, f, o)
p.Put(make([]byte, 1<<9))
2026-03-14 19:03:18 +01:00
if err := b.Wait("background-job-waiting"); err != nil {
2026-03-05 12:34:35 +01:00
t.Fatal(err)
}
c.Pass(2 * time.Millisecond)
p.Put(make([]byte, 1<<9))
2026-03-14 19:03:18 +01:00
c.Pass(2 * time.Millisecond)
if err := b.Wait("free-idle-done"); err != nil {
2026-03-05 12:34:35 +01:00
t.Fatal(err)
}
s := p.Stats()
e := pool.Stats{Put: 2, Idle: 1, Free: 1}
2026-03-14 19:03:18 +01:00
if s != e || freeCount != 1 {
t.Fatal(s, freeCount)
2026-03-05 12:34:35 +01:00
}
})
t.Run("use default algo", func(t *testing.T) {
alloc := func() ([]byte, error) { return make([]byte, 1<<9), nil }
p := pool.Make(alloc, nil, pool.Options{})
var bs [][]byte
for i := 0; i < 9; i++ {
b, err := p.Get()
if err != nil {
t.Fatal(err)
}
bs = append(bs, b)
}
for _, b := range bs[:2*len(bs)/3] {
p.Put(b)
}
s := p.Stats()
2026-03-14 19:03:18 +01:00
e := pool.Stats{Alloc: 9, Get: 9, Put: 6, Active: 3, Idle: 6, Free: 0}
2026-03-05 12:34:35 +01:00
if s != e {
t.Fatal(s)
}
})
2026-03-03 19:50:09 +01:00
}