2017-04-03 09:55:49 +00:00
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
2017-04-03 11:49:18 +00:00
SUITE : addTest ( t.name or t.expect , function ( )
2017-04-03 09:55:49 +00:00
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
2017-04-12 13:15:03 +00:00
end , { line = debug.getinfo ( 2 ) . currentline } )
2017-04-03 09:55:49 +00:00
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 } ,
2017-04-14 10:19:23 +00:00
expect = ' { a = function () ... end } ' ,
2017-04-03 09:55:49 +00:00
}
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 } ,
2017-06-09 15:24:05 +00:00
expect = ' function () \n -- source_file: \' ./test/test_function.lua \' [Line: ' .. func_line .. ' ] \n -- up_values: { SOME_RANDOM_UPVALUE = false } \n \n ... \n end '
2017-04-03 09:55:49 +00:00
}
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 } ,
2017-06-09 15:24:05 +00:00
expect = ' function () \n -- source_file: \' ./test/test_function.lua \' [Line: ' .. func_line .. ' ] \n -- up_values: { SOME_RANDOM_UPVALUE = false } \n \n ... \n end '
2017-04-03 09:55:49 +00:00
}
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 } ,
2017-06-09 15:24:05 +00:00
expect = ' function () \n -- source_file: \' ./test/test_function.lua \' [Line: ' .. func_line .. ' ] \n \n ... \n end '
2017-04-03 09:55:49 +00:00
}
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 } ,
2017-06-09 15:24:05 +00:00
expect = ' function () \n -- source_file: \' ./test/test_function.lua \' [Line: ' .. func_line .. ' ] \n -- up_values: { index = 0 } \n \n ... \n end '
2017-04-03 09:55:49 +00:00
}
end
format_test {
adv_getlocal = true ,
2017-06-11 11:53:06 +00:00
options = { embed_loaded_funcs = true } ,
2017-04-03 09:55:49 +00:00
input = loadstring ( ' return function () end ' ) ( ) ,
expect = ' function () end ' ,
}
format_test {
adv_getlocal = true ,
2017-06-11 11:53:06 +00:00
options = { embed_loaded_funcs = true } ,
2017-04-03 09:55:49 +00:00
input = loadstring ( ' return function () return function () end end ' ) ( ) ,
expect = ' function () return function () end end ' ,
}
format_test {
adv_getlocal = true ,
2017-06-11 11:53:06 +00:00
options = { embed_loaded_funcs = true } ,
2017-04-03 09:55:49 +00:00
input = loadstring ( ' return function () return function () end \n end ' ) ( ) ( ) ,
expect = ' function () end ' ,
}
format_test {
-- NOTE: This is HARD to fix. It's thus longerterm
adv_getlocal = true ,
2017-06-11 11:53:06 +00:00
options = { embed_loaded_funcs = true } ,
2017-04-03 09:55:49 +00:00
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 ' ) ( ) ,
2017-06-11 11:53:06 +00:00
options = { embed_loaded_funcs = true } ,
2017-04-03 09:55:49 +00:00
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.
2017-06-09 15:24:05 +00:00
input = loadstring ( ' return function (a, b) \n return a + b \n end ' ) ( ) ,
2017-06-11 11:53:06 +00:00
options = { embed_loaded_funcs = true } ,
2017-06-09 15:24:05 +00:00
expect = ' function (a, b) \n return a + b \n end ' ,
2017-04-03 09:55:49 +00:00
}
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 } ,
2017-06-09 15:24:05 +00:00
expect = ' function () \n -- source_file: \' ./test/test_function.lua \' [Lines: ' .. func_line .. ' - ' .. ( func_line + 2 ) .. ' ] \n \n ... \n end ' ,
2017-04-03 09:55:49 +00:00
}
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 } ,
2017-06-09 15:24:05 +00:00
expect = ' function () \n -- source_file: \' ./test/test_function.lua \' [Line: ' .. func_line .. ' ] \n \n ... \n end ' ,
2017-04-03 09:55:49 +00:00
}
end
format_test {
input = math.abs ,
expect = ' builtin function (x) ... end ' ,
}
format_test {
input = math.abs ,
options = { more_function_info = true } ,
2017-06-09 15:24:05 +00:00
expect = ' builtin function (x) \n -- math.abs \n -- Returns the absolute value of x. \n \n ... \n end ' ,
2017-04-03 09:55:49 +00:00
}
format_test {
input = math.random ,
expect = ' builtin function ([m [, n]) ... end ' ,
}
format_test {
input = math.random ,
options = { more_function_info = true } ,
2017-06-09 15:24:05 +00:00
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 ... \n end ' ,
2017-04-03 09:55:49 +00:00
}
format_test {
input = string.byte ,
options = { more_function_info = true } ,
2017-06-09 15:24:05 +00:00
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 ... \n end ' ,
2017-04-03 09:55:49 +00:00
}
2017-04-03 11:49:18 +00:00
-- 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 } ,
2017-04-14 10:19:23 +00:00
expect = ' { math.cos, math.sin, math.abs } ' ,
2017-04-03 11:49:18 +00:00
}
--------------------------------------------------------------------------------
-- 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
2017-04-03 11:56:06 +00:00
format_test {
name = ' Closures do not affect builtins ' ,
input = math.abs ,
options = { more_function_info = true , include_closure = true } ,
2017-06-09 15:24:05 +00:00
expect = ' builtin function (x) \n -- math.abs \n -- Returns the absolute value of x. \n \n ... \n end ' ,
2017-04-03 11:56:06 +00:00
}
2017-04-03 11:49:18 +00:00
--------------------------------------------------------------------------------
2017-04-03 09:55:49 +00:00
-- Indent functions nicely
format_test {
-- The tail part should align, letting people focus on the important aspects.
input = { random = math.random , abs = math.abs } ,
2017-06-09 15:24:05 +00:00
expect = ' { \n abs = builtin function (x) ... end, \n random = builtin function ([m [, n]) ... end \n } ' ,
2017-04-03 09:55:49 +00:00
}
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 } ,
2017-06-09 15:24:05 +00:00
expect = ' { \n abs = function (x) ... end, \n random = builtin function ([m [, n]) ... end \n } ' ,
2017-04-03 09:55:49 +00:00
}
2017-06-11 11:53:06 +00:00
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 } ' ,
}
2017-04-03 09:55:49 +00:00
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
} ,
2017-06-09 15:24:05 +00:00
expect = ' { \n abs = function (x) ... end, \n max = function (a, b) ... end \n } ' ,
2017-04-03 09:55:49 +00:00
}
2017-04-03 11:49:18 +00:00
2017-04-03 09:55:49 +00:00
--------------------------------------------------------------------------------
2017-04-03 11:49:18 +00:00
--[[
format_test {
adv_getlocal = true ,
options = { more_function_info = true , _all_function_info = true , max_depth = 2 } ,
input = function ( ) end ,
expect = ' ' ,
}
--]]
2017-04-03 09:55:49 +00:00
return SUITE