Jon Michael Aanes
155c877987
This one is based on the representative width of the table. Not only does this produce better results, but it's also more futureproof.
387 lines
13 KiB
Lua
387 lines
13 KiB
Lua
|
|
local SUITE = require('TestSuite').new('function')
|
|
SUITE:setEnviroment{
|
|
format = require('pretty')
|
|
}
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
local HAS_ADV_GETLOCAL = not not debug.getinfo(1, 'u').nparams -- Lua 5.1 compat
|
|
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
|
|
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
|
|
end, { line = debug.getinfo(2).currentline })
|
|
end
|
|
|
|
local function curline (delta)
|
|
return debug.getinfo(2).currentline + (delta or 0)
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Function printing
|
|
|
|
format_test {
|
|
adv_getlocal = true,
|
|
input = function () end,
|
|
expect = 'function () ... end',
|
|
}
|
|
|
|
format_test {
|
|
adv_getlocal = true,
|
|
input = function (a) end,
|
|
expect = 'function (a) ... end',
|
|
}
|
|
|
|
format_test {
|
|
adv_getlocal = true,
|
|
input = function (a, b) end,
|
|
expect = 'function (a, b) ... end',
|
|
}
|
|
|
|
format_test {
|
|
adv_getlocal = true,
|
|
input = function (...) end,
|
|
expect = 'function (...) ... end',
|
|
}
|
|
|
|
format_test {
|
|
adv_getlocal = true,
|
|
input = function (a, b, ...) end,
|
|
expect = 'function (a, b, ...) ... end',
|
|
}
|
|
|
|
do
|
|
local SOME_RANDOM_UPVALUE = false
|
|
|
|
format_test {
|
|
adv_getlocal = true,
|
|
input = function () l = SOME_RANDOM_UPVALUE end,
|
|
expect = 'function () ... end',
|
|
}
|
|
|
|
format_test {
|
|
adv_getlocal = true,
|
|
input = function () SOME_RANDOM_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 }',
|
|
}
|
|
|
|
|
|
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\t-- source_file: \'./test/test_function.lua\' [Line: '..func_line..']\n\t-- up_values: { SOME_RANDOM_UPVALUE = false }\n\n\t...\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\t-- source_file: \'./test/test_function.lua\' [Line: '..func_line..']\n\t-- up_values: { SOME_RANDOM_UPVALUE = false }\n\n\t...\nend'
|
|
}
|
|
end
|
|
|
|
do
|
|
local func_line = curline(2) -- Must be exactly 2 lines above function
|
|
format_test {
|
|
input = function () end,
|
|
adv_getlocal = true,
|
|
options = { more_function_info = true },
|
|
expect = 'function ()\n\t-- source_file: \'./test/test_function.lua\' [Line: '..func_line..']\n\n\t...\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\t-- source_file: \'./test/test_function.lua\' [Line: '..func_line..']\n\t-- up_values: { index = 0 }\n\n\t...\nend'
|
|
}
|
|
end
|
|
|
|
format_test {
|
|
adv_getlocal = true,
|
|
input = loadstring('return function () end')(),
|
|
expect = 'function () end',
|
|
}
|
|
|
|
format_test {
|
|
adv_getlocal = true,
|
|
input = loadstring('return function () return function () end end')(),
|
|
expect = 'function () return function () end end',
|
|
}
|
|
|
|
format_test {
|
|
adv_getlocal = 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,
|
|
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 = { more_function_info = 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\treturn a + b\nend')(),
|
|
options = { more_function_info = true },
|
|
expect = 'function (a, b)\n\treturn 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\t-- source_file: \'./test/test_function.lua\' [Lines: '..func_line..' - '..(func_line+2)..']\n\n\t...\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\t-- source_file: \'./test/test_function.lua\' [Line: '..func_line..']\n\n\t...\nend',
|
|
}
|
|
end
|
|
|
|
format_test {
|
|
input = math.abs,
|
|
expect = 'builtin function (x) ... end',
|
|
}
|
|
|
|
format_test {
|
|
input = math.abs,
|
|
options = { more_function_info = true },
|
|
expect = 'builtin function (x)\n\t-- math.abs\n\t-- Returns the absolute value of x.\n\n\t...\nend',
|
|
}
|
|
|
|
format_test {
|
|
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\t-- math.random\n\t-- 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\t...\nend',
|
|
}
|
|
|
|
format_test {
|
|
input = string.byte,
|
|
options = { more_function_info = true },
|
|
expect = 'builtin function (s [, i [, j]])\n\t-- string.byte\n\t-- 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\t-- Note that numerical codes are not necessarily portable across platforms.\n\n\t...\nend',
|
|
}
|
|
|
|
-- short_builtins option: If an builtin is expected to be available by some name
|
|
-- in a standard enviroment, return that name, instead of other complex info.
|
|
|
|
format_test {
|
|
input = math.random,
|
|
options = { short_builtins = true },
|
|
expect = 'math.random',
|
|
}
|
|
|
|
format_test {
|
|
input = { math.cos, math.sin, math.abs },
|
|
options = { short_builtins = true },
|
|
expect = '{ math.cos, math.sin, math.abs }',
|
|
}
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- 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
|
|
|
|
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)()',
|
|
}
|
|
|
|
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)()',
|
|
}
|
|
|
|
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 = { 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
|
|
|
|
|
|
format_test {
|
|
name = 'Closures do not affect builtins',
|
|
input = math.abs,
|
|
options = { more_function_info = true, include_closure = true },
|
|
expect = 'builtin function (x)\n\t-- math.abs\n\t-- Returns the absolute value of x.\n\n\t...\nend',
|
|
}
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Indent functions nicely
|
|
|
|
format_test {
|
|
-- The tail part should align, letting people focus on the important aspects.
|
|
input = { random = math.random, abs = math.abs },
|
|
expect = '{\n\tabs = builtin function (x) ... end,\n\trandom = builtin function ([m [, n]) ... end\n}',
|
|
}
|
|
|
|
format_test {
|
|
-- The function part should align, if some are builtin and some are not.
|
|
adv_getlocal = true,
|
|
input = { random = math.random, abs = function (x) return x < 0 and -x or x end },
|
|
expect = '{\n\tabs = function (x) ... end,\n\trandom = builtin function ([m [, n]) ... end\n}',
|
|
}
|
|
|
|
format_test {
|
|
-- No special indent if no special function modifier.
|
|
adv_getlocal = true,
|
|
input = { max = function(a, b) return a > b and a or b end,
|
|
abs = function (x) return x < 0 and -x or x end
|
|
},
|
|
expect = '{\n\tabs = function (x) ... end,\n\tmax = function (a, b) ... end\n}',
|
|
}
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
--[[
|
|
format_test {
|
|
adv_getlocal = true,
|
|
options = { more_function_info = true, _all_function_info = true, max_depth = 2 },
|
|
input = function() end,
|
|
expect = '',
|
|
}
|
|
--]]
|
|
|
|
return SUITE
|