feat(scheduler-sim-diagnostics): wire info limit into REPL dispatch

repl_cmd_info() now recognizes "limit" as an info subcommand, routing to the
already-implemented and tested repl_info_limit() with the same ntok<3 usage
guard shape as the queue/worker/resource branches. Usage string and help
text updated to list it. repl_info_limit()'s own body is untouched -- this
is a pure, additive dispatch-wiring change. Two new tests drive it through
the real repl_dispatch() entry point (not a direct call) for both `info
limit <name>` and `info limit all`.

`info limit <name|all>` is now reachable from a live REPL session --
scheduler-sim-diagnostics' first capability is functionally complete.
Remaining: task 5 (determinism/regression validation).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RLSNJhNoULvVcUhXEFafQC
This commit is contained in:
Rongsong Shen 2026-09-17 14:27:28 +00:00
parent 4f60f148fb
commit afe22d3122
No known key found for this signature in database
GPG key ID: 5BCDAAE0123E9FE2
4 changed files with 140 additions and 9 deletions

View file

@ -42,7 +42,7 @@
- Observable: a limit matched against jobs on two distinct hosts shows two separate per-host usage lines each labeled with its own scope values, and a third configured-but-untouched host shows no line at all
- _Requirements: 1.4, 2.1, 2.2, 2.3, 3.1, 3.2, 3.4_
- [ ] 4. Register `info limit` as a recognized `info` subcommand
- [x] 4. Register `info limit` as a recognized `info` subcommand
- New `else if (strcmp(tokens[1], "limit") == 0)` branch in `repl_cmd_info()` (`repl_info.c`) routes to `repl_info_limit()`; usage-string literal updated to list `limit` alongside the existing subcommands
- `test_repl_limit_table.c` gains a full dispatch-round-trip test (via `repl_dispatch()`, not a direct call) for both `info limit <name>` and `info limit all`
- Observable: `info limit <name>` and `info limit all` both work when typed as real REPL input (through `repl_dispatch()`), and `help`/usage output lists the new subcommand

View file

