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
end )
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 = ' { \n \t a = function () ... end \n } ' ,
}
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 ... \n end '
}
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 ... \n end '
}
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 ... \n end '
}
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 ... \n end '
}
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 \n end ' ) ( ) ( ) ,
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 \t return a + b \n end ' ) ( ) ,
options = { more_function_info = true } ,
expect = ' function (a, b) \n \t return a + b \n end ' ,
}
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 ... \n end ' ,
}
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 ... \n end ' ,
}
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 ... \n end ' ,
}
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 ... \n end ' ,
}
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 ... \n end ' ,
}
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-03 11:56:06 +00:00
expect = ' { \n \t math.cos, \n \t math.sin, \n \t math.abs \n } ' ,
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 } ,
expect = ' builtin function (x) \n \t -- math.abs \n \t -- Returns the absolute value of x. \n \n \t ... \n end ' ,
}
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 } ,
expect = ' { \n \t abs = builtin function (x) ... end, \n \t 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 \t abs = function (x) ... end, \n \t random = 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 \t abs = function (x) ... end, \n \t max = function (a, b) ... end \n } ' ,
}
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