xref: /netbsd-src/usr.bin/make/unit-tests/check-expect.lua (revision 7d62b00eb9ad855ffcd7da46b41e23feb5476fac)
1#!  /usr/bin/lua
2-- $NetBSD: check-expect.lua,v 1.3 2022/04/15 09:33:20 rillig Exp $
3
4--[[
5
6usage: lua ./check-expect.lua *.mk
7
8Check that each text from an '# expect: ...' comment in the .mk source files
9occurs in the corresponding .exp file, in the same order as in the .mk file.
10
11Check that each text from an '# expect[+-]offset: ...' comment in the .mk
12source files occurs in the corresponding .exp file and refers back to the
13correct line in the .mk file.
14
15]]
16
17
18local had_errors = false
19---@param fmt string
20function print_error(fmt, ...)
21  print(fmt:format(...))
22  had_errors = true
23end
24
25
26---@return nil | string[]
27local function load_lines(fname)
28  local lines = {}
29
30  local f = io.open(fname, "r")
31  if f == nil then return nil end
32
33  for line in f:lines() do
34    table.insert(lines, line)
35  end
36  f:close()
37
38  return lines
39end
40
41
42---@param exp_lines string[]
43local function collect_lineno_diagnostics(exp_lines)
44  ---@type table<string, string[]>
45  local by_location = {}
46
47  for _, line in ipairs(exp_lines) do
48    ---@type string | nil, string, string
49    local l_fname, l_lineno, l_msg =
50      line:match("^make: \"([^\"]+)\" line (%d+): (.*)")
51    if l_fname ~= nil then
52      local location = ("%s:%d"):format(l_fname, l_lineno)
53      if by_location[location] == nil then
54        by_location[location] = {}
55      end
56      table.insert(by_location[location], l_msg)
57    end
58  end
59
60  return by_location
61end
62
63
64local function check_mk(mk_fname)
65  local exp_fname = mk_fname:gsub("%.mk$", ".exp")
66  local mk_lines = load_lines(mk_fname)
67  local exp_lines = load_lines(exp_fname)
68  if exp_lines == nil then return end
69  local by_location = collect_lineno_diagnostics(exp_lines)
70  local prev_expect_line = 0
71
72  for mk_lineno, mk_line in ipairs(mk_lines) do
73    for text in mk_line:gmatch("#%s*expect:%s*(.*)") do
74      local i = prev_expect_line
75      -- As of 2022-04-15, some lines in the .exp files contain trailing
76      -- whitespace.  If possible, this should be avoided by rewriting the
77      -- debug logging.  When done, the gsub can be removed.
78      -- See deptgt-phony.exp lines 14 and 15.
79      while i < #exp_lines and text ~= exp_lines[i + 1]:gsub("%s*$", "") do
80        i = i + 1
81      end
82      if i < #exp_lines then
83        prev_expect_line = i + 1
84      else
85        print_error("error: %s:%d: '%s:%d+' must contain '%s'",
86          mk_fname, mk_lineno, exp_fname, prev_expect_line + 1, text)
87      end
88    end
89    if mk_line:match("^#%s*expect%-reset$") then
90      prev_expect_line = 0
91    end
92
93    ---@param text string
94    for offset, text in mk_line:gmatch("#%s*expect([+%-]%d+):%s*(.*)") do
95      local location = ("%s:%d"):format(mk_fname, mk_lineno + tonumber(offset))
96
97      local found = false
98      if by_location[location] ~= nil then
99        for i, message in ipairs(by_location[location]) do
100          if message ~= "" and message:find(text, 1, true) then
101            by_location[location][i] = ""
102            found = true
103            break
104          end
105        end
106      end
107
108      if not found then
109        print_error("error: %s:%d: %s must contain '%s'",
110          mk_fname, mk_lineno, exp_fname, text)
111      end
112    end
113  end
114end
115
116for _, fname in ipairs(arg) do
117  check_mk(fname)
118end
119os.exit(not had_errors)
120