Attempting more comprehensive coverage of types, including threads/coroutines and functions.
This commit is contained in:
parent
fd8d800cf0
commit
55265378f9
112
pretty.lua
112
pretty.lua
|
@ -1,4 +1,10 @@
|
|||
|
||||
local ERROR_UNKNOWN_TYPE = [[
|
||||
[pretty]: Attempting to format unsupported value of type "%s".
|
||||
A native formatting of the value is: %s
|
||||
This is a bug, and should be reported.
|
||||
]]
|
||||
|
||||
local TABLE_TYPE_EMPTY = 'EMPTY TABLE'
|
||||
local TABLE_TYPE_SEQUENCE = 'SEQUENCE'
|
||||
local TABLE_TYPE_STRING_MAP = 'STRING KEY MAP'
|
||||
|
@ -173,6 +179,22 @@ local function escape_string (str)
|
|||
return table.concat(l, '')
|
||||
end
|
||||
|
||||
local function get_function_info (f)
|
||||
-- Regarding get-info:
|
||||
-- * No need to includ 'f'. Function is already known
|
||||
-- * No need to include 'L' (active lines) option. Ignored
|
||||
-- * No need to include 'n' (name and namewhat). Won't work.
|
||||
local info = debug.getinfo(f, 'Su')
|
||||
info.params = {}
|
||||
info.ups = {}
|
||||
info.env = debug.getfenv(f)
|
||||
info.builtin = info.source == '=[C]'
|
||||
for i = 1, info.nparams do info.params[i] = debug.getlocal(f, i) end
|
||||
for i = 1, info.nups do local k, v = debug.getupvalue(f, i); info.ups[k] = v end
|
||||
|
||||
return info
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Identifyer stuff
|
||||
|
||||
|
@ -408,7 +430,8 @@ local function format_string (str, options)
|
|||
return left .. str .. right
|
||||
end
|
||||
|
||||
local function format_number (value, shorthand)
|
||||
local function format_number (value, options)
|
||||
local shorthand = options.math_shorthand
|
||||
if value ~= value then return shorthand and 'nan' or '0/0'
|
||||
elseif value == 1/0 then return shorthand and 'inf' or '1/0'
|
||||
elseif value == -1/0 then return shorthand and '-inf' or '-1/0'
|
||||
|
@ -416,13 +439,86 @@ local function format_number (value, shorthand)
|
|||
end
|
||||
end
|
||||
|
||||
function format_value (value, options, depth)
|
||||
local type = type(value)
|
||||
local function format_coroutine (value)
|
||||
return coroutine.status(value) .. ' coroutine: ' .. tostring(value):sub(9)
|
||||
end
|
||||
|
||||
if type == 'table' then return format_table(value, options, depth or 'max')
|
||||
elseif type == 'string' then return format_string(value, options)
|
||||
elseif type == 'number' then return format_number(value, options.math_shorthand)
|
||||
else return tostring(value)
|
||||
local function format_primitive (value)
|
||||
return tostring(value)
|
||||
end
|
||||
|
||||
--[[
|
||||
|
||||
if not options.more_function_info or depth ~= 0 then return table.concat(l, '') end
|
||||
|
||||
-- More info! --
|
||||
|
||||
-- source
|
||||
l[#l+1] = '\n'
|
||||
l[#l+1] = options.indent
|
||||
l[#l+1] = 'source = '
|
||||
l[#l+1] = info.short_src -- Maybe change to a longer
|
||||
|
||||
-- upvalues
|
||||
if info.nups > 0 then
|
||||
l[#l+1] = '\n'
|
||||
l[#l+1] = options.indent
|
||||
l[#l+1] = 'upvalues = '
|
||||
l[#l+1] = format_value(info.ups, options, depth + 1)
|
||||
end
|
||||
|
||||
|
||||
--if info.nups > 0 and not info.builtin and info.more_function_info then
|
||||
l[#l+1] = '('
|
||||
l[#l+1] = tostring(value)
|
||||
l[#l+1] = '): '
|
||||
l[#l+1] = format_value(info, options, depth + 1)
|
||||
--end
|
||||
]]
|
||||
|
||||
local function format_function (value, options, depth)
|
||||
local info = get_function_info(value)
|
||||
|
||||
local l = {}
|
||||
|
||||
-- Func def
|
||||
if info.builtin then l[#l+1] = 'builtin ' end
|
||||
l[#l+1] = 'function ('
|
||||
-- List parameters
|
||||
for _, param in ipairs(info.params) do
|
||||
l[#l+1] = param
|
||||
l[#l+1] = ', '
|
||||
end
|
||||
-- Show varg
|
||||
if info.isvararg then l[#l+1] = '...' end
|
||||
|
||||
-- Cleanup and finish
|
||||
if l[#l] == ', ' then l[#l] = nil end
|
||||
l[#l+1] = ') ... end'
|
||||
|
||||
return table.concat(l, '')
|
||||
end
|
||||
|
||||
local TYPE_TO_FORMAT_FUNC = {
|
||||
['nil'] = format_primitive,
|
||||
['boolean'] = format_primitive,
|
||||
['number'] = format_number,
|
||||
['string'] = format_string,
|
||||
['thread'] = format_coroutine,
|
||||
['table'] = format_table,
|
||||
|
||||
-- TODO
|
||||
['function'] = format_function,
|
||||
['userdata'] = format_primitive,
|
||||
['cdata'] = format_primitive, -- Luajit exclusive ?
|
||||
}
|
||||
|
||||
function format_value (value, options, depth)
|
||||
local format_func = TYPE_TO_FORMAT_FUNC[type(value)]
|
||||
if format_func then
|
||||
return format_func(value, options, depth)
|
||||
else
|
||||
error(ERROR_UNKNOWN_TYPE:format(type(value), tostring(value)))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -430,7 +526,7 @@ end
|
|||
|
||||
local function pretty_format (value, options)
|
||||
local options = options or {}
|
||||
options.max_depth = options.max_depth or math.huge
|
||||
options.max_depth = options.max_depth or 3--math.huge
|
||||
options.indent = options.indent or '\t'
|
||||
return format_value(value, options, 0)
|
||||
end
|
||||
|
|
|
@ -177,7 +177,7 @@ function TestSuite:runTests (parent_prefix, parent_indent)
|
|||
debug.sethook(nil, 'l')
|
||||
-- Write work (or not.)
|
||||
if success then
|
||||
print_status(ext_name, 'SUCCESS!', TERM_COLOR_CODE_GREEN)
|
||||
--print_status(ext_name, 'SUCCESS!', TERM_COLOR_CODE_GREEN)
|
||||
else
|
||||
print_status(ext_name, 'ERROR!', TERM_COLOR_CODE_RED)
|
||||
traceback = indent_string(traceback, '\t')
|
||||
|
|
|
@ -4,6 +4,12 @@ SUITE:setEnviroment{
|
|||
format = require('pretty')
|
||||
}
|
||||
|
||||
local ASSERT_ERROR_APPROX = [[
|
||||
Approximate strings not similar enough:
|
||||
Should match: %s
|
||||
Gotten: %s
|
||||
]]
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
local function format_test (t)
|
||||
|
@ -12,7 +18,13 @@ local function format_test (t)
|
|||
local input_options = t.options
|
||||
local expected_result = t.expect
|
||||
local actual_result = format(input_value, input_options)
|
||||
if not t.approx or type(actual_result) ~= 'string' then
|
||||
assert_equal(actual_result, expected_result)
|
||||
else
|
||||
if not actual_result:match(expected_result) then
|
||||
error(ASSERT_ERROR_APPROX:format(expected_result, actual_result))
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
|
@ -140,6 +152,140 @@ format_test {
|
|||
expect = 'nan',
|
||||
}
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Primitive types
|
||||
|
||||
format_test {
|
||||
input = nil,
|
||||
expect = 'nil',
|
||||
}
|
||||
|
||||
format_test {
|
||||
input = true,
|
||||
expect = 'true',
|
||||
}
|
||||
|
||||
format_test {
|
||||
input = false,
|
||||
expect = 'false',
|
||||
}
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Function printing
|
||||
|
||||
format_test {
|
||||
input = function () end,
|
||||
expect = 'function () ... end',
|
||||
}
|
||||
|
||||
format_test {
|
||||
input = function (a) end,
|
||||
expect = 'function (a) ... end',
|
||||
}
|
||||
|
||||
format_test {
|
||||
input = function (a, b) end,
|
||||
expect = 'function (a, b) ... end',
|
||||
}
|
||||
|
||||
format_test {
|
||||
input = function (...) end,
|
||||
expect = 'function (...) ... end',
|
||||
}
|
||||
|
||||
format_test {
|
||||
input = function (a, b, ...) end,
|
||||
expect = 'function (a, b, ...) ... end',
|
||||
}
|
||||
|
||||
do
|
||||
local SOME_RANDOM_UPVALUE = false
|
||||
|
||||
format_test {
|
||||
input = function () l = SOME_RANDOM_UPVALUE end,
|
||||
expect = 'function () ... end',
|
||||
}
|
||||
|
||||
format_test {
|
||||
input = function () SOME_RANDOM_UPVALUE = true end,
|
||||
expect = 'function () ... end',
|
||||
}
|
||||
|
||||
format_test {
|
||||
-- More function info is ignored if not at depth 0.
|
||||
input = { a = function () SOME_RANDOM_UPVALUE = true end },
|
||||
options = { more_function_info = true },
|
||||
expect = '{\n\ta = function () ... end\n}',
|
||||
}
|
||||
|
||||
format_test {
|
||||
input = function () l = SOME_RANDOM_UPVALUE end,
|
||||
options = { more_function_info = true },
|
||||
expect = 'function ()\n\t-- source_file = \'./test/test_pretty.lua\'\n\t-- up_values = { SOME_RANDOM_UPVALUE = false }\n\n\t...\nend'
|
||||
}
|
||||
|
||||
format_test {
|
||||
input = function () SOME_RANDOM_UPVALUE = true end,
|
||||
options = { more_function_info = true },
|
||||
expect = 'function ()\n\t-- source_file = \'./test/test_pretty.lua\'\n\t-- up_values = { SOME_RANDOM_UPVALUE = false }\n\n\t...\nend'
|
||||
}
|
||||
end
|
||||
|
||||
format_test {
|
||||
input = function () end,
|
||||
options = { more_function_info = true },
|
||||
expect = 'function () ... end',
|
||||
}
|
||||
|
||||
do
|
||||
local index = 0
|
||||
|
||||
format_test {
|
||||
input = function () index = index + 1; return index end,
|
||||
expect = 'function ()\n\t-- source_file = \'./test/test_pretty.lua\'\n\t-- up_values = { index = 0 }\n\n\t...\nend'
|
||||
}
|
||||
end
|
||||
|
||||
format_test {
|
||||
input = loadstring('return function () end')(),
|
||||
expect = 'function () ... end',
|
||||
}
|
||||
|
||||
format_test {
|
||||
input = pairs,
|
||||
expect = 'builtin function (...) ... end',
|
||||
}
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Userdata printing
|
||||
|
||||
format_test {
|
||||
input = 'DUNNO MAYBE USE LUAJIT IN SOME WAY?',
|
||||
expect = 'TODO',
|
||||
}
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Thread printing
|
||||
|
||||
do
|
||||
local suspended_coroutine = coroutine.create(function () end)
|
||||
format_test {
|
||||
input = suspended_coroutine,
|
||||
approx = true,
|
||||
expect = 'suspended coroutine: 0x%x+',
|
||||
}
|
||||
end
|
||||
|
||||
do
|
||||
local dead_coroutine = coroutine.create(function () end)
|
||||
coroutine.resume(dead_coroutine)
|
||||
format_test {
|
||||
input = dead_coroutine,
|
||||
approx = true,
|
||||
expect = 'dead coroutine: 0x%x+',
|
||||
}
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Single-line tables
|
||||
|
||||
|
@ -281,6 +427,31 @@ format_test {
|
|||
expect = '{\n\t[1] = 1,\n\t[\'whatever\'] = false\n}',
|
||||
}
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- CDATA
|
||||
|
||||
-- TODO: Add more advanced understanding of cdata.
|
||||
|
||||
if type(jit) == 'table' then
|
||||
|
||||
local ffi = require('ffi')
|
||||
ffi.cdef[[
|
||||
int poll(struct pollfd *fds, unsigned long nfds, int timeout);
|
||||
]]
|
||||
|
||||
format_test {
|
||||
input = ffi.C.poll,
|
||||
approx = true,
|
||||
expect = 'cdata<.+>: 0x%x+',
|
||||
}
|
||||
|
||||
format_test {
|
||||
input = ffi.new('int[10]'),
|
||||
approx = true,
|
||||
expect = 'cdata<.+>: 0x%x+',
|
||||
}
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
return SUITE
|
||||
|
|
Loading…
Reference in New Issue
Block a user