-- utilities to check that args of various types pass or fail
-- argument type checking
local ok = '-\t'
local fail = 'fail '
local needcheck = 'needcheck '
local badmsg = 'badmsg '
akey = 'aa'
astring = 'abc'
astrnum = '789'
anumber = 1.25
ainteger = 345
adouble = 12.75
aboolean = true
atable = {[akey]=456}
afunction = function() end
anil = nil
athread = coroutine.create(afunction)
anylua = { nil, astring, anumber, aboolean, atable, afunction, athread }
somestring = { astring, anumber }
somenumber = { anumber, astrnum }
someboolean = { aboolean }
sometable = { atable }
somefunction = { afunction }
somenil = { anil }
somekey = { akey }
notakey = { astring, anumber, aboolean, atable, afunction }
notastring = { nil, aboolean, atable, afunction, athread }
notanumber = { nil, astring, aboolean, atable, afunction, athread }
notaboolean = { nil, astring, anumber, atable, afunction, athread }
notatable = { nil, astring, anumber, aboolean, afunction, athread }
notafunction = { nil, astring, anumber, aboolean, atable, athread }
notathread = { nil, astring, anumber, aboolean, atable, afunction }
notanil = { astring, anumber, aboolean, atable, afunction, athread }
nonstring = { aboolean, atable, afunction, athread }
nonnumber = { astring, aboolean, atable, afunction, athread }
nonboolean = { astring, anumber, atable, afunction, athread }
nontable = { astring, anumber, aboolean, afunction, athread }
nonfunction = { astring, anumber, aboolean, atable, athread }
nonthread = { astring, anumber, aboolean, atable, afunction }
nonkey = { astring, anumber, aboolean, atable, afunction }
local structtypes = {
['table']='
',
['function']='',
['thread']='',
['userdata']='',
}
local function bracket(v)
return "<"..type(v)..">"
end
local function quote(v)
return "'"..v.."'"
end
local function ellipses(v)
local s = tostring(v)
return #s <= 8 and s or (string.sub(s,1,8)..'...')
end
local pretty = {
['table']=bracket,
['function']=bracket,
['thread']=bracket,
['userdata']=bracket,
['string']= quote,
['number']= ellipses,
}
local function values(list)
local t = {}
for i=1,(list.n or #list) do
local ai = list[i]
local fi = pretty[type(ai)]
t[i] = fi and fi(ai) or tostring(ai)
end
return table.concat(t,',')
end
local function types(list)
local t = {}
for i=1,(list.n or #list) do
local ai = list[i]
t[i] = type(ai)
end
return table.concat(t,',')
end
local function signature(name,arglist)
return name..'('..values(arglist)..')'
end
local function dup(t)
local s = {}
for i=1,(t.n or #t) do
s[i] = t[i]
end
return s
end
local function split(t)
local s = {}
local n = (t.n or #t)
for i=1,n-1 do
s[i] = t[i]
end
return s,t[n]
end
local function expand(argsets, typesets, ...)
local n = typesets and #typesets or 0
if n <= 0 then
table.insert(argsets,arg)
return argsets
end
local s,v = split(typesets)
for i=1,(v.n or #v) do
expand(argsets, s, v[i], unpack(arg,1,arg.n))
end
return argsets
end
local function arglists(typesets)
local argsets = expand({},typesets)
return ipairs(argsets)
end
function lookup( name )
return loadstring('return '..name)()
end
function invoke( name, arglist )
local s,c = pcall(lookup, name)
if not s then return s,c end
return pcall(c, unpack(arglist,1,arglist.n or #arglist))
end
-- messages, banners
local _print = print
local _tostring = tostring
local _find = string.find
function banner(name)
_print( '====== '.._tostring(name)..' ======' )
end
local function subbanner(name)
_print( '--- '.._tostring(name) )
end
local function pack(s,...)
return s,arg
end
-- check that all combinations of arguments pass
function checkallpass( name, typesets, typesonly )
subbanner('checkallpass')
for i,v in arglists(typesets) do
local sig = signature(name,v)
local s,r = pack( invoke( name, v ) )
if s then
if typesonly then
_print( ok, sig, types(r) )
else
_print( ok, sig, values(r) )
end
else
_print( fail, sig, values(r) )
end
end
end
-- check that all combinations of arguments fail in some way,
-- ignore error messages
function checkallerrors( name, typesets, template )
subbanner('checkallerrors')
template = _tostring(template)
for i,v in arglists(typesets) do
local sig = signature(name,v)
local s,e = invoke( name, v )
if not s then
if _find(e, template, 1, true) then
_print( ok, sig, '...'..template..'...' )
else
_print( badmsg, sig, "template='"..template.."' actual='"..e.."'" )
end
else
_print( needcheck, sig, e )
end
end
end