a73x

3dc3515c

test: the e2e harness reads the process table and its counters the way both OSes answer

a73x   2026-09-04 10:16

Commit message
test: the e2e harness reads the process table and its counters the way both OSes answer

Five Linux-only spellings, four of which failed a group on macOS 26 while
the product underneath was right.

`proxy_pid` matched a `comm` column: BSD ps prints comm as the
executable's full path where Linux prints the basename, and truncates it
to the column width in a multi-column format, so no proxy was ever found
and every scenario that tears a transport failed. It reads `args` now and
takes the basename itself.

The handoff shim compared two comm words to `ssh mux`. Only the parent
word says anything about the product; Linux names a shebang script's
process after the script and Darwin after the interpreter, so `ssh` and
`sh` are both correct answers.

Three counter checks compared `wc -l` to a string. BSD wc pads its count
to a column width, so "       1" was not "1".

The pager leg spelled the row numbers less puts on screen. less 704
starts this file at row 178 and less 668 at 179, so the leg failed by one
row while the wheel worked; it reads the top row off the grid first and
asserts the wheel moved it back 24.

The fifth had no witness: the opt-in scenario log stamped `date +%s.%N`,
and BSD date has no %N, so the column would have read a literal N. It
takes the oracle's now_ms like every other clock in the suite.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SakwJEwD9dXBoRP5kWbemW

