406 lines
14 KiB
Lua
406 lines
14 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.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 ()
|
|
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 TEST_UPVALUE = 42
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- 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',
|
|
}
|
|
|
|
format_test {
|
|
name = 'Closures don\'t affect functions printed in single mode 1',
|
|
adv_getlocal = true,
|
|
single = true,
|
|
input = function () l = TEST_UPVALUE end,
|
|
expect = 'function () ... end',
|
|
}
|
|
|
|
format_test {
|
|
name = 'Closures don\'t affect functions printed in single mode 2',
|
|
adv_getlocal = true,
|
|
single = true,
|
|
input = function () TEST_UPVALUE = true end,
|
|
expect = 'function () ... end',
|
|
}
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Basic elaborate functions
|
|
|
|
local func_line = curline(3)
|
|
format_test {
|
|
name = 'Singleline elaborate function',
|
|
input = function () end,
|
|
expect = 'function ()\n -- source_file: \'./test/test_function.lua\' [Line: '..func_line..']\n\n ...\nend',
|
|
}
|
|
|
|
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,
|
|
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,
|
|
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,
|
|
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',
|
|
}
|
|
|
|
-- 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 {
|
|
-- 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 }',
|
|
}
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Embedding functions loaded with loadstring
|
|
|
|
format_test {
|
|
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 {
|
|
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',
|
|
}
|
|
|
|
format_test {
|
|
single = true,
|
|
adv_getlocal = true,
|
|
options = { embed_loaded_funcs = true },
|
|
input = loadstring('return function () return function () end\nend')()(),
|
|
expect = 'function () end',
|
|
}
|
|
|
|
format_test {
|
|
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',
|
|
}
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- 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 abs = builtin function (x) ... end,\n random = 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 abs = function (x) ... end,\n random = builtin function ([m [, n]) ... end\n}',
|
|
}
|
|
|
|
format_test {
|
|
-- The function part should align, even if one is loaded from a string.
|
|
adv_getlocal = true,
|
|
input = { random = math.random, abs = loadstring('return function () return 1 end')() },
|
|
options = { embed_loaded_funcs = true },
|
|
expect = '{\n abs = function () return 1 end,\n random = builtin function ([m [, n]) ... end\n}',
|
|
}
|
|
|
|
format_test {
|
|
-- The end part should align when both are loaded from strings.
|
|
adv_getlocal = true,
|
|
input = { a = loadstring'return function(a) return a end'(), b = loadstring'return function (...) return ... end'() },
|
|
options = { embed_loaded_funcs = true },
|
|
expect = '{\n a = function (a) return a end,\n b = function (...) return ... 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 abs = function (x) ... end,\n max = function (a, b) ... end\n}',
|
|
}
|
|
|
|
format_test {
|
|
name = 'Functions with unicode-named parameters should align nicely',
|
|
adv_getlocal = true,
|
|
input = { a = function (ψ) return ψ end,
|
|
b = function (a) return a end
|
|
},
|
|
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 = { _all_function_info = true, max_depth = 2 },
|
|
input = function() end,
|
|
expect = '',
|
|
}
|
|
--]]
|
|
|
|
return SUITE
|