fix(xce-test-e2e-primitives): shell-style -e/+e errexit with mode-aware default — interactive REPL no longer exits on a bad command
This commit is contained in:
parent
cdc6e2377a
commit
eda1f4c9d3
2 changed files with 33 additions and 11 deletions
|
|
@ -89,6 +89,7 @@
|
|||
- Build dir is `src/xce.build` (not `src/build`). Fast incremental build: `cd src && nix develop --command bash -c "meson compile -C xce.build xce_test"`. Binary: `src/xce.build/controller/xce_test`. Behavioral tests = piped scripts gating on `$?` with `ASAN_OPTIONS=detect_leaks=0`; no controller needed (an unknown command is a convenient failing command). `xce_test` is NOT a meson-registered `test()`, so `meson test --suite controller` does not exercise it.
|
||||
- Pre-existing unrelated failure: `meson test --suite controller` reports `test_demotion_replay_yields_committed_only` FAIL (in `controller_unit_test` / `test_commit_barrier_reply.c`) — from the in-flight `ha-commit-durability` feature, in a binary that does NOT compile `test.c`. Not caused by this spec; ignore it for verdicts here.
|
||||
- 1.1: `preprocess_and_run()` is the single chokepoint where the leading-`!` shell-escape (2.1) and `$`-autovar substitution (3.1) plug in. File-scope `g_session_failed`/`g_continue_on_error`/`g_last_status` added; `main` returns `g_session_failed ? 1 : 0`; `set continue-on-error` is a process-global argc==2 special-case in `cmd_set` (not a per-context `settings[]` entry).
|
||||
- 1.1 ERREXIT MODEL (shell `-e`/`+e`, fixes the interactive UX regression where a typo exited the REPL): abort-on-first-failure is now a single flag `g_continue_on_error` with a MODE-AWARE DEFAULT set in main from `g_interactive = isatty(STDIN_FILENO)` — batch/piped → `-e` (abort + non-zero exit verdict; default `g_continue_on_error=0`), interactive TTY → `+e` (failed command prints its error, loop CONTINUES; default `g_continue_on_error=1`). The 3 abort sites in `preprocess_and_run` key ONLY on `!g_continue_on_error`. `cmd_set` accepts `set -e` (errexit on), `set +e` (errexit off), and `set continue-on-error` (== `+e`, back-compat) to override either default mid-session. Verified across all 6 combos (batch/interactive × default/-e/+e) via piped runs + a pty harness (checking the echo OUTPUT line, not the TTY's input echo).
|
||||
- Feature Flag Protocol: the flag must be fully REMOVED at the end (no pinned `#define X (1)` guarding a dead `#else`). Reviewer rejects leftover flag scaffolding as dead code. (3.1 hit this — re-mediated.)
|
||||
- 3.1: introduced `g_last_jobid[XCE_MAXNAME_LEN]={0}` (empty = unset) and `g_last_jobcount=-1` (-1 = unset) file-scope state, POPULATED later by 4.1 (jobsub) / 5.1 (joblist). `substitute_autovars(line,out,sz)` does whole-token replace of `$?`/`$jobid`/`$jobcount`; unset `$jobid`/`$jobcount` → command fails (`autovar: $X referenced before set`), no garbage. `!` shell-escape lines are NOT substituted (raw to shell — the shell owns its `$`). Added `#include "config.h"` for XCE_MAXNAME_LEN.
|
||||
- 4.1 (bug-786 fixed): `cmd_jobsub` inits local `jobid=0`, prints `jobsubmit ok` + captures `g_last_jobid` ONLY after BOTH gates: `rc>=0` AND `jobid!=0`; else typed error (`E_XCE_NOMASTER/REDIRECT/NOT_READY`/timeout switch via `xce_strerror_r`) + `return <0`. **`$jobid` capture uses `fmt_jobid(g_last_jobid, ..., jobid)` — the FULL jobid, byte-identical to how `cmd_joblist` formats `jobs[i]->jobid`** (NOT `array_jobid()`, which the display-only `ok:` line uses). This is REQUIRED so task 5.3 `expect-job $jobid` matches the joblist column for array jobs. Also moved the error check AFTER the FREEUP block → fixed a pre-existing error-path leak. Live success-path ($jobid==joblist entry) verified by code reading; runtime-verified via soak (7.2).
|
||||
|
|
|
|||
|
|
@ -137,6 +137,11 @@ static int worker_house_keeping(void*);
|
|||
static int g_session_failed = 0;
|
||||
static int g_continue_on_error = 0;
|
||||
static int g_last_status = 0;
|
||||
/* Interactive (TTY) sessions are a REPL: a failed command prints its error and
|
||||
* the loop CONTINUES — never abort the read loop on a typo. The abort-on-first-
|
||||
* failure + non-zero-exit verdict is a SCRIPTED/piped-mode concept only. Set
|
||||
* from isatty(stdin) in main. */
|
||||
static int g_interactive = 0;
|
||||
|
||||
/* Auto-variable state for the fixed `$jobid` / `$jobcount` substitution
|
||||
* (xce-test-e2e-primitives task 3.1, Req 3.1/3.3/3.5/3.6).
|
||||
|
|
@ -582,8 +587,8 @@ int preprocess_and_run(char* line)
|
|||
|
||||
if (status != 0) {
|
||||
g_session_failed = 1;
|
||||
if (!g_continue_on_error) {
|
||||
return 1; /* abort: stop the read loop */
|
||||
if (!g_continue_on_error && !g_interactive) {
|
||||
return 1; /* abort: stop the read loop (scripted mode only) */
|
||||
}
|
||||
} else {
|
||||
add_history(line);
|
||||
|
|
@ -606,8 +611,8 @@ int preprocess_and_run(char* line)
|
|||
if (subrc < 0) {
|
||||
g_last_status = subrc;
|
||||
g_session_failed = 1;
|
||||
if (!g_continue_on_error) {
|
||||
return 1; /* abort: stop the read loop */
|
||||
if (!g_continue_on_error && !g_interactive) {
|
||||
return 1; /* abort: stop the read loop (scripted mode only) */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -621,7 +626,7 @@ int preprocess_and_run(char* line)
|
|||
SET_ERROR(-rc);
|
||||
g_session_failed = 1;
|
||||
if (!g_continue_on_error) {
|
||||
return 1; /* abort: stop the read loop */
|
||||
return 1; /* errexit (-e): stop the read loop on first failure */
|
||||
}
|
||||
} else {
|
||||
add_history(line);
|
||||
|
|
@ -2534,12 +2539,19 @@ int cmd_set(int argc, char* argv[])
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Session verdict toggle (xce-test-e2e-primitives task 1.1):
|
||||
* `set continue-on-error` keeps the read loop running after a failing
|
||||
* command (the session still exits non-zero). It is not a per-context
|
||||
* setting, so it is handled here ahead of the key/value path. */
|
||||
if ((argc == 2) && (strcmp(argv[1], "continue-on-error") == 0)) {
|
||||
g_continue_on_error = 1;
|
||||
/* errexit toggle (xce-test-e2e-primitives task 1.1), shell `-e`/`+e` style.
|
||||
* Default is mode-dependent (batch `-e` abort-on-failure, interactive `+e`
|
||||
* continue); these override it for the rest of the session. The session
|
||||
* still exits non-zero if any command failed. Handled here ahead of the
|
||||
* per-context key/value path. `continue-on-error` == `+e` (back-compat). */
|
||||
if ((argc == 2) &&
|
||||
((strcmp(argv[1], "+e") == 0) ||
|
||||
(strcmp(argv[1], "continue-on-error") == 0))) {
|
||||
g_continue_on_error = 1; /* +e: keep the read loop running on failure */
|
||||
return 0;
|
||||
}
|
||||
if ((argc == 2) && (strcmp(argv[1], "-e") == 0)) {
|
||||
g_continue_on_error = 0; /* -e: abort the read loop on first failure */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -3255,6 +3267,15 @@ int main(int argc, char* argv[])
|
|||
|
||||
idgen_ctx_init(&idctx);
|
||||
|
||||
/* errexit default, modelled on shell `-e`/`+e`, keyed on the stdin mode:
|
||||
* batch (piped/redirected stdin) → `-e`: abort the read loop on the first
|
||||
* failing command and exit non-zero (a script wants fail-fast + verdict).
|
||||
* interactive (TTY) REPL → `+e`: a failed command prints its error and the
|
||||
* loop CONTINUES (never exit on a typo).
|
||||
* Override at any time with `set -e` / `set +e` (or `set continue-on-error`). */
|
||||
g_interactive = isatty(STDIN_FILENO);
|
||||
g_continue_on_error = g_interactive ? 1 : 0;
|
||||
|
||||
while (!done) {
|
||||
char* s;
|
||||
int rc;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue