aboutsummaryrefslogtreecommitdiff
path: root/contrib/bmake/unit-tests/check-expect.lua
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/bmake/unit-tests/check-expect.lua')
-rw-r--r--contrib/bmake/unit-tests/check-expect.lua219
1 files changed, 109 insertions, 110 deletions
diff --git a/contrib/bmake/unit-tests/check-expect.lua b/contrib/bmake/unit-tests/check-expect.lua
index 218056fbc021..2f3adf49baa6 100644
--- a/contrib/bmake/unit-tests/check-expect.lua
+++ b/contrib/bmake/unit-tests/check-expect.lua
@@ -1,5 +1,5 @@
#! /usr/bin/lua
--- $NetBSD: check-expect.lua,v 1.13 2025/04/13 09:29:32 rillig Exp $
+-- $NetBSD: check-expect.lua,v 1.17 2025/07/01 05:03:18 rillig Exp $
--[[
@@ -9,29 +9,32 @@ Check that the various 'expect' comments in the .mk files produce the
expected text in the corresponding .exp file.
# expect: <line>
- All of these lines must occur in the .exp file, in the same order as
- in the .mk file.
-
-# expect-reset
- Search the following 'expect:' comments from the top of the .exp
- file again.
+ Each <line> must occur in the .exp file.
+ The order in the .mk file must be the same as in the .exp file.
# expect[+-]offset: <message>
- Each message must occur in the .exp file and refer back to the
+ Each <message> must occur in the .exp file and refer back to the
source line in the .mk file.
+ Each such line in the .exp file must have a corresponding expect line
+ in the .mk file.
+ The order in the .mk file must be the same as in the .exp file.
+
+# expect-reset
+ Search the following "expect:" and "expect[+-]offset:" comments
+ from the top of the .exp file again.
# expect-not: <substring>
- The substring must not occur as part of any line of the .exp file.
+ The <substring> must not occur as part of any line in the .exp file.
# expect-not-matches: <pattern>
- The pattern (see https://lua.org/manual/5.4/manual.html#6.4.1)
- must not occur as part of any line of the .exp file.
+ The <pattern> (see https://lua.org/manual/5.4/manual.html#6.4.1)
+ must not occur as part of any line in the .exp file.
]]
local had_errors = false
---@param fmt string
-function print_error(fmt, ...)
+local function print_error(fmt, ...)
print(fmt:format(...))
had_errors = true
end
@@ -42,7 +45,9 @@ local function load_lines(fname)
local lines = {}
local f = io.open(fname, "r")
- if f == nil then return nil end
+ if f == nil then
+ return nil
+ end
for line in f:lines() do
table.insert(lines, line)
@@ -53,135 +58,129 @@ local function load_lines(fname)
end
----@param exp_lines string[]
-local function collect_lineno_diagnostics(exp_lines)
- ---@type table<string, string[]>
- local by_location = {}
+--- @shape ExpLine
+--- @field filename string | nil
+--- @field lineno number | nil
+--- @field text string
- for _, line in ipairs(exp_lines) do
- ---@type string | nil, string, string
- local l_fname, l_lineno, l_msg =
- line:match('^make: ([^:]+):(%d+): (.*)')
- if l_fname ~= nil then
- local location = ("%s:%d"):format(l_fname, l_lineno)
- if by_location[location] == nil then
- by_location[location] = {}
- end
- table.insert(by_location[location], l_msg)
+
+--- @param lines string[]
+--- @return ExpLine[]
+local function parse_exp(lines)
+ local exp_lines = {}
+ for _, line in ipairs(lines) do
+ local l_filename, l_lineno, l_text =
+ line:match('^make: ([^:]+%.mk):(%d+):%s+(.*)')
+ if not l_filename then
+ l_text = line
end
+ l_text = l_text:gsub("^%s+", ""):gsub("%s+$", "")
+ table.insert(exp_lines, {
+ filename = l_filename,
+ lineno = tonumber(l_lineno),
+ text = l_text,
+ })
end
-
- return by_location
+ return exp_lines
end
-
-local function missing(by_location)
- ---@type {filename: string, lineno: number, location: string, message: string}[]
- local missing_expectations = {}
-
- for location, messages in pairs(by_location) do
- for _, message in ipairs(messages) do
- if message ~= "" and location:find(".mk:") then
- local filename, lineno = location:match("^(%S+):(%d+)$")
- table.insert(missing_expectations, {
- filename = filename,
- lineno = tonumber(lineno),
- location = location,
- message = message
- })
- end
+---@param exp_lines ExpLine[]
+local function detect_missing_expect_lines(exp_fname, exp_lines, s, e)
+ for i = s, e do
+ local exp_line = exp_lines[i]
+ if exp_line.filename then
+ print_error("error: %s:%d requires in %s:%d: # expect+1: %s",
+ exp_fname, i, exp_line.filename, exp_line.lineno, exp_line.text)
end
end
- table.sort(missing_expectations, function(a, b)
- if a.filename ~= b.filename then
- return a.filename < b.filename
- end
- return a.lineno < b.lineno
- end)
- return missing_expectations
end
-
local function check_mk(mk_fname)
local exp_fname = mk_fname:gsub("%.mk$", ".exp")
local mk_lines = load_lines(mk_fname)
- local exp_lines = load_lines(exp_fname)
- if exp_lines == nil then return end
- local by_location = collect_lineno_diagnostics(exp_lines)
- local prev_expect_line = 0
+ local exp_raw_lines = load_lines(exp_fname)
+ if exp_raw_lines == nil then
+ return
+ end
+ local exp_lines = parse_exp(exp_raw_lines)
+
+ local exp_it = 1
for mk_lineno, mk_line in ipairs(mk_lines) do
- for text in mk_line:gmatch("#%s*expect%-not:%s*(.*)") do
- local i = 1
- while i <= #exp_lines and not exp_lines[i]:find(text, 1, true) do
- i = i + 1
- end
- if i <= #exp_lines then
- print_error("error: %s:%d: %s must not contain '%s'",
- mk_fname, mk_lineno, exp_fname, text)
+ local function match(pattern, action)
+ local _, n = mk_line:gsub(pattern, action)
+ if n > 0 then
+ match = function() end
end
end
- for text in mk_line:gmatch("#%s*expect%-not%-matches:%s*(.*)") do
- local i = 1
- while i <= #exp_lines and not exp_lines[i]:find(text) do
- i = i + 1
+ match("^#%s+expect%-not:%s*(.*)", function(text)
+ for exp_lineno, exp_line in ipairs(exp_lines) do
+ if exp_line.text:find(text, 1, true) then
+ print_error("error: %s:%d: %s:%d must not contain '%s'",
+ mk_fname, mk_lineno, exp_fname, exp_lineno, text)
+ end
end
- if i <= #exp_lines then
- print_error("error: %s:%d: %s must not match '%s'",
- mk_fname, mk_lineno, exp_fname, text)
+ end)
+
+ match("^#%s+expect%-not%-matches:%s*(.*)", function(pattern)
+ for exp_lineno, exp_line in ipairs(exp_lines) do
+ if exp_line.text:find(pattern) then
+ print_error("error: %s:%d: %s:%d must not match '%s'",
+ mk_fname, mk_lineno, exp_fname, exp_lineno, pattern)
+ end
end
- end
+ end)
- for text in mk_line:gmatch("#%s*expect:%s*(.*)") do
- local i = prev_expect_line
- -- As of 2022-04-15, some lines in the .exp files contain trailing
- -- whitespace. If possible, this should be avoided by rewriting the
- -- debug logging. When done, the trailing gsub can be removed.
- -- See deptgt-phony.exp lines 14 and 15.
- while i < #exp_lines and text ~= exp_lines[i + 1]:gsub("^%s*", ""):gsub("%s*$", "") do
+ match("^#%s+expect:%s*(.*)", function(text)
+ local i = exp_it
+ while i <= #exp_lines and text ~= exp_lines[i].text do
i = i + 1
end
- if i < #exp_lines then
- prev_expect_line = i + 1
+ if i <= #exp_lines then
+ detect_missing_expect_lines(exp_fname, exp_lines, exp_it, i - 1)
+ exp_lines[i].text = ""
+ exp_it = i + 1
else
print_error("error: %s:%d: '%s:%d+' must contain '%s'",
- mk_fname, mk_lineno, exp_fname, prev_expect_line + 1, text)
+ mk_fname, mk_lineno, exp_fname, exp_it, text)
end
- end
- if mk_line:match("^#%s*expect%-reset$") then
- prev_expect_line = 0
- end
+ end)
- ---@param text string
- for offset, text in mk_line:gmatch("#%s*expect([+%-]%d+):%s*(.*)") do
- local location = ("%s:%d"):format(mk_fname, mk_lineno + tonumber(offset))
-
- local found = false
- if by_location[location] ~= nil then
- for i, message in ipairs(by_location[location]) do
- if message == text then
- by_location[location][i] = ""
- found = true
- break
- elseif message ~= "" then
- print_error("error: %s:%d: out-of-order '%s'",
- mk_fname, mk_lineno, message)
- end
- end
+ match("^#%s+expect%-reset$", function()
+ exp_it = 1
+ end)
+
+ match("^#%s+expect([+%-]%d+):%s*(.*)", function(offset, text)
+ local msg_lineno = mk_lineno + tonumber(offset)
+
+ local i = exp_it
+ while i <= #exp_lines and text ~= exp_lines[i].text do
+ i = i + 1
end
- if not found then
- print_error("error: %s:%d: %s must contain '%s'",
- mk_fname, mk_lineno, exp_fname, text)
+ if i <= #exp_lines and exp_lines[i].lineno == msg_lineno then
+ detect_missing_expect_lines(exp_fname, exp_lines, exp_it, i - 1)
+ exp_lines[i].text = ""
+ exp_it = i + 1
+ elseif i <= #exp_lines then
+ print_error("error: %s:%d: expect%+d must be expect%+d",
+ mk_fname, mk_lineno, tonumber(offset),
+ exp_lines[i].lineno - mk_lineno)
+ else
+ print_error("error: %s:%d: %s:%d+ must contain '%s'",
+ mk_fname, mk_lineno, exp_fname, exp_it, text)
end
- end
- end
+ end)
+
+ match("^#%s+expect[+%-:]", function()
+ print_error("error: %s:%d: invalid \"expect\" line: %s",
+ mk_fname, mk_lineno, mk_line)
+ end)
- for _, m in ipairs(missing(by_location)) do
- print_error("missing: %s: # expect+1: %s", m.location, m.message)
end
+ detect_missing_expect_lines(exp_fname, exp_lines, exp_it, #exp_lines)
end
for _, fname in ipairs(arg) do