feat(scheduler-sim-diagnostics): display configured limit setting via fmt_limit()

repl_info_limit() now prints a matched limit's configured setting on both the
all and named-lookup success paths: fmt_limit() (existing libxce formatter,
unmodified) into a stack buffer, re-split and printed through a new
repl_print_limit_dump() helper matching this file's existing [sim]-prefix-
per-line convention. Inclusion and exclusion group membership both render
since fmt_group() already handles both, adopted as-is per design.md's
explicit reuse decision.

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 10:32:21 +00:00
parent d3df8fb29f
commit 117d01c77e
No known key found for this signature in database
GPG key ID: 5BCDAAE0123E9FE2
3 changed files with 199 additions and 15 deletions

View file

@ -378,6 +378,51 @@ static void add_configured_limit(xce_sched_plugin_t* plugin, const char* name)
free_limit_config(limits[0]);
}
/*
* add_configured_limit_with_host_exclusion - task 2.2's own Requirement 1.5
* fixture: an "all except X" exclusion group on the HOST dimension, the
* same shape test_limit.c's own test_build_consumer2() already establishes
* for the USER dimension (.members=["all"], .ex_members=[<excluded>]).
* Unlike add_configured_limit()/make_limit_config() (which leaves every
* consumer's xce_group_config_t* NULL, so build_consumer() falls back to
* the builtin "all" group for all four dimensions), this drives the real
* limit_update_config() with an explicit `.hosts` group config.
*
* All storage here (val, hosts_cfg, limit_cfg, the `limits` array) lives on
* this function's own stack frame; limit_update_config() -> build_limit()
* -> build_consumer() -> makeGroup() synchronously copies every member
* name into the real, persisted xce_group_t (and build_limit() itself
* strdup()s the limit/resource names) before this function returns, so
* nothing here needs to outlive the call -- the identical lifetime
* assumption test_limit.c's own test_build_limit_ok1()-style compound
* literals already rely on.
*/
static void add_configured_limit_with_host_exclusion(
xce_sched_plugin_t* plugin, const char* name, const char* included,
const char* excluded)
{
xce_limit_val_t val = { .resname = "mem",
.percent = 0,
.val = 10.0 };
xce_group_config_t hosts_cfg
= { .members = { .count = 1, .data = (char*[]) { (char*)included } },
.ex_members = { .count = 1,
.data = (char*[]) { (char*)excluded } } };
xce_limit_config_t limit_cfg = { .name = (char*)name,
.hosts = &hosts_cfg,
.val = &val };
xce_limit_config_t* limits[1] = { &limit_cfg };
xce_conf4sched_t config;
int rc;
memset(&config, 0, sizeof(config));
config.nlimits = 1;
config.limits = limits;
rc = limit_update_config(plugin, &config);
TEST_ASSERT_EQUAL_INT(0, rc);
}
/* ---------------------------------------------------------------------- */
/* Tests */
/* ---------------------------------------------------------------------- */
@ -602,6 +647,89 @@ test_info_limit_named_does_not_report_no_such_limit_for_configured_name(void)
"configured_name: PASS\n");
}
/*
* Task 2.2's own Observable #1 (Requirement 1.1, 1.5): a matched limit's
* configured setting is actually printed -- for the first time,
* repl_info_limit() finding a real match must emit something beyond
* silence. A limit configured with an exclusion group ("all except X" on
* the HOST dimension) must show BOTH the inclusion member and the
* exclusion member (fmt_group()'s "~" prefix) in the same displayed scope
* line, not just the presence of some output.
*/
static void
test_info_limit_named_shows_both_inclusion_and_exclusion_membership(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_with_host_exclusion(&plugin, "host_limit1", "all",
"excluded_host");
captured = capture_info_limit(&sim, "host_limit1");
TEST_ASSERT_NOT_NULL_MESSAGE(
strstr(captured, "limit: host_limit1"),
"the matched limit's fmt_limit() dump must include its name line "
"(proves real fmt_limit() output, not a stub)");
TEST_ASSERT_NOT_NULL_MESSAGE(
strstr(captured, "HOST = [all ~excluded_host]"),
"Requirement 1.5: the HOST scope line must show BOTH the inclusion "
"member ('all') and the exclusion member ('~excluded_host', "
"fmt_group()'s own exclusion prefix) together, distinguishing "
"included members from explicitly excluded ones");
free(captured);
printf("test_info_limit_named_shows_both_inclusion_and_exclusion_"
"membership: PASS\n");
}
/*
* Task 2.2's own Observable #2 (Requirement 1.2): `info limit all` against
* a fixture with MULTIPLE configured limits must display every one's
* configured setting, not just the first match or a truncated listing.
*/
static void test_info_limit_all_displays_every_configured_limits_setting(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_info_limit(&sim, "all");
TEST_ASSERT_NOT_NULL_MESSAGE(
strstr(captured, "limit: limitA"),
"Requirement 1.2: 'info limit all' must display limitA's "
"configured setting");
TEST_ASSERT_NOT_NULL_MESSAGE(
strstr(captured, "limit: limitB"),
"Requirement 1.2: 'info limit all' must display limitB's "
"configured setting too -- every configured limit, not just one");
free(captured);
printf("test_info_limit_all_displays_every_configured_limits_setting: "
"PASS\n");
}
int main(void)
{
UNITY_BEGIN();
@ -617,5 +745,8 @@ int main(void)
test_info_limit_named_reports_no_such_limit_when_plugin_never_registered_not_no_limits_configured);
RUN_TEST(
test_info_limit_named_does_not_report_no_such_limit_for_configured_name);
RUN_TEST(
test_info_limit_named_shows_both_inclusion_and_exclusion_membership);
RUN_TEST(test_info_limit_all_displays_every_configured_limits_setting);
return UNITY_END();
}