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:
parent
d3df8fb29f
commit
117d01c77e
3 changed files with 199 additions and 15 deletions
|
|
@ -7,6 +7,7 @@
|
|||
#include "repl_internal.h"
|
||||
#include "resreq.h"
|
||||
#include "utils.h"
|
||||
#include "xcelimit.h"
|
||||
|
||||
/*
|
||||
* repl_info.c - state inspection (scheduler-core spec, task 12.2, Req 5 AC
|
||||
|
|
@ -1155,14 +1156,52 @@ static void repl_info_resource(xce_sched_sim_driver_t* sim, const char* arg)
|
|||
}
|
||||
|
||||
/*
|
||||
* repl_info_limit - `info limit <name|all>` name/all resolution
|
||||
* (scheduler-sim-diagnostics spec, Requirement Set 1, task 2.1, Req 1.3,
|
||||
* 1.7, 4.1, 4.2). THIS task implements only the name/all resolution and
|
||||
* the two absence messages -- printing a matched limit's configured
|
||||
* setting (fmt_limit(), Req 1.1/1.2/1.5) is task 2.2's job, and printing
|
||||
* its 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
|
||||
* repl_print_limit_dump - print fmt_limit()'s (src/lib/xcelimit.c) fully
|
||||
* verbatim, library-owned buffer for a matched limit's configured setting
|
||||
* (task 2.2, Req 1.1/1.2/1.5) using this file's own existing "[sim] "
|
||||
* per-line prefix convention for a multi-line info dump -- the same
|
||||
* convention repl_info_break() and repl_info_resource_one() (both above)
|
||||
* already use. fmt_limit() embeds '\n' between its logical lines (limit
|
||||
* name, one line per configured consumer dimension via fmt_consumer()/
|
||||
* fmt_group(), then the constrained resource/value line) rather than
|
||||
* returning an array of strings, so each line is re-split here and printed
|
||||
* individually with its own "[sim] " prefix rather than emitting the whole
|
||||
* buffer behind a single leading prefix (design.md's REPL Limit Info
|
||||
* Command Responsibilities: "fmt_limit() into a stack buffer, printed
|
||||
* line-by-line with the existing [sim] prefix convention").
|
||||
*
|
||||
* Per design.md's/research.md's explicit Boundary Commitments, this
|
||||
* function only re-prefixes fmt_limit()'s own output -- it does not
|
||||
* reimplement, reformat, or second-guess anything fmt_limit()/
|
||||
* fmt_consumer()/fmt_group() decided, including their exclusion-group "~"
|
||||
* rendering (Req 1.5).
|
||||
*/
|
||||
static void repl_print_limit_dump(const char* buf)
|
||||
{
|
||||
const char* line = buf;
|
||||
|
||||
while (*line != '\0') {
|
||||
const char* nl = strchr(line, '\n');
|
||||
int len = (nl != NULL) ? (int)(nl - line) : (int)strlen(line);
|
||||
|
||||
fprintf(stdout, "[sim] %.*s\n", len, line);
|
||||
|
||||
if (nl == NULL) {
|
||||
break;
|
||||
}
|
||||
line = nl + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* repl_info_limit - `info limit <name|all>` name/all resolution (task 2.1)
|
||||
* plus, once a genuine match is found, printing that limit's configured
|
||||
* setting via the library's own fmt_limit()/fmt_group() formatters (task
|
||||
* 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.
|
||||
*
|
||||
* Requirement 1.7's "no limits configured" message is reserved for the
|
||||
|
|
@ -1202,9 +1241,16 @@ static void repl_info_limit(xce_sched_sim_driver_t* sim, const char* arg)
|
|||
if ((rc < 0) || (rc == RTTI_ITER_NO_DATA) || (limit == NULL)) {
|
||||
break;
|
||||
}
|
||||
/* task 2.2 prints this limit's configured setting here
|
||||
* (fmt_limit()); task 3 adds its live usage. This task's own
|
||||
* scope stops at proving resolution reaches a genuine match. */
|
||||
{
|
||||
char limit_buf[1024];
|
||||
int fmt_rc = fmt_limit(limit_buf, sizeof(limit_buf), limit);
|
||||
|
||||
if (fmt_rc >= 0) {
|
||||
repl_print_limit_dump(limit_buf);
|
||||
}
|
||||
}
|
||||
/* task 2.3 adds the Req 1.6 effective-host-scope note here;
|
||||
* task 3 adds this limit's live usage. */
|
||||
n_shown++;
|
||||
}
|
||||
iter_destroy(&it);
|
||||
|
|
@ -1226,9 +1272,16 @@ static void repl_info_limit(xce_sched_sim_driver_t* sim, const char* arg)
|
|||
return;
|
||||
}
|
||||
|
||||
/* task 2.2 prints the matched limit's configured setting here
|
||||
* (fmt_limit()); task 3 adds its live usage. This task's own scope
|
||||
* stops at proving resolution reaches a genuine match. */
|
||||
{
|
||||
char limit_buf[1024];
|
||||
int fmt_rc = fmt_limit(limit_buf, sizeof(limit_buf), match);
|
||||
|
||||
if (fmt_rc >= 0) {
|
||||
repl_print_limit_dump(limit_buf);
|
||||
}
|
||||
}
|
||||
/* task 2.3 adds the Req 1.6 effective-host-scope note here; task 3
|
||||
* adds this limit's live usage. */
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue