Improved assert messages for certain expressions.
This commit is contained in:
parent
aec232efcb
commit
c9842a2b29
|
@ -1,9 +1,6 @@
|
||||||
|
|
||||||
local lexer = require 'lua_lang'
|
local lexer = require 'lua_lang'
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
local function get_assert_body_text (call_info)
|
local function get_assert_body_text (call_info)
|
||||||
|
@ -72,6 +69,57 @@ local function get_value_of_string (string_str)
|
||||||
assert(false)
|
assert(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function fmt_val (val)
|
||||||
|
if type(val) == 'string' then
|
||||||
|
return string.format('%q', val)
|
||||||
|
else
|
||||||
|
return tostring(val)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_variable_and_prefix (gotten_name, level)
|
||||||
|
assert(type(gotten_name) == 'string')
|
||||||
|
assert(type(level) == 'number')
|
||||||
|
--
|
||||||
|
local gotten_val, var_scope, in_func = get_variable(gotten_name, level + 1)
|
||||||
|
local func_name = (in_func == '' and ' to anonymous function') or in_func and (' to \''..in_func..'\'') or ''
|
||||||
|
return gotten_val, ('assertion failed! bad %s \'%s\'%s'):format(var_scope, gotten_name, func_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local CONSTANT_VALUE_TOKEN = {
|
||||||
|
NUMBER = tonumber,
|
||||||
|
STRING = get_value_of_string,
|
||||||
|
TRUE = function() return true end,
|
||||||
|
FALSE = function() return false end,
|
||||||
|
NIL = function() return nil end
|
||||||
|
}
|
||||||
|
|
||||||
|
local PRIMITIVE_VALUES = {
|
||||||
|
['nil'] = true,
|
||||||
|
['boolean'] = true,
|
||||||
|
}
|
||||||
|
|
||||||
|
local COMPLEX_TYPES = {
|
||||||
|
['table'] = true,
|
||||||
|
['userdata'] = true,
|
||||||
|
['cdata'] = true,
|
||||||
|
}
|
||||||
|
|
||||||
|
local function get_value_of_const_token (token)
|
||||||
|
assert(CONSTANT_VALUE_TOKEN[token.token])
|
||||||
|
return CONSTANT_VALUE_TOKEN[token.token](token.text)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function fmt_with_type (val)
|
||||||
|
-- Primitive values ARE their type, and don't need the annotation.
|
||||||
|
if PRIMITIVE_VALUES[type(val)] then return tostring(val) end
|
||||||
|
-- Complex types are already formatted with some type information.
|
||||||
|
if COMPLEX_TYPES[type(val)] then return tostring(val) end
|
||||||
|
-- Numbers and string should have their types with them.
|
||||||
|
return type(val) .. ' ' .. fmt_val(val)
|
||||||
|
end
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
return function (condition)
|
return function (condition)
|
||||||
|
@ -79,12 +127,27 @@ return function (condition)
|
||||||
local call_info = debug.getinfo(2)
|
local call_info = debug.getinfo(2)
|
||||||
local tokens, body_text = get_assert_body(call_info)
|
local tokens, body_text = get_assert_body(call_info)
|
||||||
|
|
||||||
if tokens[1].text == 'type' and tokens[2].token == 'LPAR' and tokens[3].token == 'IDENTIFIER' and tokens[4].token == 'RPAR'and tokens[5].token == 'EQ'and tokens[6].token == 'STRING' then
|
if #tokens == 6 and tokens[1].text == 'type' and tokens[2].token == 'LPAR' and tokens[3].token == 'IDENTIFIER' and tokens[4].token == 'RPAR'and tokens[5].token == 'EQ'and tokens[6].token == 'STRING' then
|
||||||
local var_name = tokens[3].text
|
local gotten_val, prefix = get_variable_and_prefix(tokens[3].text, 2)
|
||||||
local var_val, var_scope, is_func = get_variable(var_name, 2)
|
error(('%s (%s expected, but got %s: %s)'):format(prefix, get_value_of_string(tokens[6].text), type(gotten_val), fmt_val(gotten_val)), 2)
|
||||||
local func_name = (is_func == '' and ' to anonymous function') or is_func and (' to \''..is_func..'\'') or ''
|
elseif #tokens == 3 and tokens[1].token == 'IDENTIFIER' and tokens[2].token == 'EQ' and CONSTANT_VALUE_TOKEN[tokens[3].token] then
|
||||||
error(('assertion failed! bad %s \'%s\'%s (%s expected, but got %s: %s)'):format(var_scope, var_name, func_name, get_value_of_string(tokens[6].text), type(var_val), var_val), 2)
|
local gotten_val, prefix = get_variable_and_prefix(tokens[1].text, 2)
|
||||||
|
|
||||||
|
local expected_value = get_value_of_const_token(tokens[3])
|
||||||
|
local type_annotation = (type(expected_value) == type(gotten_val)) and '' or (' '..type(gotten_val))
|
||||||
|
|
||||||
|
error(('%s (%s expected, but got%s: %s)'):format(prefix, fmt_with_type(expected_value), type_annotation, fmt_val(gotten_val)), 2)
|
||||||
|
elseif #tokens == 3 and tokens[1].token == 'IDENTIFIER' and tokens[2].token == 'NEQ' and CONSTANT_VALUE_TOKEN[tokens[3].token] then
|
||||||
|
local gotten_val, prefix = get_variable_and_prefix(tokens[1].text, 2)
|
||||||
|
|
||||||
|
local expected_value = get_value_of_const_token(tokens[3])
|
||||||
|
|
||||||
|
error(('%s (expected anything other than %s, but got %s)'):format(prefix, fmt_with_type(expected_value), fmt_val(gotten_val)), 2)
|
||||||
|
elseif #tokens == 1 and tokens[1].token == 'IDENTIFIER' then
|
||||||
|
local gotten_val, prefix = get_variable_and_prefix(tokens[1].text, 2)
|
||||||
|
error(('%s (truthy expected, but got %s)'):format(prefix, fmt_val(gotten_val)), 2)
|
||||||
else
|
else
|
||||||
|
print(require'pretty'(tokens))
|
||||||
error(('assertion failed! expression `%s` evaluated to %s'):format(body_text, condition), 2)
|
error(('assertion failed! expression `%s` evaluated to %s'):format(body_text, condition), 2)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -45,25 +45,6 @@ SUITE:addTest('argument to named function', function ()
|
||||||
assert_equal('./test/test_assert-gooder.lua:'..curline(-3)..': '..'assertion failed! bad argument #1 \'a\' to \'f\' (string expected, but got number: 2)', msg)
|
assert_equal('./test/test_assert-gooder.lua:'..curline(-3)..': '..'assertion failed! bad argument #1 \'a\' to \'f\' (string expected, but got number: 2)', msg)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
|
||||||
-- Other assert types
|
|
||||||
|
|
||||||
SUITE:addTest('compare values', function ()
|
|
||||||
local _, msg = pcall(function ()
|
|
||||||
local a = 2
|
|
||||||
assert(a == 4)
|
|
||||||
end)
|
|
||||||
assert_equal('./test/test_assert-gooder.lua:'..curline(-2)..': '..'assertion failed! bad local \'a\' (value of 4 expected, but got: 2)', msg)
|
|
||||||
end)
|
|
||||||
|
|
||||||
SUITE:addTest('compare values across types', function ()
|
|
||||||
local _, msg = pcall(function ()
|
|
||||||
local a = "hi"
|
|
||||||
assert(a == 4)
|
|
||||||
end)
|
|
||||||
assert_equal('./test/test_assert-gooder.lua:'..curline(-2)..': '..'assertion failed! bad local \'a\' (value of 4 expected, but got string: "hi")', msg)
|
|
||||||
end)
|
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
SUITE:addTest('can improve asserts in loaded strings too', function ()
|
SUITE:addTest('can improve asserts in loaded strings too', function ()
|
||||||
|
@ -80,6 +61,52 @@ SUITE:addTest('really complicated expression', function ()
|
||||||
assert_equal('./test/test_assert-gooder.lua:'..curline(-3)..': '..'assertion failed! expression `a == 3 and math.floor(2.522) == 2 or 5 == n` evaluated to false', msg)
|
assert_equal('./test/test_assert-gooder.lua:'..curline(-3)..': '..'assertion failed! expression `a == 3 and math.floor(2.522) == 2 or 5 == n` evaluated to false', msg)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
-- Other assert types
|
||||||
|
|
||||||
|
SUITE:addTest('compare values', function ()
|
||||||
|
local _, msg = pcall(function ()
|
||||||
|
local a = 2
|
||||||
|
assert(a == 4)
|
||||||
|
end)
|
||||||
|
assert_equal('./test/test_assert-gooder.lua:'..curline(-2)..': '..'assertion failed! bad local \'a\' (number 4 expected, but got: 2)', msg)
|
||||||
|
end)
|
||||||
|
|
||||||
|
SUITE:addTest('compare values across types', function ()
|
||||||
|
local _, msg = pcall(function ()
|
||||||
|
local a = "hi"
|
||||||
|
assert(a == 4)
|
||||||
|
end)
|
||||||
|
assert_equal('./test/test_assert-gooder.lua:'..curline(-2)..': '..'assertion failed! bad local \'a\' (number 4 expected, but got string: "hi")', msg)
|
||||||
|
end)
|
||||||
|
|
||||||
|
SUITE:addTest('is nil', function ()
|
||||||
|
local _, msg = pcall(function ()
|
||||||
|
local a = "hi"
|
||||||
|
assert(a == nil)
|
||||||
|
end)
|
||||||
|
assert_equal('./test/test_assert-gooder.lua:'..curline(-2)..': '..'assertion failed! bad local \'a\' (nil expected, but got string: "hi")', msg)
|
||||||
|
end)
|
||||||
|
|
||||||
|
SUITE:addTest('truthy', function ()
|
||||||
|
local _, msg = pcall(function ()
|
||||||
|
local a = false
|
||||||
|
assert(a)
|
||||||
|
end)
|
||||||
|
assert_equal('./test/test_assert-gooder.lua:'..curline(-2)..': '..'assertion failed! bad local \'a\' (truthy expected, but got false)', msg)
|
||||||
|
end)
|
||||||
|
|
||||||
|
SUITE:addTest('not equal', function (constant_value, msg_in_pars)
|
||||||
|
local func = loadstring (("return function() local a = %s; assert(a ~= %s) end"):format(constant_value, constant_value)) ()
|
||||||
|
local _, msg = pcall(setfenv(func, getfenv()))
|
||||||
|
assert_equal('[string "return function() ..."]:1: assertion failed! bad local \'a\' ('..msg_in_pars..')', msg)
|
||||||
|
end, { data = {
|
||||||
|
{ 'true' , 'expected anything other than true, but got true' },
|
||||||
|
{ 'nil', 'expected anything other than nil, but got nil' },
|
||||||
|
{ '"hi"', 'expected anything other than string "hi", but got "hi"' },
|
||||||
|
{ '42', 'expected anything other than number 42, but got 42' },
|
||||||
|
}})
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
return SUITE
|
return SUITE
|
||||||
|
|
Loading…
Reference in New Issue
Block a user