mirror of
https://github.com/XTLS/Xray-core.git
synced 2026-09-30 21:16:03 +00:00
198 lines
4.4 KiB
Go
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()
|
|
}
|