1print "testing closures and coroutines"
2--[[
3
4local A,B = 0,{g=10}
5function f(x)
6  local a = {}
7  for i=1,1000 do
8    local y = 0
9    do
10      a[i] = function () B.g = B.g+1; y = y+x; return y+A end
11    end
12  end
13  local dummy = function () return a[A] end
14  collectgarbage()
15  A = 1; assert(dummy() == a[1]); A = 0;
16  assert(a[1]() == x)
17  assert(a[3]() == x)
18  collectgarbage()
19  assert(B.g == 12)
20  return a
21end
22
23a = f(10)
24-- force a GC in this level
25local x = {[1] = {}}   -- to detect a GC
26setmetatable(x, {__mode = 'kv'})
27while x[1] do   -- repeat until GC
28  local a = A..A..A..A  -- create garbage
29  A = A+1
30end
31assert(a[1]() == 20+A)
32assert(a[1]() == 30+A)
33assert(a[2]() == 10+A)
34collectgarbage()
35assert(a[2]() == 20+A)
36assert(a[2]() == 30+A)
37assert(a[3]() == 20+A)
38assert(a[8]() == 10+A)
39assert(getmetatable(x).__mode == 'kv')
40assert(B.g == 19)
41--]]
42
43-- testing closures with 'for' control variable
44a = {}
45for i=1,10 do
46  a[i] = {set = function(x) i=x end, get = function () return i end}
47  if i == 3 then break end
48end
49assert(a[4] == nil)
50a[1].set(10)
51assert(a[2].get() == 2)
52a[2].set('a')
53assert(a[3].get() == 3)
54assert(a[2].get() == 'a')
55
56a = {}
57for i, k in pairs{'a', 'b'} do
58  a[i] = {set = function(x, y) i=x; k=y end,
59          get = function () return i, k end}
60  if i == 2 then break end
61end
62a[1].set(10, 20)
63local r,s = a[2].get()
64assert(r == 2 and s == 'b')
65r,s = a[1].get()
66assert(r == 10 and s == 20)
67a[2].set('a', 'b')
68r,s = a[2].get()
69assert(r == "a" and s == "b")
70
71
72-- testing closures with 'for' control variable x break
73for i=1,3 do
74  f = function () return i end
75  break
76end
77assert(f() == 1)
78
79for k, v in pairs{"a", "b"} do
80  f = function () return k, v end
81  break
82end
83assert(({f()})[1] == 1)
84assert(({f()})[2] == "a")
85
86
87-- testing closure x break x return x errors
88
89local b
90function f(x)
91  local first = 1
92  while 1 do
93    if x == 3 and not first then return end
94    local a = 'xuxu'
95    b = function (op, y)
96          if op == 'set' then
97            a = x+y
98          else
99            return a
100          end
101        end
102    if x == 1 then do break end
103    elseif x == 2 then return
104    else if x ~= 3 then error() end
105    end
106    first = nil
107  end
108end
109
110for i=1,3 do
111  f(i)
112  assert(b('get') == 'xuxu')
113  b('set', 10); assert(b('get') == 10+i)
114  b = nil
115end
116
117pcall(f, 4);
118assert(b('get') == 'xuxu')
119b('set', 10); assert(b('get') == 14)
120
121
122local w
123-- testing multi-level closure
124function f(x)
125  return function (y)
126    return function (z) return w+x+y+z end
127  end
128end
129
130y = f(10)
131w = 1.345
132assert(y(20)(30) == 60+w)
133
134-- testing closures x repeat-until
135
136local a = {}
137local i = 1
138repeat
139  local x = i
140  a[i] = function () i = x+1; return x end
141until i > 10 or a[i]() ~= x
142assert(i == 11 and a[1]() == 1 and a[3]() == 3 and i == 4)
143
144print'+'
145
146
147-- test for correctly closing upvalues in tail calls of vararg functions
148local function t ()
149  local function c(a,b) assert(a=="test" and b=="OK") end
150  local function v(f, ...) c("test", f() ~= 1 and "FAILED" or "OK") end
151  local x = 1
152  return v(function() return x end)
153end
154t()
155
156
157-- coroutine tests
158
159local f
160
161assert(coroutine.running() == nil)
162
163
164-- tests for global environment
165
166local function foo (a)
167  setfenv(0, a)
168  coroutine.yield(getfenv())
169  assert(getfenv(0) == a)
170  assert(getfenv(1) == _G)
171  return getfenv(1)
172end
173
174f = coroutine.wrap(foo)
175local a = {}
176assert(f(a) == _G)
177local a,b = pcall(f)
178assert(a and b == _G)
179
180
181-- tests for multiple yield/resume arguments
182
183local function eqtab (t1, t2)
184  assert(table.getn(t1) == table.getn(t2))
185  for i,v in ipairs(t1) do
186    assert(t2[i] == v)
187  end
188end
189
190_G.x = nil   -- declare x
191function foo (a, ...)
192  assert(coroutine.running() == f)
193  assert(coroutine.status(f) == "running")
194  local arg = {...}
195  for i=1,table.getn(arg) do
196    _G.x = {coroutine.yield(unpack(arg[i]))}
197  end
198  return unpack(a)
199end
200
201f = coroutine.create(foo)
202assert(type(f) == "thread" and coroutine.status(f) == "suspended")
203assert(string.find(tostring(f), "thread"))
204local s,a,b,c,d
205s,a,b,c,d = coroutine.resume(f, {1,2,3}, {}, {1}, {'a', 'b', 'c'})
206assert(s and a == nil and coroutine.status(f) == "suspended")
207s,a,b,c,d = coroutine.resume(f)
208eqtab(_G.x, {})
209assert(s and a == 1 and b == nil)
210s,a,b,c,d = coroutine.resume(f, 1, 2, 3)
211eqtab(_G.x, {1, 2, 3})
212assert(s and a == 'a' and b == 'b' and c == 'c' and d == nil)
213s,a,b,c,d = coroutine.resume(f, "xuxu")
214eqtab(_G.x, {"xuxu"})
215assert(s and a == 1 and b == 2 and c == 3 and d == nil)
216assert(coroutine.status(f) == "dead")
217s, a = coroutine.resume(f, "xuxu")
218assert(not s and string.find(a, "dead") and coroutine.status(f) == "dead")
219
220
221-- yields in tail calls
222local function foo (i) return coroutine.yield(i) end
223f = coroutine.wrap(function ()
224  for i=1,10 do
225    assert(foo(i) == _G.x)
226  end
227  return 'a'
228end)
229for i=1,10 do _G.x = i; assert(f(i) == i) end
230_G.x = 'xuxu'; assert(f('xuxu') == 'a')
231
232-- recursive
233function pf (n, i)
234  coroutine.yield(n)
235  pf(n*i, i+1)
236end
237
238f = coroutine.wrap(pf)
239local s=1
240for i=1,10 do
241  assert(f(1, 1) == s)
242  s = s*i
243end
244
245-- sieve
246function gen (n)
247  return coroutine.wrap(function ()
248    for i=2,n do coroutine.yield(i) end
249  end)
250end
251
252
253function filter (p, g)
254  return coroutine.wrap(function ()
255    while 1 do
256      local n = g()
257      if n == nil then return end
258      if math.mod(n, p) ~= 0 then coroutine.yield(n) end
259    end
260  end)
261end
262
263local x = gen(100)
264local a = {}
265while 1 do
266  local n = x()
267  if n == nil then break end
268  table.insert(a, n)
269  x = filter(n, x)
270end
271
272assert(table.getn(a) == 25 and a[table.getn(a)] == 97)
273
274
275-- errors in coroutines
276function foo ()
277  assert(debug.getinfo(1).currentline == debug.getinfo(foo).linedefined + 1)
278  assert(debug.getinfo(2).currentline == debug.getinfo(goo).linedefined)
279  coroutine.yield(3)
280  error(foo)
281end
282
283function goo() foo() end
284x = coroutine.wrap(goo)
285assert(x() == 3)
286local a,b = pcall(x)
287assert(not a and b == foo)
288
289x = coroutine.create(goo)
290a,b = coroutine.resume(x)
291assert(a and b == 3)
292a,b = coroutine.resume(x)
293assert(not a and b == foo and coroutine.status(x) == "dead")
294a,b = coroutine.resume(x)
295assert(not a and string.find(b, "dead") and coroutine.status(x) == "dead")
296
297
298-- co-routines x for loop
299function all (a, n, k)
300  if k == 0 then coroutine.yield(a)
301  else
302    for i=1,n do
303      a[k] = i
304      all(a, n, k-1)
305    end
306  end
307end
308
309local a = 0
310for t in coroutine.wrap(function () all({}, 5, 4) end) do
311  a = a+1
312end
313assert(a == 5^4)
314
315
316-- access to locals of collected corroutines
317--[[
318local C = {}; setmetatable(C, {__mode = "kv"})
319local x = coroutine.wrap (function ()
320            local a = 10
321            local function f () a = a+10; return a end
322            while true do
323              a = a+1
324              coroutine.yield(f)
325            end
326          end)
327
328C[1] = x;
329
330local f = x()
331assert(f() == 21 and x()() == 32 and x() == f)
332x = nil
333collectgarbage()
334assert(C[1] == nil)
335assert(f() == 43 and f() == 53)
336--]]
337
338
339-- old bug: attempt to resume itself
340
341function co_func (current_co)
342  assert(coroutine.running() == current_co)
343  assert(coroutine.resume(current_co) == false)
344  assert(coroutine.resume(current_co) == false)
345  return 10
346end
347
348local co = coroutine.create(co_func)
349local a,b = coroutine.resume(co, co)
350assert(a == true and b == 10)
351assert(coroutine.resume(co, co) == false)
352assert(coroutine.resume(co, co) == false)
353
354-- access to locals of erroneous coroutines
355local x = coroutine.create (function ()
356            local a = 10
357            _G.f = function () a=a+1; return a end
358            error('x')
359          end)
360
361assert(not coroutine.resume(x))
362-- overwrite previous position of local `a'
363assert(not coroutine.resume(x, 1, 1, 1, 1, 1, 1, 1))
364assert(_G.f() == 11)
365assert(_G.f() == 12)
366
367
368if not T then
369  (Message or print)('\a\n >>> testC not active: skipping yield/hook tests <<<\n\a')
370else
371
372  local turn
373
374  function fact (t, x)
375    assert(turn == t)
376    if x == 0 then return 1
377    else return x*fact(t, x-1)
378    end
379  end
380
381  local A,B,a,b = 0,0,0,0
382
383  local x = coroutine.create(function ()
384    T.setyhook("", 2)
385    A = fact("A", 10)
386  end)
387
388  local y = coroutine.create(function ()
389    T.setyhook("", 3)
390    B = fact("B", 11)
391  end)
392
393  while A==0 or B==0 do
394    if A==0 then turn = "A"; T.resume(x) end
395    if B==0 then turn = "B"; T.resume(y) end
396  end
397
398  assert(B/A == 11)
399end
400
401
402-- leaving a pending coroutine open
403_X = coroutine.wrap(function ()
404      local a = 10
405      local x = function () a = a+1 end
406      coroutine.yield()
407    end)
408
409_X()
410
411
412-- coroutine environments
413co = coroutine.create(function ()
414       coroutine.yield(getfenv(0))
415       return loadstring("return a")()
416     end)
417
418a = {a = 15}
419debug.setfenv(co, a)
420assert(debug.getfenv(co) == a)
421assert(select(2, coroutine.resume(co)) == a)
422assert(select(2, coroutine.resume(co)) == a.a)
423
424
425print'OK'
426