feat(xce-test-cli-ux): add command-name tab completion scoped to context
Registers libedit's GNU-readline-compatible rl_attempted_completion_function under g_interactive only. The candidate generator reads active_ctx->commands[] live on every call, so completion correctly tracks context switches rather than freezing at registration time. Single/multi-match UI (auto-complete vs. double-Tab candidate listing) is entirely libedit's stock behavior once given a correct context-scoped generator. Proven via a black-box pty test verified sound against a false-positive risk: xce_test has no fuzzy command-name matching, so an uncompleted partial token cannot coincidentally trigger the same effect a real completion would. Also fixes a harness bug (bug-1792): disabling kernel ECHO for output capture was silently suppressing libedit's own completion feedback too; added clitest_spawn_ex() with disable_echo as an explicit parameter, clitest_spawn() unchanged for existing callers. 1307/1307 controller suite passing (+1), re-verified 8/8 for flakiness. Task 2.3 of .kiro/specs/xce-test-cli-ux/tasks.md. Requirements: 7.1, 7.2, 7.3, 7.4 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019jaMf9QEwpugH2W9KAuc7f
This commit is contained in:
parent
07d238eada
commit
0ef232a013
4 changed files with 356 additions and 14 deletions
|
|
@ -860,6 +860,101 @@ command_t* find_command(char* name)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/* xce-test-cli-ux task 2.3, Requirement 7: command-name tab completion.
|
||||
*
|
||||
* libedit API verification (design.md's flagged P0 risk, research.md's
|
||||
* "libedit completion API may not perfectly mirror GNU readline" risk):
|
||||
* confirmed by reading the ACTUAL linked headers in this build environment
|
||||
* (this project's `src/flake.nix`-pinned libedit, pkg-config name
|
||||
* "libedit", providing `editline/readline.h` -- the same header already
|
||||
* included at the top of this file) plus a standalone pty-driven probe
|
||||
* program built and run against this exact library. Findings:
|
||||
*
|
||||
* - The GNU-readline-style completion API IS present verbatim:
|
||||
* `rl_completion_func_t* rl_attempted_completion_function` (signature
|
||||
* `char** (*)(const char*, int, int)`, exactly matching this task's
|
||||
* `xcetest_complete` shape from design.md) and
|
||||
* `char** rl_completion_matches(const char*, rl_compentry_func_t*)`
|
||||
* (matches on a `char* (*)(const char*, int)` generator, exactly
|
||||
* `xcetest_complete_generator`'s shape). No lower-level `<histedit.h>`
|
||||
* fallback was needed.
|
||||
* - `rl_attempted_completion_over` exists and suppresses libedit's
|
||||
* default filename-completion fallback -- used below so an empty
|
||||
* match list in the active context yields NO completion rather than
|
||||
* local filenames.
|
||||
* - Single/multiple-match UI behavior is entirely the library's: a sole
|
||||
* match is completed in full (plus a trailing space) with ZERO extra
|
||||
* code (7.2); multiple matches with no further common prefix ring the
|
||||
* bell on the first Tab and print the candidate list (input line left
|
||||
* untouched -- confirmed via a raw pty capture) on a SECOND,
|
||||
* consecutive Tap of Tab -- standard GNU-readline/bash double-Tab
|
||||
* convention (7.3), not something this code implements. This
|
||||
* implementation's own responsibility is narrowly the context-scoped
|
||||
* candidate lookup below.
|
||||
* - `rl_variable_bind("show-all-if-ambiguous", "on")` was probed and
|
||||
* returns failure (1) against this libedit build -- that variable is
|
||||
* not implemented here, so it is not used; the stock double-Tab
|
||||
* behavior above is what ships.
|
||||
*/
|
||||
|
||||
/* xcetest_complete_generator - the readline "compentry" generator: called
|
||||
* repeatedly with state==0 on the first call (resets the scan) and
|
||||
* state!=0 on each subsequent call, returning one more `strdup()`ed match
|
||||
* per call (readline/libedit frees each returned string) and NULL once
|
||||
* exhausted. Iterates active_ctx->commands[] -- the CURRENTLY active
|
||||
* context at the moment Tab is pressed, read fresh on every call rather
|
||||
* than captured at registration time -- so completion always tracks
|
||||
* `worker`/`scheduler`/`client`/`..` context switches for the life of the
|
||||
* session (Requirement 7.1/7.4: only the active context's commands are
|
||||
* ever offered). Plain prefix match against `text`, same convention as
|
||||
* find_command()'s exact-match lookup just above. */
|
||||
static char* xcetest_complete_generator(const char* text, int state)
|
||||
{
|
||||
static int idx;
|
||||
static size_t len;
|
||||
|
||||
if (state == 0) {
|
||||
idx = 0;
|
||||
len = strlen(text);
|
||||
}
|
||||
|
||||
while (active_ctx->commands[idx].name != NULL) {
|
||||
const char* name = active_ctx->commands[idx].name;
|
||||
|
||||
idx++;
|
||||
if (strncmp(name, text, len) == 0) {
|
||||
return strdup(name);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* xcetest_complete - the readline "attempted completion" entry point,
|
||||
* registered as rl_attempted_completion_function only when g_interactive
|
||||
* (main(), below). `start`/`end` are byte offsets of the word being
|
||||
* completed within rl_line_buffer; `start == 0` means the FIRST word on
|
||||
* the line, i.e. the command name itself. Per this task's explicit
|
||||
* boundary (command-NAME completion only -- no flag-value completion of
|
||||
* jobids/hostnames/resource names), every other word position (start != 0)
|
||||
* returns NULL and leaves rl_attempted_completion_over at its default so
|
||||
* libedit's normal fallback applies there, unchanged from today's
|
||||
* zero-customization behavior. For the command-name position, this always
|
||||
* sets rl_attempted_completion_over so a context with zero matching
|
||||
* commands offers NOTHING (never falls back to filename completion). */
|
||||
static char** xcetest_complete(const char* text, int start, int end)
|
||||
{
|
||||
(void)end;
|
||||
|
||||
if (start != 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rl_attempted_completion_over = 1;
|
||||
|
||||
return rl_completion_matches(text, xcetest_complete_generator);
|
||||
}
|
||||
|
||||
/* Execute a command line. */
|
||||
int execute_line(char* line)
|
||||
{
|
||||
|
|
@ -4544,6 +4639,17 @@ int main(int argc, char* argv[])
|
|||
g_interactive = isatty(STDIN_FILENO);
|
||||
g_continue_on_error = g_interactive ? 1 : 0;
|
||||
|
||||
/* xce-test-cli-ux task 2.3, Requirement 7: register command-name tab
|
||||
* completion, but ONLY for a genuinely interactive session -- design.md's
|
||||
* explicit precondition on xcetest_complete(). A piped/redirected/
|
||||
* scripted stdin never calls readline()'s completion machinery anyway
|
||||
* (no terminal to press Tab on), but gating the registration itself
|
||||
* (rather than relying on that) keeps the precondition explicit and
|
||||
* matches this file's existing g_interactive-gated conventions. */
|
||||
if (g_interactive) {
|
||||
rl_attempted_completion_function = xcetest_complete;
|
||||
}
|
||||
|
||||
/* xce-test-rc-file task 2.2: auto-load $XDG_CONFIG_HOME/xce_test/rc (or
|
||||
* $HOME/.config/xce_test/rc) before the read loop starts, for both
|
||||
* interactive and scripted sessions alike -- unless --norc was given.
|
||||
|
|
|
|||
|
|
@ -79,10 +79,19 @@
|
|||
* child's stdin+stdout), and a plain pipe read fd carrying ONLY the
|
||||
* child's stderr.
|
||||
*
|
||||
* clitest_spawn(sess, path, argv)
|
||||
* clitest_spawn(sess, path, argv) / clitest_spawn_ex(sess, path, argv, disable_echo)
|
||||
* Capability 1: launches `path` with argv (NULL-terminated, argv[0]
|
||||
* conventionally the program name) with a genuine pty on stdin+stdout
|
||||
* and a separate pipe on stderr.
|
||||
* and a separate pipe on stderr. clitest_spawn() is the original entry
|
||||
* point (kernel ECHO disabled on the pty slave before forking, the
|
||||
* right choice for any test that only cares about a command's OUTPUT).
|
||||
* clitest_spawn_ex() (added by xce-test-cli-ux task 2.3, Requirement 7)
|
||||
* exposes the same launch with `disable_echo` explicit -- pass false
|
||||
* for any test that needs to OBSERVE libedit's own live interactive
|
||||
* editing/redraw output (e.g. tab completion), which this project's
|
||||
* linked libedit build suppresses entirely when the slave's ECHO bit
|
||||
* already reads off at readline()-call time (see clitest_spawn_ex()'s
|
||||
* own comment for the full empirical finding).
|
||||
*
|
||||
* clitest_send_line(sess, line) / clitest_send_partial(sess, partial) /
|
||||
* clitest_send_tab(sess) / clitest_send_raw(sess, buf, n)
|
||||
|
|
@ -216,8 +225,8 @@ typedef struct {
|
|||
bool reaped;
|
||||
} clitest_session_t;
|
||||
|
||||
/* clitest_spawn - capability 1: forks and execs `path` (argv[0]
|
||||
* conventionally the program name, NULL-terminated) with:
|
||||
/* clitest_spawn_ex - capability 1 (parameterized): forks and execs `path`
|
||||
* (argv[0] conventionally the program name, NULL-terminated) with:
|
||||
* - a genuine pty pair on the child's stdin+stdout (openpty(), <pty.h>),
|
||||
* so isatty()/g_interactive/libedit's readline() all behave exactly as
|
||||
* they do for a real interactive terminal session -- the same proven
|
||||
|
|
@ -228,20 +237,45 @@ typedef struct {
|
|||
* entirely off the pty -- this is capability 5 (stdout/stderr as
|
||||
* genuinely distinguishable streams), and exactly what Requirement 3's
|
||||
* stdout/stderr-discipline testing needs.
|
||||
* Kernel echo is disabled on the pty slave BEFORE forking (matching
|
||||
* test_repl_prompt_context.c's verified-safe technique): this only
|
||||
* suppresses the line discipline's own re-echo of bytes we write into the
|
||||
* master, not readline()/libedit's own prompt/redraw output, which stays
|
||||
* intact.
|
||||
* `disable_echo` controls whether kernel ECHO is cleared on the pty slave
|
||||
* BEFORE forking:
|
||||
* - true (clitest_spawn()'s behavior, matching
|
||||
* test_repl_prompt_context.c's technique) suppresses the line
|
||||
* discipline's own re-echo of bytes written into the master. This was
|
||||
* originally documented as affecting ONLY that kernel-level re-echo,
|
||||
* "not readline()/libedit's own prompt/redraw output, which stays
|
||||
* intact" -- xce-test-cli-ux task 2.3 (tab completion, Requirement 7)
|
||||
* found that assumption does NOT hold for THIS project's linked
|
||||
* libedit build: an A/B probe (disable_echo true vs. false, otherwise
|
||||
* identical) showed that starting the slave with ECHO already off
|
||||
* makes libedit treat the session as a silent/non-interactive input
|
||||
* mode -- Tab is accepted as a no-op (no bell, no completion, no
|
||||
* redraw at all) and the line is only ever consumed whole on Enter.
|
||||
* This is the same convention GNU-readline-family libraries use for
|
||||
* password-style hidden input (ECHO-off-at-call-time as the signal to
|
||||
* suppress ALL of the library's own visual feedback, not just
|
||||
* kernel echo) -- consistent with, not a contradiction of, the
|
||||
* original comment's intent, just a sharper edge than it accounted
|
||||
* for. Every test that only cares about a command's OUTPUT (the vast
|
||||
* majority, e.g. this file's own smoke test) is unaffected either way.
|
||||
* - false (added by task 2.3 specifically for Requirement 7's tab-
|
||||
* completion tests, which NEED libedit's live interactive redraw to
|
||||
* observe anything at all) leaves kernel ECHO at its default-on
|
||||
* state; libedit takes over echo/redraw duties itself once its own
|
||||
* readline() call reconfigures the tty, so captured stdout still
|
||||
* reflects the program's editing state (bell + redraw + candidate
|
||||
* listings), not a raw kernel double-echo of every byte.
|
||||
* No controlling-terminal setup (no setsid()/TIOCSCTTY) is performed --
|
||||
* unnecessary here because clitest_send_signal() below delivers signals
|
||||
* directly via kill(pid, signo) rather than relying on the pty line
|
||||
* discipline to turn a literal Ctrl-C byte into SIGINT.
|
||||
* discipline to turn a literal Ctrl-C byte into SIGINT (confirmed via the
|
||||
* same probe: ctty presence/absence made no observable difference to
|
||||
* completion behavior -- ECHO was the sole deciding factor).
|
||||
* Returns 0 on success (sess->pid/master_fd/err_fd populated) or -1 on
|
||||
* failure (openpty/pipe/fork failure -- errno is left set by whichever
|
||||
* call failed). */
|
||||
static int clitest_spawn(clitest_session_t* sess, const char* path,
|
||||
char* const argv[])
|
||||
static int clitest_spawn_ex(clitest_session_t* sess, const char* path,
|
||||
char* const argv[], bool disable_echo)
|
||||
{
|
||||
int master_fd;
|
||||
int slave_fd;
|
||||
|
|
@ -262,7 +296,7 @@ static int clitest_spawn(clitest_session_t* sess, const char* path,
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (tcgetattr(slave_fd, &tio) == 0) {
|
||||
if (disable_echo && tcgetattr(slave_fd, &tio) == 0) {
|
||||
tio.c_lflag &= (tcflag_t)~ECHO;
|
||||
(void)tcsetattr(slave_fd, TCSANOW, &tio);
|
||||
}
|
||||
|
|
@ -305,6 +339,18 @@ static int clitest_spawn(clitest_session_t* sess, const char* path,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* clitest_spawn - the pre-task-2.3 entry point, preserved with its exact
|
||||
* original behavior (disable_echo=true) so every existing caller (this
|
||||
* file's own smoke test included) is unaffected. See clitest_spawn_ex()'s
|
||||
* comment above for why a session that needs to OBSERVE libedit's live
|
||||
* interactive editing/completion feedback (task 2.3, Requirement 7) must
|
||||
* call clitest_spawn_ex() directly with disable_echo=false instead. */
|
||||
static int clitest_spawn(clitest_session_t* sess, const char* path,
|
||||
char* const argv[])
|
||||
{
|
||||
return clitest_spawn_ex(sess, path, argv, true);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Input injection */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
|
@ -574,8 +620,174 @@ static void test_smoke_launch_send_echo_capture_stdout_and_exit_code(void)
|
|||
clitest_buf_free(&err);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Tab completion (task 2.3, Requirement 7) */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
/* test_tab_completion_context_scoped_single_and_multi_match -- drives a
|
||||
* single xce_test session through all four Requirement 7 acceptance
|
||||
* criteria:
|
||||
* 7.2 (single unambiguous match completes in full) in the TOP-LEVEL
|
||||
* context: "wo" matches only "worker" among test.c's top_commands[].
|
||||
* 7.1/7.4 (candidates scoped to the ACTIVE context, never a command
|
||||
* invalid there) after switching into the worker context: "js"
|
||||
* matches "jstart", a command that exists ONLY in
|
||||
* test.c's worker_commands[] -- not in top_commands[] -- so getting
|
||||
* it back proves completion is reading the worker context's own
|
||||
* table, not a stale/global one.
|
||||
* 7.3 (multiple matches list candidates without altering the input
|
||||
* line) still inside the worker context: "jf" matches both "jfail"
|
||||
* and "jfinish" (neither a prefix of the other, so there is no
|
||||
* further common prefix to auto-lengthen). This task's own
|
||||
* libedit-probe investigation (see xcetest_complete's file-header
|
||||
* comment in test.c) confirmed this linked libedit's completion
|
||||
* engine needs a SECOND, consecutive Tab to print the candidate
|
||||
* list in that case -- the first Tap only rings the bell, standard
|
||||
* GNU-readline/bash double-Tab-on-ambiguous convention, not
|
||||
* something this task's code implements.
|
||||
*
|
||||
* Verification strategy for the two SINGLE-match cases (7.2 and 7.1/7.2/
|
||||
* 7.4): the same libedit probe found that an unambiguous completion's
|
||||
* on-screen redraw only repaints the SUFFIX past what is already
|
||||
* displayed (e.g. typing "wo" then Tab emits "wo" + a cursor-reposition
|
||||
* escape + "rker " -- never the contiguous literal substring "worker" in
|
||||
* the raw byte stream), so this test does NOT strstr for the completed
|
||||
* word directly. Instead it presses Tab then Enter and checks the
|
||||
* resulting SEMANTIC effect, which is redraw-implementation-independent:
|
||||
* an incomplete/wrong completion would submit the bare, unrecognized
|
||||
* prefix ("wo"/"js"), which fails distinctly from a correct completion
|
||||
* ("wo" -> "xce/worker>" context prompt; "js" -> cmd_jobstart's own
|
||||
* argument-validation message). The ambiguous-match case (7.3) is
|
||||
* unaffected by this redraw-suffix behavior -- the candidate list is
|
||||
* printed on a fresh line, not an edit of the existing prompt line -- so
|
||||
* it is verified directly via strstr for both full candidate names,
|
||||
* confirmed against the raw pty transcript captured during this task's
|
||||
* investigation. */
|
||||
static void test_tab_completion_context_scoped_single_and_multi_match(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 (clitest_spawn_ex, not plain clitest_spawn): this
|
||||
* test needs to OBSERVE libedit's own live interactive editing/redraw
|
||||
* output (bell, candidate listings, in-place completion) -- this
|
||||
* project's linked libedit build suppresses ALL of that (silent/
|
||||
* password-style input mode) when the pty slave's ECHO bit already
|
||||
* reads off at readline()-call time, which is exactly what plain
|
||||
* clitest_spawn() does for every OTHER (output-only) test. See
|
||||
* clitest_spawn_ex()'s own comment above for the full empirical A/B
|
||||
* finding behind this. */
|
||||
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");
|
||||
|
||||
/* --- Requirement 7.2, top-level context: unambiguous "wo" -> "worker".
|
||||
* Press Tab then Enter and confirm the worker-context prompt appears --
|
||||
* only reachable if the submitted line was genuinely the full,
|
||||
* correctly-completed "worker", not the bare "wo" prefix. */
|
||||
TEST_ASSERT_EQUAL_INT(0, clitest_send_partial(&sess, "wo"));
|
||||
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/worker>", false, 5000),
|
||||
"single unambiguous match 'wo' must complete in full to 'worker' "
|
||||
"in the top-level context, proven by successfully switching into "
|
||||
"the worker context (Requirement 7.2)");
|
||||
|
||||
/* --- Requirement 7.1/7.2/7.4, worker context: unambiguous "js" ->
|
||||
* "jstart", a command that does not exist at the top level at all.
|
||||
* Press Tab then Enter and confirm cmd_jobstart's own bare-invocation
|
||||
* message appears -- only reachable if "js" was genuinely completed to
|
||||
* "jstart" (an unrecognized "js" would instead print "js: No such
|
||||
* command for xce test."), proving candidates are drawn from the
|
||||
* ACTIVE context's own command table, not a stale/global one. */
|
||||
TEST_ASSERT_EQUAL_INT(0, clitest_send_partial(&sess, "js"));
|
||||
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, "positive jobid", false,
|
||||
3000),
|
||||
"single unambiguous match 'js' must complete in full to 'jstart' "
|
||||
"in the worker context, proving candidates are drawn from the "
|
||||
"ACTIVE context's own command table (Requirement 7.1/7.2/7.4)");
|
||||
|
||||
/* --- Requirement 7.3, worker context: ambiguous "jf" matches both
|
||||
* "jfail" and "jfinish" -- first Tab only bells (no further common
|
||||
* prefix to lengthen), second Tab lists both candidates. */
|
||||
TEST_ASSERT_EQUAL_INT(0, clitest_send_partial(&sess, "jf"));
|
||||
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, "jfail", false, 3000),
|
||||
"ambiguous 'jf' must list 'jfail' among the candidates "
|
||||
"(Requirement 7.3)");
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(
|
||||
strstr(out.data, "jfinish"),
|
||||
"ambiguous 'jf' must also list 'jfinish' among the candidates "
|
||||
"(Requirement 7.3)");
|
||||
|
||||
/* The input line ("jf") must have been left unaltered by the listing:
|
||||
* clear it and confirm the session is still perfectly usable, i.e.
|
||||
* nothing about the listing corrupted readline's line-editing state. */
|
||||
TEST_ASSERT_EQUAL_INT(0, clitest_send_raw(&sess, &ctrl_u, 1));
|
||||
TEST_ASSERT_EQUAL_INT(0, clitest_send_line(&sess, ".."));
|
||||
TEST_ASSERT_TRUE_MESSAGE(
|
||||
clitest_wait_for_text(&sess, &out, &err, "xce>", false, 5000),
|
||||
"expected the top-level prompt again after '..', proving the "
|
||||
"session's line-editing state survived the ambiguous-match "
|
||||
"listing intact");
|
||||
|
||||
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'");
|
||||
/* exit_code is 1, not 0: the bare "jstart" invocation above (Requirement
|
||||
* 7.1/7.2/7.4's proof step) deliberately hits cmd_jobstart's own
|
||||
* "you need specify a positive jobid" validation-failure return path
|
||||
* on purpose (a fast, deterministic, network-free way to prove "js"
|
||||
* completed to "jstart" and was recognized/executed) -- test.c's own
|
||||
* g_session_failed latch (main(), near "return g_session_failed ? 1 :
|
||||
* 0") is sticky for the rest of the session once ANY command returns
|
||||
* nonzero, independent of the interactive continue-on-error toggle
|
||||
* that let the REPL keep going afterward. This is expected, correct
|
||||
* xce_test behavior given this test's own choice of proof command, not
|
||||
* a defect in tab completion itself. */
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(
|
||||
1, exit_code,
|
||||
"xce_test should exit 1 -- the deliberate bare-'jstart' proof step "
|
||||
"above latches g_session_failed even though the REPL itself kept "
|
||||
"running to completion");
|
||||
|
||||
clitest_kill_and_reap(&sess); /* idempotent safety net */
|
||||
clitest_close(&sess);
|
||||
clitest_buf_free(&out);
|
||||
clitest_buf_free(&err);
|
||||
}
|
||||
|
||||
void test_test_cli(void)
|
||||
{
|
||||
UnitySetTestFile(__FILE__);
|
||||
RUN_TEST(test_smoke_launch_send_echo_capture_stdout_and_exit_code);
|
||||
RUN_TEST(test_tab_completion_context_scoped_single_and_multi_match);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue