add tests, move expect.lua

I moved expect.lua from library/lua/test/ to library/lua/internal since
luacov is configured to ignore any file with /test/ in its path
develop
myk002 2021-04-03 13:37:11 -07:00
parent 757dbeb238
commit 5af1b627cb
No known key found for this signature in database
GPG Key ID: 8A39CA0FA0C16E78
4 changed files with 141 additions and 85 deletions

@ -1,7 +1,7 @@
-- DFHack developer test harness -- DFHack developer test harness
--@ module = true --@ module = true
local expect = require 'test.expect' local expect = require 'internal.expect'
local json = require 'json' local json = require 'json'
local script = require 'gui.script' local script = require 'gui.script'
local utils = require 'utils' local utils = require 'utils'

@ -5,7 +5,7 @@
-- these functions directly will not work as expected. -- these functions directly will not work as expected.
local expect = mkmodule('test.expect') local expect = mkmodule('internal.expect')
function expect.true_(value, comment) function expect.true_(value, comment)
return not not value, comment, 'expected true, got ' .. tostring(value) return not not value, comment, 'expected true, got ' .. tostring(value)
@ -101,20 +101,8 @@ function expect.table_eq(a, b, comment)
('key %s: "%s" ~= "%s"'):format(keystr, diff[1], diff[2]) ('key %s: "%s" ~= "%s"'):format(keystr, diff[1], diff[2])
end end
function expect.error(func, comment)
local ok = pcall(func)
if ok then
return false, comment, 'no error raised by function call'
else
return true
end
end
local function matches(obj, matcher) local function matches(obj, matcher)
if not matcher then return false end if type(matcher) == 'string' then
if type(matcher) == 'boolean' then
return true
elseif type(matcher) == 'string' then
return tostring(obj):match(matcher) return tostring(obj):match(matcher)
elseif type(matcher) == 'function' then elseif type(matcher) == 'function' then
return matcher(obj) return matcher(obj)
@ -128,7 +116,6 @@ end
-- matcher can be: -- matcher can be:
-- a string, interpreted as a lua pattern that matches the error message -- a string, interpreted as a lua pattern that matches the error message
-- a function that takes an err object and returns a boolean (true means match) -- a function that takes an err object and returns a boolean (true means match)
-- the literal value true, which matches any thrown error
function expect.error_match(matcher, func, comment) function expect.error_match(matcher, func, comment)
local ok, err = pcall(func) local ok, err = pcall(func)
if ok then if ok then
@ -139,10 +126,14 @@ function expect.error_match(matcher, func, comment)
if type(matcher) == 'string' then if type(matcher) == 'string' then
matcher_str = (': "%s"'):format(matcher) matcher_str = (': "%s"'):format(matcher)
end end
return false, return false, comment,
('error "%s" did not satisfy matcher%s'):format(err, matcher_str) ('error "%s" did not satisfy matcher%s'):format(err, matcher_str)
end end
function expect.error(func, comment)
return expect.error_match('.*', func, comment)
end
-- matches error messages output from dfhack.printerr() when the specified -- matches error messages output from dfhack.printerr() when the specified
-- callback is run. the check passes if all printerr messages are matched by -- callback is run. the check passes if all printerr messages are matched by
-- specified matchers and no matchers remain unmatched. -- specified matchers and no matchers remain unmatched.
@ -151,24 +142,18 @@ end
-- a string, interpreted as a lua pattern that matches a message -- a string, interpreted as a lua pattern that matches a message
-- a function that takes the string message and returns a boolean (true means -- a function that takes the string message and returns a boolean (true means
-- match) -- match)
-- the literal value true, which matches any message
-- the literal value false, nil, or an empty table, which match the absence of
-- printerr messages
-- a populated table that can be used to match multiple messages (explained -- a populated table that can be used to match multiple messages (explained
-- in more detail below) -- in more detail below)
-- --
-- if matcher is a table, it can contain: -- if matcher is a table, it can contain:
-- a list of strings, literal true values, and/or functions which will be -- a list of strings and/or functions which will be matched in order
-- matched in order -- a map of strings and/or functions to positive integers, which will be
-- a map of strings, literal true values, and/or functions to positive -- matched (in any order) the number of times specified
-- integers, which will be matched (in any order) the number of times
-- specified
-- --
-- when this function attempts to match a message, it will first try the next -- when this function attempts to match a message, it will first try the next
-- matcher in the list (that is, the next numeric key). if that matcher doesn't -- matcher in the list (that is, the next numeric key). if that matcher doesn't
-- exist or doesn't match, it will try all string and function keys whose values -- exist or doesn't match, it will try all string and function keys whose values
-- are numeric and > 0. if none of those match, it will check for a key equal to -- are numeric and > 0.
-- true with a value > 0.
-- --
-- if a mapped matcher is matched, it will have its value decremented by 1. -- if a mapped matcher is matched, it will have its value decremented by 1.
function expect.printerr_match(matcher, func, comment) function expect.printerr_match(matcher, func, comment)
@ -178,25 +163,17 @@ function expect.printerr_match(matcher, func, comment)
dfhack.with_finalize( dfhack.with_finalize(
function() dfhack.printerr = saved_printerr end, function() dfhack.printerr = saved_printerr end,
func) func)
if not matcher then
local num_messages = #messages
if num_messages == 0 then return true end
return false, comment, ('expected 0 calls to dfhack.printerr but got' ..
' %d'):format(num_messages)
end
if type(matcher) ~= 'table' then matcher = {matcher} end if type(matcher) ~= 'table' then matcher = {matcher} end
local true_count = matcher[true] or 0 while messages[1] do
matcher[true] = nil local msg = messages[1]
for _,msg in ipairs(messages) do if matches(msg, matcher[1]) then
local m = matcher[1]
if matches(msg, m) then
table.remove(matcher, 1) table.remove(matcher, 1)
goto continue goto continue
end end
for k,v in pairs(matcher) do for k,v in pairs(matcher) do
if type(v) == 'number' and v > 0 and matches(msg, k) then if type(v) == 'number' and v > 0 and matches(msg, k) then
local remaining = v - 1 local remaining = v - 1
if v == 0 then if remaining == 0 then
matcher[k] = nil matcher[k] = nil
else else
matcher[k] = remaining matcher[k] = remaining
@ -204,27 +181,34 @@ function expect.printerr_match(matcher, func, comment)
goto continue goto continue
end end
end end
if true_count > 0 then break -- failed to match message
true_count = true_count - 1
goto continue
end
return false, comment, ('unmatched printerr message: "%s"'):format(msg)
::continue:: ::continue::
table.remove(messages, 1)
end
local errmsgs, unmatched_messages, extra_matchers = {}, {}, {}
for _,msg in ipairs(messages) do
table.insert(unmatched_messages, ('"%s"'):format(msg))
end
if #unmatched_messages > 0 then
table.insert(errmsgs,
('unmatched dfhack.printerr() messages: %s'):format(
table.concat(unmatched_messages, ', ')))
end end
local extra_matchers = {}
for k,v in ipairs(matcher) do for k,v in ipairs(matcher) do
table.insert(extra_matchers, ('"%s"'):format(v)) table.insert(extra_matchers, ('"%s"'):format(tostring(v)))
matcher[k] = nil matcher[k] = nil
end end
for k,v in pairs(matcher) do for k,v in pairs(matcher) do
table.insert(extra_matchers, ('"%s"=%d'):format(k, v)) table.insert(extra_matchers,
end ('"%s"=%s'):format(tostring(k), tostring(v)))
if true_count > 0 then
table.insert(extra_matchers, ('true=%d'):format(true_count))
end end
if #extra_matchers > 0 then if #extra_matchers > 0 then
return false, comment, ('unmatched or invalid matchers: %s'):format( table.insert(errmsgs,
table.concat(extra_matchers, ', ')) ('unmatched or invalid matchers: %s'):format(
table.concat(extra_matchers, ', ')))
end
if #errmsgs > 0 then
return false, comment, table.concat(errmsgs, '; ')
end end
return true return true
end end

