diff options
Diffstat (limited to '')
| -rw-r--r-- | testes/coroutine.lua | 874 |
1 files changed, 874 insertions, 0 deletions
diff --git a/testes/coroutine.lua b/testes/coroutine.lua new file mode 100644 index 00000000..c8fc8572 --- /dev/null +++ b/testes/coroutine.lua | |||
| @@ -0,0 +1,874 @@ | |||
| 1 | -- $Id: coroutine.lua,v 1.42 2016/11/07 13:03:20 roberto Exp $ | ||
| 2 | -- See Copyright Notice in file all.lua | ||
| 3 | |||
| 4 | print "testing coroutines" | ||
| 5 | |||
| 6 | local debug = require'debug' | ||
| 7 | |||
| 8 | local f | ||
| 9 | |||
| 10 | local main, ismain = coroutine.running() | ||
| 11 | assert(type(main) == "thread" and ismain) | ||
| 12 | assert(not coroutine.resume(main)) | ||
| 13 | assert(not coroutine.isyieldable()) | ||
| 14 | assert(not pcall(coroutine.yield)) | ||
| 15 | |||
| 16 | |||
| 17 | -- trivial errors | ||
| 18 | assert(not pcall(coroutine.resume, 0)) | ||
| 19 | assert(not pcall(coroutine.status, 0)) | ||
| 20 | |||
| 21 | |||
| 22 | -- tests for multiple yield/resume arguments | ||
| 23 | |||
| 24 | local function eqtab (t1, t2) | ||
| 25 | assert(#t1 == #t2) | ||
| 26 | for i = 1, #t1 do | ||
| 27 | local v = t1[i] | ||
| 28 | assert(t2[i] == v) | ||
| 29 | end | ||
| 30 | end | ||
| 31 | |||
| 32 | _G.x = nil -- declare x | ||
| 33 | function foo (a, ...) | ||
| 34 | local x, y = coroutine.running() | ||
| 35 | assert(x == f and y == false) | ||
| 36 | -- next call should not corrupt coroutine (but must fail, | ||
| 37 | -- as it attempts to resume the running coroutine) | ||
| 38 | assert(coroutine.resume(f) == false) | ||
| 39 | assert(coroutine.status(f) == "running") | ||
| 40 | local arg = {...} | ||
| 41 | assert(coroutine.isyieldable()) | ||
| 42 | for i=1,#arg do | ||
| 43 | _G.x = {coroutine.yield(table.unpack(arg[i]))} | ||
| 44 | end | ||
| 45 | return table.unpack(a) | ||
| 46 | end | ||
| 47 | |||
| 48 | f = coroutine.create(foo) | ||
| 49 | assert(type(f) == "thread" and coroutine.status(f) == "suspended") | ||
| 50 | assert(string.find(tostring(f), "thread")) | ||
| 51 | local s,a,b,c,d | ||
| 52 | s,a,b,c,d = coroutine.resume(f, {1,2,3}, {}, {1}, {'a', 'b', 'c'}) | ||
| 53 | assert(s and a == nil and coroutine.status(f) == "suspended") | ||
| 54 | s,a,b,c,d = coroutine.resume(f) | ||
| 55 | eqtab(_G.x, {}) | ||
| 56 | assert(s and a == 1 and b == nil) | ||
| 57 | s,a,b,c,d = coroutine.resume(f, 1, 2, 3) | ||
| 58 | eqtab(_G.x, {1, 2, 3}) | ||
| 59 | assert(s and a == 'a' and b == 'b' and c == 'c' and d == nil) | ||
| 60 | s,a,b,c,d = coroutine.resume(f, "xuxu") | ||
| 61 | eqtab(_G.x, {"xuxu"}) | ||
| 62 | assert(s and a == 1 and b == 2 and c == 3 and d == nil) | ||
| 63 | assert(coroutine.status(f) == "dead") | ||
| 64 | s, a = coroutine.resume(f, "xuxu") | ||
| 65 | assert(not s and string.find(a, "dead") and coroutine.status(f) == "dead") | ||
| 66 | |||
| 67 | |||
| 68 | -- yields in tail calls | ||
| 69 | local function foo (i) return coroutine.yield(i) end | ||
| 70 | f = coroutine.wrap(function () | ||
| 71 | for i=1,10 do | ||
| 72 | assert(foo(i) == _G.x) | ||
| 73 | end | ||
| 74 | return 'a' | ||
| 75 | end) | ||
| 76 | for i=1,10 do _G.x = i; assert(f(i) == i) end | ||
| 77 | _G.x = 'xuxu'; assert(f('xuxu') == 'a') | ||
| 78 | |||
| 79 | -- recursive | ||
| 80 | function pf (n, i) | ||
| 81 | coroutine.yield(n) | ||
| 82 | pf(n*i, i+1) | ||
| 83 | end | ||
| 84 | |||
| 85 | f = coroutine.wrap(pf) | ||
| 86 | local s=1 | ||
| 87 | for i=1,10 do | ||
| 88 | assert(f(1, 1) == s) | ||
| 89 | s = s*i | ||
| 90 | end | ||
| 91 | |||
| 92 | -- sieve | ||
| 93 | function gen (n) | ||
| 94 | return coroutine.wrap(function () | ||
| 95 | for i=2,n do coroutine.yield(i) end | ||
| 96 | end) | ||
| 97 | end | ||
| 98 | |||
| 99 | |||
| 100 | function filter (p, g) | ||
| 101 | return coroutine.wrap(function () | ||
| 102 | while 1 do | ||
| 103 | local n = g() | ||
| 104 | if n == nil then return end | ||
| 105 | if math.fmod(n, p) ~= 0 then coroutine.yield(n) end | ||
| 106 | end | ||
| 107 | end) | ||
| 108 | end | ||
| 109 | |||
| 110 | local x = gen(100) | ||
| 111 | local a = {} | ||
| 112 | while 1 do | ||
| 113 | local n = x() | ||
| 114 | if n == nil then break end | ||
| 115 | table.insert(a, n) | ||
| 116 | x = filter(n, x) | ||
| 117 | end | ||
| 118 | |||
| 119 | assert(#a == 25 and a[#a] == 97) | ||
| 120 | x, a = nil | ||
| 121 | |||
| 122 | -- yielding across C boundaries | ||
| 123 | |||
| 124 | co = coroutine.wrap(function() | ||
| 125 | assert(not pcall(table.sort,{1,2,3}, coroutine.yield)) | ||
| 126 | assert(coroutine.isyieldable()) | ||
| 127 | coroutine.yield(20) | ||
| 128 | return 30 | ||
| 129 | end) | ||
| 130 | |||
| 131 | assert(co() == 20) | ||
| 132 | assert(co() == 30) | ||
| 133 | |||
| 134 | |||
| 135 | local f = function (s, i) return coroutine.yield(i) end | ||
| 136 | |||
| 137 | local f1 = coroutine.wrap(function () | ||
| 138 | return xpcall(pcall, function (...) return ... end, | ||
| 139 | function () | ||
| 140 | local s = 0 | ||
| 141 | for i in f, nil, 1 do pcall(function () s = s + i end) end | ||
| 142 | error({s}) | ||
| 143 | end) | ||
| 144 | end) | ||
| 145 | |||
| 146 | f1() | ||
| 147 | for i = 1, 10 do assert(f1(i) == i) end | ||
| 148 | local r1, r2, v = f1(nil) | ||
| 149 | assert(r1 and not r2 and v[1] == (10 + 1)*10/2) | ||
| 150 | |||
| 151 | |||
| 152 | function f (a, b) a = coroutine.yield(a); error{a + b} end | ||
| 153 | function g(x) return x[1]*2 end | ||
| 154 | |||
| 155 | co = coroutine.wrap(function () | ||
| 156 | coroutine.yield(xpcall(f, g, 10, 20)) | ||
| 157 | end) | ||
| 158 | |||
| 159 | assert(co() == 10) | ||
| 160 | r, msg = co(100) | ||
| 161 | assert(not r and msg == 240) | ||
| 162 | |||
| 163 | |||
| 164 | -- unyieldable C call | ||
| 165 | do | ||
| 166 | local function f (c) | ||
| 167 | assert(not coroutine.isyieldable()) | ||
| 168 | return c .. c | ||
| 169 | end | ||
| 170 | |||
| 171 | local co = coroutine.wrap(function (c) | ||
| 172 | assert(coroutine.isyieldable()) | ||
| 173 | local s = string.gsub("a", ".", f) | ||
| 174 | return s | ||
| 175 | end) | ||
| 176 | assert(co() == "aa") | ||
| 177 | end | ||
| 178 | |||
| 179 | |||
| 180 | -- errors in coroutines | ||
| 181 | function foo () | ||
| 182 | assert(debug.getinfo(1).currentline == debug.getinfo(foo).linedefined + 1) | ||
| 183 | assert(debug.getinfo(2).currentline == debug.getinfo(goo).linedefined) | ||
| 184 | coroutine.yield(3) | ||
| 185 | error(foo) | ||
| 186 | end | ||
| 187 | |||
| 188 | function goo() foo() end | ||
| 189 | x = coroutine.wrap(goo) | ||
| 190 | assert(x() == 3) | ||
| 191 | local a,b = pcall(x) | ||
| 192 | assert(not a and b == foo) | ||
| 193 | |||
| 194 | x = coroutine.create(goo) | ||
| 195 | a,b = coroutine.resume(x) | ||
| 196 | assert(a and b == 3) | ||
| 197 | a,b = coroutine.resume(x) | ||
| 198 | assert(not a and b == foo and coroutine.status(x) == "dead") | ||
| 199 | a,b = coroutine.resume(x) | ||
| 200 | assert(not a and string.find(b, "dead") and coroutine.status(x) == "dead") | ||
| 201 | |||
| 202 | |||
| 203 | -- co-routines x for loop | ||
| 204 | function all (a, n, k) | ||
| 205 | if k == 0 then coroutine.yield(a) | ||
| 206 | else | ||
| 207 | for i=1,n do | ||
| 208 | a[k] = i | ||
| 209 | all(a, n, k-1) | ||
| 210 | end | ||
| 211 | end | ||
| 212 | end | ||
| 213 | |||
| 214 | local a = 0 | ||
| 215 | for t in coroutine.wrap(function () all({}, 5, 4) end) do | ||
| 216 | a = a+1 | ||
| 217 | end | ||
| 218 | assert(a == 5^4) | ||
| 219 | |||
| 220 | |||
| 221 | -- access to locals of collected corroutines | ||
| 222 | local C = {}; setmetatable(C, {__mode = "kv"}) | ||
| 223 | local x = coroutine.wrap (function () | ||
| 224 | local a = 10 | ||
| 225 | local function f () a = a+10; return a end | ||
| 226 | while true do | ||
| 227 | a = a+1 | ||
| 228 | coroutine.yield(f) | ||
| 229 | end | ||
| 230 | end) | ||
| 231 | |||
| 232 | C[1] = x; | ||
| 233 | |||
| 234 | local f = x() | ||
| 235 | assert(f() == 21 and x()() == 32 and x() == f) | ||
| 236 | x = nil | ||
| 237 | collectgarbage() | ||
| 238 | assert(C[1] == nil) | ||
| 239 | assert(f() == 43 and f() == 53) | ||
| 240 | |||
| 241 | |||
| 242 | -- old bug: attempt to resume itself | ||
| 243 | |||
| 244 | function co_func (current_co) | ||
| 245 | assert(coroutine.running() == current_co) | ||
| 246 | assert(coroutine.resume(current_co) == false) | ||
| 247 | coroutine.yield(10, 20) | ||
| 248 | assert(coroutine.resume(current_co) == false) | ||
| 249 | coroutine.yield(23) | ||
| 250 | return 10 | ||
| 251 | end | ||
| 252 | |||
| 253 | local co = coroutine.create(co_func) | ||
| 254 | local a,b,c = coroutine.resume(co, co) | ||
| 255 | assert(a == true and b == 10 and c == 20) | ||
| 256 | a,b = coroutine.resume(co, co) | ||
| 257 | assert(a == true and b == 23) | ||
| 258 | a,b = coroutine.resume(co, co) | ||
| 259 | assert(a == true and b == 10) | ||
| 260 | assert(coroutine.resume(co, co) == false) | ||
| 261 | assert(coroutine.resume(co, co) == false) | ||
| 262 | |||
| 263 | |||
| 264 | -- other old bug when attempting to resume itself | ||
| 265 | -- (trigger C-code assertions) | ||
| 266 | do | ||
| 267 | local A = coroutine.running() | ||
| 268 | local B = coroutine.create(function() return coroutine.resume(A) end) | ||
| 269 | local st, res = coroutine.resume(B) | ||
| 270 | assert(st == true and res == false) | ||
| 271 | |||
| 272 | A = coroutine.wrap(function() return pcall(A, 1) end) | ||
| 273 | st, res = A() | ||
| 274 | assert(not st and string.find(res, "non%-suspended")) | ||
| 275 | end | ||
| 276 | |||
| 277 | |||
| 278 | -- attempt to resume 'normal' coroutine | ||
| 279 | local co1, co2 | ||
| 280 | co1 = coroutine.create(function () return co2() end) | ||
| 281 | co2 = coroutine.wrap(function () | ||
| 282 | assert(coroutine.status(co1) == 'normal') | ||
| 283 | assert(not coroutine.resume(co1)) | ||
| 284 | coroutine.yield(3) | ||
| 285 | end) | ||
| 286 | |||
| 287 | a,b = coroutine.resume(co1) | ||
| 288 | assert(a and b == 3) | ||
| 289 | assert(coroutine.status(co1) == 'dead') | ||
| 290 | |||
| 291 | -- infinite recursion of coroutines | ||
| 292 | a = function(a) coroutine.wrap(a)(a) end | ||
| 293 | assert(not pcall(a, a)) | ||
| 294 | a = nil | ||
| 295 | |||
| 296 | |||
| 297 | -- access to locals of erroneous coroutines | ||
| 298 | local x = coroutine.create (function () | ||
| 299 | local a = 10 | ||
| 300 | _G.f = function () a=a+1; return a end | ||
| 301 | error('x') | ||
| 302 | end) | ||
| 303 | |||
| 304 | assert(not coroutine.resume(x)) | ||
| 305 | -- overwrite previous position of local `a' | ||
| 306 | assert(not coroutine.resume(x, 1, 1, 1, 1, 1, 1, 1)) | ||
| 307 | assert(_G.f() == 11) | ||
| 308 | assert(_G.f() == 12) | ||
| 309 | |||
| 310 | |||
| 311 | if not T then | ||
| 312 | (Message or print)('\n >>> testC not active: skipping yield/hook tests <<<\n') | ||
| 313 | else | ||
| 314 | print "testing yields inside hooks" | ||
| 315 | |||
| 316 | local turn | ||
| 317 | |||
| 318 | function fact (t, x) | ||
| 319 | assert(turn == t) | ||
| 320 | if x == 0 then return 1 | ||
| 321 | else return x*fact(t, x-1) | ||
| 322 | end | ||
| 323 | end | ||
| 324 | |||
| 325 | local A, B = 0, 0 | ||
| 326 | |||
| 327 | local x = coroutine.create(function () | ||
| 328 | T.sethook("yield 0", "", 2) | ||
| 329 | A = fact("A", 6) | ||
| 330 | end) | ||
| 331 | |||
| 332 | local y = coroutine.create(function () | ||
| 333 | T.sethook("yield 0", "", 3) | ||
| 334 | B = fact("B", 7) | ||
| 335 | end) | ||
| 336 | |||
| 337 | while A==0 or B==0 do -- A ~= 0 when 'x' finishes (similar for 'B','y') | ||
| 338 | if A==0 then turn = "A"; assert(T.resume(x)) end | ||
| 339 | if B==0 then turn = "B"; assert(T.resume(y)) end | ||
| 340 | end | ||
| 341 | |||
| 342 | assert(B // A == 7) -- fact(7) // fact(6) | ||
| 343 | |||
| 344 | local line = debug.getinfo(1, "l").currentline + 2 -- get line number | ||
| 345 | local function foo () | ||
| 346 | local x = 10 --<< this line is 'line' | ||
| 347 | x = x + 10 | ||
| 348 | _G.XX = x | ||
| 349 | end | ||
| 350 | |||
| 351 | -- testing yields in line hook | ||
| 352 | local co = coroutine.wrap(function () | ||
| 353 | T.sethook("setglobal X; yield 0", "l", 0); foo(); return 10 end) | ||
| 354 | |||
| 355 | _G.XX = nil; | ||
| 356 | _G.X = nil; co(); assert(_G.X == line) | ||
| 357 | _G.X = nil; co(); assert(_G.X == line + 1) | ||
| 358 | _G.X = nil; co(); assert(_G.X == line + 2 and _G.XX == nil) | ||
| 359 | _G.X = nil; co(); assert(_G.X == line + 3 and _G.XX == 20) | ||
| 360 | assert(co() == 10) | ||
| 361 | |||
| 362 | -- testing yields in count hook | ||
| 363 | co = coroutine.wrap(function () | ||
| 364 | T.sethook("yield 0", "", 1); foo(); return 10 end) | ||
| 365 | |||
| 366 | _G.XX = nil; | ||
| 367 | local c = 0 | ||
| 368 | repeat c = c + 1; local a = co() until a == 10 | ||
| 369 | assert(_G.XX == 20 and c >= 5) | ||
| 370 | |||
| 371 | co = coroutine.wrap(function () | ||
| 372 | T.sethook("yield 0", "", 2); foo(); return 10 end) | ||
| 373 | |||
| 374 | _G.XX = nil; | ||
| 375 | local c = 0 | ||
| 376 | repeat c = c + 1; local a = co() until a == 10 | ||
| 377 | assert(_G.XX == 20 and c >= 5) | ||
| 378 | _G.X = nil; _G.XX = nil | ||
| 379 | |||
| 380 | do | ||
| 381 | -- testing debug library on a coroutine suspended inside a hook | ||
| 382 | -- (bug in 5.2/5.3) | ||
| 383 | c = coroutine.create(function (a, ...) | ||
| 384 | T.sethook("yield 0", "l") -- will yield on next two lines | ||
| 385 | assert(a == 10) | ||
| 386 | return ... | ||
| 387 | end) | ||
| 388 | |||
| 389 | assert(coroutine.resume(c, 1, 2, 3)) -- start coroutine | ||
| 390 | local n,v = debug.getlocal(c, 0, 1) -- check its local | ||
| 391 | assert(n == "a" and v == 1) | ||
| 392 | n,v = debug.getlocal(c, 0, -1) -- check varargs | ||
| 393 | assert(v == 2) | ||
| 394 | n,v = debug.getlocal(c, 0, -2) | ||
| 395 | assert(v == 3) | ||
| 396 | assert(debug.setlocal(c, 0, 1, 10)) -- test 'setlocal' | ||
| 397 | assert(debug.setlocal(c, 0, -2, 20)) | ||
| 398 | local t = debug.getinfo(c, 0) -- test 'getinfo' | ||
| 399 | assert(t.currentline == t.linedefined + 1) | ||
| 400 | assert(not debug.getinfo(c, 1)) -- no other level | ||
| 401 | assert(coroutine.resume(c)) -- run next line | ||
| 402 | v = {coroutine.resume(c)} -- finish coroutine | ||
| 403 | assert(v[1] == true and v[2] == 2 and v[3] == 20 and v[4] == nil) | ||
| 404 | assert(not coroutine.resume(c)) | ||
| 405 | end | ||
| 406 | |||
| 407 | do | ||
| 408 | -- testing debug library on last function in a suspended coroutine | ||
| 409 | -- (bug in 5.2/5.3) | ||
| 410 | local c = coroutine.create(function () T.testC("yield 1", 10, 20) end) | ||
| 411 | local a, b = coroutine.resume(c) | ||
| 412 | assert(a and b == 20) | ||
| 413 | assert(debug.getinfo(c, 0).linedefined == -1) | ||
| 414 | a, b = debug.getlocal(c, 0, 2) | ||
| 415 | assert(b == 10) | ||
| 416 | end | ||
| 417 | |||
| 418 | |||
| 419 | print "testing coroutine API" | ||
| 420 | |||
| 421 | -- reusing a thread | ||
| 422 | assert(T.testC([[ | ||
| 423 | newthread # create thread | ||
| 424 | pushvalue 2 # push body | ||
| 425 | pushstring 'a a a' # push argument | ||
| 426 | xmove 0 3 2 # move values to new thread | ||
| 427 | resume -1, 1 # call it first time | ||
| 428 | pushstatus | ||
| 429 | xmove 3 0 0 # move results back to stack | ||
| 430 | setglobal X # result | ||
| 431 | setglobal Y # status | ||
| 432 | pushvalue 2 # push body (to call it again) | ||
| 433 | pushstring 'b b b' | ||
| 434 | xmove 0 3 2 | ||
| 435 | resume -1, 1 # call it again | ||
| 436 | pushstatus | ||
| 437 | xmove 3 0 0 | ||
| 438 | return 1 # return result | ||
| 439 | ]], function (...) return ... end) == 'b b b') | ||
| 440 | |||
| 441 | assert(X == 'a a a' and Y == 'OK') | ||
| 442 | |||
| 443 | |||
| 444 | -- resuming running coroutine | ||
| 445 | C = coroutine.create(function () | ||
| 446 | return T.testC([[ | ||
| 447 | pushnum 10; | ||
| 448 | pushnum 20; | ||
| 449 | resume -3 2; | ||
| 450 | pushstatus | ||
| 451 | gettop; | ||
| 452 | return 3]], C) | ||
| 453 | end) | ||
| 454 | local a, b, c, d = coroutine.resume(C) | ||
| 455 | assert(a == true and string.find(b, "non%-suspended") and | ||
| 456 | c == "ERRRUN" and d == 4) | ||
| 457 | |||
| 458 | a, b, c, d = T.testC([[ | ||
| 459 | rawgeti R 1 # get main thread | ||
| 460 | pushnum 10; | ||
| 461 | pushnum 20; | ||
| 462 | resume -3 2; | ||
| 463 | pushstatus | ||
| 464 | gettop; | ||
| 465 | return 4]]) | ||
| 466 | assert(a == coroutine.running() and string.find(b, "non%-suspended") and | ||
| 467 | c == "ERRRUN" and d == 4) | ||
| 468 | |||
| 469 | |||
| 470 | -- using a main thread as a coroutine | ||
| 471 | local state = T.newstate() | ||
| 472 | T.loadlib(state) | ||
| 473 | |||
| 474 | assert(T.doremote(state, [[ | ||
| 475 | coroutine = require'coroutine'; | ||
| 476 | X = function (x) coroutine.yield(x, 'BB'); return 'CC' end; | ||
| 477 | return 'ok']])) | ||
| 478 | |||
| 479 | t = table.pack(T.testC(state, [[ | ||
| 480 | rawgeti R 1 # get main thread | ||
| 481 | pushstring 'XX' | ||
| 482 | getglobal X # get function for body | ||
| 483 | pushstring AA # arg | ||
| 484 | resume 1 1 # 'resume' shadows previous stack! | ||
| 485 | gettop | ||
| 486 | setglobal T # top | ||
| 487 | setglobal B # second yielded value | ||
| 488 | setglobal A # fist yielded value | ||
| 489 | rawgeti R 1 # get main thread | ||
| 490 | pushnum 5 # arg (noise) | ||
| 491 | resume 1 1 # after coroutine ends, previous stack is back | ||
| 492 | pushstatus | ||
| 493 | return * | ||
| 494 | ]])) | ||
| 495 | assert(t.n == 4 and t[2] == 'XX' and t[3] == 'CC' and t[4] == 'OK') | ||
| 496 | assert(T.doremote(state, "return T") == '2') | ||
| 497 | assert(T.doremote(state, "return A") == 'AA') | ||
| 498 | assert(T.doremote(state, "return B") == 'BB') | ||
| 499 | |||
| 500 | T.closestate(state) | ||
| 501 | |||
| 502 | print'+' | ||
| 503 | |||
| 504 | end | ||
| 505 | |||
| 506 | |||
| 507 | -- leaving a pending coroutine open | ||
| 508 | _X = coroutine.wrap(function () | ||
| 509 | local a = 10 | ||
| 510 | local x = function () a = a+1 end | ||
| 511 | coroutine.yield() | ||
| 512 | end) | ||
| 513 | |||
| 514 | _X() | ||
| 515 | |||
| 516 | |||
| 517 | if not _soft then | ||
| 518 | -- bug (stack overflow) | ||
| 519 | local j = 2^9 | ||
| 520 | local lim = 1000000 -- (C stack limit; assume 32-bit machine) | ||
| 521 | local t = {lim - 10, lim - 5, lim - 1, lim, lim + 1} | ||
| 522 | for i = 1, #t do | ||
| 523 | local j = t[i] | ||
| 524 | co = coroutine.create(function() | ||
| 525 | local t = {} | ||
| 526 | for i = 1, j do t[i] = i end | ||
| 527 | return table.unpack(t) | ||
| 528 | end) | ||
| 529 | local r, msg = coroutine.resume(co) | ||
| 530 | assert(not r) | ||
| 531 | end | ||
| 532 | co = nil | ||
| 533 | end | ||
| 534 | |||
| 535 | |||
| 536 | assert(coroutine.running() == main) | ||
| 537 | |||
| 538 | print"+" | ||
| 539 | |||
| 540 | |||
| 541 | print"testing yields inside metamethods" | ||
| 542 | |||
| 543 | local mt = { | ||
| 544 | __eq = function(a,b) coroutine.yield(nil, "eq"); return a.x == b.x end, | ||
| 545 | __lt = function(a,b) coroutine.yield(nil, "lt"); return a.x < b.x end, | ||
| 546 | __le = function(a,b) coroutine.yield(nil, "le"); return a - b <= 0 end, | ||
| 547 | __add = function(a,b) coroutine.yield(nil, "add"); return a.x + b.x end, | ||
| 548 | __sub = function(a,b) coroutine.yield(nil, "sub"); return a.x - b.x end, | ||
| 549 | __mod = function(a,b) coroutine.yield(nil, "mod"); return a.x % b.x end, | ||
| 550 | __unm = function(a,b) coroutine.yield(nil, "unm"); return -a.x end, | ||
| 551 | __bnot = function(a,b) coroutine.yield(nil, "bnot"); return ~a.x end, | ||
| 552 | __shl = function(a,b) coroutine.yield(nil, "shl"); return a.x << b.x end, | ||
| 553 | __shr = function(a,b) coroutine.yield(nil, "shr"); return a.x >> b.x end, | ||
| 554 | __band = function(a,b) | ||
| 555 | a = type(a) == "table" and a.x or a | ||
| 556 | b = type(b) == "table" and b.x or b | ||
| 557 | coroutine.yield(nil, "band") | ||
| 558 | return a & b | ||
| 559 | end, | ||
| 560 | __bor = function(a,b) coroutine.yield(nil, "bor"); return a.x | b.x end, | ||
| 561 | __bxor = function(a,b) coroutine.yield(nil, "bxor"); return a.x ~ b.x end, | ||
| 562 | |||
| 563 | __concat = function(a,b) | ||
| 564 | coroutine.yield(nil, "concat"); | ||
| 565 | a = type(a) == "table" and a.x or a | ||
| 566 | b = type(b) == "table" and b.x or b | ||
| 567 | return a .. b | ||
| 568 | end, | ||
| 569 | __index = function (t,k) coroutine.yield(nil, "idx"); return t.k[k] end, | ||
| 570 | __newindex = function (t,k,v) coroutine.yield(nil, "nidx"); t.k[k] = v end, | ||
| 571 | } | ||
| 572 | |||
| 573 | |||
| 574 | local function new (x) | ||
| 575 | return setmetatable({x = x, k = {}}, mt) | ||
| 576 | end | ||
| 577 | |||
| 578 | |||
| 579 | local a = new(10) | ||
| 580 | local b = new(12) | ||
| 581 | local c = new"hello" | ||
| 582 | |||
| 583 | local function run (f, t) | ||
| 584 | local i = 1 | ||
| 585 | local c = coroutine.wrap(f) | ||
| 586 | while true do | ||
| 587 | local res, stat = c() | ||
| 588 | if res then assert(t[i] == nil); return res, t end | ||
| 589 | assert(stat == t[i]) | ||
| 590 | i = i + 1 | ||
| 591 | end | ||
| 592 | end | ||
| 593 | |||
| 594 | |||
| 595 | assert(run(function () if (a>=b) then return '>=' else return '<' end end, | ||
| 596 | {"le", "sub"}) == "<") | ||
| 597 | -- '<=' using '<' | ||
| 598 | mt.__le = nil | ||
| 599 | assert(run(function () if (a<=b) then return '<=' else return '>' end end, | ||
| 600 | {"lt"}) == "<=") | ||
| 601 | assert(run(function () if (a==b) then return '==' else return '~=' end end, | ||
| 602 | {"eq"}) == "~=") | ||
| 603 | |||
| 604 | assert(run(function () return a & b + a end, {"add", "band"}) == 2) | ||
| 605 | |||
| 606 | assert(run(function () return a % b end, {"mod"}) == 10) | ||
| 607 | |||
| 608 | assert(run(function () return ~a & b end, {"bnot", "band"}) == ~10 & 12) | ||
| 609 | assert(run(function () return a | b end, {"bor"}) == 10 | 12) | ||
| 610 | assert(run(function () return a ~ b end, {"bxor"}) == 10 ~ 12) | ||
| 611 | assert(run(function () return a << b end, {"shl"}) == 10 << 12) | ||
| 612 | assert(run(function () return a >> b end, {"shr"}) == 10 >> 12) | ||
| 613 | |||
| 614 | assert(run(function () return a..b end, {"concat"}) == "1012") | ||
| 615 | |||
| 616 | assert(run(function() return a .. b .. c .. a end, | ||
| 617 | {"concat", "concat", "concat"}) == "1012hello10") | ||
| 618 | |||
| 619 | assert(run(function() return "a" .. "b" .. a .. "c" .. c .. b .. "x" end, | ||
| 620 | {"concat", "concat", "concat"}) == "ab10chello12x") | ||
| 621 | |||
| 622 | |||
| 623 | do -- a few more tests for comparsion operators | ||
| 624 | local mt1 = { | ||
| 625 | __le = function (a,b) | ||
| 626 | coroutine.yield(10) | ||
| 627 | return | ||
| 628 | (type(a) == "table" and a.x or a) <= (type(b) == "table" and b.x or b) | ||
| 629 | end, | ||
| 630 | __lt = function (a,b) | ||
| 631 | coroutine.yield(10) | ||
| 632 | return | ||
| 633 | (type(a) == "table" and a.x or a) < (type(b) == "table" and b.x or b) | ||
| 634 | end, | ||
| 635 | } | ||
| 636 | local mt2 = { __lt = mt1.__lt } -- no __le | ||
| 637 | |||
| 638 | local function run (f) | ||
| 639 | local co = coroutine.wrap(f) | ||
| 640 | local res | ||
| 641 | repeat | ||
| 642 | res = co() | ||
| 643 | until res ~= 10 | ||
| 644 | return res | ||
| 645 | end | ||
| 646 | |||
| 647 | local function test () | ||
| 648 | local a1 = setmetatable({x=1}, mt1) | ||
| 649 | local a2 = setmetatable({x=2}, mt2) | ||
| 650 | assert(a1 < a2) | ||
| 651 | assert(a1 <= a2) | ||
| 652 | assert(1 < a2) | ||
| 653 | assert(1 <= a2) | ||
| 654 | assert(2 > a1) | ||
| 655 | assert(2 >= a2) | ||
| 656 | return true | ||
| 657 | end | ||
| 658 | |||
| 659 | run(test) | ||
| 660 | |||
| 661 | end | ||
| 662 | |||
| 663 | assert(run(function () | ||
| 664 | a.BB = print | ||
| 665 | return a.BB | ||
| 666 | end, {"nidx", "idx"}) == print) | ||
| 667 | |||
| 668 | -- getuptable & setuptable | ||
| 669 | do local _ENV = _ENV | ||
| 670 | f = function () AAA = BBB + 1; return AAA end | ||
| 671 | end | ||
| 672 | g = new(10); g.k.BBB = 10; | ||
| 673 | debug.setupvalue(f, 1, g) | ||
| 674 | assert(run(f, {"idx", "nidx", "idx"}) == 11) | ||
| 675 | assert(g.k.AAA == 11) | ||
| 676 | |||
| 677 | print"+" | ||
| 678 | |||
| 679 | print"testing yields inside 'for' iterators" | ||
| 680 | |||
| 681 | local f = function (s, i) | ||
| 682 | if i%2 == 0 then coroutine.yield(nil, "for") end | ||
| 683 | if i < s then return i + 1 end | ||
| 684 | end | ||
| 685 | |||
| 686 | assert(run(function () | ||
| 687 | local s = 0 | ||
| 688 | for i in f, 4, 0 do s = s + i end | ||
| 689 | return s | ||
| 690 | end, {"for", "for", "for"}) == 10) | ||
| 691 | |||
| 692 | |||
| 693 | |||
| 694 | -- tests for coroutine API | ||
| 695 | if T==nil then | ||
| 696 | (Message or print)('\n >>> testC not active: skipping coroutine API tests <<<\n') | ||
| 697 | return | ||
| 698 | end | ||
| 699 | |||
| 700 | print('testing coroutine API') | ||
| 701 | |||
| 702 | local function apico (...) | ||
| 703 | local x = {...} | ||
| 704 | return coroutine.wrap(function () | ||
| 705 | return T.testC(table.unpack(x)) | ||
| 706 | end) | ||
| 707 | end | ||
| 708 | |||
| 709 | local a = {apico( | ||
| 710 | [[ | ||
| 711 | pushstring errorcode | ||
| 712 | pcallk 1 0 2; | ||
| 713 | invalid command (should not arrive here) | ||
| 714 | ]], | ||
| 715 | [[return *]], | ||
| 716 | "stackmark", | ||
| 717 | error | ||
| 718 | )()} | ||
| 719 | assert(#a == 4 and | ||
| 720 | a[3] == "stackmark" and | ||
| 721 | a[4] == "errorcode" and | ||
| 722 | _G.status == "ERRRUN" and | ||
| 723 | _G.ctx == 2) -- 'ctx' to pcallk | ||
| 724 | |||
| 725 | local co = apico( | ||
| 726 | "pushvalue 2; pushnum 10; pcallk 1 2 3; invalid command;", | ||
| 727 | coroutine.yield, | ||
| 728 | "getglobal status; getglobal ctx; pushvalue 2; pushstring a; pcallk 1 0 4; invalid command", | ||
| 729 | "getglobal status; getglobal ctx; return *") | ||
| 730 | |||
| 731 | assert(co() == 10) | ||
| 732 | assert(co(20, 30) == 'a') | ||
| 733 | a = {co()} | ||
| 734 | assert(#a == 10 and | ||
| 735 | a[2] == coroutine.yield and | ||
| 736 | a[5] == 20 and a[6] == 30 and | ||
| 737 | a[7] == "YIELD" and a[8] == 3 and | ||
| 738 | a[9] == "YIELD" and a[10] == 4) | ||
| 739 | assert(not pcall(co)) -- coroutine is dead now | ||
| 740 | |||
| 741 | |||
| 742 | f = T.makeCfunc("pushnum 3; pushnum 5; yield 1;") | ||
| 743 | co = coroutine.wrap(function () | ||
| 744 | assert(f() == 23); assert(f() == 23); return 10 | ||
| 745 | end) | ||
| 746 | assert(co(23,16) == 5) | ||
| 747 | assert(co(23,16) == 5) | ||
| 748 | assert(co(23,16) == 10) | ||
| 749 | |||
| 750 | |||
| 751 | -- testing coroutines with C bodies | ||
| 752 | f = T.makeCfunc([[ | ||
| 753 | pushnum 102 | ||
| 754 | yieldk 1 U2 | ||
| 755 | cannot be here! | ||
| 756 | ]], | ||
| 757 | [[ # continuation | ||
| 758 | pushvalue U3 # accessing upvalues inside a continuation | ||
| 759 | pushvalue U4 | ||
| 760 | return * | ||
| 761 | ]], 23, "huu") | ||
| 762 | |||
| 763 | x = coroutine.wrap(f) | ||
| 764 | assert(x() == 102) | ||
| 765 | eqtab({x()}, {23, "huu"}) | ||
| 766 | |||
| 767 | |||
| 768 | f = T.makeCfunc[[pushstring 'a'; pushnum 102; yield 2; ]] | ||
| 769 | |||
| 770 | a, b, c, d = T.testC([[newthread; pushvalue 2; xmove 0 3 1; resume 3 0; | ||
| 771 | pushstatus; xmove 3 0 0; resume 3 0; pushstatus; | ||
| 772 | return 4; ]], f) | ||
| 773 | |||
| 774 | assert(a == 'YIELD' and b == 'a' and c == 102 and d == 'OK') | ||
| 775 | |||
| 776 | |||
| 777 | -- testing chain of suspendable C calls | ||
| 778 | |||
| 779 | local count = 3 -- number of levels | ||
| 780 | |||
| 781 | f = T.makeCfunc([[ | ||
| 782 | remove 1; # remove argument | ||
| 783 | pushvalue U3; # get selection function | ||
| 784 | call 0 1; # call it (result is 'f' or 'yield') | ||
| 785 | pushstring hello # single argument for selected function | ||
| 786 | pushupvalueindex 2; # index of continuation program | ||
| 787 | callk 1 -1 .; # call selected function | ||
| 788 | errorerror # should never arrive here | ||
| 789 | ]], | ||
| 790 | [[ | ||
| 791 | # continuation program | ||
| 792 | pushnum 34 # return value | ||
| 793 | return * # return all results | ||
| 794 | ]], | ||
| 795 | function () -- selection function | ||
| 796 | count = count - 1 | ||
| 797 | if count == 0 then return coroutine.yield | ||
| 798 | else return f | ||
| 799 | end | ||
| 800 | end | ||
| 801 | ) | ||
| 802 | |||
| 803 | co = coroutine.wrap(function () return f(nil) end) | ||
| 804 | assert(co() == "hello") -- argument to 'yield' | ||
| 805 | a = {co()} | ||
| 806 | -- three '34's (one from each pending C call) | ||
| 807 | assert(#a == 3 and a[1] == a[2] and a[2] == a[3] and a[3] == 34) | ||
| 808 | |||
| 809 | |||
| 810 | -- testing yields with continuations | ||
| 811 | |||
| 812 | co = coroutine.wrap(function (...) return | ||
| 813 | T.testC([[ # initial function | ||
| 814 | yieldk 1 2 | ||
| 815 | cannot be here! | ||
| 816 | ]], | ||
| 817 | [[ # 1st continuation | ||
| 818 | yieldk 0 3 | ||
| 819 | cannot be here! | ||
| 820 | ]], | ||
| 821 | [[ # 2nd continuation | ||
| 822 | yieldk 0 4 | ||
| 823 | cannot be here! | ||
| 824 | ]], | ||
| 825 | [[ # 3th continuation | ||
| 826 | pushvalue 6 # function which is last arg. to 'testC' here | ||
| 827 | pushnum 10; pushnum 20; | ||
| 828 | pcall 2 0 0 # call should throw an error and return to next line | ||
| 829 | pop 1 # remove error message | ||
| 830 | pushvalue 6 | ||
| 831 | getglobal status; getglobal ctx | ||
| 832 | pcallk 2 2 5 # call should throw an error and jump to continuation | ||
| 833 | cannot be here! | ||
| 834 | ]], | ||
| 835 | [[ # 4th (and last) continuation | ||
| 836 | return * | ||
| 837 | ]], | ||
| 838 | -- function called by 3th continuation | ||
| 839 | function (a,b) x=a; y=b; error("errmsg") end, | ||
| 840 | ... | ||
| 841 | ) | ||
| 842 | end) | ||
| 843 | |||
| 844 | local a = {co(3,4,6)} | ||
| 845 | assert(a[1] == 6 and a[2] == nil) | ||
| 846 | a = {co()}; assert(a[1] == nil and _G.status == "YIELD" and _G.ctx == 2) | ||
| 847 | a = {co()}; assert(a[1] == nil and _G.status == "YIELD" and _G.ctx == 3) | ||
| 848 | a = {co(7,8)}; | ||
| 849 | -- original arguments | ||
| 850 | assert(type(a[1]) == 'string' and type(a[2]) == 'string' and | ||
| 851 | type(a[3]) == 'string' and type(a[4]) == 'string' and | ||
| 852 | type(a[5]) == 'string' and type(a[6]) == 'function') | ||
| 853 | -- arguments left from fist resume | ||
| 854 | assert(a[7] == 3 and a[8] == 4) | ||
| 855 | -- arguments to last resume | ||
| 856 | assert(a[9] == 7 and a[10] == 8) | ||
| 857 | -- error message and nothing more | ||
| 858 | assert(a[11]:find("errmsg") and #a == 11) | ||
| 859 | -- check arguments to pcallk | ||
| 860 | assert(x == "YIELD" and y == 4) | ||
| 861 | |||
| 862 | assert(not pcall(co)) -- coroutine should be dead | ||
| 863 | |||
| 864 | |||
| 865 | -- bug in nCcalls | ||
| 866 | local co = coroutine.wrap(function () | ||
| 867 | local a = {pcall(pcall,pcall,pcall,pcall,pcall,pcall,pcall,error,"hi")} | ||
| 868 | return pcall(assert, table.unpack(a)) | ||
| 869 | end) | ||
| 870 | |||
| 871 | local a = {co()} | ||
| 872 | assert(a[10] == "hi") | ||
| 873 | |||
| 874 | print'OK' | ||
