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_EMPTY = 'EMPTY TABLE'
|
||||||
local TABLE_TYPE_SEQUENCE = 'SEQUENCE'
|
local TABLE_TYPE_SEQUENCE = 'SEQUENCE'
|
||||||
local TABLE_TYPE_STRING_MAP = 'STRING KEY MAP'
|
local TABLE_TYPE_STRING_MAP = 'STRING KEY MAP'
|
||||||
|
@ -173,6 +179,22 @@ local function escape_string (str)
|
||||||
return table.concat(l, '')
|
return table.concat(l, '')
|
||||||
end
|
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
|
-- Identifyer stuff
|
||||||
|
|
||||||
|
@ -408,7 +430,8 @@ local function format_string (str, options)
|
||||||
return left .. str .. right
|
return left .. str .. right
|
||||||
end
|
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'
|
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'
|
||||||
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
|
||||||
end
|
end
|
||||||
|
|
||||||
function format_value (value, options, depth)
|
local function format_coroutine (value)
|
||||||
local type = type(value)
|
return coroutine.status(value) .. ' coroutine: ' .. tostring(value):sub(9)
|
||||||
|
end
|
||||||
|
|
||||||
if type == 'table' then return format_table(value, options, depth or 'max')
|
local function format_primitive (value)
|
||||||
elseif type == 'string' then return format_string(value, options)
|
return tostring(value)
|
||||||
elseif type == 'number' then return format_number(value, options.math_shorthand)
|
end
|
||||||
else return tostring(value)
|
|
||||||
|
--[[
|
||||||
|
|
||||||
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -430,7 +526,7 @@ end
|
||||||
|
|
||||||
local function pretty_format (value, options)
|
local function pretty_format (value, options)
|
||||||
local options = options or {}
|
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'
|
options.indent = options.indent or '\t'
|
||||||
return format_value(value, options, 0)
|
return format_value(value, options, 0)
|
||||||
end
|
end
|
||||||
|
|
|
@ -177,7 +177,7 @@ function TestSuite:runTests (parent_prefix, parent_indent)
|
||||||
debug.sethook(nil, 'l')
|
debug.sethook(nil, 'l')
|
||||||
-- Write work (or not.)
|
-- Write work (or not.)
|
||||||
if success then
|
if success then
|
||||||
print_status(ext_name, 'SUCCESS!', TERM_COLOR_CODE_GREEN)
|
--print_status(ext_name, 'SUCCESS!', TERM_COLOR_CODE_GREEN)
|
||||||
else
|
else
|
||||||
print_status(ext_name, 'ERROR!', TERM_COLOR_CODE_RED)
|
print_status(ext_name, 'ERROR!', TERM_COLOR_CODE_RED)
|
||||||
traceback = indent_string(traceback, '\t')
|
traceback = indent_string(traceback, '\t')
|
||||||
|
|
|
@ -4,6 +4,12 @@ SUITE:setEnviroment{
|
||||||
format = require('pretty')
|
format = require('pretty')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
local ASSERT_ERROR_APPROX = [[
|
||||||
|
Approximate strings not similar enough:
|
||||||
|
Should match: %s
|
||||||
|
Gotten: %s
|
||||||
|
]]
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
local function format_test (t)
|
local function format_test (t)
|
||||||
|
@ -12,7 +18,13 @@ local function format_test (t)
|
||||||
local input_options = t.options
|
local input_options = t.options
|
||||||
local expected_result = t.expect
|
local expected_result = t.expect
|
||||||
local actual_result = format(input_value, input_options)
|
local actual_result = format(input_value, input_options)
|
||||||
assert_equal(actual_result, expected_result)
|
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)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -140,6 +152,140 @@ format_test {
|
||||||
expect = 'nan',
|
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
|
-- Single-line tables
|
||||||
|
|
||||||
|
@ -281,6 +427,31 @@ format_test {
|
||||||
expect = '{\n\t[1] = 1,\n\t[\'whatever\'] = false\n}',
|
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
|
return SUITE
|
||||||
|
|
Loading…
Reference in New Issue
Block a user