1
0

Attempting more comprehensive coverage of types, including threads/coroutines and functions.

This commit is contained in:
Jon Michael Aanes 2016-12-29 15:33:43 +01:00
parent fd8d800cf0
commit 55265378f9
3 changed files with 277 additions and 10 deletions

View File

@ -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

View File

@ -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')

View File

@ -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