Ai
1 Star 0 Fork 0

studvc/gopher-lua

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
state_test.go 16.91 KB
一键复制 编辑 原始数据 按行查看 历史
Qiaosen Huang 提交于 2020-06-30 21:53 +08:00 . add IsClosed to UnitTest
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705
package lua
import (
"context"
"strings"
"testing"
"time"
)
func TestLStateIsClosed(t *testing.T) {
L := NewState()
L.Close()
errorIfNotEqual(t, true, L.IsClosed())
}
func TestCallStackOverflowWhenFixed(t *testing.T) {
L := NewState(Options{
CallStackSize: 3,
})
defer L.Close()
// expect fixed stack implementation by default (for backwards compatibility)
stack := L.stack
if _, ok := stack.(*fixedCallFrameStack); !ok {
t.Errorf("expected fixed callframe stack by default")
}
errorIfScriptNotFail(t, L, `
local function recurse(count)
if count > 0 then
recurse(count - 1)
end
end
local function c()
print(_printregs())
recurse(9)
end
c()
`, "stack overflow")
}
func TestCallStackOverflowWhenAutoGrow(t *testing.T) {
L := NewState(Options{
CallStackSize: 3,
MinimizeStackMemory: true,
})
defer L.Close()
// expect auto growing stack implementation when MinimizeStackMemory is set
stack := L.stack
if _, ok := stack.(*autoGrowingCallFrameStack); !ok {
t.Errorf("expected fixed callframe stack by default")
}
errorIfScriptNotFail(t, L, `
local function recurse(count)
if count > 0 then
recurse(count - 1)
end
end
local function c()
print(_printregs())
recurse(9)
end
c()
`, "stack overflow")
}
func TestSkipOpenLibs(t *testing.T) {
L := NewState(Options{SkipOpenLibs: true})
defer L.Close()
errorIfScriptNotFail(t, L, `print("")`,
"attempt to call a non-function object")
L2 := NewState()
defer L2.Close()
errorIfScriptFail(t, L2, `print("")`)
}
func TestGetAndReplace(t *testing.T) {
L := NewState()
defer L.Close()
L.Push(LString("a"))
L.Replace(1, LString("b"))
L.Replace(0, LString("c"))
errorIfNotEqual(t, LNil, L.Get(0))
errorIfNotEqual(t, LNil, L.Get(-10))
errorIfNotEqual(t, L.Env, L.Get(EnvironIndex))
errorIfNotEqual(t, LString("b"), L.Get(1))
L.Push(LString("c"))
L.Push(LString("d"))
L.Replace(-2, LString("e"))
errorIfNotEqual(t, LString("e"), L.Get(-2))
registry := L.NewTable()
L.Replace(RegistryIndex, registry)
L.G.Registry = registry
errorIfGFuncNotFail(t, L, func(L *LState) int {
L.Replace(RegistryIndex, LNil)
return 0
}, "registry must be a table")
errorIfGFuncFail(t, L, func(L *LState) int {
env := L.NewTable()
L.Replace(EnvironIndex, env)
errorIfNotEqual(t, env, L.Get(EnvironIndex))
return 0
})
errorIfGFuncNotFail(t, L, func(L *LState) int {
L.Replace(EnvironIndex, LNil)
return 0
}, "environment must be a table")
errorIfGFuncFail(t, L, func(L *LState) int {
gbl := L.NewTable()
L.Replace(GlobalsIndex, gbl)
errorIfNotEqual(t, gbl, L.G.Global)
return 0
})
errorIfGFuncNotFail(t, L, func(L *LState) int {
L.Replace(GlobalsIndex, LNil)
return 0
}, "_G must be a table")
L2 := NewState()
defer L2.Close()
clo := L2.NewClosure(func(L2 *LState) int {
L2.Replace(UpvalueIndex(1), LNumber(3))
errorIfNotEqual(t, LNumber(3), L2.Get(UpvalueIndex(1)))
return 0
}, LNumber(1), LNumber(2))
L2.SetGlobal("clo", clo)
errorIfScriptFail(t, L2, `clo()`)
}
func TestRemove(t *testing.T) {
L := NewState()
defer L.Close()
L.Push(LString("a"))
L.Push(LString("b"))
L.Push(LString("c"))
L.Remove(4)
errorIfNotEqual(t, LString("a"), L.Get(1))
errorIfNotEqual(t, LString("b"), L.Get(2))
errorIfNotEqual(t, LString("c"), L.Get(3))
errorIfNotEqual(t, 3, L.GetTop())
L.Remove(3)
errorIfNotEqual(t, LString("a"), L.Get(1))
errorIfNotEqual(t, LString("b"), L.Get(2))
errorIfNotEqual(t, LNil, L.Get(3))
errorIfNotEqual(t, 2, L.GetTop())
L.Push(LString("c"))
L.Remove(-10)
errorIfNotEqual(t, LString("a"), L.Get(1))
errorIfNotEqual(t, LString("b"), L.Get(2))
errorIfNotEqual(t, LString("c"), L.Get(3))
errorIfNotEqual(t, 3, L.GetTop())
L.Remove(2)
errorIfNotEqual(t, LString("a"), L.Get(1))
errorIfNotEqual(t, LString("c"), L.Get(2))
errorIfNotEqual(t, LNil, L.Get(3))
errorIfNotEqual(t, 2, L.GetTop())
}
func TestToInt(t *testing.T) {
L := NewState()
defer L.Close()
L.Push(LNumber(10))
L.Push(LString("99.9"))
L.Push(L.NewTable())
errorIfNotEqual(t, 10, L.ToInt(1))
errorIfNotEqual(t, 99, L.ToInt(2))
errorIfNotEqual(t, 0, L.ToInt(3))
}
func TestToInt64(t *testing.T) {
L := NewState()
defer L.Close()
L.Push(LNumber(10))
L.Push(LString("99.9"))
L.Push(L.NewTable())
errorIfNotEqual(t, int64(10), L.ToInt64(1))
errorIfNotEqual(t, int64(99), L.ToInt64(2))
errorIfNotEqual(t, int64(0), L.ToInt64(3))
}
func TestToNumber(t *testing.T) {
L := NewState()
defer L.Close()
L.Push(LNumber(10))
L.Push(LString("99.9"))
L.Push(L.NewTable())
errorIfNotEqual(t, LNumber(10), L.ToNumber(1))
errorIfNotEqual(t, LNumber(99.9), L.ToNumber(2))
errorIfNotEqual(t, LNumber(0), L.ToNumber(3))
}
func TestToString(t *testing.T) {
L := NewState()
defer L.Close()
L.Push(LNumber(10))
L.Push(LString("99.9"))
L.Push(L.NewTable())
errorIfNotEqual(t, "10", L.ToString(1))
errorIfNotEqual(t, "99.9", L.ToString(2))
errorIfNotEqual(t, "", L.ToString(3))
}
func TestToTable(t *testing.T) {
L := NewState()
defer L.Close()
L.Push(LNumber(10))
L.Push(LString("99.9"))
L.Push(L.NewTable())
errorIfFalse(t, L.ToTable(1) == nil, "index 1 must be nil")
errorIfFalse(t, L.ToTable(2) == nil, "index 2 must be nil")
errorIfNotEqual(t, L.Get(3), L.ToTable(3))
}
func TestToFunction(t *testing.T) {
L := NewState()
defer L.Close()
L.Push(LNumber(10))
L.Push(LString("99.9"))
L.Push(L.NewFunction(func(L *LState) int { return 0 }))
errorIfFalse(t, L.ToFunction(1) == nil, "index 1 must be nil")
errorIfFalse(t, L.ToFunction(2) == nil, "index 2 must be nil")
errorIfNotEqual(t, L.Get(3), L.ToFunction(3))
}
func TestToUserData(t *testing.T) {
L := NewState()
defer L.Close()
L.Push(LNumber(10))
L.Push(LString("99.9"))
L.Push(L.NewUserData())
errorIfFalse(t, L.ToUserData(1) == nil, "index 1 must be nil")
errorIfFalse(t, L.ToUserData(2) == nil, "index 2 must be nil")
errorIfNotEqual(t, L.Get(3), L.ToUserData(3))
}
func TestToChannel(t *testing.T) {
L := NewState()
defer L.Close()
L.Push(LNumber(10))
L.Push(LString("99.9"))
var ch chan LValue
L.Push(LChannel(ch))
errorIfFalse(t, L.ToChannel(1) == nil, "index 1 must be nil")
errorIfFalse(t, L.ToChannel(2) == nil, "index 2 must be nil")
errorIfNotEqual(t, ch, L.ToChannel(3))
}
func TestObjLen(t *testing.T) {
L := NewState()
defer L.Close()
errorIfNotEqual(t, 3, L.ObjLen(LString("abc")))
tbl := L.NewTable()
tbl.Append(LTrue)
tbl.Append(LTrue)
errorIfNotEqual(t, 2, L.ObjLen(tbl))
mt := L.NewTable()
L.SetField(mt, "__len", L.NewFunction(func(L *LState) int {
tbl := L.CheckTable(1)
L.Push(LNumber(tbl.Len() + 1))
return 1
}))
L.SetMetatable(tbl, mt)
errorIfNotEqual(t, 3, L.ObjLen(tbl))
errorIfNotEqual(t, 0, L.ObjLen(LNumber(10)))
}
func TestConcat(t *testing.T) {
L := NewState()
defer L.Close()
errorIfNotEqual(t, "a1c", L.Concat(LString("a"), LNumber(1), LString("c")))
}
func TestPCall(t *testing.T) {
L := NewState()
defer L.Close()
L.Register("f1", func(L *LState) int {
panic("panic!")
return 0
})
errorIfScriptNotFail(t, L, `f1()`, "panic!")
L.Push(L.GetGlobal("f1"))
err := L.PCall(0, 0, L.NewFunction(func(L *LState) int {
L.Push(LString("by handler"))
return 1
}))
errorIfFalse(t, strings.Contains(err.Error(), "by handler"), "")
err = L.PCall(0, 0, L.NewFunction(func(L *LState) int {
L.RaiseError("error!")
return 1
}))
errorIfFalse(t, strings.Contains(err.Error(), "error!"), "")
err = L.PCall(0, 0, L.NewFunction(func(L *LState) int {
panic("panicc!")
return 1
}))
errorIfFalse(t, strings.Contains(err.Error(), "panicc!"), "")
}
func TestCoroutineApi1(t *testing.T) {
L := NewState()
defer L.Close()
co, _ := L.NewThread()
errorIfScriptFail(t, L, `
function coro(v)
assert(v == 10)
local ret1, ret2 = coroutine.yield(1,2,3)
assert(ret1 == 11)
assert(ret2 == 12)
coroutine.yield(4)
return 5
end
`)
fn := L.GetGlobal("coro").(*LFunction)
st, err, values := L.Resume(co, fn, LNumber(10))
errorIfNotEqual(t, ResumeYield, st)
errorIfNotNil(t, err)
errorIfNotEqual(t, 3, len(values))
errorIfNotEqual(t, LNumber(1), values[0].(LNumber))
errorIfNotEqual(t, LNumber(2), values[1].(LNumber))
errorIfNotEqual(t, LNumber(3), values[2].(LNumber))
st, err, values = L.Resume(co, fn, LNumber(11), LNumber(12))
errorIfNotEqual(t, ResumeYield, st)
errorIfNotNil(t, err)
errorIfNotEqual(t, 1, len(values))
errorIfNotEqual(t, LNumber(4), values[0].(LNumber))
st, err, values = L.Resume(co, fn)
errorIfNotEqual(t, ResumeOK, st)
errorIfNotNil(t, err)
errorIfNotEqual(t, 1, len(values))
errorIfNotEqual(t, LNumber(5), values[0].(LNumber))
L.Register("myyield", func(L *LState) int {
return L.Yield(L.ToNumber(1))
})
errorIfScriptFail(t, L, `
function coro_error()
coroutine.yield(1,2,3)
myyield(4)
assert(false, "--failed--")
end
`)
fn = L.GetGlobal("coro_error").(*LFunction)
co, _ = L.NewThread()
st, err, values = L.Resume(co, fn)
errorIfNotEqual(t, ResumeYield, st)
errorIfNotNil(t, err)
errorIfNotEqual(t, 3, len(values))
errorIfNotEqual(t, LNumber(1), values[0].(LNumber))
errorIfNotEqual(t, LNumber(2), values[1].(LNumber))
errorIfNotEqual(t, LNumber(3), values[2].(LNumber))
st, err, values = L.Resume(co, fn)
errorIfNotEqual(t, ResumeYield, st)
errorIfNotNil(t, err)
errorIfNotEqual(t, 1, len(values))
errorIfNotEqual(t, LNumber(4), values[0].(LNumber))
st, err, values = L.Resume(co, fn)
errorIfNotEqual(t, ResumeError, st)
errorIfNil(t, err)
errorIfFalse(t, strings.Contains(err.Error(), "--failed--"), "error message must be '--failed--'")
st, err, values = L.Resume(co, fn)
errorIfNotEqual(t, ResumeError, st)
errorIfNil(t, err)
errorIfFalse(t, strings.Contains(err.Error(), "can not resume a dead thread"), "can not resume a dead thread")
}
func TestContextTimeout(t *testing.T) {
L := NewState()
defer L.Close()
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
L.SetContext(ctx)
errorIfNotEqual(t, ctx, L.Context())
err := L.DoString(`
local clock = os.clock
function sleep(n) -- seconds
local t0 = clock()
while clock() - t0 <= n do end
end
sleep(3)
`)
errorIfNil(t, err)
errorIfFalse(t, strings.Contains(err.Error(), "context deadline exceeded"), "execution must be canceled")
oldctx := L.RemoveContext()
errorIfNotEqual(t, ctx, oldctx)
errorIfNotNil(t, L.ctx)
}
func TestContextCancel(t *testing.T) {
L := NewState()
defer L.Close()
ctx, cancel := context.WithCancel(context.Background())
errch := make(chan error, 1)
L.SetContext(ctx)
go func() {
errch <- L.DoString(`
local clock = os.clock
function sleep(n) -- seconds
local t0 = clock()
while clock() - t0 <= n do end
end
sleep(3)
`)
}()
time.Sleep(1 * time.Second)
cancel()
err := <-errch
errorIfNil(t, err)
errorIfFalse(t, strings.Contains(err.Error(), "context canceled"), "execution must be canceled")
}
func TestContextWithCroutine(t *testing.T) {
L := NewState()
defer L.Close()
ctx, cancel := context.WithCancel(context.Background())
L.SetContext(ctx)
defer cancel()
L.DoString(`
function coro()
local i = 0
while true do
coroutine.yield(i)
i = i+1
end
return i
end
`)
co, cocancel := L.NewThread()
defer cocancel()
fn := L.GetGlobal("coro").(*LFunction)
_, err, values := L.Resume(co, fn)
errorIfNotNil(t, err)
errorIfNotEqual(t, LNumber(0), values[0])
// cancel the parent context
cancel()
_, err, values = L.Resume(co, fn)
errorIfNil(t, err)
errorIfFalse(t, strings.Contains(err.Error(), "context canceled"), "coroutine execution must be canceled when the parent context is canceled")
}
func TestPCallAfterFail(t *testing.T) {
L := NewState()
defer L.Close()
errFn := L.NewFunction(func(L *LState) int {
L.RaiseError("error!")
return 0
})
changeError := L.NewFunction(func(L *LState) int {
L.Push(errFn)
err := L.PCall(0, 0, nil)
if err != nil {
L.RaiseError("A New Error")
}
return 0
})
L.Push(changeError)
err := L.PCall(0, 0, nil)
errorIfFalse(t, strings.Contains(err.Error(), "A New Error"), "error not propogated correctly")
}
func TestRegistryFixedOverflow(t *testing.T) {
state := NewState()
defer state.Close()
reg := state.reg
expectedPanic := false
// should be non auto grow by default
errorIfFalse(t, reg.maxSize == 0, "state should default to non-auto growing implementation")
// fill the stack and check we get a panic
test := LString("test")
for i := 0; i < len(reg.array); i++ {
reg.Push(test)
}
defer func() {
rcv := recover()
if rcv != nil {
if expectedPanic {
errorIfFalse(t, rcv.(error).Error() != "registry overflow", "expected registry overflow exception, got "+rcv.(error).Error())
} else {
t.Errorf("did not expect registry overflow")
}
} else if expectedPanic {
t.Errorf("expected registry overflow exception, but didn't get panic")
}
}()
expectedPanic = true
reg.Push(test)
}
func TestRegistryAutoGrow(t *testing.T) {
state := NewState(Options{RegistryMaxSize: 300, RegistrySize: 200, RegistryGrowStep: 25})
defer state.Close()
expectedPanic := false
defer func() {
rcv := recover()
if rcv != nil {
if expectedPanic {
errorIfFalse(t, rcv.(error).Error() != "registry overflow", "expected registry overflow exception, got "+rcv.(error).Error())
} else {
t.Errorf("did not expect registry overflow")
}
} else if expectedPanic {
t.Errorf("expected registry overflow exception, but didn't get panic")
}
}()
reg := state.reg
test := LString("test")
for i := 0; i < 300; i++ {
reg.Push(test)
}
expectedPanic = true
reg.Push(test)
}
func BenchmarkCallFrameStackPushPopAutoGrow(t *testing.B) {
stack := newAutoGrowingCallFrameStack(256)
t.ResetTimer()
const Iterations = 256
for j := 0; j < t.N; j++ {
for i := 0; i < Iterations; i++ {
stack.Push(callFrame{})
}
for i := 0; i < Iterations; i++ {
stack.Pop()
}
}
}
func BenchmarkCallFrameStackPushPopFixed(t *testing.B) {
stack := newFixedCallFrameStack(256)
t.ResetTimer()
const Iterations = 256
for j := 0; j < t.N; j++ {
for i := 0; i < Iterations; i++ {
stack.Push(callFrame{})
}
for i := 0; i < Iterations; i++ {
stack.Pop()
}
}
}
// this test will intentionally not incur stack growth in order to bench the performance when no allocations happen
func BenchmarkCallFrameStackPushPopShallowAutoGrow(t *testing.B) {
stack := newAutoGrowingCallFrameStack(256)
t.ResetTimer()
const Iterations = 8
for j := 0; j < t.N; j++ {
for i := 0; i < Iterations; i++ {
stack.Push(callFrame{})
}
for i := 0; i < Iterations; i++ {
stack.Pop()
}
}
}
func BenchmarkCallFrameStackPushPopShallowFixed(t *testing.B) {
stack := newFixedCallFrameStack(256)
t.ResetTimer()
const Iterations = 8
for j := 0; j < t.N; j++ {
for i := 0; i < Iterations; i++ {
stack.Push(callFrame{})
}
for i := 0; i < Iterations; i++ {
stack.Pop()
}
}
}
func BenchmarkCallFrameStackPushPopFixedNoInterface(t *testing.B) {
stack := newFixedCallFrameStack(256).(*fixedCallFrameStack)
t.ResetTimer()
const Iterations = 256
for j := 0; j < t.N; j++ {
for i := 0; i < Iterations; i++ {
stack.Push(callFrame{})
}
for i := 0; i < Iterations; i++ {
stack.Pop()
}
}
}
func BenchmarkCallFrameStackUnwindAutoGrow(t *testing.B) {
stack := newAutoGrowingCallFrameStack(256)
t.ResetTimer()
const Iterations = 256
for j := 0; j < t.N; j++ {
for i := 0; i < Iterations; i++ {
stack.Push(callFrame{})
}
stack.SetSp(0)
}
}
func BenchmarkCallFrameStackUnwindFixed(t *testing.B) {
stack := newFixedCallFrameStack(256)
t.ResetTimer()
const Iterations = 256
for j := 0; j < t.N; j++ {
for i := 0; i < Iterations; i++ {
stack.Push(callFrame{})
}
stack.SetSp(0)
}
}
func BenchmarkCallFrameStackUnwindFixedNoInterface(t *testing.B) {
stack := newFixedCallFrameStack(256).(*fixedCallFrameStack)
t.ResetTimer()
const Iterations = 256
for j := 0; j < t.N; j++ {
for i := 0; i < Iterations; i++ {
stack.Push(callFrame{})
}
stack.SetSp(0)
}
}
type registryTestHandler int
func (registryTestHandler) registryOverflow() {
panic("registry overflow")
}
// test pushing and popping from the registry
func BenchmarkRegistryPushPopAutoGrow(t *testing.B) {
al := newAllocator(32)
sz := 256 * 20
reg := newRegistry(registryTestHandler(0), sz/2, 64, sz, al)
value := LString("test")
t.ResetTimer()
for j := 0; j < t.N; j++ {
for i := 0; i < sz; i++ {
reg.Push(value)
}
for i := 0; i < sz; i++ {
reg.Pop()
}
}
}
func BenchmarkRegistryPushPopFixed(t *testing.B) {
al := newAllocator(32)
sz := 256 * 20
reg := newRegistry(registryTestHandler(0), sz, 0, sz, al)
value := LString("test")
t.ResetTimer()
for j := 0; j < t.N; j++ {
for i := 0; i < sz; i++ {
reg.Push(value)
}
for i := 0; i < sz; i++ {
reg.Pop()
}
}
}
func BenchmarkRegistrySetTop(t *testing.B) {
al := newAllocator(32)
sz := 256 * 20
reg := newRegistry(registryTestHandler(0), sz, 32, sz*2, al)
t.ResetTimer()
for j := 0; j < t.N; j++ {
reg.SetTop(sz)
reg.SetTop(0)
}
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/studvc/gopher-lua.git
git@gitee.com:studvc/gopher-lua.git
studvc
gopher-lua
gopher-lua
master

搜索帮助