123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425 |
- print "testing closures and coroutines"
- --[[
- local A,B = 0,{g=10}
- function f(x)
- local a = {}
- for i=1,1000 do
- local y = 0
- do
- a[i] = function () B.g = B.g+1; y = y+x; return y+A end
- end
- end
- local dummy = function () return a[A] end
- collectgarbage()
- A = 1; assert(dummy() == a[1]); A = 0;
- assert(a[1]() == x)
- assert(a[3]() == x)
- collectgarbage()
- assert(B.g == 12)
- return a
- end
- a = f(10)
- -- force a GC in this level
- local x = {[1] = {}} -- to detect a GC
- setmetatable(x, {__mode = 'kv'})
- while x[1] do -- repeat until GC
- local a = A..A..A..A -- create garbage
- A = A+1
- end
- assert(a[1]() == 20+A)
- assert(a[1]() == 30+A)
- assert(a[2]() == 10+A)
- collectgarbage()
- assert(a[2]() == 20+A)
- assert(a[2]() == 30+A)
- assert(a[3]() == 20+A)
- assert(a[8]() == 10+A)
- assert(getmetatable(x).__mode == 'kv')
- assert(B.g == 19)
- --]]
- -- testing closures with 'for' control variable
- a = {}
- for i=1,10 do
- a[i] = {set = function(x) i=x end, get = function () return i end}
- if i == 3 then break end
- end
- assert(a[4] == nil)
- a[1].set(10)
- assert(a[2].get() == 2)
- a[2].set('a')
- assert(a[3].get() == 3)
- assert(a[2].get() == 'a')
- a = {}
- for i, k in pairs{'a', 'b'} do
- a[i] = {set = function(x, y) i=x; k=y end,
- get = function () return i, k end}
- if i == 2 then break end
- end
- a[1].set(10, 20)
- local r,s = a[2].get()
- assert(r == 2 and s == 'b')
- r,s = a[1].get()
- assert(r == 10 and s == 20)
- a[2].set('a', 'b')
- r,s = a[2].get()
- assert(r == "a" and s == "b")
- -- testing closures with 'for' control variable x break
- for i=1,3 do
- f = function () return i end
- break
- end
- assert(f() == 1)
- for k, v in pairs{"a", "b"} do
- f = function () return k, v end
- break
- end
- assert(({f()})[1] == 1)
- assert(({f()})[2] == "a")
- -- testing closure x break x return x errors
- local b
- function f(x)
- local first = 1
- while 1 do
- if x == 3 and not first then return end
- local a = 'xuxu'
- b = function (op, y)
- if op == 'set' then
- a = x+y
- else
- return a
- end
- end
- if x == 1 then do break end
- elseif x == 2 then return
- else if x ~= 3 then error() end
- end
- first = nil
- end
- end
- for i=1,3 do
- f(i)
- assert(b('get') == 'xuxu')
- b('set', 10); assert(b('get') == 10+i)
- b = nil
- end
- pcall(f, 4);
- assert(b('get') == 'xuxu')
- b('set', 10); assert(b('get') == 14)
- local w
- -- testing multi-level closure
- function f(x)
- return function (y)
- return function (z) return w+x+y+z end
- end
- end
- y = f(10)
- w = 1.345
- assert(y(20)(30) == 60+w)
- -- testing closures x repeat-until
- local a = {}
- local i = 1
- repeat
- local x = i
- a[i] = function () i = x+1; return x end
- until i > 10 or a[i]() ~= x
- assert(i == 11 and a[1]() == 1 and a[3]() == 3 and i == 4)
- print'+'
- -- test for correctly closing upvalues in tail calls of vararg functions
- local function t ()
- local function c(a,b) assert(a=="test" and b=="OK") end
- local function v(f, ...) c("test", f() ~= 1 and "FAILED" or "OK") end
- local x = 1
- return v(function() return x end)
- end
- t()
- -- coroutine tests
- local f
- assert(coroutine.running() == nil)
- -- tests for global environment
- local function foo (a)
- setfenv(0, a)
- coroutine.yield(getfenv())
- assert(getfenv(0) == a)
- assert(getfenv(1) == _G)
- return getfenv(1)
- end
- f = coroutine.wrap(foo)
- local a = {}
- assert(f(a) == _G)
- local a,b = pcall(f)
- assert(a and b == _G)
- -- tests for multiple yield/resume arguments
- local function eqtab (t1, t2)
- assert(table.getn(t1) == table.getn(t2))
- for i,v in ipairs(t1) do
- assert(t2[i] == v)
- end
- end
- _G.x = nil -- declare x
- function foo (a, ...)
- assert(coroutine.running() == f)
- assert(coroutine.status(f) == "running")
- local arg = {...}
- for i=1,table.getn(arg) do
- _G.x = {coroutine.yield(unpack(arg[i]))}
- end
- return unpack(a)
- end
- f = coroutine.create(foo)
- assert(type(f) == "thread" and coroutine.status(f) == "suspended")
- assert(string.find(tostring(f), "thread"))
- local s,a,b,c,d
- s,a,b,c,d = coroutine.resume(f, {1,2,3}, {}, {1}, {'a', 'b', 'c'})
- assert(s and a == nil and coroutine.status(f) == "suspended")
- s,a,b,c,d = coroutine.resume(f)
- eqtab(_G.x, {})
- assert(s and a == 1 and b == nil)
- s,a,b,c,d = coroutine.resume(f, 1, 2, 3)
- eqtab(_G.x, {1, 2, 3})
- assert(s and a == 'a' and b == 'b' and c == 'c' and d == nil)
- s,a,b,c,d = coroutine.resume(f, "xuxu")
- eqtab(_G.x, {"xuxu"})
- assert(s and a == 1 and b == 2 and c == 3 and d == nil)
- assert(coroutine.status(f) == "dead")
- s, a = coroutine.resume(f, "xuxu")
- assert(not s and string.find(a, "dead") and coroutine.status(f) == "dead")
- -- yields in tail calls
- local function foo (i) return coroutine.yield(i) end
- f = coroutine.wrap(function ()
- for i=1,10 do
- assert(foo(i) == _G.x)
- end
- return 'a'
- end)
- for i=1,10 do _G.x = i; assert(f(i) == i) end
- _G.x = 'xuxu'; assert(f('xuxu') == 'a')
- -- recursive
- function pf (n, i)
- coroutine.yield(n)
- pf(n*i, i+1)
- end
- f = coroutine.wrap(pf)
- local s=1
- for i=1,10 do
- assert(f(1, 1) == s)
- s = s*i
- end
- -- sieve
- function gen (n)
- return coroutine.wrap(function ()
- for i=2,n do coroutine.yield(i) end
- end)
- end
- function filter (p, g)
- return coroutine.wrap(function ()
- while 1 do
- local n = g()
- if n == nil then return end
- if math.mod(n, p) ~= 0 then coroutine.yield(n) end
- end
- end)
- end
- local x = gen(100)
- local a = {}
- while 1 do
- local n = x()
- if n == nil then break end
- table.insert(a, n)
- x = filter(n, x)
- end
- assert(table.getn(a) == 25 and a[table.getn(a)] == 97)
- -- errors in coroutines
- function foo ()
- assert(debug.getinfo(1).currentline == debug.getinfo(foo).linedefined + 1)
- assert(debug.getinfo(2).currentline == debug.getinfo(goo).linedefined)
- coroutine.yield(3)
- error(foo)
- end
- function goo() foo() end
- x = coroutine.wrap(goo)
- assert(x() == 3)
- local a,b = pcall(x)
- assert(not a and b == foo)
- x = coroutine.create(goo)
- a,b = coroutine.resume(x)
- assert(a and b == 3)
- a,b = coroutine.resume(x)
- assert(not a and b == foo and coroutine.status(x) == "dead")
- a,b = coroutine.resume(x)
- assert(not a and string.find(b, "dead") and coroutine.status(x) == "dead")
- -- co-routines x for loop
- function all (a, n, k)
- if k == 0 then coroutine.yield(a)
- else
- for i=1,n do
- a[k] = i
- all(a, n, k-1)
- end
- end
- end
- local a = 0
- for t in coroutine.wrap(function () all({}, 5, 4) end) do
- a = a+1
- end
- assert(a == 5^4)
- -- access to locals of collected corroutines
- --[[
- local C = {}; setmetatable(C, {__mode = "kv"})
- local x = coroutine.wrap (function ()
- local a = 10
- local function f () a = a+10; return a end
- while true do
- a = a+1
- coroutine.yield(f)
- end
- end)
- C[1] = x;
- local f = x()
- assert(f() == 21 and x()() == 32 and x() == f)
- x = nil
- collectgarbage()
- assert(C[1] == nil)
- assert(f() == 43 and f() == 53)
- --]]
- -- old bug: attempt to resume itself
- function co_func (current_co)
- assert(coroutine.running() == current_co)
- assert(coroutine.resume(current_co) == false)
- assert(coroutine.resume(current_co) == false)
- return 10
- end
- local co = coroutine.create(co_func)
- local a,b = coroutine.resume(co, co)
- assert(a == true and b == 10)
- assert(coroutine.resume(co, co) == false)
- assert(coroutine.resume(co, co) == false)
- -- access to locals of erroneous coroutines
- local x = coroutine.create (function ()
- local a = 10
- _G.f = function () a=a+1; return a end
- error('x')
- end)
- assert(not coroutine.resume(x))
- -- overwrite previous position of local `a'
- assert(not coroutine.resume(x, 1, 1, 1, 1, 1, 1, 1))
- assert(_G.f() == 11)
- assert(_G.f() == 12)
- if not T then
- (Message or print)('\a\n >>> testC not active: skipping yield/hook tests <<<\n\a')
- else
- local turn
-
- function fact (t, x)
- assert(turn == t)
- if x == 0 then return 1
- else return x*fact(t, x-1)
- end
- end
- local A,B,a,b = 0,0,0,0
- local x = coroutine.create(function ()
- T.setyhook("", 2)
- A = fact("A", 10)
- end)
- local y = coroutine.create(function ()
- T.setyhook("", 3)
- B = fact("B", 11)
- end)
- while A==0 or B==0 do
- if A==0 then turn = "A"; T.resume(x) end
- if B==0 then turn = "B"; T.resume(y) end
- end
- assert(B/A == 11)
- end
- -- leaving a pending coroutine open
- _X = coroutine.wrap(function ()
- local a = 10
- local x = function () a = a+1 end
- coroutine.yield()
- end)
- _X()
- -- coroutine environments
- co = coroutine.create(function ()
- coroutine.yield(getfenv(0))
- return loadstring("return a")()
- end)
- a = {a = 15}
- debug.setfenv(co, a)
- assert(debug.getfenv(co) == a)
- assert(select(2, coroutine.resume(co)) == a)
- assert(select(2, coroutine.resume(co)) == a.a)
- print'OK'
|