@ -0,0 +1,105 @@
local expect_raw = require('internal.expect')
function test.table_eq()
expect.true_(expect_raw.table_eq({}, {}))
expect.true_(expect_raw.table_eq({'a'}, {'a'}))
expect.true_(expect_raw.table_eq({{'a', k='val'}, 'b'},
{{'a', k='val'}, 'b'}))
expect.false_(expect_raw.table_eq(nil, nil)) -- operands must be non-nil
expect.false_(expect_raw.table_eq({}, nil))
expect.false_(expect_raw.table_eq(nil, {}))
expect.false_(expect_raw.table_eq({}, {''}))
expect.false_(expect_raw.table_eq({''}, {}))
expect.false_(expect_raw.table_eq({'a', {}}, {'a'}))
expect.false_(expect_raw.table_eq({{'a', k='val1'}, 'b'},
{{'a', k='val2'}, 'b'}))
local tab = {a='a', b='b'}
expect.true_(expect_raw.table_eq(tab, tab))
expect.true_(expect_raw.table_eq({tab}, {tab}))
local tab1, tab2 = {'a'}, {'a'}
tab1.self, tab2.self = tab1, tab2
expect.true_(expect_raw.table_eq(tab1, tab2))
tab1.other, tab2.other = tab2, tab1
expect.true_(expect_raw.table_eq(tab1, tab2))
local tabA, tabB, tabC, tabD = {k='a'}, {k='a'}, {k='a'}, {k='a'}
tabA.next, tabB.next, tabC.next, tabD.next = tabB, tabC, tabD, tabA
expect.true_(expect_raw.table_eq(tabA, tabB))
end
function test.error_match()
expect.true_(expect_raw.error_match('err', function() error('err0r') end))
expect.false_(expect_raw.error_match('err', function() end))
expect.false_(expect_raw.error_match('00', function() error('err0r') end))
expect.true_(expect_raw.error_match(
function() return true end, function() error('err0r') end))
expect.false_(expect_raw.error_match(
function() return false end, function() error('err0r') end))
end
function test.error()
expect.true_(expect_raw.error(function() error('err0r') end))
expect.true_(expect_raw.error(function() qerror('err0r') end))
expect.false_(expect_raw.error(function() end))
end
function test.printerr_match_printerr_restored()
local prev_printerr = dfhack.printerr
expect_raw.printerr_match('.*', function() dfhack.printerr('a') end)
expect.eq(prev_printerr, dfhack.printerr)
end
function test.printerr_match()
local noprint = function() end
local oneprint = function()
dfhack.printerr('a')
end
local twoprint = function()
dfhack.printerr('a')
dfhack.printerr('b')
end
local threeprint = function()
dfhack.printerr('a')
dfhack.printerr('b')
dfhack.printerr('c')
end
local multiprint = function()
dfhack.printerr('a')
dfhack.printerr('b')
dfhack.printerr('a')
end
expect.true_(expect_raw.printerr_match(nil, noprint))
expect.true_(expect_raw.printerr_match({}, noprint))
expect.false_(expect_raw.printerr_match(nil, oneprint))
expect.false_(expect_raw.printerr_match({}, oneprint))
expect.true_(expect_raw.printerr_match('a', oneprint))
expect.true_(expect_raw.printerr_match({[1]='a'}, oneprint))
expect.true_(expect_raw.printerr_match({a=1}, oneprint))
expect.true_(expect_raw.printerr_match('.', oneprint))
expect.true_(expect_raw.printerr_match({['.']=1}, oneprint))
expect.true_(expect_raw.printerr_match('.*', oneprint))
expect.true_(expect_raw.printerr_match({['.*']=1}, oneprint))
expect.false_(expect_raw.printerr_match('b', oneprint))
expect.false_(expect_raw.printerr_match({b=1}, oneprint))
expect.false_(expect_raw.printerr_match({a=1,b=1}, oneprint))
expect.true_(expect_raw.printerr_match({'a','b'}, twoprint))
expect.true_(expect_raw.printerr_match({a=1,b=1}, twoprint))
expect.false_(expect_raw.printerr_match({'b','a'}, twoprint))
expect.false_(expect_raw.printerr_match({a=1}, twoprint))
expect.false_(expect_raw.printerr_match({b=1}, twoprint))
expect.false_(expect_raw.printerr_match({a=1,b=2}, twoprint))
expect.false_(expect_raw.printerr_match({a=1,b=1,c=1}, twoprint))
expect.true_(expect_raw.printerr_match({a=1,b=1,c=1}, threeprint))
expect.true_(expect_raw.printerr_match({a=2,b=1}, multiprint))
expect.false_(expect_raw.printerr_match({a=1,b=1}, multiprint))
end

