local SUITE = require 'TestSuite' 'pretty' SUITE:setEnviroment{ format = require('pretty') } local ASSERT_ERROR_APPROX = [[ Approximate strings not similar enough: Should match: %s Gotten: %s ]] -------------------------------------------------------------------------------- -- Compat if not loadstring then loadstring = load end -- Lua 5.3 compat local HAS_ADV_GETLOCAL = not not debug.getinfo(1, 'u').nparams -- Lua 5.1 compat local HAS_UNICODE_IDEN = not not loadstring 'local ϕ = 1; return ϕ' -- Lua 5.1 compat local HAS_JIT_LIBRARY = type(rawget(_G, 'jit')) == 'table' -- Non-LuaJIT 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 actual_result = format(t.input, t.options) if not t.approx or type(actual_result) ~= 'string' then assert_equal(t.expect, actual_result) else if not actual_result:match(t.expect) then error(ASSERT_ERROR_APPROX:format(t.expect, actual_result)) end end end, { line = debug.getinfo(2).currentline }) end -------------------------------------------------------------------------------- -- Primitive types format_test { input = nil, expect = 'nil', } format_test { input = true, expect = 'true', } format_test { input = false, expect = 'false', } -------------------------------------------------------------------------------- -- Userdata printing -- TODO: Figure out a way to print userdata. -- Maybe look into using the one available debug.getupvalue(pairs, 1) -------------------------------------------------------------------------------- -- 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 -------------------------------------------------------------------------------- -- 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 { -- 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 = '{ { 1, 2, 3 }, { 4, 5, 6 } }', } format_test { input = { a = {1, 2, 3}, b = {4, 5, 6} }, expect = '{ a = { 1, 2, 3 }, b = { 4, 5, 6 } }', } format_test { name = 'Unicode characters can be used as string keys in tables', input = { ['a'] = 1, ['ψ'] = 2 }, expect = '{ a = 1, ψ = 2 }', } format_test { input = { [100] = 'Hi', [300] = 'Hello' }, expect = '{ [100] = \'Hi\', [300] = \'Hello\' }', } format_test { input = { 'Hi', [300] = 'Hello' }, expect = '{ [1] = \'Hi\', [300] = \'Hello\' }', } format_test { input = { { {} } }, expect = '{ { {} } }', } format_test { input = { [{ 1, 2 }] = { 2, 1 } }, expect = '{ [{ 1, 2 }] = { 2, 1 } }', } format_test { input = { { {1, 2}, {3, 4} }, {5, 6} }, expect = '{ { { 1, 2 }, { 3, 4 } }, { 5, 6 } }', } 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 = '{ {...}, {...} }', } format_test { input = { { {1, 2}, {3, 4} }, {5, 6} }, options = { max_depth = 2 }, expect = '{ { {...}, {...} }, { 5, 6 } }', } format_test { input = { { {1, 2}, {3, 4} }, {5, 6} }, options = { max_depth = 3 }, expect = '{ { { 1, 2 }, { 3, 4 } }, { 5, 6 } }', } format_test { input = { [{ {1,2}, {3,4} }] = 'Hello World' }, expect = '{ [{...}] = \'Hello World\' }', } format_test { input = { a = {1,2}, bcdefg = {3,4} }, expect = '{ a = { 1, 2 }, bcdefg = { 3, 4 } }', } format_test { input = { [true] = 1, [1] = false }, expect = '{ [1] = false, [true] = 1 }', } format_test { -- Proper indent input = { [1] = 1, ['whatever'] = false }, expect = '{ [1] = 1, [\'whatever\'] = false }', } format_test { -- Table view, with indent. input = { { a = 'hello', b = 'hi' }, { a = 'hi', b = 'hello' } }, expect = '{\n { a = \'hello\', b = \'hi\' },\n { a = \'hi\', b = \'hello\' }\n}', } format_test { name = 'Proper alignment when using unicode characters as keys', input = { ['djævle'] = 'dyr?', ['europa'] = 'måne', ['øå'] = 'en å på en ø?', }, expect = '{\n djævle = \'dyr?\',\n europa = \'måne\',\n øå = \'en å på en ø?\'\n}', } format_test { name = 'Format table into columns, if leaf node', input = { 'hello', 'world', 'how', 'is', 'it', 'going?', 'Im', 'doing great', 'thanks', 'that', 'was', 'what', 'I', 'were', 'expecting' }, expect = '{\n \'hello\', \'world\', \'how\',\n \'is\', \'it\', \'going?\',\n \'Im\', \'doing great\', \'thanks\',\n \'that\', \'was\', \'what\',\n \'I\', \'were\', \'expecting\'\n}', } -------------------------------------------------------------------------------- -- 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 do local a = {} local b = { a } a[1] = b local rec = { a = a, b = b } format_test { name = 'Top layers should be expanded, rather than lower layers.', input = rec, options = { max_depth = 5 }, expect = '{\n a = { {...} },\n b = { {...} }\n}', } end -------------------------------------------------------------------------------- -- CDATA -- TODO: Add more advanced understanding of cdata. if HAS_JIT_LIBRARY 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 -------------------------------------------------------------------------------- -- General SUITE:addTest('UseCase: Can print _G with max_depth = 1', function () format(_G, {max_depth = 1}) assert(true) end) SUITE:addTest('UseCase: Can load function from file that is shortly deleted', function () local module_name = 'tmp_'..os.time() -- Create module local file = io.open('./'..module_name..'.lua', 'w') file:write '\nlocal function yo ()\n -- Hello World\n return math.random()\nend\n\nreturn yo\n' file:close() -- Load module local yo = require(module_name) -- Remove module os.remove('./'..module_name..'.lua') package.loaded[module_name] = nil -- Format the function, even though the module it came from is gone. format(yo) assert(true) end) SUITE:addTest('UseCase: Can use pretty on loadstring block', function () -- TODO: Move to more appropriate test module. format(loadstring 'hi = 0') assert(true) end) -------------------------------------------------------------------------------- return SUITE