2518c30b
test: convergence on every transport scenario that ends with a live daemon
a73x 2026-08-09 17:27
Commit message
test/e2e.sh
| Old | New | ||
|---|---|---|---|
| @@ -199,6 +199,8 @@ cleanup() { | |||
| 199 | rm -f "$SOCK8" "$SOCK8T" "$SOCK11" "$OUT.start" "$OUT.start2" "$OUT.s8" \ | 199 | rm -f "$SOCK8" "$SOCK8T" "$SOCK11" "$OUT.start" "$OUT.start2" "$OUT.s8" \ |
| 200 | "$OUT.ra" "$OUT.rb" "$OUT.goal" "$OUT.g9" "$OUT.dead" \ | 200 | "$OUT.ra" "$OUT.rb" "$OUT.goal" "$OUT.g9" "$OUT.dead" \ |
| 201 | "$SOCK9" "$SOCK10" "$OUT.env1" "$OUT.env2" \ | 201 | "$SOCK9" "$SOCK10" "$OUT.env1" "$OUT.env2" \ |
| 202 | "$OUT.s8.err" "$OUT.race" "$OUT.g9.err" "$OUT.env1.err" "$OUT.env2.err" \ | ||
| 203 | "$OUT.m7.err" "$OUT.m7b.err" "$OUT.qc.err" "$OUT.qr.err" "$OUT.qk.err" \ | ||
| 202 | "$SOCK5" "$SOCK6" "$SOCK7" "$PWSH" \ | 204 | "$SOCK5" "$SOCK6" "$SOCK7" "$PWSH" \ |
| 203 | "$OUT.p1" "$OUT.p1.early" "$OUT.pb" "$OUT.pw" "$OUT.rw" "$OUT.pr" \ | 205 | "$OUT.p1" "$OUT.p1.early" "$OUT.pb" "$OUT.pw" "$OUT.rw" "$OUT.pr" \ |
| 204 | "$SOCK" "$SOCK2" "$SOCK3" "$SOCK4" "$SOCK4.second" "$QKEY" "$QKEY.bad" \ | 206 | "$SOCK" "$SOCK2" "$SOCK3" "$SOCK4" "$SOCK4.second" "$QKEY" "$QKEY.bad" \ |
| @@ -217,6 +219,7 @@ cleanup() { | |||
| 217 | trap cleanup EXIT INT TERM | 219 | trap cleanup EXIT INT TERM |
| 218 | 220 | ||
| 219 | # --- M10: --version answers "did the scp land" without a daemon anywhere. | 221 | # --- M10: --version answers "did the scp land" without a daemon anywhere. |
| 222 | # No convergence: no daemon and no client, so there is no grid on either side. | ||
| 220 | "$MUXD" --version | grep -q '^muxd 0\.' || { echo "e2e FAIL: muxd --version"; exit 1; } | 223 | "$MUXD" --version | grep -q '^muxd 0\.' || { echo "e2e FAIL: muxd --version"; exit 1; } |
| 221 | "$MUX" --version | grep -q '^mux 0\.' || { echo "e2e FAIL: mux --version"; exit 1; } | 224 | "$MUX" --version | grep -q '^mux 0\.' || { echo "e2e FAIL: mux --version"; exit 1; } |
| 222 | # Both must report the SAME version: the greps above pass just as happily for | 225 | # Both must report the SAME version: the greps above pass just as happily for |
| @@ -227,6 +230,7 @@ trap cleanup EXIT INT TERM | |||
| 227 | echo "e2e OK: --version on both binaries" | 230 | echo "e2e OK: --version on both binaries" |
| 228 | 231 | ||
| 229 | # --- M10: keygen writes 0600, prints the path, refuses a second run. | 232 | # --- M10: keygen writes 0600, prints the path, refuses a second run. |
| 233 | # No convergence: keygen touches the filesystem, never a session. | ||
| 230 | KEYOUT=$("$MUXD" keygen) | 234 | KEYOUT=$("$MUXD" keygen) |
| 231 | [ "$KEYOUT" = "$XDG_CONFIG_HOME/mux/key" ] || { | 235 | [ "$KEYOUT" = "$XDG_CONFIG_HOME/mux/key" ] || { |
| 232 | echo "e2e FAIL: keygen printed '$KEYOUT'"; exit 1; } | 236 | echo "e2e FAIL: keygen printed '$KEYOUT'"; exit 1; } |
| @@ -283,6 +287,8 @@ rm -f "$OUT.doctored" "$OUT.doctored.render" "$OUT.doctored.dump" \ | |||
| 283 | echo "e2e OK: convergence control fires on a doctored stream" | 287 | echo "e2e OK: convergence control fires on a doctored stream" |
| 284 | 288 | ||
| 285 | # --- M3: kill a client mid-run; daemon survives; reattach lands correctly. | 289 | # --- M3: kill a client mid-run; daemon survives; reattach lands correctly. |
| 290 | # No convergence on $OUT.kill: kill -9 truncates the stream mid-paint, possibly | ||
| 291 | # mid-escape-sequence, so a half-drawn capture is the expected shape here. | ||
| 286 | { printf 'seq 1 60\n'; sleep 2; } | "$MUX" --sock "$SOCK" > "$OUT.kill" & | 292 | { printf 'seq 1 60\n'; sleep 2; } | "$MUX" --sock "$SOCK" > "$OUT.kill" & |
| 287 | CPID=$! | 293 | CPID=$! |
| 288 | sleep 1 | 294 | sleep 1 |
| @@ -294,6 +300,7 @@ kill -0 "$DPID" || { echo "e2e FAIL: daemon died after client kill -9"; exit 1; | |||
| 294 | grep -q "60" "$OUT.re" || { | 300 | grep -q "60" "$OUT.re" || { |
| 295 | echo "e2e FAIL: reattach after kill missing state"; cat "$OUT.re"; exit 1; | 301 | echo "e2e FAIL: reattach after kill missing state"; cat "$OUT.re"; exit 1; |
| 296 | } | 302 | } |
| 303 | assert_converged "$OUT.re" "$SOCK" "reattach after kill" | ||
| 297 | rm -f "$OUT.kill" "$OUT.re" | 304 | rm -f "$OUT.kill" "$OUT.re" |
| 298 | 305 | ||
| 299 | # --- M5: two clients on one session. Output typed in A reaches both; then A | 306 | # --- M5: two clients on one session. Output typed in A reaches both; then A |
| @@ -315,6 +322,10 @@ grep -q "m5-after-a-left" "$OUT.b" || { | |||
| 315 | echo "e2e FAIL: client B lost its input path after A detached"; exit 1; | 322 | echo "e2e FAIL: client B lost its input path after A detached"; exit 1; |
| 316 | } | 323 | } |
| 317 | kill -0 "$DPID" || { echo "e2e FAIL: daemon died in two-client scenario"; exit 1; } | 324 | kill -0 "$DPID" || { echo "e2e FAIL: daemon died in two-client scenario"; exit 1; } |
| 325 | # B's capture, not A's: A detached before B's marker landed, so A's stream ends | ||
| 326 | # on a grid the daemon has since moved past. B saw both, and its convergence | ||
| 327 | # covers the same grid. | ||
| 328 | assert_converged "$OUT.b" "$SOCK" "two clients" | ||
| 318 | rm -f "$OUT.a" "$OUT.b" | 329 | rm -f "$OUT.a" "$OUT.b" |
| 319 | 330 | ||
| 320 | # --- M6: the same protocol over an arbitrary byte pipe. `muxd proxy` is a | 331 | # --- M6: the same protocol over an arbitrary byte pipe. `muxd proxy` is a |
| @@ -334,10 +345,12 @@ grep -q "m6-via-pipe" "$OUT.via" || { | |||
| 334 | echo "e2e FAIL: --via transport"; cat "$OUT.via"; exit 1; | 345 | echo "e2e FAIL: --via transport"; cat "$OUT.via"; exit 1; |
| 335 | } | 346 | } |
| 336 | kill -0 "$DPID" || { echo "e2e FAIL: daemon died in --via scenario"; exit 1; } | 347 | kill -0 "$DPID" || { echo "e2e FAIL: daemon died in --via scenario"; exit 1; } |
| 348 | assert_converged "$OUT.via" "$SOCK" "via transport" | ||
| 337 | rm -f "$OUT.via" | 349 | rm -f "$OUT.via" |
| 338 | 350 | ||
| 339 | # --- M10: a --via command that dies before the first frame stops claiming | 351 | # --- M10: a --via command that dies before the first frame stops claiming |
| 340 | # a connection existed. ssh's own stderr still passes through untouched. | 352 | # a connection existed. ssh's own stderr still passes through untouched. |
| 353 | # No convergence: no session was ever established, so there is no grid to match. | ||
| 341 | set +e | 354 | set +e |
| 342 | "$MUX" --via "sh -c 'exit 127'" > "$OUT.via" 2>&1 | 355 | "$MUX" --via "sh -c 'exit 127'" > "$OUT.via" 2>&1 |
| 343 | VRC=$? | 356 | VRC=$? |
| @@ -355,6 +368,7 @@ echo "e2e OK: --via failure says what happened" | |||
| 355 | # an unkillable client. With piped stdin there is no Ctrl-\ to rescue it, so | 368 | # an unkillable client. With piped stdin there is no Ctrl-\ to rescue it, so |
| 356 | # a regression here hangs forever: exit 124 below is the timeout, and it is | 369 | # a regression here hangs forever: exit 124 below is the timeout, and it is |
| 357 | # the failure this test exists to catch. | 370 | # the failure this test exists to catch. |
| 371 | # No convergence: the transport died first, so no session and no grid. | ||
| 358 | set +e | 372 | set +e |
| 359 | timeout 10 "$MUX" --via "exit 7" < /dev/null > "$OUT.dead" 2>&1 | 373 | timeout 10 "$MUX" --via "exit 7" < /dev/null > "$OUT.dead" 2>&1 |
| 360 | RC=$? | 374 | RC=$? |
| @@ -376,6 +390,8 @@ rm -f "$OUT.dead" | |||
| 376 | # session (so reconnect is allowed), its daemon is then killed under it, and | 390 | # session (so reconnect is allowed), its daemon is then killed under it, and |
| 377 | # Ctrl-\ arrives while it is retrying. Exit 0 and the message; a double-close | 391 | # Ctrl-\ arrives while it is retrying. Exit 0 and the message; a double-close |
| 378 | # of the transport would abort here instead (SIGABRT = 134). | 392 | # of the transport would abort here instead (SIGABRT = 134). |
| 393 | # No convergence: the client exits while reconnecting and its daemon is dead, | ||
| 394 | # so there is nothing left to dump against. | ||
| 379 | "$MUXD" run --sock "$SOCK2" --shell /bin/sh & | 395 | "$MUXD" run --sock "$SOCK2" --shell /bin/sh & |
| 380 | D2PID=$! | 396 | D2PID=$! |
| 381 | i=0 | 397 | i=0 |
| @@ -414,14 +430,15 @@ set +e | |||
| 414 | { sleep 0.5; printf 'printf "m7-%%s\\n" before\n'; sleep 8; \ | 430 | { sleep 0.5; printf 'printf "m7-%%s\\n" before\n'; sleep 8; \ |
| 415 | printf 'printf "m7-%%s\\n" after\n'; sleep 2.5; printf '\034'; } | \ | 431 | printf 'printf "m7-%%s\\n" after\n'; sleep 2.5; printf '\034'; } | \ |
| 416 | XDG_RUNTIME_DIR=/nonexistent-mux-e2e timeout 40 \ | 432 | XDG_RUNTIME_DIR=/nonexistent-mux-e2e timeout 40 \ |
| 417 | "$MUX" --via "$MUXD proxy --sock $SOCK" > "$OUT.m7" 2>&1 & | 433 | "$MUX" --via "$MUXD proxy --sock $SOCK" > "$OUT.m7" 2> "$OUT.m7.err" & |
| 418 | M7PID=$! | 434 | M7PID=$! |
| 419 | set -e | 435 | set -e |
| 420 | 436 | ||
| 421 | # Tear only once the session is provably live and idle: the marker is the | 437 | # Tear only once the session is provably live and idle: the marker is the |
| 422 | # shell's own output, so seeing it means attached, drained, mid-sleep. | 438 | # shell's own output, so seeing it means attached, drained, mid-sleep. |
| 423 | wait_for "$OUT.m7" "m7-before" 20 || { | 439 | wait_for "$OUT.m7" "m7-before" 20 || { |
| 424 | echo "e2e FAIL: m7 pre-tear output never arrived"; cat "$OUT.m7"; exit 1; | 440 | echo "e2e FAIL: m7 pre-tear output never arrived" |
| 441 | cat "$OUT.m7" "$OUT.m7.err" 2>/dev/null; exit 1; | ||
| 425 | } | 442 | } |
| 426 | SNAPS_BEFORE=$("$MUXD" stats --sock "$SOCK" | sed -n 's/.*snapshots=\([0-9]*\).*/\1/p') | 443 | SNAPS_BEFORE=$("$MUXD" stats --sock "$SOCK" | sed -n 's/.*snapshots=\([0-9]*\).*/\1/p') |
| 427 | PPID_PROXY=$(proxy_pid "$SOCK") | 444 | PPID_PROXY=$(proxy_pid "$SOCK") |
| @@ -437,12 +454,12 @@ RC=$? | |||
| 437 | set -e | 454 | set -e |
| 438 | [ "$RC" -eq 0 ] || { | 455 | [ "$RC" -eq 0 ] || { |
| 439 | echo "e2e FAIL: m7 client exited $RC after a transport tear (want 0)" | 456 | echo "e2e FAIL: m7 client exited $RC after a transport tear (want 0)" |
| 440 | cat "$OUT.m7"; exit 1; | 457 | cat "$OUT.m7" "$OUT.m7.err" 2>/dev/null; exit 1; |
| 441 | } | 458 | } |
| 442 | grep -q "m7-before" "$OUT.m7" || { echo "e2e FAIL: m7 pre-tear output missing"; exit 1; } | 459 | grep -q "m7-before" "$OUT.m7" || { echo "e2e FAIL: m7 pre-tear output missing"; exit 1; } |
| 443 | grep -q "m7-after" "$OUT.m7" || { | 460 | grep -q "m7-after" "$OUT.m7" || { |
| 444 | echo "e2e FAIL: m7 client did not resume after the transport was killed" | 461 | echo "e2e FAIL: m7 client did not resume after the transport was killed" |
| 445 | cat "$OUT.m7"; exit 1; | 462 | cat "$OUT.m7" "$OUT.m7.err" 2>/dev/null; exit 1; |
| 446 | } | 463 | } |
| 447 | SNAPS_AFTER=$("$MUXD" stats --sock "$SOCK" | sed -n 's/.*snapshots=\([0-9]*\).*/\1/p') | 464 | SNAPS_AFTER=$("$MUXD" stats --sock "$SOCK" | sed -n 's/.*snapshots=\([0-9]*\).*/\1/p') |
| 448 | [ -n "$SNAPS_BEFORE" ] && [ -n "$SNAPS_AFTER" ] || { | 465 | [ -n "$SNAPS_BEFORE" ] && [ -n "$SNAPS_AFTER" ] || { |
| @@ -452,7 +469,8 @@ SNAPS_AFTER=$("$MUXD" stats --sock "$SOCK" | sed -n 's/.*snapshots=\([0-9]*\).*/ | |||
| 452 | echo "e2e FAIL: reconnect was served a snapshot ($SNAPS_BEFORE -> $SNAPS_AFTER), not a delta" | 469 | echo "e2e FAIL: reconnect was served a snapshot ($SNAPS_BEFORE -> $SNAPS_AFTER), not a delta" |
| 453 | exit 1; | 470 | exit 1; |
| 454 | } | 471 | } |
| 455 | rm -f "$OUT.m7" | 472 | assert_converged "$OUT.m7" "$SOCK" "delta resume" |
| 473 | rm -f "$OUT.m7" "$OUT.m7.err" | ||
| 456 | 474 | ||
| 457 | # --- M7 Scenario B: kill the DAEMON under an attached client and start a new | 475 | # --- M7 Scenario B: kill the DAEMON under an attached client and start a new |
| 458 | # one on the same path. The seq the client holds belongs to a session that no | 476 | # one on the same path. The seq the client holds belongs to a session that no |
| @@ -467,11 +485,12 @@ while [ ! -S "$SOCK3" ] && [ "$i" -lt 50 ]; do sleep 0.1; i=$((i+1)); done | |||
| 467 | set +e | 485 | set +e |
| 468 | { sleep 0.5; printf 'printf "m7b-%%s\\n" pre-restart\n'; sleep 8; \ | 486 | { sleep 0.5; printf 'printf "m7b-%%s\\n" pre-restart\n'; sleep 8; \ |
| 469 | printf 'printf "m7b-%%s\\n" post-restart\n'; sleep 3; printf '\034'; } | \ | 487 | printf 'printf "m7b-%%s\\n" post-restart\n'; sleep 3; printf '\034'; } | \ |
| 470 | timeout 40 "$MUX" --sock "$SOCK3" > "$OUT.m7b" 2>&1 & | 488 | timeout 40 "$MUX" --sock "$SOCK3" > "$OUT.m7b" 2> "$OUT.m7b.err" & |
| 471 | M7BPID=$! | 489 | M7BPID=$! |
| 472 | set -e | 490 | set -e |
| 473 | wait_for "$OUT.m7b" "m7b-pre-restart" 20 || { | 491 | wait_for "$OUT.m7b" "m7b-pre-restart" 20 || { |
| 474 | echo "e2e FAIL: m7b pre-restart output never arrived"; cat "$OUT.m7b"; exit 1; | 492 | echo "e2e FAIL: m7b pre-restart output never arrived" |
| 493 | cat "$OUT.m7b" "$OUT.m7b.err" 2>/dev/null; exit 1; | ||
| 475 | } | 494 | } |
| 476 | 495 | ||
| 477 | # SIGKILL leaves the socket file behind; the new daemon's stale-socket | 496 | # SIGKILL leaves the socket file behind; the new daemon's stale-socket |
| @@ -490,11 +509,11 @@ RC=$? | |||
| 490 | set -e | 509 | set -e |
| 491 | [ "$RC" -eq 0 ] || { | 510 | [ "$RC" -eq 0 ] || { |
| 492 | echo "e2e FAIL: m7b client exited $RC across a daemon restart (want 0)" | 511 | echo "e2e FAIL: m7b client exited $RC across a daemon restart (want 0)" |
| 493 | cat "$OUT.m7b"; exit 1; | 512 | cat "$OUT.m7b" "$OUT.m7b.err" 2>/dev/null; exit 1; |
| 494 | } | 513 | } |
| 495 | grep -q "m7b-post-restart" "$OUT.m7b" || { | 514 | grep -q "m7b-post-restart" "$OUT.m7b" || { |
| 496 | echo "e2e FAIL: m7b client did not resume into the restarted daemon" | 515 | echo "e2e FAIL: m7b client did not resume into the restarted daemon" |
| 497 | cat "$OUT.m7b"; exit 1; | 516 | cat "$OUT.m7b" "$OUT.m7b.err" 2>/dev/null; exit 1; |
| 498 | } | 517 | } |
| 499 | # The fresh session really is fresh: a new daemon means a new shell, so the | 518 | # The fresh session really is fresh: a new daemon means a new shell, so the |
| 500 | # pre-restart marker cannot be in its grid. If it were, we would be looking | 519 | # pre-restart marker cannot be in its grid. If it were, we would be looking |
| @@ -515,7 +534,8 @@ SNAPS_NEW=$("$MUXD" stats --sock "$SOCK3" | sed -n 's/.*snapshots=\([0-9]*\).*/\ | |||
| 515 | echo "e2e FAIL: restarted daemon served no snapshot ($SNAPS_NEW); the stale seq was honoured" | 534 | echo "e2e FAIL: restarted daemon served no snapshot ($SNAPS_NEW); the stale seq was honoured" |
| 516 | exit 1; | 535 | exit 1; |
| 517 | } | 536 | } |
| 518 | rm -f "$OUT.m7b" | 537 | assert_converged "$OUT.m7b" "$SOCK3" "epoch resync" |
| 538 | rm -f "$OUT.m7b" "$OUT.m7b.err" | ||
| 519 | 539 | ||
| 520 | # --- M8: the --quic flags. Every refusal must cost nothing — no session | 540 | # --- M8: the --quic flags. Every refusal must cost nothing — no session |
| 521 | # socket, no shell, no stack trace — and the accepted case must leave a | 541 | # socket, no shell, no stack trace — and the accepted case must leave a |
| @@ -526,6 +546,7 @@ rm -f "$OUT.m7b" | |||
| 526 | 546 | ||
| 527 | # A refusal that leaves a socket behind has already started a session, which | 547 | # A refusal that leaves a socket behind has already started a session, which |
| 528 | # is the failure this ordering exists to prevent. | 548 | # is the failure this ordering exists to prevent. |
| 549 | # No convergence anywhere in refuse(): no daemon survives one, by construction. | ||
| 529 | # Takes the config home to run under as an explicit second argument, rather | 550 | # Takes the config home to run under as an explicit second argument, rather |
| 530 | # than relying on a `XDG_CONFIG_HOME=... refuse ...` prefix: POSIX says an | 551 | # than relying on a `XDG_CONFIG_HOME=... refuse ...` prefix: POSIX says an |
| 531 | # assignment prefixing a FUNCTION call may outlive it, so on a dash /bin/sh | 552 | # assignment prefixing a FUNCTION call may outlive it, so on a dash /bin/sh |
| @@ -645,6 +666,7 @@ grep -q "quic-flags-ok" "$OUT.q" || { | |||
| 645 | echo "e2e FAIL: --quic daemon's grid missing output"; exit 1; | 666 | echo "e2e FAIL: --quic daemon's grid missing output"; exit 1; |
| 646 | } | 667 | } |
| 647 | kill -0 "$D4PID" || { echo "e2e FAIL: --quic daemon died"; exit 1; } | 668 | kill -0 "$D4PID" || { echo "e2e FAIL: --quic daemon died"; exit 1; } |
| 669 | assert_converged "$OUT.q" "$SOCK4" "quic daemon serves sockets" | ||
| 648 | 670 | ||
| 649 | # --- M8 Task 3: the client speaks quic://. Same daemon, new transport. | 671 | # --- M8 Task 3: the client speaks quic://. Same daemon, new transport. |
| 650 | 672 | ||
| @@ -654,18 +676,21 @@ kill -0 "$D4PID" || { echo "e2e FAIL: --quic daemon died"; exit 1; } | |||
| 654 | set +e | 676 | set +e |
| 655 | { printf 'printf "quic-%%s\\n" attach-ok\n'; sleep 2; printf '\034'; } | \ | 677 | { printf 'printf "quic-%%s\\n" attach-ok\n'; sleep 2; printf '\034'; } | \ |
| 656 | timeout 30 "$MUX" "quic://127.0.0.1:$QPORT" --key "$QKEY" \ | 678 | timeout 30 "$MUX" "quic://127.0.0.1:$QPORT" --key "$QKEY" \ |
| 657 | --quic-idle-ms 15000 > "$OUT.qc" 2>&1 | 679 | --quic-idle-ms 15000 > "$OUT.qc" 2> "$OUT.qc.err" |
| 658 | RC=$? | 680 | RC=$? |
| 659 | set -e | 681 | set -e |
| 660 | [ "$RC" -eq 0 ] || { | 682 | [ "$RC" -eq 0 ] || { |
| 661 | echo "e2e FAIL: quic:// client exited $RC (want 0)"; cat "$OUT.qc"; exit 1; | 683 | echo "e2e FAIL: quic:// client exited $RC (want 0)" |
| 684 | cat "$OUT.qc" "$OUT.qc.err" 2>/dev/null; exit 1; | ||
| 662 | } | 685 | } |
| 663 | grep -q "quic-attach-ok" "$OUT.qc" || { | 686 | grep -q "quic-attach-ok" "$OUT.qc" || { |
| 664 | echo "e2e FAIL: quic:// client render missing output"; cat "$OUT.qc"; exit 1; | 687 | echo "e2e FAIL: quic:// client render missing output" |
| 688 | cat "$OUT.qc" "$OUT.qc.err" 2>/dev/null; exit 1; | ||
| 665 | } | 689 | } |
| 666 | "$MUXD" dump --sock "$SOCK4" | grep -q "quic-attach-ok" || { | 690 | "$MUXD" dump --sock "$SOCK4" | grep -q "quic-attach-ok" || { |
| 667 | echo "e2e FAIL: daemon grid missing the quic:// client's output"; exit 1; | 691 | echo "e2e FAIL: daemon grid missing the quic:// client's output"; exit 1; |
| 668 | } | 692 | } |
| 693 | assert_converged "$OUT.qc" "$SOCK4" "quic attach" | ||
| 669 | 694 | ||
| 670 | # 1b. Reattach. A detach leaves the session running, so coming back must | 695 | # 1b. Reattach. A detach leaves the session running, so coming back must |
| 671 | # find it — and must be served as a fresh attach (its own snapshot), | 696 | # find it — and must be served as a fresh attach (its own snapshot), |
| @@ -675,20 +700,22 @@ SNAPS_RA=$("$MUXD" stats --sock "$SOCK4" | sed -n 's/.*snapshots=\([0-9]*\).*/\1 | |||
| 675 | set +e | 700 | set +e |
| 676 | { printf 'printf "quic-%%s\\n" reattached\n'; sleep 2; printf '\034'; } | \ | 701 | { printf 'printf "quic-%%s\\n" reattached\n'; sleep 2; printf '\034'; } | \ |
| 677 | timeout 30 "$MUX" "quic://127.0.0.1:$QPORT" --key "$QKEY" \ | 702 | timeout 30 "$MUX" "quic://127.0.0.1:$QPORT" --key "$QKEY" \ |
| 678 | --quic-idle-ms 15000 > "$OUT.qc" 2>&1 | 703 | --quic-idle-ms 15000 > "$OUT.qc" 2> "$OUT.qc.err" |
| 679 | RC=$? | 704 | RC=$? |
| 680 | set -e | 705 | set -e |
| 681 | [ "$RC" -eq 0 ] || { | 706 | [ "$RC" -eq 0 ] || { |
| 682 | echo "e2e FAIL: quic:// reattach exited $RC (want 0)"; cat "$OUT.qc"; exit 1; | 707 | echo "e2e FAIL: quic:// reattach exited $RC (want 0)" |
| 708 | cat "$OUT.qc" "$OUT.qc.err" 2>/dev/null; exit 1; | ||
| 683 | } | 709 | } |
| 684 | # The earlier marker is still on the grid this client was handed, which is | 710 | # The earlier marker is still on the grid this client was handed, which is |
| 685 | # the session having survived the detach rather than a new shell. | 711 | # the session having survived the detach rather than a new shell. |
| 686 | grep -q "quic-attach-ok" "$OUT.qc" || { | 712 | grep -q "quic-attach-ok" "$OUT.qc" || { |
| 687 | echo "e2e FAIL: quic:// reattach did not land in the existing session" | 713 | echo "e2e FAIL: quic:// reattach did not land in the existing session" |
| 688 | cat "$OUT.qc"; exit 1; | 714 | cat "$OUT.qc" "$OUT.qc.err" 2>/dev/null; exit 1; |
| 689 | } | 715 | } |
| 690 | grep -q "quic-reattached" "$OUT.qc" || { | 716 | grep -q "quic-reattached" "$OUT.qc" || { |
| 691 | echo "e2e FAIL: quic:// reattach could not run a command"; cat "$OUT.qc"; exit 1; | 717 | echo "e2e FAIL: quic:// reattach could not run a command" |
| 718 | cat "$OUT.qc" "$OUT.qc.err" 2>/dev/null; exit 1; | ||
| 692 | } | 719 | } |
| 693 | SNAPS_RA2=$("$MUXD" stats --sock "$SOCK4" | sed -n 's/.*snapshots=\([0-9]*\).*/\1/p') | 720 | SNAPS_RA2=$("$MUXD" stats --sock "$SOCK4" | sed -n 's/.*snapshots=\([0-9]*\).*/\1/p') |
| 694 | [ "$((SNAPS_RA2 - SNAPS_RA))" -eq 1 ] || { | 721 | [ "$((SNAPS_RA2 - SNAPS_RA))" -eq 1 ] || { |
| @@ -700,10 +727,12 @@ CLIENTS=$("$MUXD" stats --sock "$SOCK4" | sed -n 's/.*clients=\([0-9]*\).*/\1/p' | |||
| 700 | [ "$CLIENTS" = "0" ] || { | 727 | [ "$CLIENTS" = "0" ] || { |
| 701 | echo "e2e FAIL: $CLIENTS client slots still held after a clean detach (want 0)"; exit 1; | 728 | echo "e2e FAIL: $CLIENTS client slots still held after a clean detach (want 0)"; exit 1; |
| 702 | } | 729 | } |
| 730 | assert_converged "$OUT.qc" "$SOCK4" "quic reattach" | ||
| 703 | 731 | ||
| 704 | # 2. The key is checked, and a wrong one is refused loudly rather than | 732 | # 2. The key is checked, and a wrong one is refused loudly rather than |
| 705 | # retried forever. Nothing was ever established, so the reconnect loop | 733 | # retried forever. Nothing was ever established, so the reconnect loop |
| 706 | # must not engage — that is the never-established gate, over QUIC. | 734 | # must not engage — that is the never-established gate, over QUIC. |
| 735 | # No convergence: a refused client painted no grid. | ||
| 707 | head -c 32 /dev/urandom > "$QKEY.wrong" | 736 | head -c 32 /dev/urandom > "$QKEY.wrong" |
| 708 | chmod 600 "$QKEY.wrong" | 737 | chmod 600 "$QKEY.wrong" |
| 709 | set +e | 738 | set +e |
| @@ -725,6 +754,7 @@ grep -q "did not answer" "$OUT.qc" || { | |||
| 725 | # the handshake bound allows — and during a reconnect the terminal is in | 754 | # the handshake bound allows — and during a reconnect the terminal is in |
| 726 | # raw mode, where Ctrl-\ is the only way out. A deliberately unreachable | 755 | # raw mode, where Ctrl-\ is the only way out. A deliberately unreachable |
| 727 | # port makes the handshake run its full length if nothing interrupts it. | 756 | # port makes the handshake run its full length if nothing interrupts it. |
| 757 | # No convergence: the client aborts before it ever attaches, so it paints nothing. | ||
| 728 | QDEAD=$(( QPORT + 1 )) | 758 | QDEAD=$(( QPORT + 1 )) |
| 729 | # A fifo rather than a pipeline, so what is timed is the CLIENT's exit and | 759 | # A fifo rather than a pipeline, so what is timed is the CLIENT's exit and |
| 730 | # not how long the writer happened to hang around afterwards. | 760 | # not how long the writer happened to hang around afterwards. |
| @@ -765,12 +795,12 @@ SNAPS_BEFORE=$("$MUXD" stats --sock "$SOCK4" | sed -n 's/.*snapshots=\([0-9]*\). | |||
| 765 | set +e | 795 | set +e |
| 766 | { printf 'printf "quic-%%s\\n" pre-tear\n'; sleep 9; printf '\034'; } | \ | 796 | { printf 'printf "quic-%%s\\n" pre-tear\n'; sleep 9; printf '\034'; } | \ |
| 767 | timeout 40 "$MUX" "quic://127.0.0.1:$QPORT" --key "$QKEY" \ | 797 | timeout 40 "$MUX" "quic://127.0.0.1:$QPORT" --key "$QKEY" \ |
| 768 | --quic-idle-ms 1500 > "$OUT.qr" 2>&1 & | 798 | --quic-idle-ms 1500 > "$OUT.qr" 2> "$OUT.qr.err" & |
| 769 | QRPID=$! | 799 | QRPID=$! |
| 770 | set -e | 800 | set -e |
| 771 | wait_for "$OUT.qr" "quic-pre-tear" 20 || { | 801 | wait_for "$OUT.qr" "quic-pre-tear" 20 || { |
| 772 | echo "e2e FAIL: quic reconnect client never got its pre-tear marker" | 802 | echo "e2e FAIL: quic reconnect client never got its pre-tear marker" |
| 773 | cat "$OUT.qr"; exit 1; | 803 | cat "$OUT.qr" "$OUT.qr.err" 2>/dev/null; exit 1; |
| 774 | } | 804 | } |
| 775 | 805 | ||
| 776 | # Held well past the 1500ms idle timeout, so the client cannot mistake it | 806 | # Held well past the 1500ms idle timeout, so the client cannot mistake it |
| @@ -786,10 +816,12 @@ wait "$QRPID" | |||
| 786 | RC=$? | 816 | RC=$? |
| 787 | set -e | 817 | set -e |
| 788 | [ "$RC" -eq 0 ] || { | 818 | [ "$RC" -eq 0 ] || { |
| 789 | echo "e2e FAIL: quic client exited $RC across a tear (want 0)"; cat "$OUT.qr"; exit 1; | 819 | echo "e2e FAIL: quic client exited $RC across a tear (want 0)" |
| 820 | cat "$OUT.qr" "$OUT.qr.err" 2>/dev/null; exit 1; | ||
| 790 | } | 821 | } |
| 791 | grep -q "quic-pre-tear" "$OUT.qr" || { | 822 | grep -q "quic-pre-tear" "$OUT.qr" || { |
| 792 | echo "e2e FAIL: quic client lost its session across the tear"; cat "$OUT.qr"; exit 1; | 823 | echo "e2e FAIL: quic client lost its session across the tear" |
| 824 | cat "$OUT.qr" "$OUT.qr.err" 2>/dev/null; exit 1; | ||
| 793 | } | 825 | } |
| 794 | 826 | ||
| 795 | # The counter is the only witness to HOW the resume was served: a snapshot | 827 | # The counter is the only witness to HOW the resume was served: a snapshot |
| @@ -803,6 +835,7 @@ SNAPS_AFTER=$("$MUXD" stats --sock "$SOCK4" | sed -n 's/.*snapshots=\([0-9]*\).* | |||
| 803 | echo " the attach only; the resume itself must be delta-served)" | 835 | echo " the attach only; the resume itself must be delta-served)" |
| 804 | exit 1; | 836 | exit 1; |
| 805 | } | 837 | } |
| 838 | assert_converged "$OUT.qr" "$SOCK4" "quic delta resume" | ||
| 806 | 839 | ||
| 807 | 840 | ||
| 808 | # 5. The daemon is killed outright and started again on the SAME paths — unix | 841 | # 5. The daemon is killed outright and started again on the SAME paths — unix |
| @@ -817,12 +850,12 @@ set +e | |||
| 817 | { printf 'printf "quic-%%s\\n" pre-restart\n'; sleep 3; \ | 850 | { printf 'printf "quic-%%s\\n" pre-restart\n'; sleep 3; \ |
| 818 | printf 'printf "quic-%%s\\n" post-restart\n'; sleep 3; printf '\034'; } | \ | 851 | printf 'printf "quic-%%s\\n" post-restart\n'; sleep 3; printf '\034'; } | \ |
| 819 | timeout 40 "$MUX" "quic://127.0.0.1:$QPORT" --key "$QKEY" \ | 852 | timeout 40 "$MUX" "quic://127.0.0.1:$QPORT" --key "$QKEY" \ |
| 820 | --quic-idle-ms 1500 > "$OUT.qk" 2>&1 & | 853 | --quic-idle-ms 1500 > "$OUT.qk" 2> "$OUT.qk.err" & |
| 821 | QKPID=$! | 854 | QKPID=$! |
| 822 | set -e | 855 | set -e |
| 823 | wait_for "$OUT.qk" "quic-pre-restart" 20 || { | 856 | wait_for "$OUT.qk" "quic-pre-restart" 20 || { |
| 824 | echo "e2e FAIL: quic restart client never got its pre-restart marker" | 857 | echo "e2e FAIL: quic restart client never got its pre-restart marker" |
| 825 | cat "$OUT.qk"; exit 1; | 858 | cat "$OUT.qk" "$OUT.qk.err" 2>/dev/null; exit 1; |
| 826 | } | 859 | } |
| 827 | 860 | ||
| 828 | kill -9 "$D4PID" | 861 | kill -9 "$D4PID" |
| @@ -851,11 +884,11 @@ RC=$? | |||
| 851 | set -e | 884 | set -e |
| 852 | [ "$RC" -eq 0 ] || { | 885 | [ "$RC" -eq 0 ] || { |
| 853 | echo "e2e FAIL: quic client exited $RC across a daemon restart (want 0)" | 886 | echo "e2e FAIL: quic client exited $RC across a daemon restart (want 0)" |
| 854 | cat "$OUT.qk"; exit 1; | 887 | cat "$OUT.qk" "$OUT.qk.err" 2>/dev/null; exit 1; |
| 855 | } | 888 | } |
| 856 | grep -q "quic-post-restart" "$OUT.qk" || { | 889 | grep -q "quic-post-restart" "$OUT.qk" || { |
| 857 | echo "e2e FAIL: quic client did not resume into the restarted daemon" | 890 | echo "e2e FAIL: quic client did not resume into the restarted daemon" |
| 858 | cat "$OUT.qk"; exit 1; | 891 | cat "$OUT.qk" "$OUT.qk.err" 2>/dev/null; exit 1; |
| 859 | } | 892 | } |
| 860 | # A new daemon means a new shell, so the old marker cannot be in its grid. | 893 | # A new daemon means a new shell, so the old marker cannot be in its grid. |
| 861 | # If it were, we would be looking at a client that resumed off a seq | 894 | # If it were, we would be looking at a client that resumed off a seq |
| @@ -874,6 +907,7 @@ SNAPS_NQ=$("$MUXD" stats --sock "$SOCK4" | sed -n 's/.*snapshots=\([0-9]*\).*/\1 | |||
| 874 | echo "e2e FAIL: restarted daemon served no snapshot ($SNAPS_NQ); the stale seq was honoured" | 907 | echo "e2e FAIL: restarted daemon served no snapshot ($SNAPS_NQ); the stale seq was honoured" |
| 875 | exit 1; | 908 | exit 1; |
| 876 | } | 909 | } |
| 910 | assert_converged "$OUT.qk" "$SOCK4" "quic epoch resync" | ||
| 877 | 911 | ||
| 878 | kill "$D4PID" 2>/dev/null || true | 912 | kill "$D4PID" 2>/dev/null || true |
| 879 | D4PID="" | 913 | D4PID="" |
| @@ -894,15 +928,17 @@ while [ ! -S "$SOCK9" ] && [ "$i" -lt 50 ]; do sleep 0.1; i=$((i+1)); done | |||
| 894 | set +e | 928 | set +e |
| 895 | { printf 'printf "envkey-%%s\\n" ok\n'; sleep 2; printf '\034'; } | \ | 929 | { printf 'printf "envkey-%%s\\n" ok\n'; sleep 2; printf '\034'; } | \ |
| 896 | timeout 30 "$MUX" "quic://127.0.0.1:$QPORT2" --key "$QKEY" \ | 930 | timeout 30 "$MUX" "quic://127.0.0.1:$QPORT2" --key "$QKEY" \ |
| 897 | --quic-idle-ms 15000 > "$OUT.env1" 2>&1 | 931 | --quic-idle-ms 15000 > "$OUT.env1" 2> "$OUT.env1.err" |
| 898 | RC=$? | 932 | RC=$? |
| 899 | set -e | 933 | set -e |
| 900 | [ "$RC" -eq 0 ] || { | 934 | [ "$RC" -eq 0 ] || { |
| 901 | echo "e2e FAIL: quic attach to a MUX_KEY_FILE daemon exited $RC (want 0)" | 935 | echo "e2e FAIL: quic attach to a MUX_KEY_FILE daemon exited $RC (want 0)" |
| 902 | cat "$OUT.env1"; exit 1; | 936 | cat "$OUT.env1" "$OUT.env1.err" 2>/dev/null; exit 1; |
| 903 | } | 937 | } |
| 904 | grep -q "envkey-ok" "$OUT.env1" || { | 938 | grep -q "envkey-ok" "$OUT.env1" || { |
| 905 | echo "e2e FAIL: MUX_KEY_FILE daemon served no session"; cat "$OUT.env1"; exit 1; } | 939 | echo "e2e FAIL: MUX_KEY_FILE daemon served no session" |
| 940 | cat "$OUT.env1" "$OUT.env1.err" 2>/dev/null; exit 1; } | ||
| 941 | assert_converged "$OUT.env1" "$SOCK9" "env key" | ||
| 906 | kill "$D9PID" 2>/dev/null || true | 942 | kill "$D9PID" 2>/dev/null || true |
| 907 | D9PID="" | 943 | D9PID="" |
| 908 | 944 | ||
| @@ -922,20 +958,23 @@ while [ ! -S "$SOCK10" ] && [ "$i" -lt 50 ]; do sleep 0.1; i=$((i+1)); done | |||
| 922 | set +e | 958 | set +e |
| 923 | { printf 'printf "flagwins-%%s\\n" ok\n'; sleep 2; printf '\034'; } | \ | 959 | { printf 'printf "flagwins-%%s\\n" ok\n'; sleep 2; printf '\034'; } | \ |
| 924 | timeout 30 "$MUX" "quic://127.0.0.1:$QPORT3" --key "$QKEY" \ | 960 | timeout 30 "$MUX" "quic://127.0.0.1:$QPORT3" --key "$QKEY" \ |
| 925 | --quic-idle-ms 15000 > "$OUT.env2" 2>&1 | 961 | --quic-idle-ms 15000 > "$OUT.env2" 2> "$OUT.env2.err" |
| 926 | RC=$? | 962 | RC=$? |
| 927 | set -e | 963 | set -e |
| 928 | [ "$RC" -eq 0 ] || { | 964 | [ "$RC" -eq 0 ] || { |
| 929 | echo "e2e FAIL: --key did not beat MUX_KEY_FILE: attach exited $RC (want 0)" | 965 | echo "e2e FAIL: --key did not beat MUX_KEY_FILE: attach exited $RC (want 0)" |
| 930 | cat "$OUT.env2"; exit 1; | 966 | cat "$OUT.env2" "$OUT.env2.err" 2>/dev/null; exit 1; |
| 931 | } | 967 | } |
| 932 | grep -q "flagwins-ok" "$OUT.env2" || { | 968 | grep -q "flagwins-ok" "$OUT.env2" || { |
| 933 | echo "e2e FAIL: --key-beats-env daemon served no session"; cat "$OUT.env2"; exit 1; } | 969 | echo "e2e FAIL: --key-beats-env daemon served no session" |
| 970 | cat "$OUT.env2" "$OUT.env2.err" 2>/dev/null; exit 1; } | ||
| 971 | assert_converged "$OUT.env2" "$SOCK10" "flag beats env" | ||
| 934 | kill "$D10PID" 2>/dev/null || true | 972 | kill "$D10PID" 2>/dev/null || true |
| 935 | D10PID="" | 973 | D10PID="" |
| 936 | echo "e2e OK: daemon honours MUX_KEY_FILE, and --key beats it" | 974 | echo "e2e OK: daemon honours MUX_KEY_FILE, and --key beats it" |
| 937 | 975 | ||
| 938 | rm -f "$OUT.q" "$OUT.qc" "$OUT.qr" "$OUT.qa" "$OUT.qk" "$QKEY" "$QKEY.bad" "$QKEY.wrong" | 976 | rm -f "$OUT.q" "$OUT.qc" "$OUT.qr" "$OUT.qa" "$OUT.qk" "$QKEY" "$QKEY.bad" "$QKEY.wrong" \ |
| 977 | "$OUT.qc.err" "$OUT.qr.err" "$OUT.qk.err" | ||
| 939 | 978 | ||
| 940 | # --- M10: muxd start — detached spawn, no-op rerun, race, pinned lines. | 979 | # --- M10: muxd start — detached spawn, no-op rerun, race, pinned lines. |
| 941 | # | 980 | # |
| @@ -962,9 +1001,11 @@ kill -0 "$SPID" || { echo "e2e FAIL: started daemon not alive"; exit 1; } | |||
| 962 | 1001 | ||
| 963 | # The daemon it started serves a session (marker in, marker in dump). | 1002 | # The daemon it started serves a session (marker in, marker in dump). |
| 964 | { printf 'printf "start-%%s\\n" works\n'; sleep 2; printf '\034'; } | \ | 1003 | { printf 'printf "start-%%s\\n" works\n'; sleep 2; printf '\034'; } | \ |
| 965 | "$MUX" --sock "$SOCK8" > "$OUT.s8" 2>&1 | 1004 | "$MUX" --sock "$SOCK8" > "$OUT.s8" 2> "$OUT.s8.err" |
| 966 | "$MUXD" dump --sock "$SOCK8" | grep -q "start-works" || { | 1005 | "$MUXD" dump --sock "$SOCK8" | grep -q "start-works" || { |
| 967 | echo "e2e FAIL: auto-started daemon lost the marker"; cat "$OUT.s8"; exit 1; } | 1006 | echo "e2e FAIL: auto-started daemon lost the marker" |
| 1007 | cat "$OUT.s8" "$OUT.s8.err" 2>/dev/null; exit 1; } | ||
| 1008 | assert_converged "$OUT.s8" "$SOCK8" "started daemon" | ||
| 968 | 1009 | ||
| 969 | # The log the failure path names really is there, and is no more readable | 1010 | # The log the failure path names really is there, and is no more readable |
| 970 | # than the key is: it carries whatever the daemon says about its own start. | 1011 | # than the key is: it carries whatever the daemon says about its own start. |
| @@ -1007,7 +1048,7 @@ set -e | |||
| 1007 | { [ "$RCA" = "0" ] && [ "$RCB" = "0" ]; } || { | 1048 | { [ "$RCA" = "0" ] && [ "$RCB" = "0" ]; } || { |
| 1008 | echo "e2e FAIL: race: exits $RCA/$RCB"; cat "$OUT.ra" "$OUT.rb"; exit 1; } | 1049 | echo "e2e FAIL: race: exits $RCA/$RCB"; cat "$OUT.ra" "$OUT.rb"; exit 1; } |
| 1009 | { printf 'printf "race-%%s\\n" one\n'; sleep 2; printf '\034'; } | \ | 1050 | { printf 'printf "race-%%s\\n" one\n'; sleep 2; printf '\034'; } | \ |
| 1010 | "$MUX" --sock "$SOCK8" > /dev/null 2>&1 | 1051 | "$MUX" --sock "$SOCK8" > "$OUT.race" 2> /dev/null |
| 1011 | "$MUXD" dump --sock "$SOCK8" | grep -q "race-one" || { | 1052 | "$MUXD" dump --sock "$SOCK8" | grep -q "race-one" || { |
| 1012 | echo "e2e FAIL: race: session unusable"; exit 1; } | 1053 | echo "e2e FAIL: race: session unusable"; exit 1; } |
| 1013 | SPID=$(cat "$OUT.ra" "$OUT.rb" | sed -n 's/.* pid=\([0-9]*\).*/\1/p' | while read -r p; do | 1054 | SPID=$(cat "$OUT.ra" "$OUT.rb" | sed -n 's/.* pid=\([0-9]*\).*/\1/p' | while read -r p; do |
| @@ -1030,6 +1071,9 @@ grep -q "already running" "$OUT.rb" || { | |||
| 1030 | # ...and the session the winner was serving is undisturbed. | 1071 | # ...and the session the winner was serving is undisturbed. |
| 1031 | "$MUXD" dump --sock "$SOCK8" | grep -q "race-one" || { | 1072 | "$MUXD" dump --sock "$SOCK8" | grep -q "race-one" || { |
| 1032 | echo "e2e FAIL: the refused daemon disturbed the live session"; exit 1; } | 1073 | echo "e2e FAIL: the refused daemon disturbed the live session"; exit 1; } |
| 1074 | # The race client's own capture, checked here rather than above: the refused | ||
| 1075 | # `muxd run` between the two touches no session, so the grid has not moved. | ||
| 1076 | assert_converged "$OUT.race" "$SOCK8" "start race" | ||
| 1033 | 1077 | ||
| 1034 | kill "$SPID" 2>/dev/null || true | 1078 | kill "$SPID" 2>/dev/null || true |
| 1035 | SPID="" | 1079 | SPID="" |
| @@ -1041,6 +1085,7 @@ echo "e2e OK: muxd start — spawn, no-op rerun, log truncation, race" | |||
| 1041 | # child exits on the missing key, the poll loop reaps it, and polling | 1085 | # child exits on the missing key, the poll loop reaps it, and polling |
| 1042 | # continues to the deadline; a second waitpid there gets ECHILD, which the | 1086 | # continues to the deadline; a second waitpid there gets ECHILD, which the |
| 1043 | # stdlib answers with `unreachable`, i.e. exit 134 and a stack trace. | 1087 | # stdlib answers with `unreachable`, i.e. exit 134 and a stack trace. |
| 1088 | # No convergence: the daemon under test is the one that died, so nothing to dump. | ||
| 1044 | DEADCFG="${TMPDIR:-/tmp}/mux-e2e-deadchild-$$" | 1089 | DEADCFG="${TMPDIR:-/tmp}/mux-e2e-deadchild-$$" |
| 1045 | set +e | 1090 | set +e |
| 1046 | env XDG_CONFIG_HOME="$DEADCFG" timeout 30 "$MUXD" start --sock "$SOCK8T" \ | 1091 | env XDG_CONFIG_HOME="$DEADCFG" timeout 30 "$MUXD" start --sock "$SOCK8T" \ |
| @@ -1075,10 +1120,11 @@ echo "e2e OK: a start whose daemon dies young says so, with the log path" | |||
| 1075 | GPID=$(sed -n 's/.* pid=\([0-9]*\).*/\1/p' "$OUT.goal") | 1120 | GPID=$(sed -n 's/.* pid=\([0-9]*\).*/\1/p' "$OUT.goal") |
| 1076 | [ -n "$GPID" ] || { echo "e2e FAIL: goal start reported no pid"; cat "$OUT.goal"; exit 1; } | 1121 | [ -n "$GPID" ] || { echo "e2e FAIL: goal start reported no pid"; cat "$OUT.goal"; exit 1; } |
| 1077 | { printf 'printf "goal-%%s\\n" quic\n'; sleep 2; printf '\034'; } | \ | 1122 | { printf 'printf "goal-%%s\\n" quic\n'; sleep 2; printf '\034'; } | \ |
| 1078 | timeout 30 "$MUX" "quic://127.0.0.1:$QPORT4" > "$OUT.g9" 2>&1 | 1123 | timeout 30 "$MUX" "quic://127.0.0.1:$QPORT4" > "$OUT.g9" 2> "$OUT.g9.err" |
| 1079 | "$MUXD" dump --sock "$SOCK11" | grep -q "goal-quic" || { | 1124 | "$MUXD" dump --sock "$SOCK11" | grep -q "goal-quic" || { |
| 1080 | echo "e2e FAIL: no-key-flag QUIC attach did not reach the session" | 1125 | echo "e2e FAIL: no-key-flag QUIC attach did not reach the session" |
| 1081 | cat "$OUT.g9"; exit 1; } | 1126 | cat "$OUT.g9" "$OUT.g9.err" 2>/dev/null; exit 1; } |
| 1127 | assert_converged "$OUT.g9" "$SOCK11" "goal commands" | ||
| 1082 | kill "$GPID" 2>/dev/null || true | 1128 | kill "$GPID" 2>/dev/null || true |
| 1083 | GPID="" | 1129 | GPID="" |
| 1084 | echo "e2e OK: keygen + start --quic + mux quic:// with no --key anywhere" | 1130 | echo "e2e OK: keygen + start --quic + mux quic:// with no --key anywhere" |