Files
Xray-core/common/lua/pool_test.go
T
2026-09-26 06:16:54 +08:00

198 lines
4.4 KiB
Go

package lua
import (
"context"
"errors"
"testing"
"time"
glua "github.com/yuin/gopher-lua"
)
func TestPoolFactoryFailureClosesReturnedState(t *testing.T) {
failure := errors.New("factory failed")
state := glua.NewState()
_, err := NewPool(context.Background(), func(context.Context) (*glua.LState, error) {
return state, failure
})
if !errors.Is(err, failure) || !state.IsClosed() {
t.Fatalf("NewPool error = %v, state closed = %t", err, state.IsClosed())
}
var failedState *glua.LState
calls := 0
pool, err := NewPool(context.Background(), func(context.Context) (*glua.LState, error) {
calls++
if calls == 1 {
return glua.NewState(), nil
}
failedState = glua.NewState()
return failedState, failure
})
if err != nil {
t.Fatal(err)
}
defer pool.Close()
borrowed, err := pool.Acquire()
if err != nil {
t.Fatal(err)
}
defer pool.Release(borrowed, true)
_, err = pool.Acquire()
if !errors.Is(err, failure) || !failedState.IsClosed() {
t.Fatalf("Acquire error = %v, state closed = %t", err, failedState.IsClosed())
}
}
func TestPoolReusesStatesAndLimitsIdle(t *testing.T) {
created := 0
pool, err := NewPool(context.Background(), func(context.Context) (*glua.LState, error) {
created++
return glua.NewState(), nil
})
if err != nil {
t.Fatal(err)
}
defer pool.Close()
states := make([]*glua.LState, maxIdleStates+3)
for i := range states {
states[i], err = pool.Acquire()
if err != nil {
t.Fatal(err)
}
}
for _, state := range states {
pool.Release(state, true)
}
for i, state := range states {
if got, want := state.IsClosed(), i >= maxIdleStates; got != want {
t.Fatalf("state %d closed = %t, want %t", i, got, want)
}
}
borrowed, err := pool.Acquire()
if err != nil {
t.Fatal(err)
}
if created != len(states) {
t.Fatalf("Acquire created %d states, want %d", created, len(states))
}
pool.Release(borrowed, true)
}
func TestPoolCloseCancelsAndWaitsForBorrowedState(t *testing.T) {
pool, err := NewPool(context.Background(), func(context.Context) (*glua.LState, error) {
return glua.NewState(), nil
})
if err != nil {
t.Fatal(err)
}
state, err := pool.Acquire()
if err != nil {
t.Fatal(err)
}
done := make(chan struct{})
go func() {
pool.Close()
close(done)
}()
select {
case <-pool.Context().Done():
case <-time.After(time.Second):
pool.Release(state, false)
t.Fatal("Close did not cancel the pool context")
}
select {
case <-done:
pool.Release(state, false)
t.Fatal("Close returned while a state was borrowed")
default:
}
pool.Release(state, true)
select {
case <-done:
case <-time.After(time.Second):
t.Fatal("Close did not finish after Release")
}
if !state.IsClosed() {
t.Fatal("borrowed state was not closed")
}
if _, err := pool.Acquire(); !errors.Is(err, context.Canceled) {
t.Fatalf("Acquire after Close = %v, want context.Canceled", err)
}
pool.Close()
}
func TestPoolCloseCancelsStateCreation(t *testing.T) {
started := make(chan struct{})
first := true
pool, err := NewPool(context.Background(), func(ctx context.Context) (*glua.LState, error) {
if first {
first = false
return glua.NewState(), nil
}
close(started)
<-ctx.Done()
return nil, ctx.Err()
})
if err != nil {
t.Fatal(err)
}
borrowed, err := pool.Acquire()
if err != nil {
t.Fatal(err)
}
acquireDone := make(chan error, 1)
go func() {
_, err := pool.Acquire()
acquireDone <- err
}()
<-started
closeDone := make(chan struct{})
go func() {
pool.Close()
close(closeDone)
}()
select {
case err := <-acquireDone:
if !errors.Is(err, context.Canceled) {
t.Fatalf("Acquire during Close = %v, want context.Canceled", err)
}
case <-time.After(time.Second):
t.Fatal("state creation did not stop after Close")
}
select {
case <-closeDone:
pool.Release(borrowed, false)
t.Fatal("Close returned while the initial state was borrowed")
default:
}
pool.Release(borrowed, true)
select {
case <-closeDone:
case <-time.After(time.Second):
t.Fatal("Close did not finish after Release")
}
}
func BenchmarkPoolAcquireRelease(b *testing.B) {
pool, err := NewPool(context.Background(), func(context.Context) (*glua.LState, error) {
return glua.NewState(), nil
})
if err != nil {
b.Fatal(err)
}
defer pool.Close()
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
state, err := pool.Acquire()
if err != nil {
b.Fatal(err)
}
pool.Release(state, true)
}
b.StopTimer()
}