github.com/tul/gopher-lua@v0.0.0-20181008131706-f6fcaab0c612/_lua5.1-tests/closure.lua (about) 1 print "testing closures and coroutines" 2 --[[ 3 4 local A,B = 0,{g=10} 5 function 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 21 end 22 23 a = f(10) 24 -- force a GC in this level 25 local x = {[1] = {}} -- to detect a GC 26 setmetatable(x, {__mode = 'kv'}) 27 while x[1] do -- repeat until GC 28 local a = A..A..A..A -- create garbage 29 A = A+1 30 end 31 assert(a[1]() == 20+A) 32 assert(a[1]() == 30+A) 33 assert(a[2]() == 10+A) 34 collectgarbage() 35 assert(a[2]() == 20+A) 36 assert(a[2]() == 30+A) 37 assert(a[3]() == 20+A) 38 assert(a[8]() == 10+A) 39 assert(getmetatable(x).__mode == 'kv') 40 assert(B.g == 19) 41 --]] 42 43 -- testing closures with 'for' control variable 44 a = {} 45 for 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 48 end 49 assert(a[4] == nil) 50 a[1].set(10) 51 assert(a[2].get() == 2) 52 a[2].set('a') 53 assert(a[3].get() == 3) 54 assert(a[2].get() == 'a') 55 56 a = {} 57 for 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 61 end 62 a[1].set(10, 20) 63 local r,s = a[2].get() 64 assert(r == 2 and s == 'b') 65 r,s = a[1].get() 66 assert(r == 10 and s == 20) 67 a[2].set('a', 'b') 68 r,s = a[2].get() 69 assert(r == "a" and s == "b") 70 71 72 -- testing closures with 'for' control variable x break 73 for i=1,3 do 74 f = function () return i end 75 break 76 end 77 assert(f() == 1) 78 79 for k, v in pairs{"a", "b"} do 80 f = function () return k, v end 81 break 82 end 83 assert(({f()})[1] == 1) 84 assert(({f()})[2] == "a") 85 86 87 -- testing closure x break x return x errors 88 89 local b 90 function 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 108 end 109 110 for i=1,3 do 111 f(i) 112 assert(b('get') == 'xuxu') 113 b('set', 10); assert(b('get') == 10+i) 114 b = nil 115 end 116 117 pcall(f, 4); 118 assert(b('get') == 'xuxu') 119 b('set', 10); assert(b('get') == 14) 120 121 122 local w 123 -- testing multi-level closure 124 function f(x) 125 return function (y) 126 return function (z) return w+x+y+z end 127 end 128 end 129 130 y = f(10) 131 w = 1.345 132 assert(y(20)(30) == 60+w) 133 134 -- testing closures x repeat-until 135 136 local a = {} 137 local i = 1 138 repeat 139 local x = i 140 a[i] = function () i = x+1; return x end 141 until i > 10 or a[i]() ~= x 142 assert(i == 11 and a[1]() == 1 and a[3]() == 3 and i == 4) 143 144 print'+' 145 146 147 -- test for correctly closing upvalues in tail calls of vararg functions 148 local 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) 153 end 154 t() 155 156 157 -- coroutine tests 158 159 local f 160 161 assert(coroutine.running() == nil) 162 163 164 -- tests for global environment 165 166 local 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) 172 end 173 174 f = coroutine.wrap(foo) 175 local a = {} 176 assert(f(a) == _G) 177 local a,b = pcall(f) 178 assert(a and b == _G) 179 180 181 -- tests for multiple yield/resume arguments 182 183 local 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 188 end 189 190 _G.x = nil -- declare x 191 function 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) 199 end 200 201 f = coroutine.create(foo) 202 assert(type(f) == "thread" and coroutine.status(f) == "suspended") 203 assert(string.find(tostring(f), "thread")) 204 local s,a,b,c,d 205 s,a,b,c,d = coroutine.resume(f, {1,2,3}, {}, {1}, {'a', 'b', 'c'}) 206 assert(s and a == nil and coroutine.status(f) == "suspended") 207 s,a,b,c,d = coroutine.resume(f) 208 eqtab(_G.x, {}) 209 assert(s and a == 1 and b == nil) 210 s,a,b,c,d = coroutine.resume(f, 1, 2, 3) 211 eqtab(_G.x, {1, 2, 3}) 212 assert(s and a == 'a' and b == 'b' and c == 'c' and d == nil) 213 s,a,b,c,d = coroutine.resume(f, "xuxu") 214 eqtab(_G.x, {"xuxu"}) 215 assert(s and a == 1 and b == 2 and c == 3 and d == nil) 216 assert(coroutine.status(f) == "dead") 217 s, a = coroutine.resume(f, "xuxu") 218 assert(not s and string.find(a, "dead") and coroutine.status(f) == "dead") 219 220 221 -- yields in tail calls 222 local function foo (i) return coroutine.yield(i) end 223 f = coroutine.wrap(function () 224 for i=1,10 do 225 assert(foo(i) == _G.x) 226 end 227 return 'a' 228 end) 229 for i=1,10 do _G.x = i; assert(f(i) == i) end 230 _G.x = 'xuxu'; assert(f('xuxu') == 'a') 231 232 -- recursive 233 function pf (n, i) 234 coroutine.yield(n) 235 pf(n*i, i+1) 236 end 237 238 f = coroutine.wrap(pf) 239 local s=1 240 for i=1,10 do 241 assert(f(1, 1) == s) 242 s = s*i 243 end 244 245 -- sieve 246 function gen (n) 247 return coroutine.wrap(function () 248 for i=2,n do coroutine.yield(i) end 249 end) 250 end 251 252 253 function 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) 261 end 262 263 local x = gen(100) 264 local a = {} 265 while 1 do 266 local n = x() 267 if n == nil then break end 268 table.insert(a, n) 269 x = filter(n, x) 270 end 271 272 assert(table.getn(a) == 25 and a[table.getn(a)] == 97) 273 274 275 -- errors in coroutines 276 function 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) 281 end 282 283 function goo() foo() end 284 x = coroutine.wrap(goo) 285 assert(x() == 3) 286 local a,b = pcall(x) 287 assert(not a and b == foo) 288 289 x = coroutine.create(goo) 290 a,b = coroutine.resume(x) 291 assert(a and b == 3) 292 a,b = coroutine.resume(x) 293 assert(not a and b == foo and coroutine.status(x) == "dead") 294 a,b = coroutine.resume(x) 295 assert(not a and string.find(b, "dead") and coroutine.status(x) == "dead") 296 297 298 -- co-routines x for loop 299 function 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 307 end 308 309 local a = 0 310 for t in coroutine.wrap(function () all({}, 5, 4) end) do 311 a = a+1 312 end 313 assert(a == 5^4) 314 315 316 -- access to locals of collected corroutines 317 --[[ 318 local C = {}; setmetatable(C, {__mode = "kv"}) 319 local 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 328 C[1] = x; 329 330 local f = x() 331 assert(f() == 21 and x()() == 32 and x() == f) 332 x = nil 333 collectgarbage() 334 assert(C[1] == nil) 335 assert(f() == 43 and f() == 53) 336 --]] 337 338 339 -- old bug: attempt to resume itself 340 341 function 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 346 end 347 348 local co = coroutine.create(co_func) 349 local a,b = coroutine.resume(co, co) 350 assert(a == true and b == 10) 351 assert(coroutine.resume(co, co) == false) 352 assert(coroutine.resume(co, co) == false) 353 354 -- access to locals of erroneous coroutines 355 local x = coroutine.create (function () 356 local a = 10 357 _G.f = function () a=a+1; return a end 358 error('x') 359 end) 360 361 assert(not coroutine.resume(x)) 362 -- overwrite previous position of local `a' 363 assert(not coroutine.resume(x, 1, 1, 1, 1, 1, 1, 1)) 364 assert(_G.f() == 11) 365 assert(_G.f() == 12) 366 367 368 if not T then 369 (Message or print)('\a\n >>> testC not active: skipping yield/hook tests <<<\n\a') 370 else 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) 399 end 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 413 co = coroutine.create(function () 414 coroutine.yield(getfenv(0)) 415 return loadstring("return a")() 416 end) 417 418 a = {a = 15} 419 debug.setfenv(co, a) 420 assert(debug.getfenv(co) == a) 421 assert(select(2, coroutine.resume(co)) == a) 422 assert(select(2, coroutine.resume(co)) == a.a) 423 424 425 print'OK'