github.com/qiniu/gopher-lua@v0.2017.11/state_test.go (about) 1 package lua 2 3 import ( 4 "context" 5 "strings" 6 "testing" 7 "time" 8 ) 9 10 func TestCallStackOverflow(t *testing.T) { 11 L := NewState(Options{ 12 CallStackSize: 3, 13 }) 14 defer L.Close() 15 errorIfScriptNotFail(t, L, ` 16 local function a() 17 end 18 local function b() 19 a() 20 end 21 local function c() 22 print(_printregs()) 23 b() 24 end 25 c() 26 `, "stack overflow") 27 } 28 29 func TestSkipOpenLibs(t *testing.T) { 30 L := NewState(Options{SkipOpenLibs: true}) 31 defer L.Close() 32 errorIfScriptNotFail(t, L, `print("")`, 33 "attempt to call a non-function object") 34 L2 := NewState() 35 defer L2.Close() 36 errorIfScriptFail(t, L2, `print("")`) 37 } 38 39 func TestGetAndReplace(t *testing.T) { 40 L := NewState() 41 defer L.Close() 42 L.Push(LString("a")) 43 L.Replace(1, LString("b")) 44 L.Replace(0, LString("c")) 45 errorIfNotEqual(t, LNil, L.Get(0)) 46 errorIfNotEqual(t, LNil, L.Get(-10)) 47 errorIfNotEqual(t, L.Env, L.Get(EnvironIndex)) 48 errorIfNotEqual(t, LString("b"), L.Get(1)) 49 L.Push(LString("c")) 50 L.Push(LString("d")) 51 L.Replace(-2, LString("e")) 52 errorIfNotEqual(t, LString("e"), L.Get(-2)) 53 registry := L.NewTable() 54 L.Replace(RegistryIndex, registry) 55 L.G.Registry = registry 56 errorIfGFuncNotFail(t, L, func(L *LState) int { 57 L.Replace(RegistryIndex, LNil) 58 return 0 59 }, "registry must be a table") 60 errorIfGFuncFail(t, L, func(L *LState) int { 61 env := L.NewTable() 62 L.Replace(EnvironIndex, env) 63 errorIfNotEqual(t, env, L.Get(EnvironIndex)) 64 return 0 65 }) 66 errorIfGFuncNotFail(t, L, func(L *LState) int { 67 L.Replace(EnvironIndex, LNil) 68 return 0 69 }, "environment must be a table") 70 errorIfGFuncFail(t, L, func(L *LState) int { 71 gbl := L.NewTable() 72 L.Replace(GlobalsIndex, gbl) 73 errorIfNotEqual(t, gbl, L.G.Global) 74 return 0 75 }) 76 errorIfGFuncNotFail(t, L, func(L *LState) int { 77 L.Replace(GlobalsIndex, LNil) 78 return 0 79 }, "_G must be a table") 80 81 L2 := NewState() 82 defer L2.Close() 83 clo := L2.NewClosure(func(L2 *LState) int { 84 L2.Replace(UpvalueIndex(1), LNumber(3)) 85 errorIfNotEqual(t, LNumber(3), L2.Get(UpvalueIndex(1))) 86 return 0 87 }, LNumber(1), LNumber(2)) 88 L2.SetGlobal("clo", clo) 89 errorIfScriptFail(t, L2, `clo()`) 90 } 91 92 func TestRemove(t *testing.T) { 93 L := NewState() 94 defer L.Close() 95 L.Push(LString("a")) 96 L.Push(LString("b")) 97 L.Push(LString("c")) 98 99 L.Remove(4) 100 errorIfNotEqual(t, LString("a"), L.Get(1)) 101 errorIfNotEqual(t, LString("b"), L.Get(2)) 102 errorIfNotEqual(t, LString("c"), L.Get(3)) 103 errorIfNotEqual(t, 3, L.GetTop()) 104 105 L.Remove(3) 106 errorIfNotEqual(t, LString("a"), L.Get(1)) 107 errorIfNotEqual(t, LString("b"), L.Get(2)) 108 errorIfNotEqual(t, LNil, L.Get(3)) 109 errorIfNotEqual(t, 2, L.GetTop()) 110 L.Push(LString("c")) 111 112 L.Remove(-10) 113 errorIfNotEqual(t, LString("a"), L.Get(1)) 114 errorIfNotEqual(t, LString("b"), L.Get(2)) 115 errorIfNotEqual(t, LString("c"), L.Get(3)) 116 errorIfNotEqual(t, 3, L.GetTop()) 117 118 L.Remove(2) 119 errorIfNotEqual(t, LString("a"), L.Get(1)) 120 errorIfNotEqual(t, LString("c"), L.Get(2)) 121 errorIfNotEqual(t, LNil, L.Get(3)) 122 errorIfNotEqual(t, 2, L.GetTop()) 123 } 124 125 func TestToInt(t *testing.T) { 126 L := NewState() 127 defer L.Close() 128 L.Push(LNumber(10)) 129 L.Push(LString("99.9")) 130 L.Push(L.NewTable()) 131 errorIfNotEqual(t, 10, L.ToInt(1)) 132 errorIfNotEqual(t, 99, L.ToInt(2)) 133 errorIfNotEqual(t, 0, L.ToInt(3)) 134 } 135 136 func TestToInt64(t *testing.T) { 137 L := NewState() 138 defer L.Close() 139 L.Push(LNumber(10)) 140 L.Push(LString("99.9")) 141 L.Push(L.NewTable()) 142 errorIfNotEqual(t, int64(10), L.ToInt64(1)) 143 errorIfNotEqual(t, int64(99), L.ToInt64(2)) 144 errorIfNotEqual(t, int64(0), L.ToInt64(3)) 145 } 146 147 func TestToNumber(t *testing.T) { 148 L := NewState() 149 defer L.Close() 150 L.Push(LNumber(10)) 151 L.Push(LString("99.9")) 152 L.Push(L.NewTable()) 153 errorIfNotEqual(t, LNumber(10), L.ToNumber(1)) 154 errorIfNotEqual(t, LNumber(99.9), L.ToNumber(2)) 155 errorIfNotEqual(t, LNumber(0), L.ToNumber(3)) 156 } 157 158 func TestToString(t *testing.T) { 159 L := NewState() 160 defer L.Close() 161 L.Push(LNumber(10)) 162 L.Push(LString("99.9")) 163 L.Push(L.NewTable()) 164 errorIfNotEqual(t, "10", L.ToString(1)) 165 errorIfNotEqual(t, "99.9", L.ToString(2)) 166 errorIfNotEqual(t, "", L.ToString(3)) 167 } 168 169 func TestToTable(t *testing.T) { 170 L := NewState() 171 defer L.Close() 172 L.Push(LNumber(10)) 173 L.Push(LString("99.9")) 174 L.Push(L.NewTable()) 175 errorIfFalse(t, L.ToTable(1) == nil, "index 1 must be nil") 176 errorIfFalse(t, L.ToTable(2) == nil, "index 2 must be nil") 177 errorIfNotEqual(t, L.Get(3), L.ToTable(3)) 178 } 179 180 func TestToFunction(t *testing.T) { 181 L := NewState() 182 defer L.Close() 183 L.Push(LNumber(10)) 184 L.Push(LString("99.9")) 185 L.Push(L.NewFunction(func(L *LState) int { return 0 })) 186 errorIfFalse(t, L.ToFunction(1) == nil, "index 1 must be nil") 187 errorIfFalse(t, L.ToFunction(2) == nil, "index 2 must be nil") 188 errorIfNotEqual(t, L.Get(3), L.ToFunction(3)) 189 } 190 191 func TestToUserData(t *testing.T) { 192 L := NewState() 193 defer L.Close() 194 L.Push(LNumber(10)) 195 L.Push(LString("99.9")) 196 L.Push(L.NewUserData()) 197 errorIfFalse(t, L.ToUserData(1) == nil, "index 1 must be nil") 198 errorIfFalse(t, L.ToUserData(2) == nil, "index 2 must be nil") 199 errorIfNotEqual(t, L.Get(3), L.ToUserData(3)) 200 } 201 202 func TestToChannel(t *testing.T) { 203 L := NewState() 204 defer L.Close() 205 L.Push(LNumber(10)) 206 L.Push(LString("99.9")) 207 var ch chan LValue 208 L.Push(LChannel(ch)) 209 errorIfFalse(t, L.ToChannel(1) == nil, "index 1 must be nil") 210 errorIfFalse(t, L.ToChannel(2) == nil, "index 2 must be nil") 211 errorIfNotEqual(t, ch, L.ToChannel(3)) 212 } 213 214 func TestObjLen(t *testing.T) { 215 L := NewState() 216 defer L.Close() 217 errorIfNotEqual(t, 3, L.ObjLen(LString("abc"))) 218 tbl := L.NewTable() 219 tbl.Append(LTrue) 220 tbl.Append(LTrue) 221 errorIfNotEqual(t, 2, L.ObjLen(tbl)) 222 mt := L.NewTable() 223 L.SetField(mt, "__len", L.NewFunction(func(L *LState) int { 224 tbl := L.CheckTable(1) 225 L.Push(LNumber(tbl.Len() + 1)) 226 return 1 227 })) 228 L.SetMetatable(tbl, mt) 229 errorIfNotEqual(t, 3, L.ObjLen(tbl)) 230 errorIfNotEqual(t, 0, L.ObjLen(LNumber(10))) 231 } 232 233 func TestConcat(t *testing.T) { 234 L := NewState() 235 defer L.Close() 236 errorIfNotEqual(t, "a1c", L.Concat(LString("a"), LNumber(1), LString("c"))) 237 } 238 239 func TestPCall(t *testing.T) { 240 L := NewState() 241 defer L.Close() 242 L.Register("f1", func(L *LState) int { 243 panic("panic!") 244 return 0 245 }) 246 errorIfScriptNotFail(t, L, `f1()`, "panic!") 247 L.Push(L.GetGlobal("f1")) 248 err := L.PCall(0, 0, L.NewFunction(func(L *LState) int { 249 L.Push(LString("by handler")) 250 return 1 251 })) 252 errorIfFalse(t, strings.Contains(err.Error(), "by handler"), "") 253 254 err = L.PCall(0, 0, L.NewFunction(func(L *LState) int { 255 L.RaiseError("error!") 256 return 1 257 })) 258 errorIfFalse(t, strings.Contains(err.Error(), "error!"), "") 259 260 err = L.PCall(0, 0, L.NewFunction(func(L *LState) int { 261 panic("panicc!") 262 return 1 263 })) 264 errorIfFalse(t, strings.Contains(err.Error(), "panicc!"), "") 265 } 266 267 func TestCoroutineApi1(t *testing.T) { 268 L := NewState() 269 defer L.Close() 270 co, _ := L.NewThread() 271 errorIfScriptFail(t, L, ` 272 function coro(v) 273 assert(v == 10) 274 local ret1, ret2 = coroutine.yield(1,2,3) 275 assert(ret1 == 11) 276 assert(ret2 == 12) 277 coroutine.yield(4) 278 return 5 279 end 280 `) 281 fn := L.GetGlobal("coro").(*LFunction) 282 st, err, values := L.Resume(co, fn, LNumber(10)) 283 errorIfNotEqual(t, ResumeYield, st) 284 errorIfNotNil(t, err) 285 errorIfNotEqual(t, 3, len(values)) 286 errorIfNotEqual(t, LNumber(1), values[0].(LNumber)) 287 errorIfNotEqual(t, LNumber(2), values[1].(LNumber)) 288 errorIfNotEqual(t, LNumber(3), values[2].(LNumber)) 289 290 st, err, values = L.Resume(co, fn, LNumber(11), LNumber(12)) 291 errorIfNotEqual(t, ResumeYield, st) 292 errorIfNotNil(t, err) 293 errorIfNotEqual(t, 1, len(values)) 294 errorIfNotEqual(t, LNumber(4), values[0].(LNumber)) 295 296 st, err, values = L.Resume(co, fn) 297 errorIfNotEqual(t, ResumeOK, st) 298 errorIfNotNil(t, err) 299 errorIfNotEqual(t, 1, len(values)) 300 errorIfNotEqual(t, LNumber(5), values[0].(LNumber)) 301 302 L.Register("myyield", func(L *LState) int { 303 return L.Yield(L.ToNumber(1)) 304 }) 305 errorIfScriptFail(t, L, ` 306 function coro_error() 307 coroutine.yield(1,2,3) 308 myyield(4) 309 assert(false, "--failed--") 310 end 311 `) 312 fn = L.GetGlobal("coro_error").(*LFunction) 313 co, _ = L.NewThread() 314 st, err, values = L.Resume(co, fn) 315 errorIfNotEqual(t, ResumeYield, st) 316 errorIfNotNil(t, err) 317 errorIfNotEqual(t, 3, len(values)) 318 errorIfNotEqual(t, LNumber(1), values[0].(LNumber)) 319 errorIfNotEqual(t, LNumber(2), values[1].(LNumber)) 320 errorIfNotEqual(t, LNumber(3), values[2].(LNumber)) 321 322 st, err, values = L.Resume(co, fn) 323 errorIfNotEqual(t, ResumeYield, st) 324 errorIfNotNil(t, err) 325 errorIfNotEqual(t, 1, len(values)) 326 errorIfNotEqual(t, LNumber(4), values[0].(LNumber)) 327 328 st, err, values = L.Resume(co, fn) 329 errorIfNotEqual(t, ResumeError, st) 330 errorIfNil(t, err) 331 errorIfFalse(t, strings.Contains(err.Error(), "--failed--"), "error message must be '--failed--'") 332 st, err, values = L.Resume(co, fn) 333 errorIfNotEqual(t, ResumeError, st) 334 errorIfNil(t, err) 335 errorIfFalse(t, strings.Contains(err.Error(), "can not resume a dead thread"), "can not resume a dead thread") 336 337 } 338 339 func TestContextTimeout(t *testing.T) { 340 L := NewState() 341 defer L.Close() 342 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) 343 defer cancel() 344 L.SetContext(ctx) 345 errorIfNotEqual(t, ctx, L.Context()) 346 err := L.DoString(` 347 local clock = os.clock 348 function sleep(n) -- seconds 349 local t0 = clock() 350 while clock() - t0 <= n do end 351 end 352 sleep(3) 353 `) 354 errorIfNil(t, err) 355 errorIfFalse(t, strings.Contains(err.Error(), "context deadline exceeded"), "execution must be canceled") 356 357 oldctx := L.RemoveContext() 358 errorIfNotEqual(t, ctx, oldctx) 359 errorIfNotNil(t, L.ctx) 360 } 361 362 func TestContextCancel(t *testing.T) { 363 L := NewState() 364 defer L.Close() 365 ctx, cancel := context.WithCancel(context.Background()) 366 errch := make(chan error, 1) 367 L.SetContext(ctx) 368 go func() { 369 errch <- L.DoString(` 370 local clock = os.clock 371 function sleep(n) -- seconds 372 local t0 = clock() 373 while clock() - t0 <= n do end 374 end 375 sleep(3) 376 `) 377 }() 378 time.Sleep(1 * time.Second) 379 cancel() 380 err := <-errch 381 errorIfNil(t, err) 382 errorIfFalse(t, strings.Contains(err.Error(), "context canceled"), "execution must be canceled") 383 } 384 385 func TestContextWithCroutine(t *testing.T) { 386 L := NewState() 387 defer L.Close() 388 ctx, cancel := context.WithCancel(context.Background()) 389 L.SetContext(ctx) 390 defer cancel() 391 L.DoString(` 392 function coro() 393 local i = 0 394 while true do 395 coroutine.yield(i) 396 i = i+1 397 end 398 return i 399 end 400 `) 401 co, cocancel := L.NewThread() 402 defer cocancel() 403 fn := L.GetGlobal("coro").(*LFunction) 404 _, err, values := L.Resume(co, fn) 405 errorIfNotNil(t, err) 406 errorIfNotEqual(t, LNumber(0), values[0]) 407 // cancel the parent context 408 cancel() 409 _, err, values = L.Resume(co, fn) 410 errorIfNil(t, err) 411 errorIfFalse(t, strings.Contains(err.Error(), "context canceled"), "coroutine execution must be canceled when the parent context is canceled") 412 413 } 414 415 func TestPCallAfterFail(t *testing.T) { 416 L := NewState() 417 defer L.Close() 418 errFn := L.NewFunction(func(L *LState) int { 419 L.RaiseError("error!") 420 return 0 421 }) 422 changeError := L.NewFunction(func(L *LState) int { 423 L.Push(errFn) 424 err := L.PCall(0, 0, nil) 425 if err != nil { 426 L.RaiseError("A New Error") 427 } 428 return 0 429 }) 430 L.Push(changeError) 431 err := L.PCall(0, 0, nil) 432 errorIfFalse(t, strings.Contains(err.Error(), "A New Error"), "error not propogated correctly") 433 }