summaryrefslogtreecommitdiff
path: root/testes/coroutine.lua
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2018-07-09 12:33:01 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2018-07-09 12:33:01 -0300
commit7c519dfbd0c68b952f0849e01deaa3750e1f8153 (patch)
treedde3ddbba310877db725df37a0d9f2cbe4e2a8f9 /testes/coroutine.lua
parentf59e6a93c0ad38a27a420e51abf8f13d962446b5 (diff)
downloadlua-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.lua918
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
4print "testing coroutines"
5
6local debug = require'debug'
7
8local f
9
10local main, ismain = coroutine.running()
11assert(type(main) == "thread" and ismain)
12assert(not coroutine.resume(main))
13assert(not coroutine.isyieldable())
14assert(not pcall(coroutine.yield))
15
16
17-- trivial errors
18assert(not pcall(coroutine.resume, 0))
19assert(not pcall(coroutine.status, 0))
20
21
22-- tests for multiple yield/resume arguments
23
24local 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
30end
31
32_G.x = nil -- declare x
33function 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)
46end
47
48f = coroutine.create(foo)
49assert(type(f) == "thread" and coroutine.status(f) == "suspended")
50assert(string.find(tostring(f), "thread"))
51local s,a,b,c,d
52s,a,b,c,d = coroutine.resume(f, {1,2,3}, {}, {1}, {'a', 'b', 'c'})
53assert(s and a == nil and coroutine.status(f) == "suspended")
54s,a,b,c,d = coroutine.resume(f)
55eqtab(_G.x, {})
56assert(s and a == 1 and b == nil)
57s,a,b,c,d = coroutine.resume(f, 1, 2, 3)
58eqtab(_G.x, {1, 2, 3})
59assert(s and a == 'a' and b == 'b' and c == 'c' and d == nil)
60s,a,b,c,d = coroutine.resume(f, "xuxu")
61eqtab(_G.x, {"xuxu"})
62assert(s and a == 1 and b == 2 and c == 3 and d == nil)
63assert(coroutine.status(f) == "dead")
64s, a = coroutine.resume(f, "xuxu")
65assert(not s and string.find(a, "dead") and coroutine.status(f) == "dead")
66
67
68-- yields in tail calls
69local function foo (i) return coroutine.yield(i) end
70f = coroutine.wrap(function ()
71 for i=1,10 do
72 assert(foo(i) == _G.x)
73 end
74 return 'a'
75end)
76for i=1,10 do _G.x = i; assert(f(i) == i) end
77_G.x = 'xuxu'; assert(f('xuxu') == 'a')
78
79-- recursive
80function pf (n, i)
81 coroutine.yield(n)
82 pf(n*i, i+1)
83end
84
85f = coroutine.wrap(pf)
86local s=1
87for i=1,10 do
88 assert(f(1, 1) == s)
89 s = s*i
90end
91
92-- sieve
93function gen (n)
94 return coroutine.wrap(function ()
95 for i=2,n do coroutine.yield(i) end
96 end)
97end
98
99
100function 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)
108end
109
110local x = gen(100)
111local a = {}
112while 1 do
113 local n = x()
114 if n == nil then break end
115 table.insert(a, n)
116 x = filter(n, x)
117end
118
119assert(#a == 25 and a[#a] == 97)
120x, a = nil
121
122-- yielding across C boundaries
123
124co = 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
131assert(co() == 20)
132assert(co() == 30)
133
134
135local f = function (s, i) return coroutine.yield(i) end
136
137local 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
146f1()
147for i = 1, 10 do assert(f1(i) == i) end
148local r1, r2, v = f1(nil)
149assert(r1 and not r2 and v[1] == (10 + 1)*10/2)
150
151
152function f (a, b) a = coroutine.yield(a); error{a + b} end
153function g(x) return x[1]*2 end
154
155co = coroutine.wrap(function ()
156 coroutine.yield(xpcall(f, g, 10, 20))
157 end)
158
159assert(co() == 10)
160r, msg = co(100)
161assert(not r and msg == 240)
162
163
164-- unyieldable C call
165do
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")
177end
178
179
180
181do -- 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
198end
199
200-- errors in coroutines
201function 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)
206end
207
208function goo() foo() end
209x = coroutine.wrap(goo)
210assert(x() == 3)
211local a,b = pcall(x)
212assert(not a and b == foo)
213
214x = coroutine.create(goo)
215a,b = coroutine.resume(x)
216assert(a and b == 3)
217a,b = coroutine.resume(x)
218assert(not a and b == foo and coroutine.status(x) == "dead")
219a,b = coroutine.resume(x)
220assert(not a and string.find(b, "dead") and coroutine.status(x) == "dead")
221
222
223-- co-routines x for loop
224function 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
232end
233
234local a = 0
235for t in coroutine.wrap(function () all({}, 5, 4) end) do
236 a = a+1
237end
238assert(a == 5^4)
239
240
241-- access to locals of collected corroutines
242local C = {}; setmetatable(C, {__mode = "kv"})
243local 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
252C[1] = x;
253
254local f = x()
255assert(f() == 21 and x()() == 32 and x() == f)
256x = nil
257collectgarbage()
258assert(C[1] == undef)
259assert(f() == 43 and f() == 53)
260
261
262-- old bug: attempt to resume itself
263
264function 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
271end
272
273local co = coroutine.create(co_func)
274local a,b,c = coroutine.resume(co, co)
275assert(a == true and b == 10 and c == 20)
276a,b = coroutine.resume(co, co)
277assert(a == true and b == 23)
278a,b = coroutine.resume(co, co)
279assert(a == true and b == 10)
280assert(coroutine.resume(co, co) == false)
281assert(coroutine.resume(co, co) == false)
282
283
284-- other old bug when attempting to resume itself
285-- (trigger C-code assertions)
286do
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"))
295end
296
297
298-- attempt to resume 'normal' coroutine
299local co1, co2
300co1 = coroutine.create(function () return co2() end)
301co2 = coroutine.wrap(function ()
302 assert(coroutine.status(co1) == 'normal')
303 assert(not coroutine.resume(co1))
304 coroutine.yield(3)
305 end)
306
307a,b = coroutine.resume(co1)
308assert(a and b == 3)
309assert(coroutine.status(co1) == 'dead')
310
311-- infinite recursion of coroutines
312a = function(a) coroutine.wrap(a)(a) end
313assert(not pcall(a, a))
314a = nil
315
316
317-- access to locals of erroneous coroutines
318local x = coroutine.create (function ()
319 local a = 10
320 _G.f = function () a=a+1; return a end
321 error('x')
322 end)
323
324assert(not coroutine.resume(x))
325-- overwrite previous position of local `a'
326assert(not coroutine.resume(x, 1, 1, 1, 1, 1, 1, 1))
327assert(_G.f() == 11)
328assert(_G.f() == 12)
329
330
331if not T then
332 (Message or print)('\n >>> testC not active: skipping yield/hook tests <<<\n')
333else
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
519end
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
532if 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
548end
549
550
551assert(coroutine.running() == main)
552
553print"+"
554
555
556print"testing yields inside metamethods"
557
558local function val(x)
559 if type(x) == "table" then return x.x else return x end
560end
561
562local 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
599local function new (x)
600 return setmetatable({x = x, k = {}}, mt)
601end
602
603
604local a = new(10)
605local b = new(12)
606local c = new"hello"
607
608local 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
617end
618
619
620assert(run(function () if (a>=b) then return '>=' else return '<' end end,
621 {"le", "sub"}) == "<")
622-- '<=' using '<'
623mt.__le = nil
624assert(run(function () if (a<=b) then return '<=' else return '>' end end,
625 {"lt"}) == "<=")
626assert(run(function () if (a==b) then return '==' else return '~=' end end,
627 {"eq"}) == "~=")
628
629assert(run(function () return a & b + a end, {"add", "band"}) == 2)
630
631assert(run(function () return 1 + a end, {"add"}) == 11)
632assert(run(function () return a - 25 end, {"sub"}) == -15)
633assert(run(function () return 2 * a end, {"mul"}) == 20)
634assert(run(function () return a ^ 2 end, {"pow"}) == 100)
635assert(run(function () return a / 2 end, {"div"}) == 5)
636assert(run(function () return a % 6 end, {"mod"}) == 4)
637assert(run(function () return a // 3 end, {"idiv"}) == 3)
638
639assert(run(function () return a + b end, {"add"}) == 22)
640assert(run(function () return a - b end, {"sub"}) == -2)
641assert(run(function () return a * b end, {"mul"}) == 120)
642assert(run(function () return a ^ b end, {"pow"}) == 10^12)
643assert(run(function () return a / b end, {"div"}) == 10/12)
644assert(run(function () return a % b end, {"mod"}) == 10)
645assert(run(function () return a // b end, {"idiv"}) == 0)
646
647
648assert(run(function () return a % b end, {"mod"}) == 10)
649
650assert(run(function () return ~a & b end, {"bnot", "band"}) == ~10 & 12)
651assert(run(function () return a | b end, {"bor"}) == 10 | 12)
652assert(run(function () return a ~ b end, {"bxor"}) == 10 ~ 12)
653assert(run(function () return a << b end, {"shl"}) == 10 << 12)
654assert(run(function () return a >> b end, {"shr"}) == 10 >> 12)
655
656assert(run(function () return 10 & b end, {"band"}) == 10 & 12)
657assert(run(function () return a | 2 end, {"bor"}) == 10 | 2)
658assert(run(function () return a ~ 2 end, {"bxor"}) == 10 ~ 2)
659
660assert(run(function () return a..b end, {"concat"}) == "1012")
661
662assert(run(function() return a .. b .. c .. a end,
663 {"concat", "concat", "concat"}) == "1012hello10")
664
665assert(run(function() return "a" .. "b" .. a .. "c" .. c .. b .. "x" end,
666 {"concat", "concat", "concat"}) == "ab10chello12x")
667
668
669do -- 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
705end
706
707assert(run(function ()
708 a.BB = print
709 return a.BB
710 end, {"nidx", "idx"}) == print)
711
712-- getuptable & setuptable
713do local _ENV = _ENV
714 f = function () AAA = BBB + 1; return AAA end
715end
716g = new(10); g.k.BBB = 10;
717debug.setupvalue(f, 1, g)
718assert(run(f, {"idx", "nidx", "idx"}) == 11)
719assert(g.k.AAA == 11)
720
721print"+"
722
723print"testing yields inside 'for' iterators"
724
725local 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
730assert(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
739if T==nil then
740 (Message or print)('\n >>> testC not active: skipping coroutine API tests <<<\n')
741 return
742end
743
744print('testing coroutine API')
745
746local function apico (...)
747 local x = {...}
748 return coroutine.wrap(function ()
749 return T.testC(table.unpack(x))
750 end)
751end
752
753local a = {apico(
754[[
755 pushstring errorcode
756 pcallk 1 0 2;
757 invalid command (should not arrive here)
758]],
759[[return *]],
760"stackmark",
761error
762)()}
763assert(#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
769local 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
775assert(co() == 10)
776assert(co(20, 30) == 'a')
777a = {co()}
778assert(#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)
783assert(not pcall(co)) -- coroutine is dead now
784
785
786f = T.makeCfunc("pushnum 3; pushnum 5; yield 1;")
787co = coroutine.wrap(function ()
788 assert(f() == 23); assert(f() == 23); return 10
789end)
790assert(co(23,16) == 5)
791assert(co(23,16) == 5)
792assert(co(23,16) == 10)
793
794
795-- testing coroutines with C bodies
796f = 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
807x = coroutine.wrap(f)
808assert(x() == 102)
809eqtab({x()}, {23, "huu"})
810
811
812f = T.makeCfunc[[pushstring 'a'; pushnum 102; yield 2; ]]
813
814a, 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
818assert(a == 'YIELD' and b == 'a' and c == 102 and d == 'OK')
819
820
821-- testing chain of suspendable C calls
822
823local count = 3 -- number of levels
824
825f = 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]],
839function () -- selection function
840 count = count - 1
841 if count == 0 then return coroutine.yield
842 else return f
843 end
844end
845)
846
847co = coroutine.wrap(function () return f(nil) end)
848assert(co() == "hello") -- argument to 'yield'
849a = {co()}
850-- three '34's (one from each pending C call)
851assert(#a == 3 and a[1] == a[2] and a[2] == a[3] and a[3] == 34)
852
853
854-- testing yields with continuations
855
856co = 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)
886end)
887
888local a = {co(3,4,6)}
889assert(a[1] == 6 and a[2] == undef)
890a = {co()}; assert(a[1] == undef and _G.status == "YIELD" and _G.ctx == 2)
891a = {co()}; assert(a[1] == undef and _G.status == "YIELD" and _G.ctx == 3)
892a = {co(7,8)};
893-- original arguments
894assert(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
898assert(a[7] == 3 and a[8] == 4)
899-- arguments to last resume
900assert(a[9] == 7 and a[10] == 8)
901-- error message and nothing more
902assert(a[11]:find("errmsg") and #a == 11)
903-- check arguments to pcallk
904assert(x == "YIELD" and y == 4)
905
906assert(not pcall(co)) -- coroutine should be dead
907
908
909-- bug in nCcalls
910local co = coroutine.wrap(function ()
911 local a = {pcall(pcall,pcall,pcall,pcall,pcall,pcall,pcall,error,"hi")}
912 return pcall(assert, table.unpack(a))
913end)
914
915local a = {co()}
916assert(a[10] == "hi")
917
918print'OK'