Skip to content

Avoid SIGPIPE in output pattern checks - #370

Merged
jserv merged 3 commits into
sysprog21:mainfrom
Suzu1Dev:fix/test-runner-large-output
Sep 5, 2026
Merged

Avoid SIGPIPE in output pattern checks#370
jserv merged 3 commits into
sysprog21:mainfrom
Suzu1Dev:fix/test-runner-large-output

Conversation

@Suzu1Dev

@Suzu1Dev Suzu1Dev commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

A successful command with a match near the start of a large output can fail
run_check or run_pipe: grep exits early, printf receives SIGPIPE, and pipefail
reports a failed pattern check. Feed captured output through a here-string.

The regression emits a matching first line followed by 30,000 lines. Both
early-match cases fail before the fix; all eight cases pass afterward. Late
matches, missing patterns and nonzero command exits are also covered.

Validated on Apple M3 Pro, macOS 27.0 (26A5425a), SDK 26.5:

  • make test-runner: 8 checks pass under Bash 3.2 and Bash 5.2.
  • BusyBox integration using the changed runner: 80 pass, 0 fail, 4 skip.
  • make check-format: passes, including 57 shell scripts.
  • Baseline make check stopped at an UNMEASURED throughput guardrail; its
    separate rerun after parallel builds stopped passes. The baseline
    AArch64 matrix reports 279 pass, 0 fail, 10 skip.

Summary by cubic

Fixes flaky failures in run_check, run_pipe, and the matrix and static-bins pattern checks when a successful command matched early in large output: grep exited before the pipe finished writing, SIGPIPE surfaced under pipefail, and the check was reported as failed. All captured-output pattern checks now read from a here-string instead of piping through echo/printf.

  • Replaces piped grep -qE checks with grep -qE "$pattern" <<< "$output" in tests/lib/test-runner.sh, tests/test-matrix.sh, and tests/test-static-bins.sh, preserving diff's expected exit status of 1.
  • Adds tests/test-runner.sh covering early matches, late matches, missing patterns, and nonzero command exits, and wires it into make check via mk/tests.mk; the generated output exceeds pipe capacity so the early-exit path is actually exercised.

Written for commit 6a85186. Summary will update on new commits.

Review in cubic

cubic-dev-ai[bot]

This comment was marked as resolved.

Comment thread tests/lib/test-runner.sh
test_excerpt "$output"
fail=$((fail + 1))
elif printf "%s\n" "$output" | grep -qE "$pattern"; then
elif grep -qE "$pattern" <<< "$output"; then

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Six identical copies of this pipeline survive outside this file, all under set -euo pipefail: tests/test-matrix.sh:565,633 and tests/test-static-bins.sh:93,127,161,358. They lose a matching pattern on large output exactly the way these two did, and the new lane does not reach them. Apply the same here-string to all six.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied the here-string change to all six checks in 6a85186.

Comment thread tests/test-runner.sh
if [ "${1:-}" = "--emit" ]; then
awk 'BEGIN {
print "match-begin"
for (i = 0; i < 30000; i++)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

30000 is load-bearing: it is what pushes the emitted output past the pipe buffer so the pre-fix writer takes SIGPIPE. Nothing here says so, so a later trim to something that fits in 64 KB leaves this lane green against the bug it exists to catch. State the constraint in one line above the loop.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added the pipe-capacity constraint above the loop in d1275e0.

Comment thread tests/test-runner.sh
else
run_check test-runner.sh "$pattern" --emit "$rc"
fi
[ "$pass" -eq "$want" ] && [ "$fail" -eq "$((1 - want))" ]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reads the counters from the sourced lib and only sees a zero baseline because the command substitution gives each call its own subshell. Drop that subshell later and every assertion after the first compares accumulated totals instead. Name the dependency in one line so the next reader does not refactor it away.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documented the subshell isolation of the pass/fail counters in d1275e0.

@Suzu1Dev
Suzu1Dev marked this pull request as draft September 4, 2026 23:05
@Suzu1Dev
Suzu1Dev marked this pull request as ready for review September 5, 2026 00:08
cubic-dev-ai[bot]

This comment was marked as resolved.

@jserv jserv left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rebase latest main branch, resolve conflicts, and response to review messages.

run_check and run_pipe can reject matching output when grep exits before
printf finishes writing. Under pipefail, the resulting SIGPIPE makes a
successful command appear to have missed its expected pattern.

Pass captured output through a here-string. Cover early and late
matches, missing patterns, and nonzero command exits in make check.
The output must exceed pipe capacity for early grep exit to expose
SIGPIPE. Each assertion also relies on a command-substitution subshell
inheriting zero pass/fail counts without updating the parent shell.
Six captured-output checks still use echo piped to grep -q. An early
match can leave echo writing to a closed pipe and report a false failure
under pipefail. Use here-strings while retaining diff's expected exit
status of 1.

Extracted before/after paths reproduce all six early-match failures and
confirm the fixes on Bash 3.2 and 5.2. Late matches, missing patterns,
timeouts, and the matrix and diff exit-status checks retain their
expected results. The shared runner tests and make check-format pass.
@Suzu1Dev
Suzu1Dev force-pushed the fix/test-runner-large-output branch from 992650d to 6a85186 Compare September 5, 2026 06:36
@Suzu1Dev

Suzu1Dev commented Sep 5, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto b54747a and resolved the conflict in mk/tests.mk.

@jserv
jserv merged commit d51e5b6 into sysprog21:main Sep 5, 2026
15 checks passed
@jserv

jserv commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Thank @Suzu1Dev for contributing!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants