summaryrefslogtreecommitdiffstatshomepage
path: root/test/testutil.lua
diff options
context:
space:
mode:
authorJustin M. Keyes <justinkz@gmail.com>2026-04-25 10:33:39 -0400
committerGitHub <noreply@github.com>2026-04-25 10:33:39 -0400
commit0a9016689e63d5ff05e8f7d87ee7625dd6576cdc (patch)
treef023b7abee010e65f74a9fd90ae1be52c7423926 /test/testutil.lua
parent4ed2e66d2ec20c2bf0325283b0e785ca3abb15c0 (diff)
ci: ensure full sanitizer logs are shown in CI #39361HEADmaster
Problem: When ASAN detects an error in a child process, the process may be killed by test teardown before ASAN finishes writing its log file. This results in truncated logs with no stack trace. Also, the sanitizer log content was only written to stdout (which the test runner may not display) and not included in the test_assert error message. Solution: - Poll (up to 2s) for the ASAN "SUMMARY" line before reading, so the crashing process has time to finish writing. - Include full log content in the test_assert error message, so it appears in CI output regardless of stdout handling. - Warn when the log appears truncated (no SUMMARY line found).
Diffstat (limited to 'test/testutil.lua')
-rw-r--r--test/testutil.lua71
1 files changed, 51 insertions, 20 deletions
diff --git a/test/testutil.lua b/test/testutil.lua
index 6745613ac1..f89d6b9c2f 100644
--- a/test/testutil.lua
+++ b/test/testutil.lua
@@ -325,45 +325,73 @@ function M.glob(initial_path, re, exc_re)
return ret
end
+--- Reads sanitizer/valgrind log file lines, filtering out useless warnings.
+--- Waits briefly for the ASAN "SUMMARY" line so we don't read a truncated report
+--- (the crashing process may still be writing when we check).
+local function read_sanitizer_log(file)
+ local lines = {} --- @type string[]
+ local has_summary = false
+ -- Poll for up to 2 seconds for the log to be complete.
+ for _ = 1, 20 do
+ lines = {}
+ local warning_line = 0
+ local fd = assert(io.open(file))
+ for line in fd:lines() do
+ local cur_warning_line = check_logs_useless_lines[line]
+ if cur_warning_line == warning_line + 1 then
+ warning_line = cur_warning_line
+ else
+ lines[#lines + 1] = line
+ end
+ if line:find('SUMMARY') then
+ has_summary = true
+ end
+ end
+ fd:close()
+ if has_summary or #lines == 0 then
+ break
+ end
+ uv.sleep(100)
+ end
+ if not has_summary and #lines > 0 then
+ lines[#lines + 1] = '(WARNING: sanitizer log may be truncated, no SUMMARY line found)'
+ end
+ return lines
+end
+
function M.check_logs()
local log_dir = os.getenv('LOG_DIR')
- local runtime_errors = {}
+ local runtime_errors = {} --- @type string[]
+ local runtime_errors_detail = {} --- @type string[]
if log_dir and M.isdir(log_dir) then
for tail in vim.fs.dir(log_dir) do
if tail:sub(1, 30) == 'valgrind-' or tail:find('san%.') then
- local file = log_dir .. '/' .. tail
- local fd = assert(io.open(file))
- local start_msg = ('='):rep(20) .. ' File ' .. file .. ' ' .. ('='):rep(20)
- local lines = {} --- @type string[]
- local warning_line = 0
- for line in fd:lines() do
- local cur_warning_line = check_logs_useless_lines[line]
- if cur_warning_line == warning_line + 1 then
- warning_line = cur_warning_line
- else
- lines[#lines + 1] = line
- end
- end
- fd:close()
+ local file = ('%s/%s'):format(log_dir, tail)
+ local lines = read_sanitizer_log(file)
if #lines > 0 then
+ local start_msg = ('%s File %s %s'):format(('='):rep(20), file, ('='):rep(20))
+ local end_msg = select(1, start_msg:gsub('.', '='))
+ local lines_str = ('= %s'):format(table.concat(lines, '\n= '))
+ local detail = ('%s\n%s\n%s'):format(start_msg, lines_str, end_msg)
--- @type boolean?, file*?
local status, f
- local out = io.stdout
if os.getenv('SYMBOLIZER') then
status, f = pcall(M.repeated_read_cmd, os.getenv('SYMBOLIZER'), '-l', file)
end
+ local out = io.stdout
out:write(start_msg .. '\n')
if status then
assert(f)
for line in f:lines() do
- out:write('= ' .. line .. '\n')
+ out:write(('= %s\n'):format(line))
end
f:close()
else
- out:write('= ' .. table.concat(lines, '\n= ') .. '\n')
+ out:write(lines_str .. '\n')
end
- out:write(select(1, start_msg:gsub('.', '=')) .. '\n')
+ out:write(end_msg .. '\n')
table.insert(runtime_errors, file)
+ table.insert(runtime_errors_detail, detail)
end
os.remove(file)
end
@@ -371,7 +399,10 @@ function M.check_logs()
end
test_assert(
0 == #runtime_errors,
- string.format('Found runtime errors in logfile(s): %s', table.concat(runtime_errors, ', '))
+ string.format(
+ 'Found runtime errors in logfile(s):\n%s',
+ table.concat(runtime_errors_detail, '\n')
+ )
)
end