@ -1,36 +1,3 @@
local expect_raw = require('test.expect')
function test.internal_in_test() function test.internal_in_test()
expect.true_(dfhack.internal.IN_TEST) expect.true_(dfhack.internal.IN_TEST)
end end
function test.table_eq()
expect.true_(expect_raw.table_eq({}, {}))
expect.true_(expect_raw.table_eq({'a'}, {'a'}))
expect.true_(expect_raw.table_eq({{'a', k='val'}, 'b'},
{{'a', k='val'}, 'b'}))
expect.false_(expect_raw.table_eq(nil, nil)) -- operands must be non-nil
expect.false_(expect_raw.table_eq({}, nil))
expect.false_(expect_raw.table_eq(nil, {}))
expect.false_(expect_raw.table_eq({}, {''}))
expect.false_(expect_raw.table_eq({''}, {}))
expect.false_(expect_raw.table_eq({'a', {}}, {'a'}))
expect.false_(expect_raw.table_eq({{'a', k='val1'}, 'b'},
{{'a', k='val2'}, 'b'}))
local tab = {a='a', b='b'}
expect.true_(expect_raw.table_eq(tab, tab))
expect.true_(expect_raw.table_eq({tab}, {tab}))
local tab1, tab2 = {'a'}, {'a'}
tab1.self, tab2.self = tab1, tab2
expect.true_(expect_raw.table_eq(tab1, tab2))
tab1.other, tab2.other = tab2, tab1
expect.true_(expect_raw.table_eq(tab1, tab2))
local tabA, tabB, tabC, tabD = {k='a'}, {k='a'}, {k='a'}, {k='a'}
tabA.next, tabB.next, tabC.next, tabD.next = tabB, tabC, tabD, tabA
expect.true_(expect_raw.table_eq(tabA, tabB))
end