2016-12-28 23:51:07 +00:00
local SUITE = require ( ' TestSuite ' ) . new ( ' pretty ' )
SUITE : setEnviroment {
format = require ( ' pretty ' )
}
2016-12-29 14:33:43 +00:00
local ASSERT_ERROR_APPROX = [ [
Approximate strings not similar enough :
Should match : % s
Gotten : % s
] ]
2016-12-28 23:51:07 +00:00
--------------------------------------------------------------------------------
2017-01-05 14:50:44 +00:00
-- Lua 5.1 compat:
local HAS_ADV_GETLOCAL = not not debug.getinfo ( 1 , ' u ' ) . nparams
-- Lua 5.3 compat:
if not loadstring then
loadstring = load
end
2016-12-28 23:51:07 +00:00
local function format_test ( t )
2017-01-05 14:50:44 +00:00
if t.longterm then return end
if t.adv_getlocal and not HAS_ADV_GETLOCAL then return end
2016-12-28 23:51:07 +00:00
SUITE : addTest ( 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 )
2016-12-29 14:33:43 +00:00
if not t.approx or type ( actual_result ) ~= ' string ' then
2017-04-03 09:09:39 +00:00
assert_equal ( expected_result , actual_result )
2016-12-29 14:33:43 +00:00
else
if not actual_result : match ( expected_result ) then
error ( ASSERT_ERROR_APPROX : format ( expected_result , actual_result ) )
end
end
2016-12-28 23:51:07 +00:00
end )
end
--------------------------------------------------------------------------------
-- Strings
format_test {
input = ' Hello World ' ,
expect = ' \' Hello World \' ' ,
}
format_test {
input = ' Hello \' World \' ' ,
expect = ' \" Hello \' World \' \" ' ,
}
format_test {
input = ' Hello \" World \" ' ,
expect = ' \' Hello \" World \" \' ' ,
}
format_test {
input = ' Hello [[World]] ' ,
expect = ' \' Hello [[World]] \' ' ,
}
format_test {
input = ' \' Hello \' [[World]] ' ,
expect = ' \" \' Hello \' [[World]] \" ' ,
}
format_test {
input = ' \' Hello \' \" there \" [[World]] ' ,
expect = ' [=[ \' Hello \' \" there \" [[World]]]=] ' ,
}
format_test {
input = ' \' Hello \' \" there \" [=[World]=] ' ,
expect = ' [[ \' Hello \' \" there \" [=[World]=]]] ' ,
}
format_test {
input = ' \n Hello World ' ,
expect = ' \' \\ nHello World \' ' ,
}
format_test {
input = ' \' \" \n ' ,
expect = ' [[ \n \' \" \n ]] ' ,
}
format_test {
input = ' \n ' ,
expect = ' \' \\ n \' ' ,
}
format_test {
input = ' \\ ' ,
expect = ' \' \\ \\ \' ' ,
}
format_test {
input = ' \000 ' ,
expect = ' \' \\ 000 \' ' ,
}
format_test {
input = ' \a \b \v \r \f ' ,
expect = ' \' \\ a \\ b \\ v \\ r \\ f \' ' ,
}
format_test {
input = ' ø ' ,
expect = ' \' ø \' ' ,
}
--------------------------------------------------------------------------------
-- Numbers
format_test {
input = 0 ,
expect = ' 0 ' ,
}
format_test {
input = 2000 ,
expect = ' 2000 ' ,
}
format_test {
input = - 2000 ,
expect = ' -2000 ' ,
}
2017-01-16 15:10:10 +00:00
--------------------------------------------------------------------------------
-- Numbers, Other formats
format_test {
input = 10 ^ 5 ,
expect = ' 10^5 ' ,
}
format_test {
input = 10 ^- 6 ,
expect = ' 10^-6 ' ,
}
format_test {
input = 2 ^ 17 ,
expect = ' 2^17 ' ,
}
format_test {
input = 2 ^- 7 ,
expect = ' 2^-7 ' ,
}
format_test {
input = 1 / 3 ,
expect = ' 1/3 ' ,
}
format_test {
input = 9 / 17 ,
expect = ' 9/17 ' ,
}
format_test {
input = math.sqrt ( 2 ) ,
expect = ' math.sqrt(2) ' ,
}
format_test {
input = 1 / math.sqrt ( 2 ) ,
expect = ' 1/math.sqrt(2) ' ,
}
format_test {
input = math.log ( 2 ) ,
expect = ' math.log(2) ' ,
}
--------------------------------------------------------------------------------
-- Numbers, Constants
format_test {
input = math.pi ,
expect = ' math.pi ' ,
}
format_test {
input = math.exp ( 1 ) ,
expect = ' math.exp(1) ' ,
}
2016-12-28 23:51:07 +00:00
format_test {
input = 1 / 0 , -- Same as math.huge
expect = ' 1/0 ' ,
}
format_test {
input = - 1 / 0 , -- Same as -math.huge
expect = ' -1/0 ' ,
}
format_test {
input = 0 / 0 , -- Same as nan
expect = ' 0/0 ' ,
}
format_test {
input = 1 / 0 , -- Same as math.huge
options = { math_shorthand = true } ,
2017-01-16 15:10:10 +00:00
expect = ' ∞ ' ,
2016-12-28 23:51:07 +00:00
}
format_test {
input = - 1 / 0 , -- Same as -math.huge
options = { math_shorthand = true } ,
2017-01-16 15:10:10 +00:00
expect = ' -∞ ' ,
2016-12-28 23:51:07 +00:00
}
format_test {
input = 0 / 0 , -- Same as nan
options = { math_shorthand = true } ,
expect = ' nan ' ,
}
2017-01-16 15:10:10 +00:00
format_test {
input = math.pi ,
options = { math_shorthand = true } ,
expect = ' π ' ,
}
format_test {
input = math.exp ( 1 ) ,
options = { math_shorthand = true } ,
expect = ' e ' ,
}
format_test {
input = { 1 / 2 , 1 / 3 , 1 / 4 , 1 / 5 } ,
expect = ' { 1/2, 1/3, 1/4, 1/5 } ' ,
}
2016-12-29 14:33:43 +00:00
--------------------------------------------------------------------------------
-- Primitive types
format_test {
input = nil ,
expect = ' nil ' ,
}
format_test {
input = true ,
expect = ' true ' ,
}
format_test {
input = false ,
expect = ' false ' ,
}
--------------------------------------------------------------------------------
-- Function printing
format_test {
2017-01-05 14:50:44 +00:00
adv_getlocal = true ,
2016-12-29 14:33:43 +00:00
input = function ( ) end ,
expect = ' function () ... end ' ,
}
format_test {
2017-01-05 14:50:44 +00:00
adv_getlocal = true ,
2016-12-29 14:33:43 +00:00
input = function ( a ) end ,
expect = ' function (a) ... end ' ,
}
format_test {
2017-01-05 14:50:44 +00:00
adv_getlocal = true ,
2016-12-29 14:33:43 +00:00
input = function ( a , b ) end ,
expect = ' function (a, b) ... end ' ,
}
format_test {
2017-01-05 14:50:44 +00:00
adv_getlocal = true ,
2016-12-29 14:33:43 +00:00
input = function ( ... ) end ,
expect = ' function (...) ... end ' ,
}
format_test {
2017-01-05 14:50:44 +00:00
adv_getlocal = true ,
2016-12-29 14:33:43 +00:00
input = function ( a , b , ... ) end ,
expect = ' function (a, b, ...) ... end ' ,
}
do
local SOME_RANDOM_UPVALUE = false
format_test {
2017-01-05 14:50:44 +00:00
adv_getlocal = true ,
2016-12-29 14:33:43 +00:00
input = function ( ) l = SOME_RANDOM_UPVALUE end ,
expect = ' function () ... end ' ,
}
format_test {
2017-01-05 14:50:44 +00:00
adv_getlocal = true ,
2016-12-29 14:33:43 +00:00
input = function ( ) SOME_RANDOM_UPVALUE = true end ,
expect = ' function () ... end ' ,
}
format_test {
-- More function info is ignored if not at depth 0.
2017-01-05 14:50:44 +00:00
adv_getlocal = true ,
2016-12-29 14:33:43 +00:00
input = { a = function ( ) SOME_RANDOM_UPVALUE = true end } ,
options = { more_function_info = true } ,
expect = ' { \n \t a = function () ... end \n } ' ,
}
2017-01-05 14:50:44 +00:00
local func_line = debug.getinfo ( 1 ) . currentline + 2 -- Must be exactly 2 lines above function
2016-12-29 14:33:43 +00:00
format_test {
input = function ( ) l = SOME_RANDOM_UPVALUE end ,
2017-01-05 14:50:44 +00:00
adv_getlocal = true ,
2016-12-29 14:33:43 +00:00
options = { more_function_info = true } ,
2017-01-05 14:50:44 +00:00
expect = ' function () \n \t -- source_file: \' ./test/test_pretty.lua \' [Line: ' .. func_line .. ' ] \n \t -- up_values: { SOME_RANDOM_UPVALUE = false } \n \n \t ... \n end '
2016-12-29 14:33:43 +00:00
}
2017-01-05 14:50:44 +00:00
local func_line = debug.getinfo ( 1 ) . currentline + 2 -- Must be exactly 2 lines above function
2016-12-29 14:33:43 +00:00
format_test {
input = function ( ) SOME_RANDOM_UPVALUE = true end ,
2017-01-05 14:50:44 +00:00
adv_getlocal = true ,
2016-12-29 14:33:43 +00:00
options = { more_function_info = true } ,
2017-01-05 14:50:44 +00:00
expect = ' function () \n \t -- source_file: \' ./test/test_pretty.lua \' [Line: ' .. func_line .. ' ] \n \t -- up_values: { SOME_RANDOM_UPVALUE = false } \n \n \t ... \n end '
2016-12-29 14:33:43 +00:00
}
end
2017-01-05 14:50:44 +00:00
do
local func_line = debug.getinfo ( 1 ) . currentline + 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_pretty.lua \' [Line: ' .. func_line .. ' ] \n \n \t ... \n end '
}
end
2016-12-29 14:33:43 +00:00
do
local index = 0
format_test {
input = function ( ) index = index + 1 ; return index end ,
2017-01-05 14:50:44 +00:00
adv_getlocal = true ,
2016-12-29 15:17:58 +00:00
expect = ' function () ... end '
}
2017-01-05 14:50:44 +00:00
local func_line = debug.getinfo ( 1 ) . currentline + 2 -- Must be exactly 2 lines above function
2016-12-29 15:17:58 +00:00
format_test {
input = function ( ) index = index + 1 ; return index end ,
2017-01-05 14:50:44 +00:00
adv_getlocal = true ,
2016-12-29 15:17:58 +00:00
options = { more_function_info = true } ,
2017-01-05 14:50:44 +00:00
expect = ' function () \n \t -- source_file: \' ./test/test_pretty.lua \' [Line: ' .. func_line .. ' ] \n \t -- up_values: { index = 0 } \n \n \t ... \n end '
2016-12-29 14:33:43 +00:00
}
end
format_test {
2017-01-05 14:50:44 +00:00
adv_getlocal = true ,
2016-12-29 14:33:43 +00:00
input = loadstring ( ' return function () end ' ) ( ) ,
2016-12-29 15:54:31 +00:00
expect = ' function () end ' ,
}
format_test {
2017-01-05 14:50:44 +00:00
adv_getlocal = true ,
2016-12-29 15:54:31 +00:00
input = loadstring ( ' return function () return function () end end ' ) ( ) ,
expect = ' function () return function () end end ' ,
}
format_test {
longterm = true ,
2017-01-05 14:50:44 +00:00
adv_getlocal = true ,
2016-12-29 15:54:31 +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
longterm = true ,
2017-01-05 14:50:44 +00:00
adv_getlocal = true ,
2016-12-29 15:54:31 +00:00
input = loadstring ( ' return function () return function () end end ' ) ( ) ( ) ,
expect = ' function () end ' ,
2016-12-29 14:33:43 +00:00
}
2016-12-29 15:17:58 +00:00
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 } ,
2016-12-29 15:54:31 +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.
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 ' ,
2016-12-29 15:17:58 +00:00
}
2017-01-05 14:50:44 +00:00
do
local func_line = debug.getinfo ( 1 ) . currentline + 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_pretty.lua \' [Lines: ' .. func_line .. ' - ' .. ( func_line + 2 ) .. ' ] \n \n \t ... \n end ' ,
}
2016-12-29 15:17:58 +00:00
2017-01-05 14:50:44 +00:00
local func_line = debug.getinfo ( 1 ) . currentline + 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_pretty.lua \' [Line: ' .. func_line .. ' ] \n \n \t ... \n end ' ,
}
end
2016-12-29 15:17:58 +00:00
2016-12-29 17:40:30 +00:00
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 ' ,
}
2016-12-30 11:02:46 +00:00
format_test {
input = string.byte ,
options = { more_function_info = true } ,
2016-12-31 15:50:12 +00:00
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 ' ,
2016-12-30 11:02:46 +00:00
}
2017-01-05 13:28:31 +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.
2017-01-05 14:50:44 +00:00
adv_getlocal = true ,
2017-01-05 13:28:31 +00:00
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.
2017-01-05 14:50:44 +00:00
adv_getlocal = true ,
2017-01-05 13:28:31 +00:00
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 } ' ,
}
2016-12-29 14:33:43 +00:00
--------------------------------------------------------------------------------
-- Userdata printing
2016-12-29 15:54:31 +00:00
-- TODO. First off figure out a way to test this stuff.
2016-12-29 17:40:30 +00:00
-- Maybe look into using the one available debug.getupvalue(pairs, 1)
2016-12-29 14:33:43 +00:00
--------------------------------------------------------------------------------
-- Thread printing
do
local suspended_coroutine = coroutine.create ( function ( ) end )
format_test {
input = suspended_coroutine ,
approx = true ,
expect = ' suspended coroutine: 0x%x+ ' ,
}
end
do
local dead_coroutine = coroutine.create ( function ( ) end )
coroutine.resume ( dead_coroutine )
format_test {
input = dead_coroutine ,
approx = true ,
expect = ' dead coroutine: 0x%x+ ' ,
}
end
2016-12-28 23:51:07 +00:00
--------------------------------------------------------------------------------
-- Single-line tables
format_test {
input = { } ,
expect = ' {} ' ,
}
format_test {
input = { 1 , 2 , 3 } ,
expect = ' { 1, 2, 3 } ' ,
}
format_test {
input = { ' Hello ' , ' World ' } ,
expect = ' { \' Hello \' , \' World \' } ' ,
}
format_test {
input = { a = 1 , b = 2 } ,
expect = ' { a = 1, b = 2 } ' ,
}
format_test {
input = { __hello = true } ,
expect = ' { __hello = true } ' ,
}
format_test {
input = { [ ' ]] ' ] = true } ,
expect = ' { [ \' ]] \' ] = true } ' ,
}
format_test {
input = { [ ' and ' ] = true } ,
expect = ' { [ \' and \' ] = true } ' ,
}
format_test {
input = { [ false ] = false , [ true ] = true } ,
expect = ' { [false] = false, [true] = true } ' ,
}
format_test {
input = { [ 100 ] = ' Hi ' , [ 300 ] = ' Hello ' } ,
expect = ' { [100] = \' Hi \' , [300] = \' Hello \' } ' ,
}
format_test { -- Order does not matter
input = { b = 1 , a = 2 } ,
expect = ' { a = 2, b = 1 } ' ,
}
format_test { -- Can include empty tables
input = { { } , { } , { } } ,
expect = ' { {}, {}, {} } ' ,
}
format_test { -- Can include very small tables
input = { { 1 } , { 2 } , { 3 } } ,
expect = ' { { 1 }, { 2 }, { 3 } } ' ,
}
--------------------------------------------------------------------------------
-- Multi-line tables
format_test {
input = { { 1 , 2 , 3 } , { 4 , 5 , 6 } } ,
expect = ' { \n \t { 1, 2, 3 }, \n \t { 4, 5, 6 } \n } ' ,
}
format_test {
input = { a = { 1 , 2 , 3 } , b = { 4 , 5 , 6 } } ,
expect = ' { \n \t a = { 1, 2, 3 }, \n \t b = { 4, 5, 6 } \n } ' ,
}
format_test {
input = { ' Hi ' , [ 300 ] = ' Hello ' } ,
expect = ' { \n \t [1] = \' Hi \' , \n \t [300] = \' Hello \' \n } ' ,
}
format_test {
input = { { { } } } ,
expect = ' { \n \t { {} } \n } ' ,
}
format_test {
input = { [ { 1 , 2 } ] = { 2 , 1 } } ,
expect = ' { \n \t [{ 1, 2 }] = { 2, 1 } \n } ' ,
}
format_test {
input = { { { 1 , 2 } , { 3 , 4 } } , { 5 , 6 } } ,
expect = ' { \n \t { \n \t \t { 1, 2 }, \n \t \t { 3, 4 } \n \t }, \n \t { 5, 6 } \n } ' ,
}
format_test {
input = { { { 1 , 2 } , { 3 , 4 } } , { 5 , 6 } } ,
options = { max_depth = 0 } ,
expect = ' {...} ' ,
}
format_test {
input = { { { 1 , 2 } , { 3 , 4 } } , { 5 , 6 } } ,
options = { max_depth = 1 } ,
expect = ' { \n \t {...}, \n \t {...} \n } ' ,
}
format_test {
input = { { { 1 , 2 } , { 3 , 4 } } , { 5 , 6 } } ,
options = { max_depth = 2 } ,
expect = ' { \n \t { \n \t \t {...}, \n \t \t {...} \n \t }, \n \t { 5, 6 } \n } ' ,
}
format_test {
input = { { { 1 , 2 } , { 3 , 4 } } , { 5 , 6 } } ,
options = { max_depth = 3 } ,
expect = ' { \n \t { \n \t \t { 1, 2 }, \n \t \t { 3, 4 } \n \t }, \n \t { 5, 6 } \n } ' ,
}
format_test {
input = { [ { { 1 , 2 } , { 3 , 4 } } ] = ' Hello World ' } ,
expect = ' { \n \t [{...}] = \' Hello World \' \n } ' ,
}
2016-12-29 04:34:44 +00:00
format_test {
input = { a = { 1 , 2 } , bcdefg = { 3 , 4 } } ,
expect = ' { \n \t a = { 1, 2 }, \n \t bcdefg = { 3, 4 } \n } ' ,
}
format_test {
input = { [ true ] = 1 , [ 1 ] = false } ,
expect = ' { \n \t [true] = 1, \n \t [1] = false \n } ' ,
}
2016-12-29 11:11:48 +00:00
format_test {
2017-01-05 13:28:31 +00:00
-- Proper indent
2016-12-29 11:11:48 +00:00
input = { [ 1 ] = 1 , [ ' whatever ' ] = false } ,
expect = ' { \n \t [1] = 1, \n \t [ \' whatever \' ] = false \n } ' ,
}
2017-01-05 13:28:31 +00:00
format_test {
-- Table view, with indent.
input = { { a = ' hello ' , b = ' hi ' } , { a = ' hi ' , b = ' hello ' } } ,
expect = ' { \n \t { a = \' hello \' , b = \' hi \' }, \n \t { a = \' hi \' , b = \' hello \' } \n } ' ,
}
2017-01-16 15:22:46 +00:00
--------------------------------------------------------------------------------
-- Table recursion
do
local recursive = { }
recursive [ 1 ] = recursive
format_test {
input = recursive ,
options = { max_depth = 5 } ,
expect = ' { {...} } ' ,
}
format_test {
input = recursive ,
options = { max_depth = 5 , recursion = ' ignore ' } ,
expect = ' { {...} } ' ,
}
format_test {
input = recursive ,
options = { max_depth = 5 , recursion = ' marked ' } ,
expect = ' <1>{ <1>{...} } ' ,
}
end
2017-01-16 15:10:10 +00:00
--------------------------------------------------------------------------------
-- Table Sorting
format_test {
input = { ' c ' , ' b ' , ' a ' } ,
expect = ' { \' c \' , \' b \' , \' a \' } ' ,
}
format_test {
input = { a = 1 , a1 = 0 } ,
expect = ' { a = 1, a1 = 0 } ' ,
}
format_test {
input = { a10 = 1 , a1 = 0 } ,
expect = ' { a1 = 0, a10 = 1 } ' ,
}
format_test {
input = { a00 = 0 , a1 = 1 } ,
expect = ' { a00 = 0, a1 = 1 } ' ,
}
format_test {
input = { a = { } , b = true } ,
expect = ' { b = true, a = {} } ' ,
}
format_test {
input = { a = { } , b = true , b1 = false } ,
expect = ' { b = true, b1 = false, a = {} } ' ,
}
format_test {
input = { { } , true , false , 5 } ,
expect = ' { {}, true, false, 5 } ' ,
}
-- TODO: Add more tests for sorting.
2016-12-29 14:33:43 +00:00
--------------------------------------------------------------------------------
-- CDATA
-- TODO: Add more advanced understanding of cdata.
if type ( jit ) == ' table ' then
local ffi = require ( ' ffi ' )
ffi.cdef [ [
int poll ( struct pollfd * fds , unsigned long nfds , int timeout ) ;
] ]
format_test {
input = ffi.C . poll ,
approx = true ,
expect = ' cdata<.+>: 0x%x+ ' ,
}
format_test {
input = ffi.new ( ' int[10] ' ) ,
approx = true ,
expect = ' cdata<.+>: 0x%x+ ' ,
}
end
2016-12-28 23:51:07 +00:00
--------------------------------------------------------------------------------
return SUITE