From f332d6bf95eef3ee9dc98980a263c108eccc8c7f Mon Sep 17 00:00:00 2001 From: Jon Michael Aanes Date: Tue, 25 Jul 2017 17:49:06 +0200 Subject: [PATCH] Major restructure in how tables are formatted. Options `max_depth` and `recursion` has been deprecated. --- README.md | 5 +- analyze_structure.lua | 3 +- function.lua | 22 ++++++--- number.lua | 17 +++++-- pretty.lua | 87 ++++++++++++++++----------------- pstring.lua | 38 ++++++++------ test/test_analyze_structure.lua | 58 +++++++++++----------- test/test_function.lua | 33 ++++++++++++- test/test_pretty.lua | 79 +++++++++--------------------- test/test_resilience.lua | 7 +-- 10 files changed, 181 insertions(+), 168 deletions(-) diff --git a/README.md b/README.md index 7777993..8d45422 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,6 @@ option a bad value. Tasks to be done before `pretty` can be called version 1.0.0, in order of priority: -- Depricate `max_depth` and `recursion` options. - Add a dedicated unicode submodule, to handle some minor alignment and character escaping issues. `pretty` should escape all malformed unicode sequences. @@ -123,6 +122,10 @@ It would be nice to have the following, but these are secondary: - Expand on the comment output in output, for `__tostring` methods, and global namespaces like `io` or `math`. - Fit output within a predefined width limit. Default to 80. +- Look into tool for understanding complex structures with recursive + definitions. Whatever modes are thought up, they should be automatic modes, + not an options. Should at least include modes for self-referential tables + and Directed-Acyclic-Graphs. ## Alternative pretty printers diff --git a/analyze_structure.lua b/analyze_structure.lua index 62c7e81..14145e9 100644 --- a/analyze_structure.lua +++ b/analyze_structure.lua @@ -228,6 +228,8 @@ local function get_table_info (t) info.is_tabular = is_tabular(t) info.is_uniform = has_uniform_structure(t) info.is_leaf_node = is_leaf_node(t) + info.key_types = get_key_types(t) + info.value_types = get_value_types(t) -- Determine type of table if not info.has_seq and not info.has_map then info.type = TABLE_TYPE.EMPTY @@ -246,7 +248,6 @@ end local function analyze_structure (root, max_depth, info) -- Argument fixing local info = info or {} - local max_depth = max_depth or math.huge -- Quick return if type(root) ~= 'table' then return info end diff --git a/function.lua b/function.lua index 6afd5ed..c3c9e4d 100644 --- a/function.lua +++ b/function.lua @@ -278,16 +278,24 @@ end -------------------------------------------------------------------------------- -return function (value, depth, l, format_value) +local DISPLAY = { -- TODO: Move To common file + HIDE = 1, + SMALL = 2, + INLINE = 3, + EXPAND = 4, +} + + +return function (value, display, l, format_value) assert(type(value) == 'function') - assert(type(depth) == 'number' and type(l) == 'table' and type(format_value) == 'function') + assert(type(display) == 'number' and type(l) == 'table' and type(format_value) == 'function') local info = get_function_info(value) local function_params, function_body = nil, '...' - if not info.docs and info.defined_how ~= 'C' and (depth == 0 or info.defined_how == 'string') then - -- Only look for documentation, when at depth 0, or when defined in + if not info.docs and info.defined_how ~= 'C' and (display == DISPLAY.EXPAND or info.defined_how == 'string') then + -- Only look for documentation, when at display = DISPLAY.EXPAND, or when defined in -- string. We don't want to open a ton of files constantly when -- formatting a table. info = get_function_body_info(info) @@ -297,7 +305,7 @@ return function (value, depth, l, format_value) end end - if info.builtin and depth == math.huge then + if info.builtin and display == DISPLAY.HIDE then assert(info.name) return l(info.name); end @@ -320,7 +328,7 @@ return function (value, depth, l, format_value) l[#l+1] = { 'align', 'func_def', width_of_strings_in_l(l, top_before) } -- Cleanup and finish - if depth ~= 0 then + if display ~= DISPLAY.EXPAND then l[#l+1] = (function_body:sub(1,1) == '\n') and '' or ' ' l[#l+1] = function_body l[#l+1] = { 'align', 'func_end', #function_body } @@ -366,7 +374,7 @@ return function (value, depth, l, format_value) if info.nups > 0 and (not info.builtin and not info.docs) then l[#l+1] = indent l[#l+1] = '-- Up values: ' - format_value(info.ups, depth + 1, l) + format_value(info.ups, DISPLAY.INLINE, l) end -- Ignore spacing and function body if it's a Λ string. diff --git a/number.lua b/number.lua index 76bd564..41b1faa 100644 --- a/number.lua +++ b/number.lua @@ -208,12 +208,19 @@ local function format_hard_num (n) assert(false) end -return function (value, depth, l) - -- Formats the number nicely. If depth is 0 and we have some space for extra - -- info, we give some tidbits, to help investigation. +local DISPLAY = { -- TODO: Move To common file + HIDE = 1, + SMALL = 2, + INLINE = 3, + EXPAND = 4, +} + +return function (value, display, l) + -- Formats the number nicely. If display is DISPLAY.EXPAND, we have some + -- space for extra info, we give some tidbits, to help investigation. assert(type(value) == 'number') - assert(type(depth) == 'number' and type(l) == 'table') + assert(type(display) == 'number' and type(l) == 'table') -- First format a "soft" version. This number is not guarenteed to accurate. -- It's purpose is to give a general idea of the value. @@ -221,7 +228,7 @@ return function (value, depth, l) -- If we have space for it, format a "hard" value, also. This number is as -- short as possible, while evaluating precisely to the value of the number. - if depth == 0 then + if display == DISPLAY.EXPAND then local hard_repr = format_hard_num(value) if l[#l] ~= hard_repr then l[#l+1] = ' -- Approx: ' diff --git a/pretty.lua b/pretty.lua index f114757..8bb3a5d 100644 --- a/pretty.lua +++ b/pretty.lua @@ -78,6 +78,13 @@ local VALUE_TYPE_SORT_ORDER = { ['function'] = 7, } +local DISPLAY = { -- TODO: Move To common file + HIDE = 1, + SMALL = 2, + INLINE = 3, + EXPAND = 4, +} + -------------------------------------------------------------------------------- -- Key-value-pair Util @@ -355,27 +362,26 @@ local function align_into_tabular_style (l, start_i, stop_i) return fix_alignment(l, start_i) end -local function fix_seperator_info (l, indent_char, max_depth) +local function fix_seperator_info (l, indent_char) -- Error Checking assert(type(l) == 'table') assert(type(indent_char) == 'string') - assert(type(max_depth) == 'number') -- Do stuff - local depth, inline_depth = 0, nil + local display, inline_depth = 0, nil for i = 1, #l do if type(l[i]) ~= 'table' then -- Do nothing elseif l[i][1] == 'seperator' then assert(l[i][2] == nil or type(l[i][2]) == 'string') - l[i] = (l[i][2] or '') .. (inline_depth and ' ' or ('\n' .. indent_char:rep(depth))) + l[i] = (l[i][2] or '') .. (inline_depth and ' ' or ('\n' .. indent_char:rep(display))) elseif l[i][1] == 'indent' then - depth, inline_depth = depth + 1, inline_depth or l[i][3] == 'inline' and depth + 1 or nil - l[i] = l[i][2] .. (inline_depth and ' ' or ('\n' .. indent_char:rep(depth))) + display, inline_depth = display + 1, inline_depth or l[i][3] == 'inline' and display + 1 or nil + l[i] = l[i][2] .. (inline_depth and ' ' or ('\n' .. indent_char:rep(display))) elseif l[i][1] == 'unindent' then - l[i] = (inline_depth and ' ' or ('\n' .. indent_char:rep(depth-1))) .. l[i][2] - depth, inline_depth = depth - 1, (depth ~= inline_depth) and inline_depth or nil + l[i] = (inline_depth and ' ' or ('\n' .. indent_char:rep(display-1))) .. l[i][2] + display, inline_depth = display - 1, (display ~= inline_depth) and inline_depth or nil end end end @@ -388,25 +394,25 @@ local TABLE_TYPE = import 'table_type' -------------------------------------------------------------------------------- -- Key-value pair formatting. -local function format_key_and_value_string_map (key, value, depth, l, format_value) +local function format_key_and_value_string_map (key, value, display, l, format_value) l[#l+1] = key l[#l+1] = { 'align', 'key', #key } l[#l+1] = ' = ' - return format_value(value, depth, l) + return format_value(value, display, l) end -local function format_key_and_value_arbitr_map (key, value, depth, l, format_value) +local function format_key_and_value_arbitr_map (key, value, display, l, format_value) local index_before_key = #l+1 l[#l+1] = '[' - format_value(key, math.huge, l) + format_value(key, DISPLAY.HIDE, l) l[#l+1] = ']' l[#l+1] = { 'align', 'key', width_of_strings_in_l(l, index_before_key) } l[#l+1] = ' = ' - return format_value(value, depth, l) + return format_value(value, display, l) end -local function format_key_and_value_sequence (key, value, depth, l, format_value) - return format_value(value, depth, l) +local function format_key_and_value_sequence (key, value, display, l, format_value) + return format_value(value, display, l) end local TABLE_TYPE_TO_PAIR_FORMAT = { @@ -421,30 +427,24 @@ local TABLE_TYPE_TO_PAIR_FORMAT = { ------------------------------------------------------------------------------- -- Table formatting -local function format_table (t, depth, l, format_value) +local function format_table (t, display, l, format_value) -- Error Checking assert(type(t) == 'table') - assert(type(depth) == 'number' and type(l) == 'table') + assert(type(display) == 'number' and type(l) == 'table') - -- Do stuff - if not l.info[t] then analyze_structure(t, l.options.max_depth-depth, l.info) end + -- Find table info + if not l.info[t] then analyze_structure(t, display, l.info) end local table_info = l.info[t] assert(table_info) - if l.options.recursion == 'marked' and table_info.marker then - l[#l+1], l[#l+2], l[#l+3] = '<', table_info.marker, '>' - end - - local already_visited = l.visited[t] - l.visited[t] = true - - -- If empty, visited or above max-depth, give a small represetation: `{...}` - if table_info.type == TABLE_TYPE.EMPTY or depth >= l.options.max_depth or (already_visited and l.options.recursion ~= 'revisit') then + -- If empty or not a lot of space, give a small represetation: `{...}` + if table_info.type == TABLE_TYPE.EMPTY or display <= DISPLAY.SMALL then l '{' if l.options._table_addr_comment then l[#l+1] = ' --[[' .. table_info.address .. ']] ' end if table_info.type ~= TABLE_TYPE.EMPTY then l[#l+1] = '...' end return l '}' end + -- Get key-value pairs, and possibly fill holes. local key_value_pairs = get_key_value_pairs_in_proper_order(t) if table_info.type == TABLE_TYPE.SEQUENCE and l.info[t].has_holes then @@ -457,10 +457,13 @@ local function format_table (t, depth, l, format_value) assert(pair_format_func) -- Begin formatting table. + local next_display = display - 1 + if (l.info[t].value_types.nr_types >= 2) then next_display = DISPLAY.SMALL end + l[#l+1] = {'indent', '{'} if l.options._table_addr_comment then l[#l+1], l[#l+2] = '--[['..table_info.address..']]', {'seperator'} end for _, pair in ipairs(key_value_pairs) do - pair_format_func(pair[1], pair[2], depth + 1, l, format_value) + pair_format_func(pair[1], pair[2], next_display, l, format_value) l[#l+1] = {'seperator', ','} end if l[#l][1] == 'seperator' then l[#l] = nil end @@ -488,13 +491,13 @@ end ------------------------------------------------------------------------------- -- Coroutine formatting -local function format_coroutine (value, depth, l) +local function format_coroutine (value, _, l) -- Formats a coroutine. Unfortunantly we cannot gather a lot of information -- about coroutines. -- Error check assert(type(value) == 'thread') - assert(type(depth) == 'number' and type(l) == 'table') + assert(type(l) == 'table') -- Do stuff l[#l+1] = coroutine.status(value) @@ -505,9 +508,9 @@ end ------------------------------------------------------------------------------- -- Primitive formatting -local function format_primitive (value, depth, l) +local function format_primitive (value, _, l) -- Error check - assert(type(depth) == 'number' and type(l) == 'table') + assert(type(l) == 'table') -- Do stuff l[#l+1] = tostring(value) end @@ -525,12 +528,12 @@ local TYPE_TO_FORMAT_FUNC = { ['cdata'] = format_primitive, -- TODO & Luajit only } -local function format_value (value, depth, l) - assert(type(depth) == 'number' and type(l) == 'table') +local function format_value (value, display, l) + assert(type(display) == 'number' and type(l) == 'table') local formatting = TYPE_TO_FORMAT_FUNC[type(value)] --print(value, formatting) if formatting then - formatting(value, depth, l, format_value) + formatting(value, display, l, format_value) else error(ERROR_UNKNOWN_TYPE:format(type(value), tostring(value)), 2) end @@ -551,12 +554,9 @@ setmetatable(StringBuilder, { local DEBUG_OPTION_USED = { } local KNOWN_OPTIONS = { - _table_addr_comment = { type = 'boolean', default = false, debug = 'debug' }, -- TODO: Maybe automatically display table address when depth = 0? + _table_addr_comment = { type = 'boolean', default = false, debug = 'debug' }, -- TODO: Maybe automatically display table address when display = 0? indent = { type = 'string', default = ' ' }, - - max_depth = { type = 'number', default = math.huge }, - recursion = { type = 'string', default = 'ignore', accepted = {['ignore'] = true, ['marked'] = true, ['revisit'] = true} }, -- TODO: Completely depricate this option. I do not like it. } local function ensure_that_all_options_are_known (input_options) @@ -601,15 +601,14 @@ local function pretty_format (value, options) -- Setup StringBuilder local l = StringBuilder() - l.visited = { next_mark = 1 } l.options = options - l.info = analyze_structure(value, options.max_depth) + l.info = analyze_structure(value, 3) -- Format value. - format_value(value, 0, l) + format_value(value, DISPLAY.EXPAND, l) -- If any alignment info still exists, ignore it - fix_seperator_info(l, l.options.indent, l.options.max_depth) + fix_seperator_info(l, l.options.indent) ignore_alignment_info(l) return table.concat(l, '') diff --git a/pstring.lua b/pstring.lua index df7921f..2cf38f8 100644 --- a/pstring.lua +++ b/pstring.lua @@ -91,8 +91,7 @@ end -------------------------------------------------------------------------------- - -local function format_shortform_string (str, depth, l) +local function format_shortform_string (str, _, l) l[#l+1] = SHORT_STR_DELIMITER l[#l+1] = escape_string(str) l[#l+1] = SHORT_STR_DELIMITER @@ -119,7 +118,7 @@ local function safe_cut (str, si, ei) end -local function format_cut_string (str, depth, l) +local function format_cut_string (str, _, l) -- Calculate string local str = escape_string(str) str = safe_cut(str, 1, NR_CHARS_IN_LONG_STRING - #STRING_CONT_INDICATOR) @@ -131,7 +130,7 @@ local function format_cut_string (str, depth, l) l[#l+1] = STRING_CONT_INDICATOR end -local function format_concatted_string (str, depth, l) +local function format_concatted_string (str, _, l) -- Cuts the string up into smaller individual substrings, each Concatted -- together. Is uglier compared to longform, but is at least idempotent. @@ -139,7 +138,7 @@ local function format_concatted_string (str, depth, l) -- Error checking assert( type(str) == 'string' ) - assert(type(depth) == 'number' and type(l) == 'table') + assert( type(l) == 'table' ) -- Calculate local width_without_overhead = MAX_HORIZONAL_CHARACTER - 2*#SHORT_STR_DELIMITER - #' ..' @@ -163,11 +162,11 @@ local function format_concatted_string (str, depth, l) l[#l] = '' end -local function format_longform_string (str, depth, l) +local function format_longform_string (str, _, l) -- Error checking - assert( type(str) == 'string' ) - assert(type(depth) == 'number' and type(l) == 'table') + assert(type(str) == 'string') + assert(type(l) == 'table') -- Calculate local level_required = smallest_secure_longform_string_level(str) @@ -179,25 +178,32 @@ local function format_longform_string (str, depth, l) l[#l+1] = ']'..string.rep('=', level_required)..']' end -return function (str, depth, l) +local DISPLAY = { -- TODO: Move To common file + HIDE = 1, + SMALL = 2, + INLINE = 3, + EXPAND = 4, +} + +return function (str, display, l) -- pretty.format_string -- TODO: Prefer \ddd style escaping to shorter (\n, \t), when many of the -- \ddd already exist in the text. -- Error checking - assert( type(str) == 'string' ) - assert(type(depth) == 'number' and type(l) == 'table') + assert(type(str) == 'string') + assert(type(display) == 'number' and type(l) == 'table') -- Do work if #str < NR_CHARS_IN_LONG_STRING then - return format_shortform_string(str, depth, l) - elseif depth > 0 then - return format_cut_string (str, depth, l) + return format_shortform_string(str, nil, l) + elseif display < DISPLAY.EXPAND then + return format_cut_string (str, nil, l) elseif does_string_require_escaping (str) then - return format_concatted_string(str, depth, l) + return format_concatted_string(str, nil, l) else - return format_longform_string(str, depth, l) + return format_longform_string(str, nil, l) end end diff --git a/test/test_analyze_structure.lua b/test/test_analyze_structure.lua index b9f95a3..2cf94b9 100644 --- a/test/test_analyze_structure.lua +++ b/test/test_analyze_structure.lua @@ -10,7 +10,7 @@ SUITE:setEnviroment { SUITE:addTest('Empty Table', function () local input = {} -- Empty! - local table_info = analyze_structure(input)[input] + local table_info = analyze_structure(input, math.huge)[input] assert_equal(table_info.type, TABLE_TYPE.EMPTY, 'Returned bad type: '..table_info.type) assert_equal(table_info.has_seq, false) @@ -19,7 +19,7 @@ end) SUITE:addTest('Sequence', function () local input = { 1, 2, 3 } - local table_info = analyze_structure(input)[input] + local table_info = analyze_structure(input, math.huge)[input] assert_equal(table_info.type, TABLE_TYPE.SEQUENCE, 'Returned bad type: '..table_info.type) assert_equal(table_info.has_seq, true) @@ -28,7 +28,7 @@ end) SUITE:addTest('Sequence with holes', function () local input = { 1, nil, 3 } - local table_info = analyze_structure(input)[input] + local table_info = analyze_structure(input, math.huge)[input] assert_equal(table_info.type, TABLE_TYPE.SEQUENCE, 'Returned bad type: '..table_info.type) assert_equal(table_info.has_seq, true) @@ -38,7 +38,7 @@ end) SUITE:addTest('Sequence with hole on start', function () local input = { nil, 2, 3 } - local table_info = analyze_structure(input)[input] + local table_info = analyze_structure(input, math.huge)[input] assert_equal(table_info.type, TABLE_TYPE.SEQUENCE, 'Returned bad type: '..table_info.type) assert_equal(table_info.has_seq, true) @@ -48,7 +48,7 @@ end) SUITE:addTest('Pure Map', function () local input = { a = 1, [true] = 2, c = 3 } - local table_info = analyze_structure(input)[input] + local table_info = analyze_structure(input, math.huge)[input] assert_equal(table_info.type, TABLE_TYPE.PURE_MAP, 'Returned bad type: '..table_info.type) assert_equal(table_info.has_seq, false) @@ -57,7 +57,7 @@ end) SUITE:addTest('Boolean set', function () local input = { [true] = true, [false] = false } - local table_info = analyze_structure(input)[input] + local table_info = analyze_structure(input, math.huge)[input] assert_equal(table_info.type, TABLE_TYPE.SET, 'Returned bad type: '..table_info.type) assert_equal(table_info.has_seq, false) @@ -66,7 +66,7 @@ end) SUITE:addTest('A Mixed table', function () local input = { 300, [300] = 1 } - local table_info = analyze_structure(input)[input] + local table_info = analyze_structure(input, math.huge)[input] assert_equal(table_info.has_seq, true) assert_equal(table_info.has_map, true) @@ -75,7 +75,7 @@ end) SUITE:addTest('String Map', function () local input = { a = 1, b = 2, c = 3 } - local table_info = analyze_structure(input)[input] + local table_info = analyze_structure(input, math.huge)[input] assert_equal(table_info.type, TABLE_TYPE.STRING_MAP, 'Returned bad type: '..table_info.type) assert_equal(table_info.has_seq, false) @@ -84,7 +84,7 @@ end) SUITE:addTest('Set', function () local input = { a = true, b = true, c = true } - local table_info = analyze_structure(input)[input] + local table_info = analyze_structure(input, math.huge)[input] assert_equal(table_info.type, TABLE_TYPE.SET, 'Returned bad type: '..table_info.type) assert_equal(table_info.is_set, true) @@ -92,35 +92,35 @@ end) SUITE:addTest('Tabular of sequences', function () local input = { a = {1, 2, 3}, b = {4, 5, 6}, c = {7, 8, 9} } - local table_info = analyze_structure(input)[input] + local table_info = analyze_structure(input, math.huge)[input] assert_equal(table_info.is_tabular, {true, true, true}) end) SUITE:addTest('Tabular of maps', function () local input = { a = {a = 1, b = 2}, b = {a = 3, b = 4}, c = {a = 2, b = 7} } - local table_info = analyze_structure(input)[input] + local table_info = analyze_structure(input, math.huge)[input] assert_equal(table_info.is_tabular, {a = true, b = true}) end) SUITE:addTest('Not Tabular, due to no-sub-tables', function () local input = { a = 1, b = {4}, c = 7 } - local table_info = analyze_structure(input)[input] + local table_info = analyze_structure(input, math.huge)[input] assert_equal(table_info.is_tabular, false) end) SUITE:addTest('Not Tabular, due to not being identical sub-tables', function () local input = { a = { a = 1 }, b = { b = 2 }, c = { c = 3 } } - local table_info = analyze_structure(input)[input] + local table_info = analyze_structure(input, math.huge)[input] assert_equal(table_info.is_tabular, false) end) SUITE:addTest('Not Tabular, due to varying lengths', function () local input = { { 1 }, { 2, 3 }, { 4, 5, 6 } } - local table_info = analyze_structure(input)[input] + local table_info = analyze_structure(input, math.huge)[input] assert_equal(table_info.is_tabular, false) end) @@ -153,7 +153,7 @@ end) SUITE:addTest('goto is special', function () local input = { ['goto'] = 'hi' } - local table_info = analyze_structure(input)[input] + local table_info = analyze_structure(input, math.huge)[input] assert_equal(table_info.type, TABLE_TYPE.PURE_MAP, 'Returned bad type: '..table_info.type) end) @@ -164,7 +164,7 @@ end) SUITE:addTest('Recursive Numeration, Simple', function () local input = {} input[1] = input - local info = analyze_structure(input) + local info = analyze_structure(input, math.huge) assert_equal(1, info[input].marker) end) @@ -172,7 +172,7 @@ end) SUITE:addTest('Recursive Numeration, Multiple-appear', function () local input = { {}, { {} } } input[1][1] = input[2][1] - local info = analyze_structure(input) + local info = analyze_structure(input, math.huge) assert_equal(1, info[input[1][1]].marker) end) @@ -181,7 +181,7 @@ SUITE:addTest('Recursive Numeration, Multiple at once', function () local input = { {}, { {} } } input[1][1] = input[2][1] input[1][2] = input - local info = analyze_structure(input) + local info = analyze_structure(input, math.huge) assert_equal('number', type(info[input[1][1]].marker)) assert_equal('number', type(info[input].marker)) @@ -190,7 +190,7 @@ end) SUITE:addTest('Recursive Numeration, Through Keys', function () local input = { } input[input] = input - local info = analyze_structure(input) + local info = analyze_structure(input, math.huge) assert_equal(1, info[input].marker) end) @@ -200,14 +200,14 @@ end) SUITE:addTest('Wont crash, even though metatable.__index throws errors', function () local input = setmetatable({ 'hi', 'hello' }, {__index = function (_, k) error('Undefined access on key: '..k) end}) - local info = analyze_structure(input) + local info = analyze_structure(input, math.huge) assert(true) end) SUITE:addTest('Can count elements, even though metatable.__index throws errors', function () local input = setmetatable({ 'hi', 'hello' }, {__index = function (_, k) error('Undefined access on key: '..k) end}) - local info = analyze_structure(input) + local info = analyze_structure(input, math.huge) assert_equal(2, info[input].seq_elems) end) @@ -217,21 +217,21 @@ end) SUITE:addTest('Can detect uniform structure', function () local input = { a = 'b', b = 'a' } - local info = analyze_structure(input) + local info = analyze_structure(input, math.huge) assert_equal(true, info[input].is_uniform) end) SUITE:addTest('Can detect uniform structure with different key value types', function () local input = { a = 1, b = 4 } - local info = analyze_structure(input) + local info = analyze_structure(input, math.huge) assert_equal(true, info[input].is_uniform) end) SUITE:addTest('Can detect basic non-uniform structure', function () local input = { a = 'b', b = 5 } - local info = analyze_structure(input) + local info = analyze_structure(input, math.huge) assert_equal(false, info[input].is_uniform) end) @@ -240,14 +240,14 @@ SUITE:addTest('Can detect pseudo-uniform structure with nested tables', function -- It's pseudo-uniform because of the child having uniform structure with -- equal number of elements. local input = { a = { d = 7 }, b = { p = 3 } } - local info = analyze_structure(input) + local info = analyze_structure(input, math.huge) assert_equal(true, info[input].is_uniform) end) SUITE:addTest('Can detect non-uniform structure with nested tables', function () local input = { a = { 'a', 'b', 'c' }, b = { p = 3, 'hi' } } - local info = analyze_structure(input) + local info = analyze_structure(input, math.huge) assert_equal(false, info[input].is_uniform) end) @@ -258,13 +258,13 @@ end) -- API stuff SUITE:addTest('next_mark does not escape', function () - local info = analyze_structure( {} ) + local info = analyze_structure( {}, math.huge ) assert(not info.next_mark) end) SUITE:addTest('We can extend an already existing info table', function () local tab1, tab2 = {}, {} - local info = analyze_structure( tab1 ) + local info = analyze_structure( tab1, math.huge ) analyze_structure( tab2, math.huge, info ) assert(info[tab1]) assert(info[tab2]) @@ -272,7 +272,7 @@ end) SUITE:addTest('When we extend an info table, we retain the previous root', function () local tab1, tab2 = {}, {} - local info = analyze_structure( tab1 ) + local info = analyze_structure( tab1, math.huge ) analyze_structure( tab2, math.huge, info ) assert(tab1 == info.root) end) diff --git a/test/test_function.lua b/test/test_function.lua index d249059..0dc9aed 100644 --- a/test/test_function.lua +++ b/test/test_function.lua @@ -89,6 +89,8 @@ format_test { expect = 'function () ... end', } + + -------------------------------------------------------------------------------- -- Basic elaborate functions @@ -112,7 +114,7 @@ format_test { name = 'Elaborate function with upvalue included 1', input = function () l = TEST_UPVALUE end, adv_getlocal = true, - expect = 'function ()\n -- Source file: \'./test/test_function.lua\' [Line: '..func_line..']\n -- Up values: { TEST_UPVALUE = 42 }\n\n ...\nend' + expect = 'function ()\n -- Source file: \'./test/test_function.lua\' [Line: '..func_line..']\n -- Up values:\n -- TEST_UPVALUE = 42\n\n ...\nend' } local func_line = curline(3) -- Must be exactly 3 lines above function @@ -120,9 +122,36 @@ format_test { name = 'Elaborate function with upvalue included 2', input = function () TEST_UPVALUE = true end, adv_getlocal = true, - expect = 'function ()\n -- Source file: \'./test/test_function.lua\' [Line: '..func_line..']\n -- Up values: { TEST_UPVALUE = 42 }\n\n ...\nend' + expect = 'function ()\n -- Source file: \'./test/test_function.lua\' [Line: '..func_line..']\n -- Up values:\n -- TEST_UPVALUE = 42\n\n ...\nend' } +do + local a, b, c = 'hi', {1, 2, 3}, function() end + local function test (x) return x + a + b + c end + + local func_line = curline(3) -- Must be exactly 3 lines above function + format_test { + name = 'Elaborate function with multiple upvalues', + input = test, + adv_getlocal = true, + expect = 'function ()\n -- Source file: \'./test/test_function.lua\' [Line: '..func_line..']\n -- Up values:\n -- a = \'hi\'\n -- b = {...}\n -- c = function() ... end\n\n ...\nend' + } +end + +do + local a, b, cool = 'hi', {1, 2, 3}, function() end + local function test (x) return x + a + b + cool end + + local func_line = curline(3) -- Must be exactly 3 lines above function + format_test { + name = 'Elaborate function with multiple upvalues, nicely aligned', + input = test, + adv_getlocal = true, + expect = 'function ()\n -- Source file: \'./test/test_function.lua\' [Line: '..func_line..']\n -- Up values:\n -- a = \'hi\'\n -- b = {...}\n -- cool = function() ... end\n\n ...\nend' + } +end + + -------------------------------------------------------------------------------- -- Elaborate functions with documentation diff --git a/test/test_pretty.lua b/test/test_pretty.lua index cca9c7d..c64d1b5 100644 --- a/test/test_pretty.lua +++ b/test/test_pretty.lua @@ -182,30 +182,6 @@ format_test { expect = '{ { { 1, 2 }, { 3, 4 } }, { 5, 6 } }', } -format_test { - input = { { {1, 2}, {3, 4} }, {5, 6} }, - options = { max_depth = 0 }, - expect = '{...}', -} - -format_test { - input = { { {1, 2}, {3, 4} }, {5, 6} }, - options = { max_depth = 1 }, - expect = '{ {...}, {...} }', -} - -format_test { - input = { { {1, 2}, {3, 4} }, {5, 6} }, - options = { max_depth = 2 }, - expect = '{ { {...}, {...} }, { 5, 6 } }', -} - -format_test { - input = { { {1, 2}, {3, 4} }, {5, 6} }, - options = { max_depth = 3 }, - expect = '{ { { 1, 2 }, { 3, 4 } }, { 5, 6 } }', -} - format_test { input = { [{ {1,2}, {3,4} }] = 'Hello World' }, expect = '{ [{...}] = \'Hello World\' }', @@ -247,6 +223,19 @@ format_test { expect = '{\n hello_world_1 = \'dyr?\',\n hello_world_2 = \'dyr!\',\n hello\204\133_wo\204\133rld_3 = \'dyr.\'\n}', } +-- Depth Tests + +format_test { + name = 'Tables with a mix of values don\'t expand subtables', + input = { + a = 'hello', + b = {1, 2, 3}, + c = {1, 2, 3}, + d = {1, 2, 3}, + }, + expect = '{\n a = \'hello\',\n b = {...},\n c = {...},\n d = {...}\n}', +} + -------------------------------------------------------------------------------- -- Pattern specific table display. @@ -331,38 +320,14 @@ format_test { -------------------------------------------------------------------------------- -- Table recursion -do - local recursive = {} - recursive[1] = recursive - format_test { - input = recursive, - options = { max_depth = 5 }, - expect = '{ {...} }', - } - format_test { - input = recursive, - options = { max_depth = 5, recursion = 'ignore' }, - expect = '{ {...} }', - } - format_test { - input = recursive, - options = { max_depth = 5, recursion = 'marked' }, - expect = '<1>{ <1>{...} }', - } -end +SUITE:addTest('Avoid infinite loops in recursion', function () + local rec = {} + rec[1] = rec + format(rec) + assert(true) -- We don't care about the output. +end) -do - local a = {} - local b = { a } - a[1] = b - local rec = { a = a, b = b } - format_test { - name = 'Top layers should be expanded, rather than lower layers.', - input = rec, - options = { max_depth = 5 }, - expect = '{\n a = { {...} },\n b = { {...} }\n}', - } -end +-- TODO: This is a very complex topic, and will expanded upon after 1.0.0. -------------------------------------------------------------------------------- -- CDATA @@ -392,8 +357,8 @@ end -------------------------------------------------------------------------------- -- General -SUITE:addTest('UseCase: Can print _G with max_depth = 1', function () - format(_G, {max_depth = 1}) +SUITE:addTest('UseCase: Can print global enviroment', function () + format(_G) assert(true) end) diff --git a/test/test_resilience.lua b/test/test_resilience.lua index 8b51098..7640395 100644 --- a/test/test_resilience.lua +++ b/test/test_resilience.lua @@ -37,12 +37,7 @@ SUITE:addTest('Dont allow unknown options', function () end) SUITE:addTest('Dont allow bad types in options', function () - local error_msg = bad_call(pretty, 'Hello World', { max_depth = "hello world" }) - assert(error_msg) -end) - -SUITE:addTest('Dont allow bad values in recursion', function () - local error_msg = bad_call(pretty, 'Hello World', { recursion = "hello world" }) + local error_msg = bad_call(pretty, 'Hello World', { indent = 51 }) assert(error_msg) end)