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'