280 lines
7.6 KiB
Lua
280 lines
7.6 KiB
Lua
|
-- $Id: api.lua,v 1.147 2016/11/07 13:06:25 roberto Exp $
|
||
|
-- See Copyright Notice in file all.lua
|
||
|
|
||
|
if T==nil then
|
||
|
(Message or print)('\n >>> testC not active: skipping API tests <<<\n')
|
||
|
return
|
||
|
end
|
||
|
|
||
|
local debug = require "debug"
|
||
|
|
||
|
local pack = table.pack
|
||
|
|
||
|
|
||
|
function tcheck (t1, t2)
|
||
|
assert(t1.n == (t2.n or #t2) + 1)
|
||
|
for i = 2, t1.n do assert(t1[i] == t2[i - 1]) end
|
||
|
end
|
||
|
|
||
|
|
||
|
local function checkerr (msg, f, ...)
|
||
|
local stat, err = pcall(f, ...)
|
||
|
assert(not stat and string.find(err, msg))
|
||
|
end
|
||
|
|
||
|
|
||
|
print('testing C API')
|
||
|
|
||
|
--[=[
|
||
|
do
|
||
|
local f = T.makeCfunc[[
|
||
|
getglobal error
|
||
|
pushstring bola
|
||
|
pcall 1 1 1 # call 'error' with given handler
|
||
|
pushstatus
|
||
|
return 2 # return error message and status
|
||
|
]]
|
||
|
|
||
|
local msg, st = f({}) -- invalid handler
|
||
|
assert(st == "ERRERR" and string.find(msg, "error handling"))
|
||
|
local msg, st = f(nil) -- invalid handler
|
||
|
assert(st == "ERRERR" and string.find(msg, "error handling"))
|
||
|
|
||
|
local a = setmetatable({}, {__call = function (_, x) return x:upper() end})
|
||
|
local msg, st = f(a) -- callable handler
|
||
|
assert(st == "ERRRUN" and msg == "BOLA")
|
||
|
end
|
||
|
|
||
|
do -- test returning more results than fit in the caller stack
|
||
|
local a = {}
|
||
|
for i=1,1000 do a[i] = true end; a[999] = 10
|
||
|
local b = T.testC([[pcall 1 -1 0; pop 1; tostring -1; return 1]],
|
||
|
table.unpack, a)
|
||
|
assert(b == "10")
|
||
|
end
|
||
|
|
||
|
|
||
|
-- testing globals
|
||
|
_G.a = 14; _G.b = "a31"
|
||
|
local a = {T.testC[[
|
||
|
getglobal a;
|
||
|
getglobal b;
|
||
|
getglobal b;
|
||
|
setglobal a;
|
||
|
return *
|
||
|
]]}
|
||
|
assert(a[2] == 14 and a[3] == "a31" and a[4] == nil and _G.a == "a31")
|
||
|
|
||
|
|
||
|
-- colect in cl the `val' of all collected userdata
|
||
|
tt = {}
|
||
|
cl = {n=0}
|
||
|
A = nil; B = nil
|
||
|
local F
|
||
|
F = function (x)
|
||
|
local udval = T.udataval(x)
|
||
|
table.insert(cl, udval)
|
||
|
local d = T.newuserdata(100) -- cria lixo
|
||
|
d = nil
|
||
|
assert(debug.getmetatable(x).__gc == F)
|
||
|
assert(load("table.insert({}, {})"))() -- cria mais lixo
|
||
|
collectgarbage() -- forca coleta de lixo durante coleta!
|
||
|
assert(debug.getmetatable(x).__gc == F) -- coleta anterior nao melou isso?
|
||
|
local dummy = {} -- cria lixo durante coleta
|
||
|
if A ~= nil then
|
||
|
assert(type(A) == "userdata")
|
||
|
assert(T.udataval(A) == B)
|
||
|
debug.getmetatable(A) -- just acess it
|
||
|
end
|
||
|
A = x -- ressucita userdata
|
||
|
B = udval
|
||
|
return 1,2,3
|
||
|
end
|
||
|
tt.__gc = F
|
||
|
|
||
|
-- test whether udate collection frees memory in the right time
|
||
|
do
|
||
|
collectgarbage();
|
||
|
collectgarbage();
|
||
|
local x = collectgarbage("count");
|
||
|
local a = T.newuserdata(5001)
|
||
|
assert(T.testC("objsize 2; return 1", a) == 5001)
|
||
|
assert(collectgarbage("count") >= x+4)
|
||
|
a = nil
|
||
|
collectgarbage();
|
||
|
assert(collectgarbage("count") <= x+1)
|
||
|
-- udata without finalizer
|
||
|
x = collectgarbage("count")
|
||
|
collectgarbage("stop")
|
||
|
for i=1,1000 do T.newuserdata(0) end
|
||
|
assert(collectgarbage("count") > x+10)
|
||
|
collectgarbage()
|
||
|
assert(collectgarbage("count") <= x+1)
|
||
|
-- udata with finalizer
|
||
|
collectgarbage()
|
||
|
x = collectgarbage("count")
|
||
|
collectgarbage("stop")
|
||
|
a = {__gc = function () end}
|
||
|
for i=1,1000 do debug.setmetatable(T.newuserdata(0), a) end
|
||
|
assert(collectgarbage("count") >= x+10)
|
||
|
collectgarbage() -- this collection only calls TM, without freeing memory
|
||
|
assert(collectgarbage("count") >= x+10)
|
||
|
collectgarbage() -- now frees memory
|
||
|
assert(collectgarbage("count") <= x+1)
|
||
|
collectgarbage("restart")
|
||
|
end
|
||
|
|
||
|
|
||
|
collectgarbage("stop")
|
||
|
|
||
|
-- create 3 userdatas with tag `tt'
|
||
|
a = T.newuserdata(0); debug.setmetatable(a, tt); na = T.udataval(a)
|
||
|
b = T.newuserdata(0); debug.setmetatable(b, tt); nb = T.udataval(b)
|
||
|
c = T.newuserdata(0); debug.setmetatable(c, tt); nc = T.udataval(c)
|
||
|
|
||
|
-- create userdata without meta table
|
||
|
x = T.newuserdata(4)
|
||
|
y = T.newuserdata(0)
|
||
|
|
||
|
-- checkerr("FILE%* expected, got userdata", io.input, a)
|
||
|
-- checkerr("FILE%* expected, got userdata", io.input, x)
|
||
|
|
||
|
assert(debug.getmetatable(x) == nil and debug.getmetatable(y) == nil)
|
||
|
|
||
|
d=T.ref(a);
|
||
|
e=T.ref(b);
|
||
|
f=T.ref(c);
|
||
|
t = {T.getref(d), T.getref(e), T.getref(f)}
|
||
|
assert(t[1] == a and t[2] == b and t[3] == c)
|
||
|
|
||
|
t=nil; a=nil; c=nil;
|
||
|
T.unref(e); T.unref(f)
|
||
|
|
||
|
collectgarbage()
|
||
|
|
||
|
]=]
|
||
|
|
||
|
-------------------------------------------------------------------------
|
||
|
-- testing memory limits
|
||
|
-------------------------------------------------------------------------
|
||
|
T.totalmem(T.totalmem()+45*2048) -- set low memory limit
|
||
|
local function fillstack(x,a,b,c,d,e,f,g,h,i)
|
||
|
local j,k,l,m,n,o,p,q,r = a,b,c,d,e,f,g,h,i
|
||
|
if x > 0 then fillstack(x-1,j,k,l,m,n,o,p,q,r) end
|
||
|
end
|
||
|
|
||
|
local calls = 1
|
||
|
local function gcfunc(ud) -- upval: calls
|
||
|
local j,k,l,m,n,o,p,q,r = 0,0,0,0,0,0,0,0,0
|
||
|
print (('GC for %s called with %u fillstacks'):format(tostring(ud),calls))
|
||
|
calls = calls + 10
|
||
|
fillstack(calls,j,k,l,m,n,o,p,q,r)
|
||
|
end
|
||
|
local a ={}
|
||
|
collectgarbage("restart")
|
||
|
collectgarbage("setpause",120)
|
||
|
collectgarbage("setstepmul",1000)
|
||
|
for i = 1, 20 do
|
||
|
local UD = T.newuserdata(2048); debug.setmetatable(UD, {__gc = gcfunc})
|
||
|
a[i] = UD
|
||
|
print (('a[%u] = %s'):format(i, tostring(UD)))
|
||
|
end
|
||
|
|
||
|
for i = 1, 20 do
|
||
|
j = (513 * i % 20) + 1
|
||
|
k = (13 + 257 * i % 20) + 1
|
||
|
calls = calls + 1
|
||
|
local UD = T.newuserdata(2048); debug.setmetatable(UD, {__gc = gcfunc})
|
||
|
print (('setting a[%u] = nil, a[%u] = %s'):format(j, k, tostring(UD)))
|
||
|
a[j]=nil; a[k] = UD
|
||
|
end
|
||
|
print ("Done")
|
||
|
--[=[
|
||
|
checkerr("block too big", T.newuserdata, math.maxinteger)
|
||
|
collectgarbage()
|
||
|
T.totalmem(T.totalmem()+5000) -- set low memory limit (+5k)
|
||
|
checkerr("not enough memory", load"local a={}; for i=1,100000 do a[i]=i end")
|
||
|
T.totalmem(0) -- restore high limit
|
||
|
|
||
|
-- test memory errors; increase memory limit in small steps, so that
|
||
|
-- we get memory errors in different parts of a given task, up to there
|
||
|
-- is enough memory to complete the task without errors
|
||
|
function testamem (s, f)
|
||
|
collectgarbage(); collectgarbage()
|
||
|
local M = T.totalmem()
|
||
|
local oldM = M
|
||
|
local a,b = nil
|
||
|
while 1 do
|
||
|
M = M+7 -- increase memory limit in small steps
|
||
|
T.totalmem(M)
|
||
|
a, b = pcall(f)
|
||
|
T.totalmem(0) -- restore high limit
|
||
|
if a and b then break end -- stop when no more errors
|
||
|
collectgarbage()
|
||
|
if not a and not -- `real' error?
|
||
|
(string.find(b, "memory") or string.find(b, "overflow")) then
|
||
|
error(b, 0) -- propagate it
|
||
|
end
|
||
|
end
|
||
|
print("\nlimit for " .. s .. ": " .. M-oldM)
|
||
|
return b
|
||
|
end
|
||
|
|
||
|
|
||
|
-- testing memory errors when creating a new state
|
||
|
|
||
|
b = testamem("state creation", T.newstate)
|
||
|
T.closestate(b); -- close new state
|
||
|
|
||
|
-- testing luaL_newmetatable
|
||
|
local mt_xuxu, res, top = T.testC("newmetatable xuxu; gettop; return 3")
|
||
|
assert(type(mt_xuxu) == "table" and res and top == 3)
|
||
|
local d, res, top = T.testC("newmetatable xuxu; gettop; return 3")
|
||
|
assert(mt_xuxu == d and not res and top == 3)
|
||
|
d, res, top = T.testC("newmetatable xuxu1; gettop; return 3")
|
||
|
assert(mt_xuxu ~= d and res and top == 3)
|
||
|
|
||
|
x = T.newuserdata(0);
|
||
|
y = T.newuserdata(0);
|
||
|
T.testC("pushstring xuxu; gettable R; setmetatable 2", x)
|
||
|
assert(getmetatable(x) == mt_xuxu)
|
||
|
|
||
|
-- testing luaL_testudata
|
||
|
-- correct metatable
|
||
|
local res1, res2, top = T.testC([[testudata -1 xuxu
|
||
|
testudata 2 xuxu
|
||
|
gettop
|
||
|
return 3]], x)
|
||
|
assert(res1 and res2 and top == 4)
|
||
|
|
||
|
-- wrong metatable
|
||
|
res1, res2, top = T.testC([[testudata -1 xuxu1
|
||
|
testudata 2 xuxu1
|
||
|
gettop
|
||
|
return 3]], x)
|
||
|
assert(not res1 and not res2 and top == 4)
|
||
|
|
||
|
-- non-existent type
|
||
|
res1, res2, top = T.testC([[testudata -1 xuxu2
|
||
|
testudata 2 xuxu2
|
||
|
gettop
|
||
|
return 3]], x)
|
||
|
assert(not res1 and not res2 and top == 4)
|
||
|
|
||
|
-- userdata has no metatable
|
||
|
res1, res2, top = T.testC([[testudata -1 xuxu
|
||
|
testudata 2 xuxu
|
||
|
gettop
|
||
|
return 3]], y)
|
||
|
assert(not res1 and not res2 and top == 4)
|
||
|
|
||
|
-- erase metatables
|
||
|
do
|
||
|
local r = debug.getregistry()
|
||
|
assert(r.xuxu == mt_xuxu and r.xuxu1 == d)
|
||
|
r.xuxu = nil; r.xuxu1 = nil
|
||
|
end
|
||
|
]=]
|
||
|
print'OK'
|
||
|
|