commit 36ff1dbc2ebdbcd1ec00a70eeb4a3f5382e195cf from: isaac date: Sat Jun 27 00:54:26 2026 UTC regress: cut test timeouts 60/90s -> 15s, document binary-flavor gotcha The whole suite completes in ~1s, so a 90s alarm backstop is absurdly long -- a genuine hang now fails in 15s (10s for the chat/compact stub self-alarm), still a 15x margin over normal runtime. Applied across all eight suites that arm an alarm (chat, compact, exec, fetch, imsg, net, sandbox, tui). Also adds .scratch/regress-debug-binary-gotcha documenting why chat/compact/tui hang/fail when src/ was last built FUGU_DEBUG=1 (they exec the in-tree binaries, which die under pledge), plus the stale top-level vs obj/ binary trap. commit - ae9e12808009767d79769218f0969b7fbe8ccb4d commit + 36ff1dbc2ebdbcd1ec00a70eeb4a3f5382e195cf blob - /dev/null blob + 36a0c1924b643d52e6fd71f48517ed5417617064 (mode 644) --- /dev/null +++ .scratch/regress-debug-binary-gotcha/README.md @@ -0,0 +1,62 @@ +# Regress gotcha: in-tree binary flavor breaks chat/compact/tui + +Status: note (known constraint; possible fix below) +Origin: test-hang diagnosis 2026-06-26. + +## The trap + +Most regress suites are self-contained: they compile the source they exercise +into their own `regress//obj/` (e.g. `regress/net` builds its own copy of +`net_run.c`). Those are immune to whatever flavor `src/` was last built in. + +**Three suites are different** — `chat`, `compact`, and `tui` launch the real +fugu end to end by exec'ing the **in-tree `src/` binaries** via `FUGU_BIN` / +`FUGU_*_BIN` (set in their Makefiles to `../../src/parent/fugu`, etc.). They run +whatever is sitting in `src/` right now. + +So if you do: + +``` +cd src && make FUGU_DEBUG=1 # UBSan-trap + Fortify build +cd ../regress && make regress +``` + +…`chat`/`compact`/`tui` exec the **FUGU_DEBUG** workers. That instrumentation +does not survive fugu's own `pledge(2)`/`unveil(2)` sandbox at runtime — the +worker dies — so the end-to-end exchange never produces `pong`: + +- `chat` / `compact` **hang** (the stub server blocks waiting for a connection + that never comes), and +- `tui` **fails** deterministically (no `pong`, cascading check failures). + +It looks like a broken test; it's actually just the wrong binary flavor. The fix +is `cd src && make` (default) before running these three. + +## Sibling trap: top-level vs obj/ binary + +`make regress` runs the binary built in `regress//obj/`. A stale real +binary can also sit at `regress//_test` (a leftover, not a +symlink). Running `./chat_test` **by hand** from the suite dir executes that +stale top-level copy, NOT the freshly built `obj/` one. Always go through +`make regress`, or run `./obj/_test` explicitly. + +## Possible fix (if a true FUGU_DEBUG regress is ever wanted) + +A `FUGU_DEBUG` regress run is meaningful for the self-contained suites (they +recompile under the debug flags) but not for the three end-to-end ones. Options: + +1. Have the chat/compact/tui Makefiles point `FUGU_*_BIN` at a **separately + built default** copy of the workers (e.g. a `obj-default/` staging dir), + independent of how `src/` was last built. +2. Or skip those three under `FUGU_DEBUG` with a clear `SKIP` line (no silent + omission), since the sandbox + sanitizer combination is fundamentally + incompatible on OpenBSD (no ASan runtime; UBSan-trap/Fortify violate pledge). + +Until then: build `src/` default before `make regress`, full stop. + +## Related + +- Timeouts were shortened from 60/90s to 15s (10s for the stub self-alarm) on + 2026-06-26 — a 1-second suite shouldn't take 90s to notice a hang. +- The orphan-on-timeout bug (the stub being left to block forever) was fixed the + same day; see the `regress: tear down child processes on timeout` commit. blob - 9eed4da39c55a42fc7a9ac987a817224725ab712 blob + bbb1b2441cc7befb644938e371a4107acadf9706 --- regress/chat/chat_test.c +++ regress/chat/chat_test.c @@ -230,7 +230,7 @@ main(int argc, char *argv[]) signal(SIGALRM, on_alarm); signal(SIGPIPE, SIG_IGN); - alarm(90); + alarm(15); if (mkdtemp(home) == NULL) err(1, "mkdtemp"); @@ -257,7 +257,7 @@ main(int argc, char *argv[]) err(1, "fork"); if (sp == 0) { signal(SIGALRM, SIG_DFL); /* self-terminate if we block */ - alarm(60); + alarm(10); stub_server(lfd, argv[1], argv[2]); _exit(0); } blob - a63a5886ae0201616fa6f418980de563dc589aa8 blob + f70707523fac09730a6bd801e82df801d089406d --- regress/compact/compact_test.c +++ regress/compact/compact_test.c @@ -294,7 +294,7 @@ main(int argc, char *argv[]) signal(SIGALRM, on_alarm); signal(SIGPIPE, SIG_IGN); - alarm(90); + alarm(15); if (mkdtemp(home) == NULL) err(1, "mkdtemp"); @@ -320,7 +320,7 @@ main(int argc, char *argv[]) err(1, "fork"); if (sp == 0) { signal(SIGALRM, SIG_DFL); /* self-terminate if we block */ - alarm(60); + alarm(10); stub_server(lfd, argv[1], argv[2]); _exit(0); } blob - 38318ee15b719f0604141db433fb779e7624cf2e blob + 36dbd67c22800a99574b48308a5e3a900c727aa1 --- regress/exec/exec_test.c +++ regress/exec/exec_test.c @@ -169,7 +169,7 @@ main(void) signal(SIGALRM, on_alarm); signal(SIGPIPE, SIG_IGN); - alarm(60); + alarm(15); if (mkdtemp(td) == NULL) err(1, "mkdtemp"); blob - 0b2c75196d2c709c04b2afb5008760e3f14a33b3 blob + 9606f81a7b7fa236c22ff3d3f29fd1a7db2d2f20 --- regress/fetch/fetch_test.c +++ regress/fetch/fetch_test.c @@ -322,7 +322,7 @@ main(int argc, char *argv[]) signal(SIGALRM, on_alarm); signal(SIGPIPE, SIG_IGN); - alarm(90); + alarm(15); if ((lfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) err(1, "socket"); blob - 2cf997ad5d164b10e68555279af35dff40fa3c6d blob + 0056cbb178f98a7e5a9b2d9407d2356dce2ead7b --- regress/imsg/imsg_test.c +++ regress/imsg/imsg_test.c @@ -407,7 +407,7 @@ main(void) int sv[2], status; signal(SIGALRM, on_alarm); - alarm(60); /* a deadlock should fail the test, not hang CI */ + alarm(15); /* a deadlock should fail the test, not hang CI */ if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1) err(1, "socketpair"); blob - 57a5ea561045f022b319f604b5a8c3003093ab0d blob + 7422807b4f55b4ebfe530e85639166ad0ef52207 --- regress/net/net_test.c +++ regress/net/net_test.c @@ -678,7 +678,7 @@ main(int argc, char *argv[]) signal(SIGALRM, on_alarm); signal(SIGPIPE, SIG_IGN); - alarm(60); + alarm(15); /* tools_schema(): a valid JSON array, with the web tools gated by the flag. */ { blob - 64a161c1377c83da3e0f36be75926f1d7cb1f3d7 blob + a23c802cc82f21a010d4f1a0231466afa7b9afc7 --- regress/sandbox/sandbox_test.c +++ regress/sandbox/sandbox_test.c @@ -187,7 +187,7 @@ main(void) signal(SIGALRM, on_alarm); signal(SIGPIPE, SIG_IGN); - alarm(60); + alarm(15); if (mkdtemp(td) == NULL) err(1, "mkdtemp"); blob - 562649126dc2a099692e5987f8c1c5b1facd22f8 blob + 6afae8114be3161e21fe79fb54bb65fa9e53c337 --- regress/tui/tui_test.c +++ regress/tui/tui_test.c @@ -254,7 +254,7 @@ main(int argc, char *argv[]) signal(SIGALRM, on_alarm); signal(SIGPIPE, SIG_IGN); - alarm(90); + alarm(15); if (mkdtemp(home) == NULL) err(1, "mkdtemp");