Home | History | Annotate | Line # | Download | only in unit-tests
check-expect.lua revision 1.5
      1  1.1  rillig #!  /usr/bin/lua
      2  1.5  rillig -- $NetBSD: check-expect.lua,v 1.5 2023/06/01 07:27:30 rillig Exp $
      3  1.1  rillig 
      4  1.1  rillig --[[
      5  1.1  rillig 
      6  1.1  rillig usage: lua ./check-expect.lua *.mk
      7  1.1  rillig 
      8  1.4  rillig Check that the various 'expect' comments in the .mk files produce the
      9  1.4  rillig expected text in the corresponding .exp file.
     10  1.1  rillig 
     11  1.4  rillig # expect: <line>
     12  1.4  rillig         All of these lines must occur in the .exp file, in the same order as
     13  1.4  rillig         in the .mk file.
     14  1.4  rillig 
     15  1.4  rillig # expect-reset
     16  1.4  rillig         Search the following 'expect:' comments from the top of the .exp
     17  1.4  rillig         file again.
     18  1.4  rillig 
     19  1.4  rillig # expect[+-]offset: <message>
     20  1.4  rillig         Each message must occur in the .exp file and refer back to the
     21  1.4  rillig         source line in the .mk file.
     22  1.4  rillig 
     23  1.4  rillig # expect-all
     24  1.4  rillig         Each message from the .exp file that can be matched by an
     25  1.4  rillig         'expect[+-]offset' comment must actually be matched.
     26  1.1  rillig ]]
     27  1.1  rillig 
     28  1.1  rillig 
     29  1.1  rillig local had_errors = false
     30  1.1  rillig ---@param fmt string
     31  1.1  rillig function print_error(fmt, ...)
     32  1.1  rillig   print(fmt:format(...))
     33  1.1  rillig   had_errors = true
     34  1.1  rillig end
     35  1.1  rillig 
     36  1.1  rillig 
     37  1.1  rillig ---@return nil | string[]
     38  1.1  rillig local function load_lines(fname)
     39  1.1  rillig   local lines = {}
     40  1.1  rillig 
     41  1.1  rillig   local f = io.open(fname, "r")
     42  1.1  rillig   if f == nil then return nil end
     43  1.1  rillig 
     44  1.1  rillig   for line in f:lines() do
     45  1.1  rillig     table.insert(lines, line)
     46  1.1  rillig   end
     47  1.1  rillig   f:close()
     48  1.1  rillig 
     49  1.1  rillig   return lines
     50  1.1  rillig end
     51  1.1  rillig 
     52  1.1  rillig 
     53  1.1  rillig ---@param exp_lines string[]
     54  1.1  rillig local function collect_lineno_diagnostics(exp_lines)
     55  1.1  rillig   ---@type table<string, string[]>
     56  1.1  rillig   local by_location = {}
     57  1.1  rillig 
     58  1.1  rillig   for _, line in ipairs(exp_lines) do
     59  1.1  rillig     ---@type string | nil, string, string
     60  1.1  rillig     local l_fname, l_lineno, l_msg =
     61  1.5  rillig       line:match('^make: "([^"]+)" line (%d+): (.*)')
     62  1.1  rillig     if l_fname ~= nil then
     63  1.1  rillig       local location = ("%s:%d"):format(l_fname, l_lineno)
     64  1.1  rillig       if by_location[location] == nil then
     65  1.1  rillig         by_location[location] = {}
     66  1.1  rillig       end
     67  1.1  rillig       table.insert(by_location[location], l_msg)
     68  1.1  rillig     end
     69  1.1  rillig   end
     70  1.1  rillig 
     71  1.1  rillig   return by_location
     72  1.1  rillig end
     73  1.1  rillig 
     74  1.1  rillig 
     75  1.1  rillig local function check_mk(mk_fname)
     76  1.1  rillig   local exp_fname = mk_fname:gsub("%.mk$", ".exp")
     77  1.1  rillig   local mk_lines = load_lines(mk_fname)
     78  1.1  rillig   local exp_lines = load_lines(exp_fname)
     79  1.1  rillig   if exp_lines == nil then return end
     80  1.1  rillig   local by_location = collect_lineno_diagnostics(exp_lines)
     81  1.1  rillig   local prev_expect_line = 0
     82  1.4  rillig   local match_all = false
     83  1.1  rillig 
     84  1.1  rillig   for mk_lineno, mk_line in ipairs(mk_lines) do
     85  1.1  rillig     for text in mk_line:gmatch("#%s*expect:%s*(.*)") do
     86  1.1  rillig       local i = prev_expect_line
     87  1.3  rillig       -- As of 2022-04-15, some lines in the .exp files contain trailing
     88  1.3  rillig       -- whitespace.  If possible, this should be avoided by rewriting the
     89  1.3  rillig       -- debug logging.  When done, the gsub can be removed.
     90  1.3  rillig       -- See deptgt-phony.exp lines 14 and 15.
     91  1.3  rillig       while i < #exp_lines and text ~= exp_lines[i + 1]:gsub("%s*$", "") do
     92  1.1  rillig         i = i + 1
     93  1.1  rillig       end
     94  1.1  rillig       if i < #exp_lines then
     95  1.1  rillig         prev_expect_line = i + 1
     96  1.1  rillig       else
     97  1.1  rillig         print_error("error: %s:%d: '%s:%d+' must contain '%s'",
     98  1.1  rillig           mk_fname, mk_lineno, exp_fname, prev_expect_line + 1, text)
     99  1.1  rillig       end
    100  1.1  rillig     end
    101  1.2  rillig     if mk_line:match("^#%s*expect%-reset$") then
    102  1.2  rillig       prev_expect_line = 0
    103  1.2  rillig     end
    104  1.1  rillig 
    105  1.1  rillig     ---@param text string
    106  1.1  rillig     for offset, text in mk_line:gmatch("#%s*expect([+%-]%d+):%s*(.*)") do
    107  1.1  rillig       local location = ("%s:%d"):format(mk_fname, mk_lineno + tonumber(offset))
    108  1.1  rillig 
    109  1.1  rillig       local found = false
    110  1.1  rillig       if by_location[location] ~= nil then
    111  1.1  rillig         for i, message in ipairs(by_location[location]) do
    112  1.1  rillig           if message ~= "" and message:find(text, 1, true) then
    113  1.1  rillig             by_location[location][i] = ""
    114  1.1  rillig             found = true
    115  1.1  rillig             break
    116  1.1  rillig           end
    117  1.1  rillig         end
    118  1.1  rillig       end
    119  1.1  rillig 
    120  1.1  rillig       if not found then
    121  1.1  rillig         print_error("error: %s:%d: %s must contain '%s'",
    122  1.1  rillig           mk_fname, mk_lineno, exp_fname, text)
    123  1.1  rillig       end
    124  1.1  rillig     end
    125  1.4  rillig 
    126  1.4  rillig     if mk_line:match("^#%s*expect%-all$") then
    127  1.4  rillig       match_all = true
    128  1.4  rillig     end
    129  1.4  rillig   end
    130  1.4  rillig 
    131  1.4  rillig   if match_all then
    132  1.4  rillig     -- XXX: The messages are not sorted in any meaningful way.
    133  1.4  rillig     for location, messages in pairs(by_location) do
    134  1.4  rillig       for _, message in ipairs(messages) do
    135  1.4  rillig         if message ~= "" then
    136  1.4  rillig           print_error("error: %s: missing 'expect' comment for '%s'",
    137  1.4  rillig             location, message)
    138  1.4  rillig         end
    139  1.4  rillig       end
    140  1.4  rillig     end
    141  1.1  rillig   end
    142  1.1  rillig end
    143  1.1  rillig 
    144  1.1  rillig for _, fname in ipairs(arg) do
    145  1.1  rillig   check_mk(fname)
    146  1.1  rillig end
    147  1.1  rillig os.exit(not had_errors)
    148