fix(xce-test-cli-ux): fix bug-1711's display and text defects

Eight display/text defects in test.c: worker_status_str's "UNKOWN"
typo, cmd_runtasks's "sequentail" typo (display-only, verified inert
against the real launch_type consumer), cmd_reslist's right-justified
columns and unlabeled hex Flags (now matching workerlist/state_raw's
conventions), joblist's raw-epoch timestamps (now via a format_job_time
helper delegating to the same formatter ctrlstatus already uses) and
combined NTasks column split into MinTasks/MaxTasks, job_state_str's
missing raw-state fallback for ambiguous RUN+EXIT (mirroring
print_jobinfo's existing pattern), and getconf's no-op group/config
list stubs (now genuinely implemented from already-fetched data,
matching the three sibling print functions' convention).

worker_status_str/job_state_str/format_job_time are pure functions with
no networking/REPL/global-state dependency, extracted into a new
joblist_display.c/.h module with a permanent 20-test standalone suite --
following this codebase's own resval_typeresolve.c/misc.c precedent for
testing functions that would otherwise be stranded inside untестable
test.c. cmd_reslist/joblist_query/cmd_getconf's table-printing bodies
inline fetch+print and remain untestable without a live cluster.
1348/1348 across the full controller suite (+20), 0 regressions.

Task 2.5 of .kiro/specs/xce-test-cli-ux/tasks.md.
Requirements: 11.1, 11.3, 11.4, 11.5, 11.6, 11.7, 11.8

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019jaMf9QEwpugH2W9KAuc7f
This commit is contained in:
Rongsong Shen 2026-09-09 15:34:55 +00:00
parent c709eba813
commit a8154acb0a
No known key found for this signature in database
GPG key ID: 5BCDAAE0123E9FE2
6 changed files with 545 additions and 62 deletions

View file

@ -33,6 +33,8 @@
#include "snapverify.h" /* offline checkpoint-file structural validator (cmd_snapverify) */
#include "configcompare.h" /* cross-member active-config comparator (cmd_configcompare) */
#include "confirm.h" /* destructive-command confirmation gate (xce-test-cli-ux task 2.1) */
#include "joblist_display.h" /* worker_status_str/job_state_str/format_job_time
* (xce-test-cli-ux task 2.5 remediation) */
#include "rcfile.h" /* startup rc-file path resolution + loading (xce-test-rc-file task 2.2) */
#include "resval_typeresolve.h" /* resval_resolve_type (resource-value-verification task 2.1) */
#include "xce/cluster.h" /* xce_parse_peers, xce_cluster_config_t */
@ -1719,10 +1721,23 @@ int cmd_reslist(int argc, char* argv[])
return rc;
}
printf("%10s%10s%10s%64s\n", "Name", "Flags", "Interval", "Description");
/* Note the extra literal space before the Description specifier: unlike
* cmd_workerlist's all-left-justified text columns (which self-separate
* via their own trailing padding), Interval stays right-justified here,
* so without an explicit separator a short Interval value would run
* directly into the now-left-justified Description text. */
printf("%-10s%10s%10s %-64s\n", "Name", "Flags", "Interval",
"Description");
for (i = 0; i < rc; i++) {
printf("%10s%10x%10d%64s\n", res[i]->name, res[i]->flags,
/* bug-1711 (Requirement 11.4): prefix as hex ("0x%x"), matching this
* file's own state_raw convention (print_jobinfo), instead of an
* unlabeled bare %x that is ambiguous vs decimal. */
char flags_buf[16];
snprintf(flags_buf, sizeof(flags_buf), "0x%x", res[i]->flags);
printf("%-10s%10s%10d %-64s\n", res[i]->name, flags_buf,
res[i]->interval, res[i]->desc);
}
@ -1731,30 +1746,12 @@ int cmd_reslist(int argc, char* argv[])
return 0;
}
static char* worker_status_str(uint32_t status)
{
/* Admin-set CLOSED takes priority over the dynamic UNAVAIL bit: the
* config-upgrade-snapshot soak (task 8.6) needs to assert a host's
* administratively-set status specifically, and a host closed via
* hostctrl that later disconnects (e.g. its xce_test `worker`/`join`
* connection exits after the script ends) would otherwise print only
* "UNAVAIL", masking the admin bit this check exists to verify. */
if ((status & XCE_WORKER_CLOSED) != 0) {
return "CLOSED";
} else if ((status & XCE_WORKER_UNAVAIL) != 0) {
return "UNAVAIL";
} else if ((status & XCE_WORKER_LOCKEDU) != 0) {
return "LOCKU";
} else if ((status & XCE_WORKER_LOCKEDW) != 0) {
return "LOCKW";
} else if ((status & XCE_WORKER_BUSY) != 0) {
return "BUSY";
} else if (status == 0) {
return "OK";
}
return "UNKOWN";
}
/* worker_status_str() -- extracted to joblist_display.c/.h (xce-test-cli-ux
* task 2.5 remediation): pure, dependency-light display formatting, no
* networking/REPL/global-state coupling, so it can be unit-tested outside
* of this file (see test_joblist_display.c), mirroring set_resval()'s own
* extraction to resval_typeresolve.c below. Call sites are unchanged --
* they now resolve through joblist_display.h's declaration. */
/* forward decl: defined near cmd_resval, below, which also uses it */
static void format_bytes_human(double bytes, char* buf, size_t bufsize);
@ -1804,31 +1801,11 @@ int cmd_workerlist(int argc, char* argv[])
return 0;
}
static char* job_state_str(int state)
{
if ((state & JOB_STAT_PEND) != 0) {
if ((state & JOB_STAT_PSUSP) != 0) {
return "PSUSP";
}
return "PEND";
} else if ((state & JOB_STAT_RUN) != 0) {
if ((state & JOB_STAT_USUSP) != 0) {
return "USUSP";
} else if ((state & JOB_STAT_SSUSP) != 0) {
return "SSUSP";
}
return "RUN";
} else if ((state & JOB_STAT_EXIT) != 0) {
return "EXIT";
} else if ((state & JOB_STAT_DONE) != 0) {
return "DONE";
} else if ((state & JOB_STAT_ERR) != 0) {
return "ERROR";
}
return "UNKNOWN";
}
/* job_state_str()/format_job_time() -- extracted to joblist_display.c/.h
* (xce-test-cli-ux task 2.5 remediation), same rationale as
* worker_status_str() above: pure, dependency-light display formatting,
* unit-tested outside of this file in test_joblist_display.c. Call sites
* (below, and print_jobinfo's "state:" line) are unchanged. */
/* Shared, print-free joblist core for `cmd_joblist` and the `expect-jobs` /
* `expect-job` assertions (xce-test-e2e-primitives task 5.1, design.md:81,
@ -1869,9 +1846,9 @@ static int joblist_query(const char* find_jobid, int* contains, int print_table,
}
if (print_table) {
printf("%-26s%-10s%-10s%-8s%-16s%-16s%-16s%10s%30s\n", "JobId", "User",
"Queue", "State", "SubmitTime", "StartTime", "FinishTime",
"NTasks", "Command");
printf("%-26s%-10s%-10s%-8s%-24s%-24s%-24s%12s%12s%30s\n", "JobId",
"User", "Queue", "State", "SubmitTime", "StartTime",
"FinishTime", "MinTasks", "MaxTasks", "Command");
}
for (i = 0; i < rc; i++) {
@ -1885,10 +1862,19 @@ static int joblist_query(const char* find_jobid, int* contains, int print_table,
}
if (print_table) {
printf("%-26s%-10s%-10s%-8s%-16ld%-16ld%-16ld%4d,%-5d%30s\n", buf,
char submit_buf[32];
char start_buf[32];
char finish_buf[32];
printf("%-26s%-10s%-10s%-8s%-24s%-24s%-24s%12d%12d%30s\n", buf,
jobs[i]->submit->user, jobs[i]->submit->queue,
job_state_str(jobs[i]->state), jobs[i]->queue_time,
jobs[i]->start_time, jobs[i]->finish_time,
job_state_str(jobs[i]->state),
format_job_time(jobs[i]->queue_time, submit_buf,
sizeof(submit_buf)),
format_job_time(jobs[i]->start_time, start_buf,
sizeof(start_buf)),
format_job_time(jobs[i]->finish_time, finish_buf,
sizeof(finish_buf)),
jobs[i]->submit->mintasks, jobs[i]->submit->maxtasks,
jobs[i]->submit->start_info->cmdline);
}
@ -3257,7 +3243,7 @@ int cmd_runtasks(int argc, char* argv[])
if (totaltasks > 1) {
hosts[i].launch_type = strdup("mpi");
} else {
hosts[i].launch_type = strdup("sequentail");
hosts[i].launch_type = strdup("sequential");
}
}
}
@ -3950,9 +3936,43 @@ static int print_workerlist(xce_workerlist_t wlist)
return 0;
}
static int print_grouplist(xce_group_configlist_t glist) { return 0; }
/* bug-1711 (Requirement 11.8): print the already-fetched group records
* (conf->uglist / conf->hglist) instead of the previous no-op stub, which
* always left the "User Groups"/"Host Groups" header followed by silent
* nothing -- indistinguishable from a genuinely empty cluster. Mirrors
* print_reslist/print_queuelist/print_workerlist's own one-record-per-block
* convention just above. */
static int print_grouplist(xce_group_configlist_t glist)
{
int i, j;
static int print_configlist(xce_configlist_t clist) { return 0; }
for (i = 0; i < glist.count; i++) {
printf("name: %s\nmembers:", glist.groups[i]->name);
for (j = 0; j < glist.groups[i]->members.count; j++) {
printf(" %s", glist.groups[i]->members.data[j]);
}
printf("\nex_members:");
for (j = 0; j < glist.groups[i]->ex_members.count; j++) {
printf(" %s", glist.groups[i]->ex_members.data[j]);
}
printf("\n\n");
}
return 0;
}
/* bug-1711 (Requirement 11.8): print the already-fetched config records
* (conf->cfglist) instead of the previous no-op stub -- see print_grouplist
* above for the same fix applied to the "Configs" section. */
static int print_configlist(xce_configlist_t clist)
{
int i;
for (i = 0; i < clist.count; i++) {
printf("name: %s\nvalue: %s\n\n", clist.configs[i]->name,
clist.configs[i]->value);
}
return 0;
}
int cmd_getconf(int argc, char* argv[])
{