From 5af1b627cba8b730955f49967c596ac2f10e528f Mon Sep 17 00:00:00 2001 From: myk002 Date: Sat, 3 Apr 2021 13:37:11 -0700 Subject: [PATCH] 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 --- ci/test.lua | 2 +- library/lua/{test => internal}/expect.lua | 86 ++++++++---------- test/library/internal/expect_unit.lua | 105 ++++++++++++++++++++++ test/test.lua | 33 ------- 4 files changed, 141 insertions(+), 85 deletions(-) rename library/lua/{test => internal}/expect.lua (78%) create mode 100644 test/library/internal/expect_unit.lua diff --git a/ci/test.lua b/ci/test.lua index e0746f79a..596b2c716 100644 --- a/ci/test.lua +++ b/ci/test.lua @@ -1,7 +1,7 @@ -- DFHack developer test harness --@ module = true -local expect = require 'test.expect' +local expect = require 'internal.expect' local json = require 'json' local script = require 'gui.script' local utils = require 'utils' diff --git a/library/lua/test/expect.lua b/library/lua/internal/expect.lua similarity index 78% rename from library/lua/test/expect.lua rename to library/lua/internal/expect.lua index 84178a080..106d96b08 100644 --- a/library/lua/test/expect.lua +++ b/library/lua/internal/expect.lua @@ -5,7 +5,7 @@ -- these functions directly will not work as expected. -local expect = mkmodule('test.expect') +local expect = mkmodule('internal.expect') function expect.true_(value, comment) 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]) 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) - if not matcher then return false end - if type(matcher) == 'boolean' then - return true - elseif type(matcher) == 'string' then + if type(matcher) == 'string' then return tostring(obj):match(matcher) elseif type(matcher) == 'function' then return matcher(obj) @@ -128,7 +116,6 @@ end -- matcher can be: -- 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) --- the literal value true, which matches any thrown error function expect.error_match(matcher, func, comment) local ok, err = pcall(func) if ok then @@ -139,10 +126,14 @@ function expect.error_match(matcher, func, comment) if type(matcher) == 'string' then matcher_str = (': "%s"'):format(matcher) end - return false, + return false, comment, ('error "%s" did not satisfy matcher%s'):format(err, matcher_str) end +function expect.error(func, comment) + return expect.error_match('.*', func, comment) +end + -- matches error messages output from dfhack.printerr() when the specified -- callback is run. the check passes if all printerr messages are matched by -- specified matchers and no matchers remain unmatched. @@ -151,24 +142,18 @@ end -- a string, interpreted as a lua pattern that matches a message -- a function that takes the string message and returns a boolean (true means -- 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 -- in more detail below) -- -- if matcher is a table, it can contain: --- a list of strings, literal true values, and/or functions which will be --- matched in order --- a map of strings, literal true values, and/or functions to positive --- integers, which will be matched (in any order) the number of times --- specified +-- a list of strings and/or functions which will be matched in order +-- a map of strings and/or functions to positive 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 -- 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 --- are numeric and > 0. if none of those match, it will check for a key equal to --- true with a value > 0. +-- are numeric and > 0. -- -- if a mapped matcher is matched, it will have its value decremented by 1. function expect.printerr_match(matcher, func, comment) @@ -178,25 +163,17 @@ function expect.printerr_match(matcher, func, comment) dfhack.with_finalize( function() dfhack.printerr = saved_printerr end, 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 - local true_count = matcher[true] or 0 - matcher[true] = nil - for _,msg in ipairs(messages) do - local m = matcher[1] - if matches(msg, m) then + while messages[1] do + local msg = messages[1] + if matches(msg, matcher[1]) then table.remove(matcher, 1) goto continue end for k,v in pairs(matcher) do if type(v) == 'number' and v > 0 and matches(msg, k) then local remaining = v - 1 - if v == 0 then + if remaining == 0 then matcher[k] = nil else matcher[k] = remaining @@ -204,27 +181,34 @@ function expect.printerr_match(matcher, func, comment) goto continue end end - if true_count > 0 then - true_count = true_count - 1 - goto continue - end - return false, comment, ('unmatched printerr message: "%s"'):format(msg) + break -- failed to match message ::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 - local extra_matchers = {} 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 end for k,v in pairs(matcher) do - table.insert(extra_matchers, ('"%s"=%d'):format(k, v)) - end - if true_count > 0 then - table.insert(extra_matchers, ('true=%d'):format(true_count)) + table.insert(extra_matchers, + ('"%s"=%s'):format(tostring(k), tostring(v))) end if #extra_matchers > 0 then - return false, comment, ('unmatched or invalid matchers: %s'):format( - table.concat(extra_matchers, ', ')) + table.insert(errmsgs, + ('unmatched or invalid matchers: %s'):format( + table.concat(extra_matchers, ', '))) + end + if #errmsgs > 0 then + return false, comment, table.concat(errmsgs, '; ') end return true end diff --git a/test/library/internal/expect_unit.lua b/test/library/internal/expect_unit.lua new file mode 100644 index 000000000..681dc28bb --- /dev/null +++ b/test/library/internal/expect_unit.lua @@ -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 diff --git a/test/test.lua b/test/test.lua index c2b083d38..11f038d66 100644 --- a/test/test.lua +++ b/test/test.lua @@ -1,36 +1,3 @@ -local expect_raw = require('test.expect') - function test.internal_in_test() expect.true_(dfhack.internal.IN_TEST) 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