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