diff options
| author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2018-07-09 12:33:01 -0300 |
|---|---|---|
| committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2018-07-09 12:33:01 -0300 |
| commit | 7c519dfbd0c68b952f0849e01deaa3750e1f8153 (patch) | |
| tree | dde3ddbba310877db725df37a0d9f2cbe4e2a8f9 /testes/coroutine.lua | |
| parent | f59e6a93c0ad38a27a420e51abf8f13d962446b5 (diff) | |
| download | lua-7c519dfbd0c68b952f0849e01deaa3750e1f8153.tar.gz lua-7c519dfbd0c68b952f0849e01deaa3750e1f8153.tar.bz2 lua-7c519dfbd0c68b952f0849e01deaa3750e1f8153.zip | |
Added manual and tests for version 5.4-w2
Diffstat (limited to 'testes/coroutine.lua')
| -rw-r--r-- | testes/coroutine.lua | 918 |
1 files changed, 918 insertions, 0 deletions
diff --git a/testes/coroutine.lua b/testes/coroutine.lua new file mode 100644 index 00000000..22087320 --- /dev/null +++ b/testes/coroutine.lua | |||
| @@ -0,0 +1,918 @@ | |||
| 1 | -- $Id: coroutine.lua,v 1.48 2018/03/12 14:19:36 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 | |||
| 181 | do -- testing single trace of coroutines | ||
| 182 | local X | ||
| 183 | local co = coroutine.create(function () | ||
| 184 | coroutine.yield(10) | ||
| 185 | return 20; | ||
| 186 | end) | ||
| 187 | local trace = {} | ||
| 188 | local function dotrace (event) | ||
| 189 | trace[#trace + 1] = event | ||
| 190 | end | ||
| 191 | debug.sethook(co, dotrace, "clr") | ||
| 192 | repeat until not coroutine.resume(co) | ||
| 193 | local correcttrace = {"call", "line", "call", "return", "line", "return"} | ||
| 194 | assert(#trace == #correcttrace) | ||
| 195 | for k, v in pairs(trace) do | ||
| 196 | assert(v == correcttrace[k]) | ||
| 197 | end | ||
| 198 | end | ||
| 199 | |||
| 200 | -- errors in coroutines | ||
| 201 | function foo () | ||
| 202 | assert(debug.getinfo(1).currentline == debug.getinfo(foo).linedefined + 1) | ||
| 203 | assert(debug.getinfo(2).currentline == debug.getinfo(goo).linedefined) | ||
| 204 | coroutine.yield(3) | ||
| 205 | error(foo) | ||
| 206 | end | ||
| 207 | |||
| 208 | function goo() foo() end | ||
| 209 | x = coroutine.wrap(goo) | ||
| 210 | assert(x() == 3) | ||
| 211 | local a,b = pcall(x) | ||
| 212 | assert(not a and b == foo) | ||
| 213 | |||
| 214 | x = coroutine.create(goo) | ||
| 215 | a,b = coroutine.resume(x) | ||
| 216 | assert(a and b == 3) | ||
| 217 | a,b = coroutine.resume(x) | ||
| 218 | assert(not a and b == foo and coroutine.status(x) == "dead") | ||
| 219 | a,b = coroutine.resume(x) | ||
| 220 | assert(not a and string.find(b, "dead") and coroutine.status(x) == "dead") | ||
| 221 | |||
| 222 | |||
| 223 | -- co-routines x for loop | ||
| 224 | function all (a, n, k) | ||
| 225 | if k == 0 then coroutine.yield(a) | ||
| 226 | else | ||
| 227 | for i=1,n do | ||
| 228 | a[k] = i | ||
| 229 | all(a, n, k-1) | ||
| 230 | end | ||
| 231 | end | ||
| 232 | end | ||
| 233 | |||
| 234 | local a = 0 | ||
| 235 | for t in coroutine.wrap(function () all({}, 5, 4) end) do | ||
| 236 | a = a+1 | ||
| 237 | end | ||
| 238 | assert(a == 5^4) | ||
| 239 | |||
| 240 | |||
| 241 | -- access to locals of collected corroutines | ||
| 242 | local C = {}; setmetatable(C, {__mode = "kv"}) | ||
| 243 | local x = coroutine.wrap (function () | ||
| 244 | local a = 10 | ||
| 245 | local function f () a = a+10; return a end | ||
| 246 | while true do | ||
| 247 | a = a+1 | ||
| 248 | coroutine.yield(f) | ||
| 249 | end | ||
| 250 | end) | ||
| 251 | |||
| 252 | C[1] = x; | ||
| 253 | |||
| 254 | local f = x() | ||
| 255 | assert(f() == 21 and x()() == 32 and x() == f) | ||
| 256 | x = nil | ||
| 257 | collectgarbage() | ||
| 258 | assert(C[1] == undef) | ||
| 259 | assert(f() == 43 and f() == 53) | ||
| 260 | |||
| 261 | |||
| 262 | -- old bug: attempt to resume itself | ||
| 263 | |||
| 264 | function co_func (current_co) | ||
| 265 | assert(coroutine.running() == current_co) | ||
| 266 | assert(coroutine.resume(current_co) == false) | ||
| 267 | coroutine.yield(10, 20) | ||
| 268 | assert(coroutine.resume(current_co) == false) | ||
| 269 | coroutine.yield(23) | ||
| 270 | return 10 | ||
| 271 | end | ||
| 272 | |||
| 273 | local co = coroutine.create(co_func) | ||
| 274 | local a,b,c = coroutine.resume(co, co) | ||
| 275 | assert(a == true and b == 10 and c == 20) | ||
| 276 | a,b = coroutine.resume(co, co) | ||
| 277 | assert(a == true and b == 23) | ||
| 278 | a,b = coroutine.resume(co, co) | ||
| 279 | assert(a == true and b == 10) | ||
| 280 | assert(coroutine.resume(co, co) == false) | ||
| 281 | assert(coroutine.resume(co, co) == false) | ||
| 282 | |||
| 283 | |||
| 284 | -- other old bug when attempting to resume itself | ||
| 285 | -- (trigger C-code assertions) | ||
| 286 | do | ||
| 287 | local A = coroutine.running() | ||
| 288 | local B = coroutine.create(function() return coroutine.resume(A) end) | ||
| 289 | local st, res = coroutine.resume(B) | ||
| 290 | assert(st == true and res == false) | ||
| 291 | |||
| 292 | A = coroutine.wrap(function() return pcall(A, 1) end) | ||
| 293 | st, res = A() | ||
| 294 | assert(not st and string.find(res, "non%-suspended")) | ||
| 295 | end | ||
| 296 | |||
| 297 | |||
| 298 | -- attempt to resume 'normal' coroutine | ||
| 299 | local co1, co2 | ||
| 300 | co1 = coroutine.create(function () return co2() end) | ||
| 301 | co2 = coroutine.wrap(function () | ||
| 302 | assert(coroutine.status(co1) == 'normal') | ||
| 303 | assert(not coroutine.resume(co1)) | ||
| 304 | coroutine.yield(3) | ||
| 305 | end) | ||
| 306 | |||
| 307 | a,b = coroutine.resume(co1) | ||
| 308 | assert(a and b == 3) | ||
| 309 | assert(coroutine.status(co1) == 'dead') | ||
| 310 | |||
| 311 | -- infinite recursion of coroutines | ||
| 312 | a = function(a) coroutine.wrap(a)(a) end | ||
| 313 | assert(not pcall(a, a)) | ||
| 314 | a = nil | ||
| 315 | |||
| 316 | |||
| 317 | -- access to locals of erroneous coroutines | ||
| 318 | local x = coroutine.create (function () | ||
| 319 | local a = 10 | ||
| 320 | _G.f = function () a=a+1; return a end | ||
| 321 | error('x') | ||
| 322 | end) | ||
| 323 | |||
| 324 | assert(not coroutine.resume(x)) | ||
| 325 | -- overwrite previous position of local `a' | ||
| 326 | assert(not coroutine.resume(x, 1, 1, 1, 1, 1, 1, 1)) | ||
| 327 | assert(_G.f() == 11) | ||
| 328 | assert(_G.f() == 12) | ||
| 329 | |||
| 330 | |||
| 331 | if not T then | ||
| 332 | (Message or print)('\n >>> testC not active: skipping yield/hook tests <<<\n') | ||
| 333 | else | ||
| 334 | print "testing yields inside hooks" | ||
| 335 | |||
| 336 | local turn | ||
| 337 | |||
| 338 | function fact (t, x) | ||
| 339 | assert(turn == t) | ||
| 340 | if x == 0 then return 1 | ||
| 341 | else return x*fact(t, x-1) | ||
| 342 | end | ||
| 343 | end | ||
| 344 | |||
| 345 | local A, B = 0, 0 | ||
| 346 | |||
| 347 | local x = coroutine.create(function () | ||
| 348 | T.sethook("yield 0", "", 2) | ||
| 349 | A = fact("A", 6) | ||
| 350 | end) | ||
| 351 | |||
| 352 | local y = coroutine.create(function () | ||
| 353 | T.sethook("yield 0", "", 3) | ||
| 354 | B = fact("B", 7) | ||
| 355 | end) | ||
| 356 | |||
| 357 | while A==0 or B==0 do -- A ~= 0 when 'x' finishes (similar for 'B','y') | ||
| 358 | if A==0 then turn = "A"; assert(T.resume(x)) end | ||
| 359 | if B==0 then turn = "B"; assert(T.resume(y)) end | ||
| 360 | end | ||
| 361 | |||
| 362 | assert(B // A == 7) -- fact(7) // fact(6) | ||
| 363 | |||
| 364 | local line = debug.getinfo(1, "l").currentline + 2 -- get line number | ||
| 365 | local function foo () | ||
| 366 | local x = 10 --<< this line is 'line' | ||
| 367 | x = x + 10 | ||
| 368 | _G.XX = x | ||
| 369 | end | ||
| 370 | |||
| 371 | -- testing yields in line hook | ||
| 372 | local co = coroutine.wrap(function () | ||
| 373 | T.sethook("setglobal X; yield 0", "l", 0); foo(); return 10 end) | ||
| 374 | |||
| 375 | _G.XX = nil; | ||
| 376 | _G.X = nil; co(); assert(_G.X == line) | ||
| 377 | _G.X = nil; co(); assert(_G.X == line + 1) | ||
| 378 | _G.X = nil; co(); assert(_G.X == line + 2 and _G.XX == nil) | ||
| 379 | _G.X = nil; co(); assert(_G.X == line + 3 and _G.XX == 20) | ||
| 380 | assert(co() == 10) | ||
| 381 | |||
| 382 | -- testing yields in count hook | ||
| 383 | co = coroutine.wrap(function () | ||
| 384 | T.sethook("yield 0", "", 1); foo(); return 10 end) | ||
| 385 | |||
| 386 | _G.XX = nil; | ||
| 387 | local c = 0 | ||
| 388 | repeat c = c + 1; local a = co() until a == 10 | ||
| 389 | assert(_G.XX == 20 and c >= 5) | ||
| 390 | |||
| 391 | co = coroutine.wrap(function () | ||
| 392 | T.sethook("yield 0", "", 2); foo(); return 10 end) | ||
| 393 | |||
| 394 | _G.XX = nil; | ||
| 395 | local c = 0 | ||
| 396 | repeat c = c + 1; local a = co() until a == 10 | ||
| 397 | assert(_G.XX == 20 and c >= 5) | ||
| 398 | _G.X = nil; _G.XX = nil | ||
| 399 | |||
| 400 | do | ||
| 401 | -- testing debug library on a coroutine suspended inside a hook | ||
| 402 | -- (bug in 5.2/5.3) | ||
| 403 | c = coroutine.create(function (a, ...) | ||
| 404 | T.sethook("yield 0", "l") -- will yield on next two lines | ||
| 405 | assert(a == 10) | ||
| 406 | return ... | ||
| 407 | end) | ||
| 408 | |||
| 409 | assert(coroutine.resume(c, 1, 2, 3)) -- start coroutine | ||
| 410 | local n,v = debug.getlocal(c, 0, 1) -- check its local | ||
| 411 | assert(n == "a" and v == 1) | ||
| 412 | assert(debug.setlocal(c, 0, 1, 10)) -- test 'setlocal' | ||
| 413 | local t = debug.getinfo(c, 0) -- test 'getinfo' | ||
| 414 | assert(t.currentline == t.linedefined + 1) | ||
| 415 | assert(not debug.getinfo(c, 1)) -- no other level | ||
| 416 | assert(coroutine.resume(c)) -- run next line | ||
| 417 | v = {coroutine.resume(c)} -- finish coroutine | ||
| 418 | assert(v[1] == true and v[2] == 2 and v[3] == 3 and v[4] == undef) | ||
| 419 | assert(not coroutine.resume(c)) | ||
| 420 | end | ||
| 421 | |||
| 422 | do | ||
| 423 | -- testing debug library on last function in a suspended coroutine | ||
| 424 | -- (bug in 5.2/5.3) | ||
| 425 | local c = coroutine.create(function () T.testC("yield 1", 10, 20) end) | ||
| 426 | local a, b = coroutine.resume(c) | ||
| 427 | assert(a and b == 20) | ||
| 428 | assert(debug.getinfo(c, 0).linedefined == -1) | ||
| 429 | a, b = debug.getlocal(c, 0, 2) | ||
| 430 | assert(b == 10) | ||
| 431 | end | ||
| 432 | |||
| 433 | |||
| 434 | print "testing coroutine API" | ||
| 435 | |||
| 436 | -- reusing a thread | ||
| 437 | assert(T.testC([[ | ||
| 438 | newthread # create thread | ||
| 439 | pushvalue 2 # push body | ||
| 440 | pushstring 'a a a' # push argument | ||
| 441 | xmove 0 3 2 # move values to new thread | ||
| 442 | resume -1, 1 # call it first time | ||
| 443 | pushstatus | ||
| 444 | xmove 3 0 0 # move results back to stack | ||
| 445 | setglobal X # result | ||
| 446 | setglobal Y # status | ||
| 447 | pushvalue 2 # push body (to call it again) | ||
| 448 | pushstring 'b b b' | ||
| 449 | xmove 0 3 2 | ||
| 450 | resume -1, 1 # call it again | ||
| 451 | pushstatus | ||
| 452 | xmove 3 0 0 | ||
| 453 | return 1 # return result | ||
| 454 | ]], function (...) return ... end) == 'b b b') | ||
| 455 | |||
| 456 | assert(X == 'a a a' and Y == 'OK') | ||
| 457 | |||
| 458 | |||
| 459 | -- resuming running coroutine | ||
| 460 | C = coroutine.create(function () | ||
| 461 | return T.testC([[ | ||
| 462 | pushnum 10; | ||
| 463 | pushnum 20; | ||
| 464 | resume -3 2; | ||
| 465 | pushstatus | ||
| 466 | gettop; | ||
| 467 | return 3]], C) | ||
| 468 | end) | ||
| 469 | local a, b, c, d = coroutine.resume(C) | ||
| 470 | assert(a == true and string.find(b, "non%-suspended") and | ||
| 471 | c == "ERRRUN" and d == 4) | ||
| 472 | |||
| 473 | a, b, c, d = T.testC([[ | ||
| 474 | rawgeti R 1 # get main thread | ||
| 475 | pushnum 10; | ||
| 476 | pushnum 20; | ||
| 477 | resume -3 2; | ||
| 478 | pushstatus | ||
| 479 | gettop; | ||
| 480 | return 4]]) | ||
| 481 | assert(a == coroutine.running() and string.find(b, "non%-suspended") and | ||
| 482 | c == "ERRRUN" and d == 4) | ||
| 483 | |||
| 484 | |||
| 485 | -- using a main thread as a coroutine | ||
| 486 | local state = T.newstate() | ||
| 487 | T.loadlib(state) | ||
| 488 | |||
| 489 | assert(T.doremote(state, [[ | ||
| 490 | coroutine = require'coroutine'; | ||
| 491 | X = function (x) coroutine.yield(x, 'BB'); return 'CC' end; | ||
| 492 | return 'ok']])) | ||
| 493 | |||
| 494 | t = table.pack(T.testC(state, [[ | ||
| 495 | rawgeti R 1 # get main thread | ||
| 496 | pushstring 'XX' | ||
| 497 | getglobal X # get function for body | ||
| 498 | pushstring AA # arg | ||
| 499 | resume 1 1 # 'resume' shadows previous stack! | ||
| 500 | gettop | ||
| 501 | setglobal T # top | ||
| 502 | setglobal B # second yielded value | ||
| 503 | setglobal A # fist yielded value | ||
| 504 | rawgeti R 1 # get main thread | ||
| 505 | pushnum 5 # arg (noise) | ||
| 506 | resume 1 1 # after coroutine ends, previous stack is back | ||
| 507 | pushstatus | ||
| 508 | return * | ||
| 509 | ]])) | ||
| 510 | assert(t.n == 4 and t[2] == 'XX' and t[3] == 'CC' and t[4] == 'OK') | ||
| 511 | assert(T.doremote(state, "return T") == '2') | ||
| 512 | assert(T.doremote(state, "return A") == 'AA') | ||
| 513 | assert(T.doremote(state, "return B") == 'BB') | ||
| 514 | |||
| 515 | T.closestate(state) | ||
| 516 | |||
| 517 | print'+' | ||
| 518 | |||
| 519 | end | ||
| 520 | |||
| 521 | |||
| 522 | -- leaving a pending coroutine open | ||
| 523 | _X = coroutine.wrap(function () | ||
| 524 | local a = 10 | ||
| 525 | local x = function () a = a+1 end | ||
| 526 | coroutine.yield() | ||
| 527 | end) | ||
| 528 | |||
| 529 | _X() | ||
| 530 | |||
| 531 | |||
| 532 | if not _soft then | ||
| 533 | -- bug (stack overflow) | ||
| 534 | local j = 2^9 | ||
| 535 | local lim = 1000000 -- (C stack limit; assume 32-bit machine) | ||
| 536 | local t = {lim - 10, lim - 5, lim - 1, lim, lim + 1} | ||
| 537 | for i = 1, #t do | ||
| 538 | local j = t[i] | ||
| 539 | co = coroutine.create(function() | ||
| 540 | local t = {} | ||
| 541 | for i = 1, j do t[i] = i end | ||
| 542 | return table.unpack(t) | ||
| 543 | end) | ||
| 544 | local r, msg = coroutine.resume(co) | ||
| 545 | assert(not r) | ||
| 546 | end | ||
| 547 | co = nil | ||
| 548 | end | ||
| 549 | |||
| 550 | |||
| 551 | assert(coroutine.running() == main) | ||
| 552 | |||
| 553 | print"+" | ||
| 554 | |||
| 555 | |||
| 556 | print"testing yields inside metamethods" | ||
| 557 | |||
| 558 | local function val(x) | ||
| 559 | if type(x) == "table" then return x.x else return x end | ||
| 560 | end | ||
| 561 | |||
| 562 | local mt = { | ||
| 563 | __eq = function(a,b) coroutine.yield(nil, "eq"); return val(a) == val(b) end, | ||
| 564 | __lt = function(a,b) coroutine.yield(nil, "lt"); return val(a) < val(b) end, | ||
| 565 | __le = function(a,b) coroutine.yield(nil, "le"); return a - b <= 0 end, | ||
| 566 | __add = function(a,b) coroutine.yield(nil, "add"); | ||
| 567 | return val(a) + val(b) end, | ||
| 568 | __sub = function(a,b) coroutine.yield(nil, "sub"); return val(a) - val(b) end, | ||
| 569 | __mul = function(a,b) coroutine.yield(nil, "mul"); return val(a) * val(b) end, | ||
| 570 | __div = function(a,b) coroutine.yield(nil, "div"); return val(a) / val(b) end, | ||
| 571 | __idiv = function(a,b) coroutine.yield(nil, "idiv"); | ||
| 572 | return val(a) // val(b) end, | ||
| 573 | __pow = function(a,b) coroutine.yield(nil, "pow"); return val(a) ^ val(b) end, | ||
| 574 | __mod = function(a,b) coroutine.yield(nil, "mod"); return val(a) % val(b) end, | ||
| 575 | __unm = function(a,b) coroutine.yield(nil, "unm"); return -val(a) end, | ||
| 576 | __bnot = function(a,b) coroutine.yield(nil, "bnot"); return ~val(a) end, | ||
| 577 | __shl = function(a,b) coroutine.yield(nil, "shl"); | ||
| 578 | return val(a) << val(b) end, | ||
| 579 | __shr = function(a,b) coroutine.yield(nil, "shr"); | ||
| 580 | return val(a) >> val(b) end, | ||
| 581 | __band = function(a,b) | ||
| 582 | coroutine.yield(nil, "band") | ||
| 583 | return val(a) & val(b) | ||
| 584 | end, | ||
| 585 | __bor = function(a,b) coroutine.yield(nil, "bor"); | ||
| 586 | return val(a) | val(b) end, | ||
| 587 | __bxor = function(a,b) coroutine.yield(nil, "bxor"); | ||
| 588 | return val(a) ~ val(b) end, | ||
| 589 | |||
| 590 | __concat = function(a,b) | ||
| 591 | coroutine.yield(nil, "concat"); | ||
| 592 | return val(a) .. val(b) | ||
| 593 | end, | ||
| 594 | __index = function (t,k) coroutine.yield(nil, "idx"); return t.k[k] end, | ||
| 595 | __newindex = function (t,k,v) coroutine.yield(nil, "nidx"); t.k[k] = v end, | ||
| 596 | } | ||
| 597 | |||
| 598 | |||
| 599 | local function new (x) | ||
| 600 | return setmetatable({x = x, k = {}}, mt) | ||
| 601 | end | ||
| 602 | |||
| 603 | |||
| 604 | local a = new(10) | ||
| 605 | local b = new(12) | ||
| 606 | local c = new"hello" | ||
| 607 | |||
| 608 | local function run (f, t) | ||
| 609 | local i = 1 | ||
| 610 | local c = coroutine.wrap(f) | ||
| 611 | while true do | ||
| 612 | local res, stat = c() | ||
| 613 | if res then assert(t[i] == undef); return res, t end | ||
| 614 | assert(stat == t[i]) | ||
| 615 | i = i + 1 | ||
| 616 | end | ||
| 617 | end | ||
| 618 | |||
| 619 | |||
| 620 | assert(run(function () if (a>=b) then return '>=' else return '<' end end, | ||
| 621 | {"le", "sub"}) == "<") | ||
| 622 | -- '<=' using '<' | ||
| 623 | mt.__le = nil | ||
| 624 | assert(run(function () if (a<=b) then return '<=' else return '>' end end, | ||
| 625 | {"lt"}) == "<=") | ||
| 626 | assert(run(function () if (a==b) then return '==' else return '~=' end end, | ||
| 627 | {"eq"}) == "~=") | ||
| 628 | |||
| 629 | assert(run(function () return a & b + a end, {"add", "band"}) == 2) | ||
| 630 | |||
| 631 | assert(run(function () return 1 + a end, {"add"}) == 11) | ||
| 632 | assert(run(function () return a - 25 end, {"sub"}) == -15) | ||
| 633 | assert(run(function () return 2 * a end, {"mul"}) == 20) | ||
| 634 | assert(run(function () return a ^ 2 end, {"pow"}) == 100) | ||
| 635 | assert(run(function () return a / 2 end, {"div"}) == 5) | ||
| 636 | assert(run(function () return a % 6 end, {"mod"}) == 4) | ||
| 637 | assert(run(function () return a // 3 end, {"idiv"}) == 3) | ||
| 638 | |||
| 639 | assert(run(function () return a + b end, {"add"}) == 22) | ||
| 640 | assert(run(function () return a - b end, {"sub"}) == -2) | ||
| 641 | assert(run(function () return a * b end, {"mul"}) == 120) | ||
| 642 | assert(run(function () return a ^ b end, {"pow"}) == 10^12) | ||
| 643 | assert(run(function () return a / b end, {"div"}) == 10/12) | ||
| 644 | assert(run(function () return a % b end, {"mod"}) == 10) | ||
| 645 | assert(run(function () return a // b end, {"idiv"}) == 0) | ||
| 646 | |||
| 647 | |||
| 648 | assert(run(function () return a % b end, {"mod"}) == 10) | ||
| 649 | |||
| 650 | assert(run(function () return ~a & b end, {"bnot", "band"}) == ~10 & 12) | ||
| 651 | assert(run(function () return a | b end, {"bor"}) == 10 | 12) | ||
| 652 | assert(run(function () return a ~ b end, {"bxor"}) == 10 ~ 12) | ||
| 653 | assert(run(function () return a << b end, {"shl"}) == 10 << 12) | ||
| 654 | assert(run(function () return a >> b end, {"shr"}) == 10 >> 12) | ||
| 655 | |||
| 656 | assert(run(function () return 10 & b end, {"band"}) == 10 & 12) | ||
| 657 | assert(run(function () return a | 2 end, {"bor"}) == 10 | 2) | ||
| 658 | assert(run(function () return a ~ 2 end, {"bxor"}) == 10 ~ 2) | ||
| 659 | |||
| 660 | assert(run(function () return a..b end, {"concat"}) == "1012") | ||
| 661 | |||
| 662 | assert(run(function() return a .. b .. c .. a end, | ||
| 663 | {"concat", "concat", "concat"}) == "1012hello10") | ||
| 664 | |||
| 665 | assert(run(function() return "a" .. "b" .. a .. "c" .. c .. b .. "x" end, | ||
| 666 | {"concat", "concat", "concat"}) == "ab10chello12x") | ||
| 667 | |||
| 668 | |||
| 669 | do -- a few more tests for comparsion operators | ||
| 670 | local mt1 = { | ||
| 671 | __le = function (a,b) | ||
| 672 | coroutine.yield(10) | ||
| 673 | return (val(a) <= val(b)) | ||
| 674 | end, | ||
| 675 | __lt = function (a,b) | ||
| 676 | coroutine.yield(10) | ||
| 677 | return val(a) < val(b) | ||
| 678 | end, | ||
| 679 | } | ||
| 680 | local mt2 = { __lt = mt1.__lt } -- no __le | ||
| 681 | |||
| 682 | local function run (f) | ||
| 683 | local co = coroutine.wrap(f) | ||
| 684 | local res | ||
| 685 | repeat | ||
| 686 | res = co() | ||
| 687 | until res ~= 10 | ||
| 688 | return res | ||
| 689 | end | ||
| 690 | |||
| 691 | local function test () | ||
| 692 | local a1 = setmetatable({x=1}, mt1) | ||
| 693 | local a2 = setmetatable({x=2}, mt2) | ||
| 694 | assert(a1 < a2) | ||
| 695 | assert(a1 <= a2) | ||
| 696 | assert(1 < a2) | ||
| 697 | assert(1 <= a2) | ||
| 698 | assert(2 > a1) | ||
| 699 | assert(2 >= a2) | ||
| 700 | return true | ||
| 701 | end | ||
| 702 | |||
| 703 | run(test) | ||
| 704 | |||
| 705 | end | ||
| 706 | |||
| 707 | assert(run(function () | ||
| 708 | a.BB = print | ||
| 709 | return a.BB | ||
| 710 | end, {"nidx", "idx"}) == print) | ||
| 711 | |||
| 712 | -- getuptable & setuptable | ||
| 713 | do local _ENV = _ENV | ||
| 714 | f = function () AAA = BBB + 1; return AAA end | ||
| 715 | end | ||
| 716 | g = new(10); g.k.BBB = 10; | ||
| 717 | debug.setupvalue(f, 1, g) | ||
| 718 | assert(run(f, {"idx", "nidx", "idx"}) == 11) | ||
| 719 | assert(g.k.AAA == 11) | ||
| 720 | |||
| 721 | print"+" | ||
| 722 | |||
| 723 | print"testing yields inside 'for' iterators" | ||
| 724 | |||
| 725 | local f = function (s, i) | ||
| 726 | if i%2 == 0 then coroutine.yield(nil, "for") end | ||
| 727 | if i < s then return i + 1 end | ||
| 728 | end | ||
| 729 | |||
| 730 | assert(run(function () | ||
| 731 | local s = 0 | ||
| 732 | for i in f, 4, 0 do s = s + i end | ||
| 733 | return s | ||
| 734 | end, {"for", "for", "for"}) == 10) | ||
| 735 | |||
| 736 | |||
| 737 | |||
| 738 | -- tests for coroutine API | ||
| 739 | if T==nil then | ||
| 740 | (Message or print)('\n >>> testC not active: skipping coroutine API tests <<<\n') | ||
| 741 | return | ||
| 742 | end | ||
| 743 | |||
| 744 | print('testing coroutine API') | ||
| 745 | |||
| 746 | local function apico (...) | ||
| 747 | local x = {...} | ||
| 748 | return coroutine.wrap(function () | ||
| 749 | return T.testC(table.unpack(x)) | ||
| 750 | end) | ||
| 751 | end | ||
| 752 | |||
| 753 | local a = {apico( | ||
| 754 | [[ | ||
| 755 | pushstring errorcode | ||
| 756 | pcallk 1 0 2; | ||
| 757 | invalid command (should not arrive here) | ||
| 758 | ]], | ||
| 759 | [[return *]], | ||
| 760 | "stackmark", | ||
| 761 | error | ||
| 762 | )()} | ||
| 763 | assert(#a == 4 and | ||
| 764 | a[3] == "stackmark" and | ||
| 765 | a[4] == "errorcode" and | ||
| 766 | _G.status == "ERRRUN" and | ||
| 767 | _G.ctx == 2) -- 'ctx' to pcallk | ||
| 768 | |||
| 769 | local co = apico( | ||
| 770 | "pushvalue 2; pushnum 10; pcallk 1 2 3; invalid command;", | ||
| 771 | coroutine.yield, | ||
| 772 | "getglobal status; getglobal ctx; pushvalue 2; pushstring a; pcallk 1 0 4; invalid command", | ||
| 773 | "getglobal status; getglobal ctx; return *") | ||
| 774 | |||
| 775 | assert(co() == 10) | ||
| 776 | assert(co(20, 30) == 'a') | ||
| 777 | a = {co()} | ||
| 778 | assert(#a == 10 and | ||
| 779 | a[2] == coroutine.yield and | ||
| 780 | a[5] == 20 and a[6] == 30 and | ||
| 781 | a[7] == "YIELD" and a[8] == 3 and | ||
| 782 | a[9] == "YIELD" and a[10] == 4) | ||
| 783 | assert(not pcall(co)) -- coroutine is dead now | ||
| 784 | |||
| 785 | |||
| 786 | f = T.makeCfunc("pushnum 3; pushnum 5; yield 1;") | ||
| 787 | co = coroutine.wrap(function () | ||
| 788 | assert(f() == 23); assert(f() == 23); return 10 | ||
| 789 | end) | ||
| 790 | assert(co(23,16) == 5) | ||
| 791 | assert(co(23,16) == 5) | ||
| 792 | assert(co(23,16) == 10) | ||
| 793 | |||
| 794 | |||
| 795 | -- testing coroutines with C bodies | ||
| 796 | f = T.makeCfunc([[ | ||
| 797 | pushnum 102 | ||
| 798 | yieldk 1 U2 | ||
| 799 | cannot be here! | ||
| 800 | ]], | ||
| 801 | [[ # continuation | ||
| 802 | pushvalue U3 # accessing upvalues inside a continuation | ||
| 803 | pushvalue U4 | ||
| 804 | return * | ||
| 805 | ]], 23, "huu") | ||
| 806 | |||
| 807 | x = coroutine.wrap(f) | ||
| 808 | assert(x() == 102) | ||
| 809 | eqtab({x()}, {23, "huu"}) | ||
| 810 | |||
| 811 | |||
| 812 | f = T.makeCfunc[[pushstring 'a'; pushnum 102; yield 2; ]] | ||
| 813 | |||
| 814 | a, b, c, d = T.testC([[newthread; pushvalue 2; xmove 0 3 1; resume 3 0; | ||
| 815 | pushstatus; xmove 3 0 0; resume 3 0; pushstatus; | ||
| 816 | return 4; ]], f) | ||
| 817 | |||
| 818 | assert(a == 'YIELD' and b == 'a' and c == 102 and d == 'OK') | ||
| 819 | |||
| 820 | |||
| 821 | -- testing chain of suspendable C calls | ||
| 822 | |||
| 823 | local count = 3 -- number of levels | ||
| 824 | |||
| 825 | f = T.makeCfunc([[ | ||
| 826 | remove 1; # remove argument | ||
| 827 | pushvalue U3; # get selection function | ||
| 828 | call 0 1; # call it (result is 'f' or 'yield') | ||
| 829 | pushstring hello # single argument for selected function | ||
| 830 | pushupvalueindex 2; # index of continuation program | ||
| 831 | callk 1 -1 .; # call selected function | ||
| 832 | errorerror # should never arrive here | ||
| 833 | ]], | ||
| 834 | [[ | ||
| 835 | # continuation program | ||
| 836 | pushnum 34 # return value | ||
| 837 | return * # return all results | ||
| 838 | ]], | ||
| 839 | function () -- selection function | ||
| 840 | count = count - 1 | ||
| 841 | if count == 0 then return coroutine.yield | ||
| 842 | else return f | ||
| 843 | end | ||
| 844 | end | ||
| 845 | ) | ||
| 846 | |||
| 847 | co = coroutine.wrap(function () return f(nil) end) | ||
| 848 | assert(co() == "hello") -- argument to 'yield' | ||
| 849 | a = {co()} | ||
| 850 | -- three '34's (one from each pending C call) | ||
| 851 | assert(#a == 3 and a[1] == a[2] and a[2] == a[3] and a[3] == 34) | ||
| 852 | |||
| 853 | |||
| 854 | -- testing yields with continuations | ||
| 855 | |||
| 856 | co = coroutine.wrap(function (...) return | ||
| 857 | T.testC([[ # initial function | ||
| 858 | yieldk 1 2 | ||
| 859 | cannot be here! | ||
| 860 | ]], | ||
| 861 | [[ # 1st continuation | ||
| 862 | yieldk 0 3 | ||
| 863 | cannot be here! | ||
| 864 | ]], | ||
| 865 | [[ # 2nd continuation | ||
| 866 | yieldk 0 4 | ||
| 867 | cannot be here! | ||
| 868 | ]], | ||
| 869 | [[ # 3th continuation | ||
| 870 | pushvalue 6 # function which is last arg. to 'testC' here | ||
| 871 | pushnum 10; pushnum 20; | ||
| 872 | pcall 2 0 0 # call should throw an error and return to next line | ||
| 873 | pop 1 # remove error message | ||
| 874 | pushvalue 6 | ||
| 875 | getglobal status; getglobal ctx | ||
| 876 | pcallk 2 2 5 # call should throw an error and jump to continuation | ||
| 877 | cannot be here! | ||
| 878 | ]], | ||
| 879 | [[ # 4th (and last) continuation | ||
| 880 | return * | ||
| 881 | ]], | ||
| 882 | -- function called by 3th continuation | ||
| 883 | function (a,b) x=a; y=b; error("errmsg") end, | ||
| 884 | ... | ||
| 885 | ) | ||
| 886 | end) | ||
| 887 | |||
| 888 | local a = {co(3,4,6)} | ||
| 889 | assert(a[1] == 6 and a[2] == undef) | ||
| 890 | a = {co()}; assert(a[1] == undef and _G.status == "YIELD" and _G.ctx == 2) | ||
| 891 | a = {co()}; assert(a[1] == undef and _G.status == "YIELD" and _G.ctx == 3) | ||
| 892 | a = {co(7,8)}; | ||
| 893 | -- original arguments | ||
| 894 | assert(type(a[1]) == 'string' and type(a[2]) == 'string' and | ||
| 895 | type(a[3]) == 'string' and type(a[4]) == 'string' and | ||
| 896 | type(a[5]) == 'string' and type(a[6]) == 'function') | ||
| 897 | -- arguments left from fist resume | ||
| 898 | assert(a[7] == 3 and a[8] == 4) | ||
| 899 | -- arguments to last resume | ||
| 900 | assert(a[9] == 7 and a[10] == 8) | ||
| 901 | -- error message and nothing more | ||
| 902 | assert(a[11]:find("errmsg") and #a == 11) | ||
| 903 | -- check arguments to pcallk | ||
| 904 | assert(x == "YIELD" and y == 4) | ||
| 905 | |||
| 906 | assert(not pcall(co)) -- coroutine should be dead | ||
| 907 | |||
| 908 | |||
| 909 | -- bug in nCcalls | ||
| 910 | local co = coroutine.wrap(function () | ||
| 911 | local a = {pcall(pcall,pcall,pcall,pcall,pcall,pcall,pcall,error,"hi")} | ||
| 912 | return pcall(assert, table.unpack(a)) | ||
| 913 | end) | ||
| 914 | |||
| 915 | local a = {co()} | ||
| 916 | assert(a[10] == "hi") | ||
| 917 | |||
| 918 | print'OK' | ||