test/e2e_01_boot.sh
Old New
@@ -908,7 +908,8 @@ SCOMM=$(pid_comm "$SPID")
908 echo "e2e FAIL: the daemon mux d start -d brought up has comm '$SCOMM', want 'mux'" 908 echo "e2e FAIL: the daemon mux d start -d brought up has comm '$SCOMM', want 'mux'"
909 exit 1; } 909 exit 1; }
910 # Non-tty stderr: exactly two lines, no dots. 910 # Non-tty stderr: exactly two lines, no dots.
911 [ "$(wc -l < "$OUT.start")" = "2" ] || { 911 # -eq and not =, here and below: BSD wc pads its count with spaces.
912 [ "$(wc -l < "$OUT.start")" -eq 2 ] || {
912 echo "e2e FAIL: non-tty start not exactly two lines:"; cat "$OUT.start"; exit 1; } 913 echo "e2e FAIL: non-tty start not exactly two lines:"; cat "$OUT.start"; exit 1; }
913 914
914 # The daemon it started serves a session (marker in, marker in dump). 915 # The daemon it started serves a session (marker in, marker in dump).
@@ -1111,7 +1112,7 @@ grep -q "daemon did not answer" "$OUT.dead" || {
1111 cat "$OUT.dead"; exit 1; } 1112 cat "$OUT.dead"; exit 1; }
1112 # Non-tty: exactly two lines, same as the success path. The newline that 1113 # Non-tty: exactly two lines, same as the success path. The newline that
1113 # terminates the dot line is tty-only, so nothing blank creeps in here. 1114 # terminates the dot line is tty-only, so nothing blank creeps in here.
1114 [ "$(wc -l < "$OUT.dead")" = "2" ] || { 1115 [ "$(wc -l < "$OUT.dead")" -eq 2 ] || {
1115 echo "e2e FAIL: non-tty failure not exactly two lines:"; cat "$OUT.dead"; exit 1; } 1116 echo "e2e FAIL: non-tty failure not exactly two lines:"; cat "$OUT.dead"; exit 1; }
1116 rm -rf "$DEADCFG" 1117 rm -rf "$DEADCFG"
1117 ok "a start whose daemon dies young says so, with the log path" 1118 ok "a start whose daemon dies young says so, with the log path"
test/e2e_04_handoff.sh
Old New
@@ -89,10 +89,12 @@ ssh_shim_head "$SSHIM_DIR/ssh"
89 cat >> "$SSHIM_DIR/ssh" <<'SHIM' 89 cat >> "$SSHIM_DIR/ssh" <<'SHIM'
90 echo $$ >> "${SSHIM_PIDLOG:?}" 90 echo $$ >> "${SSHIM_PIDLOG:?}"
91 # ps rather than the lib's pid_comm: this shim is written to disk and run 91 # ps rather than the lib's pid_comm: this shim is written to disk and run
92 # as its own `sh` by the client under test, so nothing the suite sourced 92 # as its own `sh` by the client under test, so nothing the suite sourced is
93 # is in scope here. `ps -o comm=` is the same answer the oracle gives and 93 # in scope here. Basenames taken by hand, for pid_comm's own reason: BSD ps
94 # is spelled the same way on every OS the suite could run on. 94 # prints comm as the executable's full path where Linux prints the
95 printf '%s %s\n' "$(ps -o comm= -p $$)" "$(ps -o comm= -p $PPID)" >> "${SSHIM_COMMLOG:?}" 95 # basename.
96 _sc=$(ps -o comm= -p $$); _sp=$(ps -o comm= -p $PPID)
97 printf '%s %s\n' "${_sc##*/}" "${_sp##*/}" >> "${SSHIM_COMMLOG:?}"
96 shift 98 shift
97 printf '%s\n' "$*" >> "${SSHIM_ARGLOG:?}" 99 printf '%s\n' "$*" >> "${SSHIM_ARGLOG:?}"
98 exec /bin/sh -c "$*" 100 exec /bin/sh -c "$*"
@@ -152,13 +154,20 @@ HASKS=$(grep -c -- 'mux d endpoint --start$' "$SSHIM_ARGLOG" || true)
152 [ "$HASKS" -eq 1 ] || { 154 [ "$HASKS" -eq 1 ] || {
153 echo "e2e FAIL: cold handoff ran the asking word $HASKS times, want exactly 1" 155 echo "e2e FAIL: cold handoff ran the asking word $HASKS times, want exactly 1"
154 cat "$SSHIM_ARGLOG"; exit 1; } 156 cat "$SSHIM_ARGLOG"; exit 1; }
155 # Asked of /proc, not of the client: that child was `ssh` itself, sitting 157 # Asked of the OS, not of the client: that child sat directly under `mux`,
156 # directly under `mux`. What this does NOT catch is a 158 # with nothing interposed. Only the PARENT word is pinned. The child's own
157 # `/bin/sh -c` line put back — bash and dash both exec a single simple 159 # comm is not comparable across the two OSes — Linux names a shebang
158 # command in place, so the shell is gone by the time /proc is read. That 160 # script's process after the SCRIPT (`ssh`) and Darwin after the
159 # regression is build.zig folder rule 5's, measured there; this leg pins the 161 # INTERPRETER (`sh`), so both words are correct answers and neither says
160 # shape the rule cannot see, that the argv reaches a real ssh unmangled. 162 # anything about the product. It is still recorded, because a failure here
161 HBADCOMM=$(grep -cv '^ssh mux$' "$SSHIM_COMMLOG" || true) 163 # is read by eye.
164 #
165 # What this does NOT catch is a `/bin/sh -c` line put back — bash and dash
166 # both exec a single simple command in place, so the shell is gone by the
167 # time the process table is read. That regression is build.zig folder rule
168 # 5's, measured there; this leg pins the shape the rule cannot see, that
169 # the argv reaches a real ssh unmangled.
170 HBADCOMM=$(awk '$2 != "mux" {n++} END {print n+0}' "$SSHIM_COMMLOG")
162 [ "$HBADCOMM" -eq 0 ] || { 171 [ "$HBADCOMM" -eq 0 ] || {
163 echo "e2e FAIL: the handoff spawned something other than ssh straight off mux:" 172 echo "e2e FAIL: the handoff spawned something other than ssh straight off mux:"
164 cat "$SSHIM_COMMLOG"; exit 1; } 173 cat "$SSHIM_COMMLOG"; exit 1; }
test/e2e_08_mouse.sh
Old New
@@ -200,9 +200,18 @@ ok "an application that asked for the mouse gets the wheel, and the client does
200 # scenario before the fix: four notches, grid unmoved. 200 # scenario before the fix: four notches, grid unmoved.
201 # 201 #
202 # `less +G` on 200 lines: the view starts at the END, so a wheel-UP is the 202 # `less +G` on 200 lines: the view starts at the END, so a wheel-UP is the
203 # direction with somewhere to go. Eight notches is 24 rows, one screenful, 203 # direction with somewhere to go. Eight notches at less's default of three
204 # which moves the top from 178 to 154 — and 178 leaves the screen entirely, 204 # rows a notch is 24 rows, one screenful, so the top row moves back by 24
205 # so the assertion has both a needle and its negative. 205 # and the row that was at the top leaves the screen entirely — a needle and
206 # its negative.
207 #
208 # The two row numbers are READ off the grid before the wheel rather than
209 # written here, because how many of the 24 rows a pager spends on its own
210 # status line is the pager's business and differs between builds: less 704
211 # (Debian) starts this file at 178 and less 668 (macOS 26) at 179, and a
212 # leg that spelled 178 and 154 failed on the Mac by exactly one row while
213 # the wheel worked perfectly. What this leg is about is the 24, not where
214 # the pager chose to begin.
206 # 215 #
207 # LESS is cleared in the script rather than trusted: an operator with 216 # LESS is cleared in the script rather than trusted: an operator with
208 # `LESS=--mouse` exported would have a pager that DOES ask for the mouse, 217 # `LESS=--mouse` exported would have a pager that DOES ask for the mouse,
@@ -229,6 +238,18 @@ until "$MUX" d dump --sock "$SOCK35" | grep -qx "200"; do
229 "$MUX" d dump --sock "$SOCK35" | tail -3; exit 1; } 238 "$MUX" d dump --sock "$SOCK35" | tail -3; exit 1; }
230 sleep 0.1 239 sleep 0.1
231 done 240 done
241 # The row the pager put at the top, read now and not written down: the
242 # daemon's session is 80x24 and the ptyclient below attaches at 80x24, so
243 # nothing between here and the wheel moves the view.
244 PGRTOP=$("$MUX" d dump --sock "$SOCK35" | sed -n '1p')
245 case "$PGRTOP" in
246 ''|*[!0-9]*)
247 echo "e2e FAIL: pager: the top row of the settled grid is not a line number: '$PGRTOP'"
248 "$MUX" d dump --sock "$SOCK35" | head -3; exit 1 ;;
249 esac
250 [ "$PGRTOP" -gt 24 ] || {
251 echo "e2e FAIL: pager: top row $PGRTOP leaves no room for a screenful of wheel"
252 "$MUX" d dump --sock "$SOCK35" | head -3; exit 1; }
232 set +e 253 set +e
233 hostroom pager 254 hostroom pager
234 XDG_STATE_HOME="$HOSTROOM" timeout 40 "$PTYCLIENT" --cols 80 --rows 24 --out "$OUT.pgr" --err "$OUT.pgr.err" \ 255 XDG_STATE_HOME="$HOSTROOM" timeout 40 "$PTYCLIENT" --cols 80 --rows 24 --out "$OUT.pgr" --err "$OUT.pgr.err" \
@@ -250,11 +271,13 @@ rc0 "pager: ptyclient leg exited $RC:" "$OUT.pgr.log"
250 "$MUX" d dump --sock "$SOCK35" > "$OUT.pgrcap" 2>&1 271 "$MUX" d dump --sock "$SOCK35" > "$OUT.pgrcap" 2>&1
251 # A whole line, because the pager's status line carries the file's PATH — 272 # A whole line, because the pager's status line carries the file's PATH —
252 # which holds this run's pid and could spell any short number. 273 # which holds this run's pid and could spell any short number.
253 grep -qx "154" "$OUT.pgrcap" || { 274 PGRWANT=$(( PGRTOP - 24 ))
254 echo "e2e FAIL: pager: the wheel moved nothing on the alternate screen:" 275 grep -qx "$PGRWANT" "$OUT.pgrcap" || {
276 echo "e2e FAIL: pager: the wheel did not move the alternate screen back 24 rows"
277 echo " (top was $PGRTOP, so row $PGRWANT should be on screen):"
255 cat "$OUT.pgrcap"; exit 1; } 278 cat "$OUT.pgrcap"; exit 1; }
256 grep -qx "178" "$OUT.pgrcap" && { 279 grep -qx "$PGRTOP" "$OUT.pgrcap" && {
257 echo "e2e FAIL: pager: the old top row is still on screen, so the view did not move a screenful:" 280 echo "e2e FAIL: pager: the old top row $PGRTOP is still on screen, so the view did not move a screenful:"
258 cat "$OUT.pgrcap"; exit 1; } 281 cat "$OUT.pgrcap"; exit 1; }
259 # The pager never asked for the mouse, so it must never have SEEN a mouse 282 # The pager never asked for the mouse, so it must never have SEEN a mouse
260 # report: what reached it was arrow keys, which leave no text behind. 283 # report: what reached it was arrow keys, which leave no text behind.
test/e2e_09_hosts.sh
Old New
@@ -186,7 +186,9 @@ defer_kill "$HEPID"
186 grep -qxF -- "--sock $HESOCK" "$HESTATE/mux/hosts" || { 186 grep -qxF -- "--sock $HESOCK" "$HESTATE/mux/hosts" || {
187 echo "e2e FAIL: hosts: the empty file did not gain the local daemon's line:" 187 echo "e2e FAIL: hosts: the empty file did not gain the local daemon's line:"
188 cat "$HESTATE/mux/hosts"; exit 1; } 188 cat "$HESTATE/mux/hosts"; exit 1; }
189 [ "$(wc -l < "$HESTATE/mux/hosts")" = "1" ] || { 189 # -eq and not =: BSD wc pads its count to a column width, so the string
190 # comparison read " 1" and failed on a file that was exactly right.
191 [ "$(wc -l < "$HESTATE/mux/hosts")" -eq 1 ] || {
190 echo "e2e FAIL: hosts: the first mux wrote more than the daemon it attached to:" 192 echo "e2e FAIL: hosts: the first mux wrote more than the daemon it attached to:"
191 cat "$HESTATE/mux/hosts"; exit 1; } 193 cat "$HESTATE/mux/hosts"; exit 1; }
192 # ...and the session behind that line is a real shell, asked of the OS: the 194 # ...and the session behind that line is a real shell, asked of the OS: the
test/e2e_lib.sh
Old New
@@ -964,9 +964,17 @@ assert_stopped() {
964 # anywhere in the line, because one binary means the CLIENT is a `mux` too 964 # anywhere in the line, because one binary means the CLIENT is a `mux` too
965 # and the delayed-link scripts name the same socket — a pattern match here 965 # and the delayed-link scripts name the same socket — a pattern match here
966 # takes out the very client under test, and so would `pkill -f proxy`. 966 # takes out the very client under test, and so would `pkill -f proxy`.
967 #
968 # Read out of `args` alone, and the program name taken as a BASENAME of its
969 # first word. A `comm` column cannot carry this question across the two
970 # OSes: BSD ps prints comm as the executable's full path where Linux prints
971 # the basename, and in a multi-column format it truncates that path to the
972 # column width, so `$2=="mux"` matched nothing at all on macOS and every
973 # scenario that tears a transport failed as "could not find the proxy".
967 proxy_pid() { 974 proxy_pid() {
968 ps -eo pid,comm,args | 975 ps -eo pid,args |
969 awk -v s="$1" '$2=="mux" && $4=="d" && $5=="proxy" && index($0,s) {print $1}' | 976 awk -v s="$1" '{ n = split($2, _p, "/") }
977 _p[n]=="mux" && $3=="d" && $4=="proxy" && index($0,s) {print $1}' |
970 head -1 978 head -1
971 } 979 }
972 980
@@ -1131,13 +1139,15 @@ ok() {
1131 # test/coverage.sh maps each kcov database to the scenario that was 1139 # test/coverage.sh maps each kcov database to the scenario that was
1132 # running when the traced process wrote it, and the timestamps are also 1140 # running when the traced process wrote it, and the timestamps are also
1133 # the only per-scenario timing this suite has ever been able to report. 1141 # the only per-scenario timing this suite has ever been able to report.
1142 # Milliseconds through the oracle's now_ms, not `date +%s.%N`: BSD date
1143 # has no %N and printed a literal N into the column on a Mac.
1134 # 1144 #
1135 # An `if` rather than `[ -n ... ] && printf`, for wait_sock's reason: a 1145 # An `if` rather than `[ -n ... ] && printf`, for wait_sock's reason: a
1136 # false guard as the last command in a function becomes that function's 1146 # false guard as the last command in a function becomes that function's
1137 # exit status, and under `set -e` every unstamped run would abort at its 1147 # exit status, and under `set -e` every unstamped run would abort at its
1138 # first passing scenario. 1148 # first passing scenario.
1139 if [ -n "${E2E_OK_LOG:-}" ]; then 1149 if [ -n "${E2E_OK_LOG:-}" ]; then
1140 printf '%s\t%s\t%s\n' "$OK_COUNT" "$(date +%s.%N)" "$1" >> "$E2E_OK_LOG" 1150 printf '%s\t%s\t%s\n' "$OK_COUNT" "$(now_ms)" "$1" >> "$E2E_OK_LOG"
1141 fi 1151 fi
1142 # Prefix slicing. This suite is linear and stateful — the M13 scenarios 1152 # Prefix slicing. This suite is linear and stateful — the M13 scenarios
1143 # run inside sessions the M10 scenarios created — so a prefix is the only 1153 # run inside sessions the M10 scenarios created — so a prefix is the only