@ -305,6 +305,8 @@ static void repl_print_help(void)
"Dump resource(s).");
fprintf(stdout, FIELD_FMT, "info summary", "Quick overview.");
fprintf(stdout, FIELD_FMT, "info config", "Show sim parameters.");
fprintf(stdout, FIELD_FMT, "info limit <name>|all",
"Show limit setting(s) and usage.");
fprintf(stdout, "\nSnapshot & Parameters:\n");
fprintf(stdout, FIELD_FMT, "snapshot [FILE]",

View file

@ -1483,9 +1483,12 @@ static void repl_print_limit_usage(xce_sched_sim_driver_t* sim,
* 2.2, Req 1.1/1.2/1.5) -- scheduler-sim-diagnostics spec, Requirement Set
* 1. Printing the Req 1.6 effective-host-scope note is task 2.3's job, and
* printing live usage instances (Req 2.1-2.3, 3.1-3.4) is task 3's job.
* Not yet wired into repl_cmd_info() (task 4 adds that branch) -- static
* and currently uncalled from production code; exercised directly by
* test_repl_limit_table.c per this task's own scope.
* Wired into repl_cmd_info()'s own `else if (strcmp(tokens[1], "limit") ==
* 0)` dispatch branch (task 4, Req 4.1/4.2) -- reachable both as real typed
* REPL input (via repl_dispatch(), test_repl_limit_table.c's own dispatch-
* round-trip tests) and directly (every other test in that file, per task
* 2.1/2.3/3.1/3.2's own pre-task-4 scope). Stays `static`: only ever called
* from within this file, same as every other `repl_info_*()` helper here.
*
* Requirement 1.7's "no limits configured" message is reserved for the
* `all` path specifically -- its own acceptance criterion scopes it to
@ -1792,10 +1795,11 @@ static void repl_info_break(xce_sched_sim_driver_t* sim)
* repl_cmd_info - Req 5 AC 22's "info ..." family: dispatches on the second
* token to repl_info_break()/repl_info_session()/repl_info_config()/
* repl_info_job()/repl_info_queue()/repl_info_worker()/repl_info_resource()/
* repl_info_summary() (all above). No `info` subcommand ever hands control
* back to the driver loop -- inspecting state is never a reason to advance
* the simulation. repl.c's repl_dispatch() calls this (repl_internal.h's
* own extern declaration).
* repl_info_summary()/repl_info_limit() (all above; repl_info_limit() wired
* in by task 4, scheduler-sim-diagnostics spec Req 4.1/4.2). No `info`
* subcommand ever hands control back to the driver loop -- inspecting state
* is never a reason to advance the simulation. repl.c's repl_dispatch()
* calls this (repl_internal.h's own extern declaration).
*/
bool repl_cmd_info(xce_sched_sim_driver_t* sim, const char* line)
{
@ -1804,7 +1808,7 @@ bool repl_cmd_info(xce_sched_sim_driver_t* sim, const char* line)
if (ntok < 2) {
fprintf(stdout, "[sim] info: usage: info <break|session|config|job|jobs|queue|"
"worker|resource|summary> [arg]\n");
"worker|resource|summary|limit> [arg]\n");
return false;
}
@ -1842,6 +1846,12 @@ bool repl_cmd_info(xce_sched_sim_driver_t* sim, const char* line)
} else {
repl_info_resource(sim, tokens[2]);
}
} else if (strcmp(tokens[1], "limit") == 0) {
if (ntok < 3) {
fprintf(stdout, "[sim] info limit: usage: info limit <name>|all\n");
} else {
repl_info_limit(sim, tokens[2]);
}
} else {
fprintf(stdout, "[sim] info: unrecognized subcommand '%s'\n", tokens[1]);
}

View file

@ -272,6 +272,33 @@ static char* capture_info_limit(xce_sched_sim_driver_t* sim, const char* arg)
return capture_stdout_around(run_info_limit_for_capture);
}
/*
* Captures the output of dispatching one REPL command line THROUGH THE REAL
* repl_dispatch() entry point -- task 4's own dispatch-round-trip
* requirement (unlike capture_info_limit() above, which calls
* repl_info_limit() directly, bypassing repl_cmd_info()'s tokenization and
* dispatch entirely). Same g_sim/g_line/run_for_capture()/capture() shape
* as capture_info_limit() above, and the same pattern
* test_repl_resource_table.c's/test_repl_breakpoints_inspection.c's own
* capture_dispatch() already establish for this file family. Never a
* "step"/"continue"/"quit" line (this helper does not expect
* repl_dispatch() to return true).
*/
static xce_sched_sim_driver_t* g_dispatch_sim;
static const char* g_dispatch_line;
static void run_dispatch_for_capture(void)
{
(void)repl_dispatch(g_dispatch_sim, g_dispatch_line);
}
static char* capture_dispatch(xce_sched_sim_driver_t* sim, const char* line)
{
g_dispatch_sim = sim;
g_dispatch_line = line;
return capture_stdout_around(run_dispatch_for_capture);
}
/* ---------------------------------------------------------------------- */
/* Limit configuration fixture helpers */
/* ---------------------------------------------------------------------- */
@ -1492,6 +1519,94 @@ test_info_limit_named_shows_no_active_usage_instance_when_host_scoped_vres_exist
"scoped_vres_exists_but_no_worker_populated: PASS\n");
}
/*
* Task 4 (Requirement Set 1, Req 4.1/4.2): the first tests in this file to
* drive `info limit <name>`/`info limit all` through the REAL repl_dispatch()
* entry point (capture_dispatch(), above) instead of calling
* repl_info_limit() directly (capture_info_limit(), used by every test
* above this point) -- proving the `else if (strcmp(tokens[1], "limit") ==
* 0)` dispatch branch this task adds to repl_cmd_info() (repl_info.c)
* actually routes real typed-in REPL input to repl_info_limit(), not just
* that repl_info_limit() itself works in isolation (already proven by every
* test above). Before this task's GREEN phase, repl_cmd_info() has no
* "limit" branch at all, so `tokens[1] == "limit"` falls into its own
* unrecognized-subcommand else branch ("[sim] info: unrecognized subcommand
* 'limit'") -- both assertions below are expected to fail at RED.
*/
static void test_info_limit_dispatch_named_reaches_repl_info_limit_via_real_dispatch(
void)
{
xce_sched_sim_driver_t sim;
xce_sched_plugin_t plugin;
xce_scheduler_t group_sched;
char* captured;
int rc;
memset(&sim, 0, sizeof(sim));
rc = xce_sched_core_init(&(sim.core));
TEST_ASSERT_EQUAL_INT(0, rc);
register_limit_plugin(&sim, &plugin, &group_sched);
add_configured_limit(&plugin, "ug1_slots");
captured = capture_dispatch(&sim, "info limit ug1_slots");
TEST_ASSERT_NULL_MESSAGE(
strstr(captured, "unrecognized subcommand"),
"Req 4.1: 'info limit <name>' typed as real REPL input must be "
"recognized -- repl_cmd_info() must route it to repl_info_limit(), "
"not fall through to the unrecognized-subcommand branch");
TEST_ASSERT_NOT_NULL_MESSAGE(
strstr(captured, "limit: ug1_slots"),
"Req 4.1: dispatching 'info limit ug1_slots' through repl_dispatch() "
"must produce the same fmt_limit() dump a direct repl_info_limit() "
"call already produces for this fixture");
free(captured);
printf("test_info_limit_dispatch_named_reaches_repl_info_limit_via_real_"
"dispatch: PASS\n");
}
static void
test_info_limit_dispatch_all_reaches_repl_info_limit_via_real_dispatch(void)
{
xce_sched_sim_driver_t sim;
xce_sched_plugin_t plugin;
xce_scheduler_t group_sched;
char* captured;
int rc;
memset(&sim, 0, sizeof(sim));
rc = xce_sched_core_init(&(sim.core));
TEST_ASSERT_EQUAL_INT(0, rc);
register_limit_plugin(&sim, &plugin, &group_sched);
add_configured_limit(&plugin, "limitA");
add_configured_limit(&plugin, "limitB");
captured = capture_dispatch(&sim, "info limit all");
TEST_ASSERT_NULL_MESSAGE(
strstr(captured, "unrecognized subcommand"),
"Req 4.2: 'info limit all' typed as real REPL input must be "
"recognized -- repl_cmd_info() must route it to repl_info_limit(), "
"not fall through to the unrecognized-subcommand branch");
TEST_ASSERT_NOT_NULL_MESSAGE(
strstr(captured, "limit: limitA"),
"Req 4.2: dispatching 'info limit all' through repl_dispatch() must "
"display every configured limit's setting, starting with limitA");
TEST_ASSERT_NOT_NULL_MESSAGE(
strstr(captured, "limit: limitB"),
"Req 4.2: ... and limitB too -- every configured limit, not just "
"one");
free(captured);
printf("test_info_limit_dispatch_all_reaches_repl_info_limit_via_real_"
"dispatch: PASS\n");
}
int main(void)
{
UNITY_BEGIN();
@ -1528,5 +1643,9 @@ int main(void)
test_info_limit_named_shows_accounted_zero_for_host_with_capacity_but_never_charged);
RUN_TEST(
test_info_limit_named_shows_no_active_usage_instance_when_host_scoped_vres_exists_but_no_worker_populated);
RUN_TEST(
test_info_limit_dispatch_named_reaches_repl_info_limit_via_real_dispatch);
RUN_TEST(
test_info_limit_dispatch_all_reaches_repl_info_limit_via_real_dispatch);
return UNITY_END();
}