test(xce-test-cli-ux): verify help/completion/context-switching hold together (task 4.4)
Add one black-box regression test proving process-level help, in-session help content, and context-scoped tab completion work correctly when exercised together in one continuous session (context switches + completion + help lookups), not just individually as each was already covered by tasks 1.6/2.3/3.5. This closes the final task of the xce-test-cli-ux implementation plan (23/23). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019jaMf9QEwpugH2W9KAuc7f
This commit is contained in:
parent
31d6606067
commit
38595099b9
2 changed files with 366 additions and 1 deletions
|
|
@ -172,11 +172,12 @@
|
|||
- _Depends: 2.4, 3.3, 1.7_
|
||||
- _Done (requirement 13.7 MET): run twice independently (implementer + reviewer), each against separately-deployed live single-node controllers, sourcing a script file containing a destructive `qctrl -i` line from an otherwise-interactive session. With the confirmation toggle at its default, the prompt appeared on stderr with wording byte-identical to a directly-typed invocation of the same command, and decline reported "qctrl: not run (declined)" -- proving the gate fires for a sourced-file line exactly as it would for a typed one. After `set -y` (issued at the top-level context), re-sourcing the same file produced zero prompt text, and `set +y` afterward restored prompting for a directly-typed invocation, confirming no lingering session-state confusion. The reviewer additionally confirmed this structurally, not just behaviorally: `confirm_destructive()`'s only gating checks are `confirm_is_skip_set()` and `!xcetest_is_interactive()` (a read-only accessor over `g_interactive`, set once at startup from `isatty(STDIN_FILENO)` and never reassigned), and the only per-source-invocation state that exists (`g_source_depth`, used solely for cycle detection) has zero references anywhere near the gate -- there is no code path capable of distinguishing a sourced line from a typed one for this decision. Also verified the bonus interaction flagged by task 3.3's Implementation Note: with `set -e` active, a sourced file's declined destructive line does NOT trip abort-on-failure (a subsequent `echo` line in the same file still ran), confirming decline's `return 0` per `confirm.h`'s contract. No defect found. The already-logged bug-1797 (`cmd_qctrl`'s RPC-error-path queue-name leak) recurred via the `source` call path in both runs -- confirmed to be the identical pre-existing leak, not a new one; occurrence count bumped in buglog.json. No source files were changed; both deploys were fully cleaned up._
|
||||
|
||||
- [ ] 4.4 Verify process-level help, tab completion, and consolidated help content end-to-end
|
||||
- [x] 4.4 Verify process-level help, tab completion, and consolidated help content end-to-end
|
||||
- Confirm the help/version flags and in-session help behavior from 1.6 and 3.5, and the tab-completion behavior from 2.3, hold together in the full built client, not just in isolation
|
||||
- Observable completion: a black-box run confirms the help flag's output describes the in-session help command, a representative command's help output includes its generated flag block and hand-authored example, and tab completion in two different contexts offers the expected candidates
|
||||
- _Requirements: 4.1, 4.2, 4.3, 7.1, 7.2, 7.3, 7.4, 8.1, 8.2, 8.3, 8.4, 8.5, 8.6, 8.7, 11.2_
|
||||
- _Depends: 1.6, 2.3, 3.5, 1.7_
|
||||
- _Done (requirements 4.1-4.3, 7.1-7.4, 8.1-8.7, 11.2 MET): no live cluster needed (purely client-side). Existing per-mechanism coverage from tasks 1.6/2.3/3.5 was confirmed solid, but no test chained context-switching + tab completion + help lookup together in one continuous session -- a genuine "holds together, not just in isolation" gap. Added one new black-box test, `test_help_completion_and_context_switching_hold_together_in_one_session` (test_test_cli.c), covering: top-level vs. client-context double-Tab on the identical "w" prefix producing different, context-correct candidate sets; an unambiguous Tab-completion executed as a real (safely no-op, pre-RPC-validated) command; a mid-session `help jobkill` showing its purpose description, generated flag block, and hand-authored example; a further switch to worker context; and a `help jobkill` lookup there correctly reported not-found with the corrected grammar, leaking no client-context text. Both implementer and reviewer independently reproduced all three observable-completion criteria via their own standalone driver runs against the real binary, and the reviewer additionally cross-checked the completion candidate sets directly against test.c's four command tables. No defect found; no product code changed, only the one new permanent test (1331/1331 tests, 0 ASAN diagnostics)._
|
||||
|
||||
## Implementation Notes
|
||||
- Audited all 139 `printf(` call sites in test.c; 33 were genuine diagnostic/warning/failure-path messages misrouted to stdout (not covered by the already-correct `flagspec_reject`/`perror` mechanisms) and were converted to `fprintf(stderr, ...)` — concentrated in `cmd_expect_jobs`/`cmd_expect_job`/`cmd_wait_primary` (entirely unrouted; these predate the flagspec migration since they take positional args, not flags), plus `get_setting`, `cmd_help`'s not-found path (a shared `FILE* out` var, stderr only when not-found; bare `help`'s listing and a successful lookup stay stdout), `cmd_ctrlstatus`, `cmd_jobrun`, `cmd_jobsub`, `cmd_jobstart`/`jobfinish`/`jobfail`, `resupdate_report_one`/`cmd_resupdate`, `cmd_set`. Deliberately left as stdout (Requirement 3 doesn't apply): `cmd_resval`'s intentional always-succeeds "not found" design, and the entire async worker/scheduler RPC-handler subsystem (`handle_notify`/`run_job`/etc.) — these are inbound-request callbacks dispatched outside the REPL's `cmd_*` table, not "a command's own diagnostic or result data." Found but explicitly left alone as pre-existing and out of THIS task's scope (stream routing, not return-code correctness): `cmd_resupdate` still unconditionally `return 0`s even on an outright RPC failure or a per-resource rejection — worth a follow-up bug entry if this area is touched again. Requirement 3.4 (redirection-invariance) holds by construction: no code branches a stream CHOICE on `isatty`/`g_interactive` anywhere in the file. (task 3.6)
|
||||
|
|
|
|||
|
|
@ -2852,6 +2852,368 @@ test_help_context_scoped_not_found_no_cross_context_hint(void)
|
|||
clitest_buf_free(&err);
|
||||
}
|
||||
|
||||
/*
|
||||
* test_help_completion_and_context_switching_hold_together_in_one_session --
|
||||
* xce-test-cli-ux task 4.4, Requirements 4.1-4.3, 7.1-7.4, 8.1-8.7, 11.2.
|
||||
*
|
||||
* Every mechanism this test touches (process-level help/version, in-session
|
||||
* `help <cmd>`, tab completion) already has dedicated per-mechanism coverage
|
||||
* elsewhere in this file (and, for process-level flags, in
|
||||
* test_xcetest_process_entry.c from task 1.6). What none of those tests do
|
||||
* is run several of these mechanisms back-to-back in ONE live session,
|
||||
* interleaved with real context switches -- exactly task 4.4's own
|
||||
* "hold together ... not just in isolation" framing. This test is that
|
||||
* missing combined/integration run:
|
||||
*
|
||||
* 1. Top-level, ambiguous "w" (ambiguous across worker/waldump/
|
||||
* walcompare) is double-Tab listed; the candidate set is confirmed to
|
||||
* include "worker" (a top-level-only command, not present at all in
|
||||
* the client context) and to EXCLUDE "workerlist"/"wait-primary"
|
||||
* (client-only).
|
||||
* 2. Top-level, unambiguous "cl" -> Tab -> Enter completes to "client" in
|
||||
* full and switches context (Requirement 7.1/7.2).
|
||||
* 3. Client context, ambiguous "w" is double-Tab listed; the candidate
|
||||
* set now includes "workerlist"/"waldump"/"walcompare"/"wait-primary"
|
||||
* -- a DIFFERENT set than step 1's, for the IDENTICAL typed prefix, in
|
||||
* the SAME running process (Requirement 7.1/7.4).
|
||||
* 4. Client context, unambiguous "jobk" -> Tab -> Enter completes to
|
||||
* "jobkill" and actually executes it bare (no live controller needed:
|
||||
* cmd_jobkill's own missing-jobid validation returns before ever
|
||||
* reaching the RPC/confirmation-gate code, exactly like task 2.3's
|
||||
* "jstart" proof step).
|
||||
* 5. Still in the client context -- and only NOW, after two context
|
||||
* switches and two tab-completion demonstrations -- `help jobkill` is
|
||||
* looked up. Its rendered text is confirmed to include the
|
||||
* hand-authored purpose description (8.1), the flagspec_render_help()
|
||||
* -generated "-y, --yes" flag line (8.2), the hand-authored example
|
||||
* invocation (8.3), and the confirmation-gate/`set -y` scripting note
|
||||
* (8.4) -- proving prior session state (context switches, completion)
|
||||
* did not corrupt this content.
|
||||
* 6. `..` then `worker` switches to a THIRD context (a context this
|
||||
* session has not yet visited).
|
||||
* 7. Worker context, ambiguous "j" (jstart/jfinish/jfail) is double-Tab
|
||||
* listed; the candidate set is confirmed to include jstart/jfinish/
|
||||
* jfail and to EXCLUDE every client-only "job*" command (jobkill,
|
||||
* jobrun, jobsub, jobinfo, jobckpt) -- a second, independent
|
||||
* different-contexts-different-candidates proof (worker vs. client).
|
||||
* 8. `help jobkill` in the worker context -- a name valid in the OLD
|
||||
* (client) context but not this one -- is confirmed reported as not
|
||||
* found, with the corrected grammar (11.2: "match", not "matchs"),
|
||||
* never leaking client context's jobkill text into the not-found
|
||||
* response.
|
||||
*
|
||||
* Buffers are freed and reinitialized between stages (the same idiom
|
||||
* test_help_context_scoped_not_found_no_cross_context_hint above already
|
||||
* uses across its own context switch) so each stage's assertions search
|
||||
* only that stage's own freshly captured output, never a stale match left
|
||||
* over from an earlier stage's text.
|
||||
*
|
||||
* Requirement 4.1-4.3 (process-level -h/--help/--version describing the
|
||||
* in-session `help` command) is deliberately NOT re-exercised here: the
|
||||
* task's own guidance treats a process-level flag invocation as inherently
|
||||
* a separate process from an interactive session, and
|
||||
* test_xcetest_process_entry.c (task 1.6) already covers it end-to-end
|
||||
* against the real binary. This task's implementer report quotes that
|
||||
* output directly instead of duplicating the check here.
|
||||
*/
|
||||
static void
|
||||
test_help_completion_and_context_switching_hold_together_in_one_session(void)
|
||||
{
|
||||
char xce_test_path[PATH_MAX];
|
||||
char* const argv[] = { "xce_test", "--norc", NULL };
|
||||
clitest_session_t sess;
|
||||
clitest_buf_t out;
|
||||
clitest_buf_t err;
|
||||
int exit_code = -999;
|
||||
const char ctrl_u = '\x15'; /* unix-line-discard */
|
||||
const char enter = '\n';
|
||||
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(
|
||||
0, resolve_xce_test_path(xce_test_path, sizeof(xce_test_path)),
|
||||
"must be able to resolve the sibling xce_test binary's path");
|
||||
|
||||
clitest_buf_init(&out);
|
||||
clitest_buf_init(&err);
|
||||
|
||||
/* disable_echo=false for the WHOLE session: it needs libedit's live
|
||||
* completion feedback (steps 1, 3, 7) as well as ordinary command
|
||||
* output (steps 2, 4, 5, 6, 8) -- task 2.3's own header comment
|
||||
* confirms full-line sends behave identically either way, and its own
|
||||
* combined tab-completion test already mixes both under
|
||||
* disable_echo=false throughout. */
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(
|
||||
0, clitest_spawn_ex(&sess, xce_test_path, argv, false),
|
||||
"clitest_spawn_ex must launch xce_test behind a real pty with "
|
||||
"kernel ECHO left on, so libedit's own tab-completion feedback is "
|
||||
"observable");
|
||||
|
||||
TEST_ASSERT_TRUE_MESSAGE(
|
||||
clitest_wait_for_text(&sess, &out, &err, "xce>", false, 5000),
|
||||
"expected the initial 'xce>' interactive prompt on stdout");
|
||||
|
||||
/* --- Step 1: top-level, ambiguous "w" -> {worker, waldump,
|
||||
* walcompare}. "worker" exists ONLY at top level (client has
|
||||
* "workerlist" instead), so its presence here plus its absence in step
|
||||
* 3's client-context listing is the clearest possible proof that
|
||||
* candidates are context-scoped for the SAME typed prefix. */
|
||||
clitest_buf_free(&out);
|
||||
clitest_buf_free(&err);
|
||||
clitest_buf_init(&out);
|
||||
clitest_buf_init(&err);
|
||||
TEST_ASSERT_EQUAL_INT(0, clitest_send_partial(&sess, "w"));
|
||||
TEST_ASSERT_EQUAL_INT(0, clitest_send_tab(&sess));
|
||||
TEST_ASSERT_EQUAL_INT(0, clitest_send_tab(&sess));
|
||||
TEST_ASSERT_TRUE_MESSAGE(
|
||||
clitest_wait_for_text(&sess, &out, &err, "walcompare", false, 3000),
|
||||
"top-level ambiguous 'w' must list 'walcompare' among the "
|
||||
"candidates");
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(
|
||||
strstr(out.data, "worker"),
|
||||
"top-level ambiguous 'w' must list 'worker' among the candidates "
|
||||
"(Requirement 7.1/7.3)");
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(
|
||||
strstr(out.data, "waldump"),
|
||||
"top-level ambiguous 'w' must list 'waldump' among the "
|
||||
"candidates");
|
||||
TEST_ASSERT_NULL_MESSAGE(
|
||||
strstr(out.data, "workerlist"),
|
||||
"top-level ambiguous 'w' must NOT offer 'workerlist' -- that "
|
||||
"command exists only in the client context (Requirement 7.4)");
|
||||
TEST_ASSERT_NULL_MESSAGE(
|
||||
strstr(out.data, "wait-primary"),
|
||||
"top-level ambiguous 'w' must NOT offer 'wait-primary' -- that "
|
||||
"command exists only in the client context (Requirement 7.4)");
|
||||
|
||||
/* Clear the unaltered "w" input line (Requirement 7.3) and confirm the
|
||||
* session is still usable by switching context next. */
|
||||
TEST_ASSERT_EQUAL_INT(0, clitest_send_raw(&sess, &ctrl_u, 1));
|
||||
|
||||
/* --- Step 2: top-level, unambiguous "cl" -> Tab -> Enter completes in
|
||||
* full to "client" and switches context (Requirement 7.1/7.2). */
|
||||
clitest_buf_free(&out);
|
||||
clitest_buf_free(&err);
|
||||
clitest_buf_init(&out);
|
||||
clitest_buf_init(&err);
|
||||
TEST_ASSERT_EQUAL_INT(0, clitest_send_partial(&sess, "cl"));
|
||||
TEST_ASSERT_EQUAL_INT(0, clitest_send_tab(&sess));
|
||||
TEST_ASSERT_EQUAL_INT(0, clitest_send_raw(&sess, &enter, 1));
|
||||
TEST_ASSERT_TRUE_MESSAGE(
|
||||
clitest_wait_for_text(&sess, &out, &err, "xce/client>", false, 5000),
|
||||
"single unambiguous match 'cl' must complete in full to 'client' "
|
||||
"and switch context (Requirement 7.1/7.2)");
|
||||
|
||||
/* --- Step 3: client context, ambiguous "w" -> {workerlist, waldump,
|
||||
* walcompare, wait-primary} -- a DIFFERENT candidate set than step 1's,
|
||||
* for the IDENTICAL typed prefix, in the SAME running process. */
|
||||
clitest_buf_free(&out);
|
||||
clitest_buf_free(&err);
|
||||
clitest_buf_init(&out);
|
||||
clitest_buf_init(&err);
|
||||
TEST_ASSERT_EQUAL_INT(0, clitest_send_partial(&sess, "w"));
|
||||
TEST_ASSERT_EQUAL_INT(0, clitest_send_tab(&sess));
|
||||
TEST_ASSERT_EQUAL_INT(0, clitest_send_tab(&sess));
|
||||
TEST_ASSERT_TRUE_MESSAGE(
|
||||
clitest_wait_for_text(&sess, &out, &err, "wait-primary", false,
|
||||
3000),
|
||||
"client-context ambiguous 'w' must list 'wait-primary' among the "
|
||||
"candidates -- proving the SAME prefix offers a DIFFERENT "
|
||||
"candidate in this context than at top level (Requirement 7.1/7.4)");
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(
|
||||
strstr(out.data, "workerlist"),
|
||||
"client-context ambiguous 'w' must list 'workerlist' (not bare "
|
||||
"'worker', which is not a client command) among the candidates");
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(
|
||||
strstr(out.data, "waldump"),
|
||||
"client-context ambiguous 'w' must also list 'waldump'");
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(
|
||||
strstr(out.data, "walcompare"),
|
||||
"client-context ambiguous 'w' must also list 'walcompare'");
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, clitest_send_raw(&sess, &ctrl_u, 1));
|
||||
|
||||
/* --- Step 4: client context, unambiguous "jobk" -> Tab -> Enter
|
||||
* completes in full to "jobkill" and actually runs it bare. No live
|
||||
* controller is needed: cmd_jobkill's own missing-'-j' validation
|
||||
* returns (to stderr) before ever reaching the RPC/confirmation-gate
|
||||
* code, exactly like task 2.3's "jstart" proof step above. */
|
||||
clitest_buf_free(&out);
|
||||
clitest_buf_free(&err);
|
||||
clitest_buf_init(&out);
|
||||
clitest_buf_init(&err);
|
||||
TEST_ASSERT_EQUAL_INT(0, clitest_send_partial(&sess, "jobk"));
|
||||
TEST_ASSERT_EQUAL_INT(0, clitest_send_tab(&sess));
|
||||
TEST_ASSERT_EQUAL_INT(0, clitest_send_raw(&sess, &enter, 1));
|
||||
TEST_ASSERT_TRUE_MESSAGE(
|
||||
clitest_wait_for_text(&sess, &out, &err,
|
||||
"you need to specify jobid with -j", true,
|
||||
3000),
|
||||
"single unambiguous match 'jobk' must complete in full to "
|
||||
"'jobkill' in the client context (a command absent from both the "
|
||||
"top-level and worker contexts), proven by hitting cmd_jobkill's "
|
||||
"own bare-invocation validation message (Requirement 7.1/7.2/7.4)");
|
||||
|
||||
/* --- Step 5: NOW, mid-session (after two context switches and two
|
||||
* tab-completion demonstrations), look up jobkill's full help text.
|
||||
* Requirements 8.1-8.4: purpose description, generated flag block,
|
||||
* hand-authored example, and confirmation-gate scripting note. */
|
||||
clitest_buf_free(&out);
|
||||
clitest_buf_free(&err);
|
||||
clitest_buf_init(&out);
|
||||
clitest_buf_init(&err);
|
||||
TEST_ASSERT_EQUAL_INT(0, clitest_send_line(&sess, "help jobkill"));
|
||||
/* Each phrase gets its OWN clitest_wait_for_text() call (not a single
|
||||
* wait followed by plain strstr) -- cmd_help composes this output from
|
||||
* several separate prints (description -> generated flagspec_render_
|
||||
* help() block -> hand-authored tail), which this harness's poll loop
|
||||
* can observe arriving across more than one read() chunk. This matches
|
||||
* test_help_jobkill_shows_generated_flag_and_confirmation_note's own
|
||||
* established pattern above, not the single-wait-then-strstr shortcut
|
||||
* used for the double-Tab candidate listings elsewhere in this test
|
||||
* (those really are one burst -- this multi-part composition is not,
|
||||
* confirmed by this exact test failing on a bare strstr on first
|
||||
* write). */
|
||||
TEST_ASSERT_TRUE_MESSAGE(
|
||||
clitest_wait_for_text(&sess, &out, &err,
|
||||
"Sends a signal to a running job", false,
|
||||
5000),
|
||||
"Requirement 8.1: mid-session 'help jobkill' must still include "
|
||||
"its purpose description");
|
||||
TEST_ASSERT_TRUE_MESSAGE(
|
||||
clitest_wait_for_text(&sess, &out, &err, "-y, --yes", false, 5000),
|
||||
"Requirement 8.2: mid-session 'help jobkill' must still show the "
|
||||
"generated -y/--yes flag line");
|
||||
TEST_ASSERT_TRUE_MESSAGE(
|
||||
clitest_wait_for_text(&sess, &out, &err, "example: jobkill", false,
|
||||
5000),
|
||||
"Requirement 8.3: mid-session 'help jobkill' must still retain "
|
||||
"its example invocation");
|
||||
TEST_ASSERT_TRUE_MESSAGE(
|
||||
clitest_wait_for_text(&sess, &out, &err,
|
||||
"destructive action and prompts for "
|
||||
"confirmation",
|
||||
false, 5000),
|
||||
"Requirement 8.4: mid-session 'help jobkill' must still mention "
|
||||
"the confirmation gate as a related scripting note");
|
||||
TEST_ASSERT_TRUE_MESSAGE(
|
||||
clitest_wait_for_text(&sess, &out, &err, "set -y", false, 5000),
|
||||
"the confirmation note must still point at `set -y` as the "
|
||||
"session-wide bypass, even mid-session");
|
||||
|
||||
/* --- Step 6: switch to a THIRD context this session has not yet
|
||||
* visited (worker), via '..' then 'worker' as plainly-typed lines. */
|
||||
clitest_buf_free(&out);
|
||||
clitest_buf_free(&err);
|
||||
clitest_buf_init(&out);
|
||||
clitest_buf_init(&err);
|
||||
TEST_ASSERT_EQUAL_INT(0, clitest_send_line(&sess, ".."));
|
||||
TEST_ASSERT_TRUE_MESSAGE(
|
||||
clitest_wait_for_text(&sess, &out, &err, "xce>", false, 5000),
|
||||
"expected to return to the top-level context after '..'");
|
||||
TEST_ASSERT_EQUAL_INT(0, clitest_send_line(&sess, "worker"));
|
||||
TEST_ASSERT_TRUE_MESSAGE(
|
||||
clitest_wait_for_text(&sess, &out, &err, "xce/worker>", false, 5000),
|
||||
"expected to enter the worker context");
|
||||
|
||||
/* --- Step 7: worker context, ambiguous "j" -> {jstart, jfinish,
|
||||
* jfail}, and NONE of the client-only "job*" commands -- a second,
|
||||
* independent different-contexts-different-candidates proof. */
|
||||
clitest_buf_free(&out);
|
||||
clitest_buf_free(&err);
|
||||
clitest_buf_init(&out);
|
||||
clitest_buf_init(&err);
|
||||
TEST_ASSERT_EQUAL_INT(0, clitest_send_partial(&sess, "j"));
|
||||
TEST_ASSERT_EQUAL_INT(0, clitest_send_tab(&sess));
|
||||
TEST_ASSERT_EQUAL_INT(0, clitest_send_tab(&sess));
|
||||
TEST_ASSERT_TRUE_MESSAGE(
|
||||
clitest_wait_for_text(&sess, &out, &err, "jfinish", false, 3000),
|
||||
"worker-context ambiguous 'j' must list 'jfinish' among the "
|
||||
"candidates");
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(strstr(out.data, "jstart"),
|
||||
"worker-context ambiguous 'j' must also "
|
||||
"list 'jstart'");
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(strstr(out.data, "jfail"),
|
||||
"worker-context ambiguous 'j' must also "
|
||||
"list 'jfail'");
|
||||
TEST_ASSERT_NULL_MESSAGE(
|
||||
strstr(out.data, "jobkill"),
|
||||
"worker-context ambiguous 'j' must NOT offer 'jobkill' -- that "
|
||||
"command exists only in the client context (Requirement 7.4)");
|
||||
TEST_ASSERT_NULL_MESSAGE(
|
||||
strstr(out.data, "jobrun"),
|
||||
"worker-context ambiguous 'j' must NOT offer 'jobrun' either "
|
||||
"(client-only)");
|
||||
TEST_ASSERT_NULL_MESSAGE(
|
||||
strstr(out.data, "jobsub"),
|
||||
"worker-context ambiguous 'j' must NOT offer 'jobsub' either "
|
||||
"(client-only)");
|
||||
TEST_ASSERT_NULL_MESSAGE(
|
||||
strstr(out.data, "jobinfo"),
|
||||
"worker-context ambiguous 'j' must NOT offer 'jobinfo' either "
|
||||
"(client-only)");
|
||||
TEST_ASSERT_NULL_MESSAGE(
|
||||
strstr(out.data, "jobckpt"),
|
||||
"worker-context ambiguous 'j' must NOT offer 'jobckpt' either "
|
||||
"(client-only)");
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, clitest_send_raw(&sess, &ctrl_u, 1));
|
||||
|
||||
/* --- Step 8: 'help jobkill' in the worker context -- a name valid in
|
||||
* the OLD (client) context but not this one -- must be reported not
|
||||
* found, with the corrected grammar (Requirement 11.2), never leaking
|
||||
* client context's jobkill text into the not-found response. This is
|
||||
* the same not-found behavior test_help_context_scoped_not_found_no_
|
||||
* cross_context_hint proves from the top-level context; here it is
|
||||
* proven from the worker context, mid-session, after everything above
|
||||
* has already run. */
|
||||
clitest_buf_free(&out);
|
||||
clitest_buf_free(&err);
|
||||
clitest_buf_init(&out);
|
||||
clitest_buf_init(&err);
|
||||
TEST_ASSERT_EQUAL_INT(0, clitest_send_line(&sess, "help jobkill"));
|
||||
TEST_ASSERT_TRUE_MESSAGE(
|
||||
clitest_wait_for_text(&sess, &out, &err,
|
||||
"No commands match `jobkill'", true, 5000),
|
||||
"Requirement 8.7/11.2: from the worker context (jobkill's OLD "
|
||||
"context, client, is not the active one), 'help jobkill' must be "
|
||||
"reported as not found there, with corrected grammar");
|
||||
TEST_ASSERT_NULL_MESSAGE(
|
||||
strstr(err.data, "matchs"),
|
||||
"the corrected not-found message must not contain the old "
|
||||
"'matchs' typo anywhere in the captured output");
|
||||
TEST_ASSERT_NULL_MESSAGE(
|
||||
strstr(out.data, "No commands match"),
|
||||
"the not-found diagnostic must not also appear on stdout");
|
||||
TEST_ASSERT_NULL_MESSAGE(
|
||||
strstr(out.data, "Sends a signal to a running job"),
|
||||
"the worker-context not-found response must never leak client "
|
||||
"context's jobkill help content");
|
||||
TEST_ASSERT_NULL_MESSAGE(
|
||||
strstr(err.data, "Sends a signal to a running job"),
|
||||
"the worker-context not-found response must never leak client "
|
||||
"context's jobkill help content, on stderr either");
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, clitest_send_line(&sess, "quit"));
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(
|
||||
0, clitest_wait_for_exit(&sess, &out, &err, 5000, &exit_code),
|
||||
"xce_test did not exit within the harness's safety timeout after "
|
||||
"'quit'");
|
||||
/* Step 4's deliberate bare "jobkill" latches g_session_failed for the
|
||||
* rest of the session (same mechanism, and same deliberate choice of
|
||||
* proof command, as test_tab_completion_context_scoped_single_and_
|
||||
* multi_match above) even though the REPL itself kept running to
|
||||
* completion through every later step. */
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(
|
||||
1, exit_code,
|
||||
"xce_test should exit 1 -- the deliberate bare-'jobkill' proof "
|
||||
"step in step 4 latches g_session_failed even though the REPL "
|
||||
"itself kept running to completion through every later step");
|
||||
|
||||
clitest_kill_and_reap(&sess);
|
||||
clitest_close(&sess);
|
||||
clitest_buf_free(&out);
|
||||
clitest_buf_free(&err);
|
||||
}
|
||||
|
||||
/*
|
||||
* test_expect_jobs_malformed_count_diagnostic_on_stderr_not_stdout --
|
||||
* xce-test-cli-ux task 3.6, Requirement 3.1/3.2/3.3/3.4: `expect-jobs` given
|
||||
|
|
@ -3074,6 +3436,8 @@ void test_test_cli(void)
|
|||
RUN_TEST(test_help_reslist_shows_json_example);
|
||||
RUN_TEST(test_help_unknown_command_shows_corrected_grammar);
|
||||
RUN_TEST(test_help_context_scoped_not_found_no_cross_context_hint);
|
||||
RUN_TEST(
|
||||
test_help_completion_and_context_switching_hold_together_in_one_session);
|
||||
RUN_TEST(test_expect_jobs_malformed_count_diagnostic_on_stderr_not_stdout);
|
||||
RUN_TEST(test_wait_primary_invalid_timeout_diagnostic_on_stderr_not_stdout);
|
||||
RUN_TEST(test_set_list_success_result_on_stdout_not_stderr);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue