1
0

Updated test_function.lua to reflect the new design. The system also warns against using experimental and debug options.

This commit is contained in:
Jon Michael Aanes 2017-06-24 18:53:59 +02:00
parent 4a41111d1e
commit 3bb599e976
3 changed files with 249 additions and 248 deletions

View File

@ -123,8 +123,8 @@ local function get_function_paramlist_and_body (str, start_line, end_line)
local start_line_index = get_line_index(str, start_line)
local end_line_index = get_line_index(str, end_line + 1)
local function_params, function_body = str:sub(start_line_index, end_line_index):match('function%s*[a-zA-Z0-9_.]*%s*(%([a-zA-Z0-9_,. \t]*%))[ \t]*(.-)[ \t]*end')
--print(function_params, function_body)
local function_params, function_body = str:sub(start_line_index, end_line_index):match('.*function%s*[a-zA-Z0-9_.]*%s*(%([a-zA-Z0-9_,. \t]*%))[ \t]*(.+)[ \t]*end')
function_body = function_body:match('^%s*(.-)%s*$')
assert(type(function_params) == 'string' and type(function_body) == 'string')
return function_params, function_body
end
@ -160,11 +160,13 @@ local function add_indent_to_string (str, indent)
assert(type(indent) == 'string')
local l = {}
for line in string.gmatch(str, '\n', true) do
for line in str:gmatch('[^\n]+', true) do
l[#l+1] = indent
l[#l+1] = line
l[#l+1] = '\n'
end
return table.concat(l, '\n')
if l[#l] == '\n' then l[#l] = nil end
return table.concat(l, '')
end
--------------------------------------------------------------------------------
@ -204,7 +206,7 @@ return function (value, depth, l, format_value)
local info = get_function_info(value)
if l.options.include_closure and not info.builtin then
if l.options._include_closure and not info.builtin then
return format_function_with_closure(value, depth, l, format_value)
end
@ -237,7 +239,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 not l.options.more_function_info or depth ~= 0 then
if depth ~= 0 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 }
@ -288,16 +290,22 @@ return function (value, depth, l, format_value)
-- NOTE: This is for testing/debugging/experimentation purposes, and is
-- not designed to be pretty.
-- Native
l[#l+1] = indent
l[#l+1] = '--[[ Function Body:\n\t'
l[#l+1] = '-- native_repr: '
l[#l+1] = tostring(value)
-- Function body
l[#l+1] = indent
l[#l+1] = '--[[ function_body:\n\t'
l[#l+1] = add_indent_to_string(get_function_str_from_file(info.short_src, info.linedefined, info.lastlinedefined), l.options.indent)
l[#l+1] = indent
l[#l+1] = '--]]'
-- Full info
l[#l+1] = indent
l[#l+1] = '--[[\n\tNative repr:'
l[#l+1] = tostring(value)
l[#l+1] = indent
l[#l+1] = '--[[ full_info:\n'
info.env = nil
format_value(info, depth + 1, l)
l[#l+1] = indent
l[#l+1] = '--]]'

View File

@ -463,19 +463,19 @@ setmetatable(StringBuilder, {
--------------------------------------------------------------------------------
local DEBUG_OPTIONS = { _all_function_info = true, _table_addr_comment = true }
local DEBUG_OPTION_USED = { }
local KNOWN_OPTIONS = {
_table_addr_comment = { type = 'boolean', default = false },
_all_function_info = { type = 'boolean', default = false },
_table_addr_comment = { type = 'boolean', default = false, debug = 'debug' },
_all_function_info = { type = 'boolean', default = false, debug = 'debug' },
_include_closure = { type = 'boolean', default = false, debug = 'experimental' },
cut_strings = { type = 'boolean', default = false },
include_closure = { type = 'boolean', default = false },
indent = { type = 'string', default = ' ' },
max_depth = { type = 'number', default = math.huge },
embed_loaded_funcs = { type = 'boolean', default = false },
more_function_info = { type = 'boolean', default = false },
embed_loaded_funcs = { type = 'boolean', default = false }, -- TODO: Outphase this, in favor of automatically embedding "small enough" functions.
recursion = { type = 'string', default = 'ignore', accepted = {['ignore'] = true, ['marked'] = true} },
short_builtins = { type = 'boolean', default = false },
short_builtins = { type = 'boolean', default = false }, -- TODO: Outphase this. Rather automatically use the short versions in places where it would be strange to find the function, like keys, etc.
}
local function ensure_that_all_options_are_known (options)
@ -488,6 +488,9 @@ local function ensure_that_all_options_are_known (options)
error(('[pretty]: Bad value given to option %s: %s (%s). Expected value of type %s'):format(option_name, option_value, type(option_value), KNOWN_OPTIONS[option_name].type), 2)
elseif KNOWN_OPTIONS[option_name].accepted and not KNOWN_OPTIONS[option_name].accepted[option_value] then
error(('[pretty]: Bad value given to option %s: %s (%s). Expected one of: %s'):format(option_name, option_value, type(option_value), table.concat(KNOWN_OPTIONS[option_name].accepted, ', ')), 2)
elseif KNOWN_OPTIONS[option_name].debug and not DEBUG_OPTION_USED[option_name] then
DEBUG_OPTION_USED[option_name] = true
print(('[pretty]: Using %s option "%s".\n Please note that this option may change at any time. It is not stable,\n not tested, and may indeed break or be removed without warning.'):format(KNOWN_OPTIONS[option_name].debug, option_name))
end
end
-- Assign default values

View File

@ -10,217 +10,153 @@ local HAS_ADV_GETLOCAL = not not debug.getinfo(1, 'u').nparams -- Lua 5.1 compa
if not loadstring then loadstring = load end -- Lua 5.3 compat
local function format_test (t)
if t.longterm then return end
if t.adv_getlocal and not HAS_ADV_GETLOCAL then return end
if t.single then
return SUITE:addTest(t.name or t.expect, function ()
local format = format({t.input}, t.options):gsub('^{%s+(.-)%s+}$', function(a) return '{ '..a..' }' end)
assert_equal('{ '..t.expect..' }', format)
end, { line = debug.getinfo(2).currentline })
end
SUITE:addTest(t.name or t.expect, function ()
local input_value = t.input
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(expected_result, actual_result)
else
if not actual_result:match(expected_result) then
error(ASSERT_ERROR_APPROX:format(expected_result, actual_result))
end
end
assert_equal(t.expect, format(t.input, t.options))
end, { line = debug.getinfo(2).currentline })
end
local function curline (delta)
return debug.getinfo(2).currentline + (delta or 0)
end
local function curline (delta) return debug.getinfo(2).currentline + (delta or 0) end
local TEST_UPVALUE = 42
--------------------------------------------------------------------------------
-- Function printing
-- Basic inline functions
format_test {
name = 'Basic function formatting',
adv_getlocal = true,
single = true,
input = function () end,
expect = 'function () ... end',
}
format_test {
name = 'Function with a single argument',
adv_getlocal = true,
single = true,
input = function (a) end,
expect = 'function (a) ... end',
}
format_test {
name = 'Function with multiple arguments',
adv_getlocal = true,
single = true,
input = function (a, b) end,
expect = 'function (a, b) ... end',
}
format_test {
name = 'Function with vararg',
adv_getlocal = true,
single = true,
input = function (...) end,
expect = 'function (...) ... end',
}
format_test {
name = 'Function with vararg and multiple arguments',
adv_getlocal = true,
single = true,
input = function (a, b, ...) end,
expect = 'function (a, b, ...) ... end',
}
do
local SOME_RANDOM_UPVALUE = false
format_test {
format_test {
name = 'Closures don\'t affect functions printed in single mode 1',
adv_getlocal = true,
input = function () l = SOME_RANDOM_UPVALUE end,
single = true,
input = function () l = TEST_UPVALUE end,
expect = 'function () ... end',
}
}
format_test {
format_test {
name = 'Closures don\'t affect functions printed in single mode 2',
adv_getlocal = true,
input = function () SOME_RANDOM_UPVALUE = true end,
single = true,
input = function () TEST_UPVALUE = true end,
expect = 'function () ... end',
}
}
format_test {
-- More function info is ignored if not at depth 0.
adv_getlocal = true,
input = { a = function () SOME_RANDOM_UPVALUE = true end },
options = { more_function_info = true },
expect = '{ a = function () ... end }',
}
--------------------------------------------------------------------------------
-- Basic elaborate functions
local func_line = curline(2) -- Must be exactly 2 lines above function
format_test {
input = function () l = SOME_RANDOM_UPVALUE end,
adv_getlocal = true,
options = { more_function_info = true },
expect = 'function ()\n -- source_file: \'./test/test_function.lua\' [Line: '..func_line..']\n -- up_values: { SOME_RANDOM_UPVALUE = false }\n\n ...\nend'
}
local func_line = curline(2) -- Must be exactly 2 lines above function
format_test {
input = function () SOME_RANDOM_UPVALUE = true end,
adv_getlocal = true,
options = { more_function_info = true },
expect = 'function ()\n -- source_file: \'./test/test_function.lua\' [Line: '..func_line..']\n -- up_values: { SOME_RANDOM_UPVALUE = false }\n\n ...\nend'
}
end
do
local func_line = curline(2) -- Must be exactly 2 lines above function
format_test {
local func_line = curline(3)
format_test {
name = 'Singleline elaborate function',
input = function () end,
adv_getlocal = true,
options = { more_function_info = true },
expect = 'function ()\n -- source_file: \'./test/test_function.lua\' [Line: '..func_line..']\n\n ...\nend'
}
end
do
local index = 0
format_test {
input = function () index = index + 1; return index end,
adv_getlocal = true,
expect = 'function () ... end'
}
local func_line = curline(2) -- Must be exactly 2 lines above function
format_test {
input = function () index = index + 1; return index end,
adv_getlocal = true,
options = { more_function_info = true },
expect = 'function ()\n -- source_file: \'./test/test_function.lua\' [Line: '..func_line..']\n -- up_values: { index = 0 }\n\n ...\nend'
}
end
format_test {
adv_getlocal = true,
options = { embed_loaded_funcs = true },
input = loadstring('return function () end')(),
expect = 'function () end',
}
format_test {
adv_getlocal = true,
options = { embed_loaded_funcs = true },
input = loadstring('return function () return function () end end')(),
expect = 'function () return function () end end',
}
format_test {
adv_getlocal = true,
options = { embed_loaded_funcs = true },
input = loadstring('return function () return function () end\nend')()(),
expect = 'function () end',
}
format_test {
-- NOTE: This is HARD to fix. It's thus longerterm
adv_getlocal = true,
options = { embed_loaded_funcs = true },
input = loadstring('return function () return function () end end')()(),
expect = 'function () end',
}
format_test {
-- More function info allows one to even get the function whole, if it was defined in a string.
input = loadstring('return function (a, b) return a + b end')(),
options = { embed_loaded_funcs = true },
expect = 'function (a, b) return a + b end',
}
format_test {
-- More function info allows one to even get the function whole, if it was defined in a string.
input = loadstring('return function (a, b)\n return a + b\nend')(),
options = { embed_loaded_funcs = true },
expect = 'function (a, b)\n return a + b\nend',
}
do
local func_line = curline(2) -- Must be exactly 2 lines above function
format_test {
input = function ()
-- NOTE: This function must cover 3 lines of code!
end,
adv_getlocal = true,
options = { more_function_info = true },
expect = 'function ()\n -- source_file: \'./test/test_function.lua\' [Lines: '..func_line..' - '..(func_line+2)..']\n\n ...\nend',
}
local func_line = curline(2) -- Must be exactly 2 lines above function
format_test {
input = function () --[[ NOTE: This function must cover a single line of code! ]] end,
adv_getlocal = true,
options = { more_function_info = true },
expect = 'function ()\n -- source_file: \'./test/test_function.lua\' [Line: '..func_line..']\n\n ...\nend',
}
end
}
local func_line = curline(3)
format_test {
name = 'Multiline elaborate function',
input = function ()
end,
expect = 'function ()\n -- source_file: \'./test/test_function.lua\' [Lines: '..func_line..' - '..(func_line+1)..']\n\n ...\nend',
}
local func_line = curline(3) -- Must be exactly 3 lines above function
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'
}
local func_line = curline(3) -- Must be exactly 3 lines above function
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'
}
local func_line = curline(3)
format_test {
name = 'Elaborate function with documentation',
input = function ()
-- Hello World
if true then return false end
return true
end,
expect = 'function ()\n -- source_file: \'./test/test_function.lua\' [Lines: '..func_line..' - '..(func_line+1)..']\n -- Hello World\n\n ...\nend',
}
--------------------------------------------------------------------------------
-- Builtins
format_test {
single = true,
input = math.abs,
expect = 'builtin function (x) ... end',
}
format_test {
input = math.abs,
options = { more_function_info = true },
expect = 'builtin function (x)\n -- math.abs\n -- Returns the absolute value of x.\n\n ...\nend',
}
format_test {
single = true,
input = math.random,
expect = 'builtin function ([m [, n]) ... end',
}
format_test {
input = math.random,
options = { more_function_info = true },
expect = 'builtin function ([m [, n])\n -- math.random\n -- When called without arguments, returns a uniform pseudo-random real number in the range [0,1). When called with an integer number m, math.random returns a uniform pseudo-random integer in the range [1, m]. When called with two integer numbers m and n, math.random returns a uniform pseudo-random integer in the range [m, n].\n\n ...\nend',
}
format_test {
input = string.byte,
options = { more_function_info = true },
expect = 'builtin function (s [, i [, j]])\n -- string.byte\n -- Returns the internal numerical codes of the characters s[i], s[i+1], ..., s[j]. The default value for i is 1; the default value for j is i.\n -- Note that numerical codes are not necessarily portable across platforms.\n\n ...\nend',
}
@ -228,126 +164,69 @@ format_test {
-- in a standard enviroment, return that name, instead of other complex info.
format_test {
-- NOTE: These tests may be counter to intention, soon.
input = math.random,
options = { short_builtins = true },
expect = 'math.random',
}
format_test {
-- NOTE: These tests may be counter to intention, soon.
input = { math.cos, math.sin, math.abs },
options = { short_builtins = true },
expect = '{ math.cos, math.sin, math.abs }',
}
format_test {
name = 'Replace function with short version, if key',
input = { [math.random] = 365 },
expect = '{ [math.random] = 365 }',
}
--------------------------------------------------------------------------------
-- Closure creation
-- include_closure option: If a function uses upvalues, return code that creates
-- a closure for that function, including the function code.
-- NOTE: Without AST traversal, it's impossible to garentee that values refered
-- by multiple recursive closures are the same, because they can actually refer
-- to different variables with the same name, as the following example shows:
--
-- local val_a = 1
-- local func_a = function () return val_a end
-- local val_a = 2
-- local func_c = function () return func_a() + val_a end
do
format_test {
adv_getlocal = true,
options = { more_function_info = true, include_closure = true },
input = function (a, b) return (a > b) and a or b end,
expect = 'function (a, b) return (a > b) and a or b end',
}
end
do
local input_func = (function ()
local i = 0
return function (a) i = i + a; return i end
end)()
for i = 1, 10 do input_func(1) end
format_test {
adv_getlocal = true,
options = { more_function_info = true, include_closure = true },
input = input_func,
expect = '(function () local i = 10; return function (a) i = i + a; return i end end)()',
}
end
do
local input_func = loadstring([[
return function ()
local i = 0
return function (a) i = i + a; return i end
end]])()()
for i = 1, 10 do input_func(1) end
format_test {
name = 'Can include variables in closure',
adv_getlocal = true,
options = { more_function_info = true, include_closure = true },
input = input_func,
expect = '(function () local i = 10; return function (a) i = i + a; return i end end)()',
}
end
-- Embedding functions loaded with loadstring
format_test {
name = 'Can include functions in closure',
adv_getlocal = true,
options = { more_function_info = true, include_closure = true },
input = (function ()
local custom_max = function (a, b) return (a > b) and a or b end
return function (a, b) return custom_max(a, b) + 2 end
end)(),
expect = '(function () local custom_max = function (a, b) return (a > b) and a or b end; return function (a, b) return custom_max(a, b) + 2 end end)()',
single = true,
name = 'It\'s possible to get loadstring functions whole',
input = loadstring('return function (a, b) return a + b end')(),
options = { embed_loaded_funcs = true },
expect = 'function (a, b) return a + b end',
}
format_test {
name = 'Can include functions defined with `function <name> () ...` style',
adv_getlocal = true,
options = { more_function_info = true, include_closure = true },
input = (function ()
local function custom_max (a, b) return (a > b) and a or b end
return function (a, b) return custom_max(a, b) + 2 end
end)(),
expect = '(function () local custom_max = function (a, b) return (a > b) and a or b end; return function (a, b) return custom_max(a, b) + 2 end end)()',
single = true,
name = 'Whitespace is automatically stripped from loadstring functions',
input = loadstring('return function (a, b)\n return a + b\nend')(),
options = { embed_loaded_funcs = true },
expect = 'function (a, b) return a + b end',
}
do
local custom_max = function (a, b) return (a > b) and a or b end
format_test {
name = 'Can include functions from outside scope into closure',
format_test {
single = true,
adv_getlocal = true,
options = { more_function_info = true, include_closure = true },
input = (function ()
return function (a, b) return custom_max(a, b) + 2 end
end)(),
expect = '(function () local custom_max = function (a, b) return (a > b) and a or b end; return function (a, b) return custom_max(a, b) + 2 end end)()',
}
end
do
local a_func = function (x) return x + 2 end
local b_func = function (x) return a_func(x) * a_func(x) end
local c_func = function (x) return b_func(a_func(x)) end
format_test {
name = 'Can support recursive closures',
adv_getlocal = true,
options = { more_function_info = true, include_closure = true },
input = c_func,
expect = '(function () local b_func = (function () local a_func = function (x) return x + 2 end; return function (x) return a_func(x) * a_func(x) end end)(); local a_func = function (x) return x + 2 end; return function (x) return b_func(a_func(x)) end end)()',
}
end
options = { embed_loaded_funcs = true },
input = loadstring('return function () return function () end\nend')()(),
expect = 'function () end',
}
format_test {
name = 'Closures do not affect builtins',
input = math.abs,
options = { more_function_info = true, include_closure = true },
expect = 'builtin function (x)\n -- math.abs\n -- Returns the absolute value of x.\n\n ...\nend',
name = 'When finding the correct function becomes too hard, just ignore it 1',
single = true,
adv_getlocal = true,
options = { embed_loaded_funcs = true },
input = loadstring('return function () return function () end end')(),
expect = 'function () ... end',
}
format_test {
name = 'When finding the correct function becomes too hard, just ignore it 2',
single = true,
adv_getlocal = true,
options = { embed_loaded_funcs = true },
input = loadstring('return function () return function () end end')()(),
expect = 'function () ... end',
}
--------------------------------------------------------------------------------
@ -400,13 +279,124 @@ format_test {
expect = '{\n a = function (ψ) ... end\n b = function (a) ... end\n}',
}
--------------------------------------------------------------------------------
-- Closure creation
-- _include_closure option: If a function uses upvalues, return code that creates
-- a closure for that function, including the function code.
-- NOTE: Without AST traversal, it's impossible to garentee that values refered
-- by multiple recursive closures are the same, because they can actually refer
-- to different variables with the same name, as the following example shows:
--
-- local val_a = 1
-- local func_a = function () return val_a end
-- local val_a = 2
-- local func_c = function () return func_a() + val_a end
-- NOTE: The following tests are all EXPERIMENTAL! All of the tests use features
-- which may change at any time.
format_test {
name = 'Closures do not affect non-upvalue function',
adv_getlocal = true,
options = { _include_closure = true },
input = function (a, b) return (a > b) and a or b end,
expect = 'function (a, b) return (a > b) and a or b end',
}
format_test {
name = 'Closures do not affect builtins',
input = math.abs,
options = { _include_closure = true },
expect = 'builtin function (x)\n -- math.abs\n -- Returns the absolute value of x.\n\n ...\nend',
}
do
local input_func = (function ()
local i = 0
return function (a) i = i + a; return i end
end)()
for i = 1, 10 do input_func(1) end
format_test {
adv_getlocal = true,
options = { _include_closure = true },
input = input_func,
expect = '(function () local i = 10; return function (a) i = i + a; return i end end)()',
}
end
do
local input_func = loadstring([[
return function ()
local i = 0
return function (a) i = i + a; return i end
end]])()()
for i = 1, 10 do input_func(1) end
format_test {
name = 'Can include variables in closure',
adv_getlocal = true,
options = { _include_closure = true },
input = input_func,
expect = '(function () local i = 10; return function (a) i = i + a; return i end end)()',
}
end
format_test {
name = 'Can include functions in closure',
adv_getlocal = true,
options = { _include_closure = true },
input = (function ()
local custom_max = function (a, b) return (a > b) and a or b end
return function (a, b) return custom_max(a, b) + 2 end
end)(),
expect = '(function () local custom_max = function (a, b) return (a > b) and a or b end; return function (a, b) return custom_max(a, b) + 2 end end)()',
}
format_test {
name = 'Can include functions defined with `function <name> () ...` style',
adv_getlocal = true,
options = { _include_closure = true },
input = (function ()
local function custom_max (a, b) return (a > b) and a or b end
return function (a, b) return custom_max(a, b) + 2 end
end)(),
expect = '(function () local custom_max = function (a, b) return (a > b) and a or b end; return function (a, b) return custom_max(a, b) + 2 end end)()',
}
do
local custom_max = function (a, b) return (a > b) and a or b end
format_test {
name = 'Can include functions from outside scope into closure',
adv_getlocal = true,
options = { _include_closure = true },
input = (function ()
return function (a, b) return custom_max(a, b) + 2 end
end)(),
expect = '(function () local custom_max = function (a, b) return (a > b) and a or b end; return function (a, b) return custom_max(a, b) + 2 end end)()',
}
end
do
local a_func = function (x) return x + 2 end
local b_func = function (x) return a_func(x) * a_func(x) end
local c_func = function (x) return b_func(a_func(x)) end
format_test {
name = 'Can support recursive closures',
adv_getlocal = true,
options = { _include_closure = true },
input = c_func,
expect = '(function () local b_func = (function () local a_func = function (x) return x + 2 end; return function (x) return a_func(x) * a_func(x) end end)(); local a_func = function (x) return x + 2 end; return function (x) return b_func(a_func(x)) end end)()',
}
end
--------------------------------------------------------------------------------
--[[
format_test {
adv_getlocal = true,
options = { more_function_info = true, _all_function_info = true, max_depth = 2 },
options = { _all_function_info = true, max_depth = 2 },
input = function() end,
expect = '',
}