feat(scheduler-sim-diagnostics): add fmt_limit_units() sibling (task 8.1)
Adds fmt_limit_units(buf, len, limit, units_mode) to src/lib/xcelimit.c, a units-mode-aware sibling of fmt_limit() following the fmt_jobid()/fmt_jobid_ex() extended-sibling pattern. Reuses fmt_limit()'s exact consumer-dump loop untouched; only the final value line branches: percent limits always render bare "%f%%" regardless of mode, byte-valued (or "swp"-special-cased) absolute values render via xce_format_units_value() (task 7), everything else keeps the unconditional "%f". fmt_limit() itself is now a one-line XCE_UNITS_RAW wrapper around fmt_limit_units() -- provably byte-identical to its prior output for every input shape, so its 5 existing direct unit tests needed zero changes (the regression proof this refactor relies on). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RLSNJhNoULvVcUhXEFafQC
This commit is contained in:
parent
eb23c158ae
commit
9f54c5753f
4 changed files with 362 additions and 5 deletions
|
|
@ -76,7 +76,7 @@
|
|||
- _Boundary: Shared Units Formatter, REPL Limit Info Command (the retyped composer and its thin-wrapper callers live in `repl_info.c`/`repl_fmt.c`)_
|
||||
|
||||
- [ ] 8. Core: Human-readable configured-setting display
|
||||
- [ ] 8.1 Add a display-mode-aware way to render a limit's configured setting
|
||||
- [x] 8.1 Add a display-mode-aware way to render a limit's configured setting
|
||||
- New entry point produces the same configured-setting output the existing formatter already produces, except a byte-valued, non-percentage resource's amount renders in human-readable form according to a supplied display mode
|
||||
- The existing formatter's own output is provably unchanged for every input it can be given — proven by its complete pre-existing test coverage continuing to pass with zero modification
|
||||
- Observable: the existing configured-setting formatter's test coverage passes unmodified, and a new test confirms the mode-aware variant renders a byte-valued resource's amount differently across each supported display mode while leaving a percentage or non-byte-valued resource's rendering untouched
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include "group.h"
|
||||
#include "kvmgr.h"
|
||||
#include "utils.h"
|
||||
#include "xce/types.h"
|
||||
|
||||
extern int build_consumer(xce_consumer_t**, var, xce_consumer_type_t, int,
|
||||
|
|
@ -20,6 +21,14 @@ extern int build_vres_name(char**, xce_limit_t*, char*, char*, char*);
|
|||
extern int is_host_based_limit(var, xce_limit_t*);
|
||||
extern int fmt_consumer(char*, int, xce_consumer_t*);
|
||||
extern int fmt_limit(char*, int, xce_limit_t*);
|
||||
/* fmt_limit_units() -- scheduler-sim-diagnostics spec, Requirement 6, task
|
||||
* 8.1. Sibling of fmt_limit() (the fmt_jobid()/fmt_jobid_ex() pattern):
|
||||
* identical consumer-dump loop, but the final configured-value line renders
|
||||
* a byte-valued, non-percentage resource's amount via xce_format_units_value()
|
||||
* according to the supplied units_mode instead of always the bare "%f".
|
||||
* fmt_limit() itself is now a one-line wrapper: fmt_limit_units(buf, len,
|
||||
* limit, XCE_UNITS_RAW) -- byte-identical to its own pre-existing output. */
|
||||
extern int fmt_limit_units(char*, int, xce_limit_t*, xce_units_mode_t);
|
||||
extern void kill_limit(xce_limit_t*);
|
||||
extern int instantiate_limit(xce_vres_t**, var, xce_limit_t*, char*, char*,
|
||||
char*);
|
||||
|
|
|
|||
|
|
@ -360,12 +360,299 @@ static void test_match_limit2(void)
|
|||
destroy(umgr);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
* fmt_limit_units() -- scheduler-sim-diagnostics spec, Requirement 6,
|
||||
* task 8.1. fmt_limit() itself becomes a one-line XCE_UNITS_RAW wrapper
|
||||
* around this new sibling entry point; the five tests above (unmodified)
|
||||
* are the regression proof that the wrapper is byte-identical to
|
||||
* fmt_limit()'s pre-existing output. These new tests cover
|
||||
* fmt_limit_units()'s own additional behavior: byte-valued resources
|
||||
* rendered per mode, percentage-based and non-byte-valued resources left
|
||||
* unaffected by mode, and the "swp" MB->bytes special case mirroring
|
||||
* repl_format_numeric_resource_value()'s existing convention.
|
||||
* ------------------------------------------------------------------- */
|
||||
|
||||
static void test_fmt_limit_units_byte_valued_resource_renders_per_mode(void)
|
||||
{
|
||||
var umgr;
|
||||
var qmgr;
|
||||
var hmgr;
|
||||
var pmgr;
|
||||
xce_limit_t* limit;
|
||||
int rc;
|
||||
xce_limit_config_t limit_config = {
|
||||
.name = "limit_mem",
|
||||
.per_user = 0,
|
||||
.per_queue = 0,
|
||||
.per_project = 0,
|
||||
.per_host = 0,
|
||||
.users = NULL,
|
||||
.queues = NULL,
|
||||
.hosts = NULL,
|
||||
.projects = NULL,
|
||||
.val
|
||||
= &(xce_limit_val_t) { .resname = "mem", .percent = 0,
|
||||
/* 1 GiB, exactly representable as float --
|
||||
* avoids float-rounding ambiguity in the
|
||||
* expected-string literals below. */
|
||||
.val = 1073741824.0 }
|
||||
};
|
||||
char buf[1024];
|
||||
|
||||
groupmgr_init(&umgr, 4, 4);
|
||||
groupmgr_init(&qmgr, 4, 4);
|
||||
groupmgr_init(&hmgr, 4, 4);
|
||||
groupmgr_init(&pmgr, 4, 4);
|
||||
|
||||
rc = build_limit(&limit, umgr, qmgr, hmgr, pmgr, &limit_config);
|
||||
TEST_ASSERT_TRUE(rc == 1);
|
||||
|
||||
memset(buf, 0x0, sizeof(buf));
|
||||
rc = fmt_limit_units(buf, sizeof(buf) - 1, limit, XCE_UNITS_RAW);
|
||||
TEST_ASSERT_TRUE(rc > 0);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(
|
||||
strstr(buf, "\nmem = 1073741824.000000\n"),
|
||||
"RAW mode must render the bare %f value for a byte-valued resource");
|
||||
|
||||
memset(buf, 0x0, sizeof(buf));
|
||||
rc = fmt_limit_units(buf, sizeof(buf) - 1, limit, XCE_UNITS_HUMAN);
|
||||
TEST_ASSERT_TRUE(rc > 0);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(
|
||||
strstr(buf, "\nmem = 1.00 GB\n"),
|
||||
"HUMAN mode must render the human-scaled value alone");
|
||||
|
||||
memset(buf, 0x0, sizeof(buf));
|
||||
rc = fmt_limit_units(buf, sizeof(buf) - 1, limit, XCE_UNITS_BOTH);
|
||||
TEST_ASSERT_TRUE(rc > 0);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(
|
||||
strstr(buf, "\nmem = 1073741824.000000 (1.00 GB)\n"),
|
||||
"BOTH mode must render raw and human forms together");
|
||||
|
||||
kill_limit(limit);
|
||||
|
||||
destroy(pmgr);
|
||||
destroy(hmgr);
|
||||
destroy(qmgr);
|
||||
destroy(umgr);
|
||||
}
|
||||
|
||||
static void test_fmt_limit_units_percent_limit_ignores_units_mode(void)
|
||||
{
|
||||
var umgr;
|
||||
var qmgr;
|
||||
var hmgr;
|
||||
var pmgr;
|
||||
xce_limit_t* limit;
|
||||
int rc;
|
||||
xce_limit_config_t limit_config = {
|
||||
.name = "limit_mem_pct",
|
||||
.per_user = 0,
|
||||
.per_queue = 0,
|
||||
.per_project = 0,
|
||||
.per_host = 0,
|
||||
.users = NULL,
|
||||
.queues = NULL,
|
||||
.hosts = NULL,
|
||||
.projects = NULL,
|
||||
.val
|
||||
= &(xce_limit_val_t) { .resname = "mem", .percent = 1, .val = 80.0 }
|
||||
};
|
||||
char buf[1024];
|
||||
xce_units_mode_t modes[] = { XCE_UNITS_RAW, XCE_UNITS_HUMAN,
|
||||
XCE_UNITS_BOTH };
|
||||
size_t i;
|
||||
|
||||
groupmgr_init(&umgr, 4, 4);
|
||||
groupmgr_init(&qmgr, 4, 4);
|
||||
groupmgr_init(&hmgr, 4, 4);
|
||||
groupmgr_init(&pmgr, 4, 4);
|
||||
|
||||
rc = build_limit(&limit, umgr, qmgr, hmgr, pmgr, &limit_config);
|
||||
TEST_ASSERT_TRUE(rc == 1);
|
||||
|
||||
for (i = 0; i < (sizeof(modes) / sizeof(modes[0])); i++) {
|
||||
memset(buf, 0x0, sizeof(buf));
|
||||
rc = fmt_limit_units(buf, sizeof(buf) - 1, limit, modes[i]);
|
||||
TEST_ASSERT_TRUE(rc > 0);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(
|
||||
strstr(buf, "\nmem = 80.000000%\n"),
|
||||
"a percentage-based limit's value must render identically "
|
||||
"regardless of units_mode (Requirement 6 AC3) -- a percentage "
|
||||
"is never byte-scaled");
|
||||
}
|
||||
|
||||
kill_limit(limit);
|
||||
|
||||
destroy(pmgr);
|
||||
destroy(hmgr);
|
||||
destroy(qmgr);
|
||||
destroy(umgr);
|
||||
}
|
||||
|
||||
static void test_fmt_limit_units_non_byte_valued_resource_ignores_units_mode(
|
||||
void)
|
||||
{
|
||||
var umgr;
|
||||
var qmgr;
|
||||
var hmgr;
|
||||
var pmgr;
|
||||
xce_limit_t* limit;
|
||||
int rc;
|
||||
xce_limit_config_t limit_config = {
|
||||
.name = "limit_slots",
|
||||
.per_user = 0,
|
||||
.per_queue = 0,
|
||||
.per_project = 0,
|
||||
.per_host = 0,
|
||||
.users = NULL,
|
||||
.queues = NULL,
|
||||
.hosts = NULL,
|
||||
.projects = NULL,
|
||||
.val
|
||||
= &(xce_limit_val_t) { .resname = "slots", .percent = 0, .val = 3.0 }
|
||||
};
|
||||
char buf[1024];
|
||||
xce_units_mode_t modes[] = { XCE_UNITS_RAW, XCE_UNITS_HUMAN,
|
||||
XCE_UNITS_BOTH };
|
||||
size_t i;
|
||||
|
||||
groupmgr_init(&umgr, 4, 4);
|
||||
groupmgr_init(&qmgr, 4, 4);
|
||||
groupmgr_init(&hmgr, 4, 4);
|
||||
groupmgr_init(&pmgr, 4, 4);
|
||||
|
||||
rc = build_limit(&limit, umgr, qmgr, hmgr, pmgr, &limit_config);
|
||||
TEST_ASSERT_TRUE(rc == 1);
|
||||
|
||||
for (i = 0; i < (sizeof(modes) / sizeof(modes[0])); i++) {
|
||||
memset(buf, 0x0, sizeof(buf));
|
||||
rc = fmt_limit_units(buf, sizeof(buf) - 1, limit, modes[i]);
|
||||
TEST_ASSERT_TRUE(rc > 0);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(
|
||||
strstr(buf, "\nslots = 3.000000\n"),
|
||||
"a non-byte-valued resource's value must render identically "
|
||||
"regardless of units_mode (Requirement 6 AC3)");
|
||||
}
|
||||
|
||||
kill_limit(limit);
|
||||
|
||||
destroy(pmgr);
|
||||
destroy(hmgr);
|
||||
destroy(qmgr);
|
||||
destroy(umgr);
|
||||
}
|
||||
|
||||
static void test_fmt_limit_units_swp_resource_applies_mb_to_bytes_conversion(
|
||||
void)
|
||||
{
|
||||
var umgr;
|
||||
var qmgr;
|
||||
var hmgr;
|
||||
var pmgr;
|
||||
xce_limit_t* limit;
|
||||
int rc;
|
||||
xce_limit_config_t limit_config = {
|
||||
.name = "limit_swp",
|
||||
.per_user = 0,
|
||||
.per_queue = 0,
|
||||
.per_project = 0,
|
||||
.per_host = 0,
|
||||
.users = NULL,
|
||||
.queues = NULL,
|
||||
.hosts = NULL,
|
||||
.projects = NULL,
|
||||
.val
|
||||
= &(xce_limit_val_t) { .resname = "swp", .percent = 0,
|
||||
/* 1024 MB == 1 GiB once converted to bytes */
|
||||
.val = 1024.0 }
|
||||
};
|
||||
char buf[1024];
|
||||
|
||||
groupmgr_init(&umgr, 4, 4);
|
||||
groupmgr_init(&qmgr, 4, 4);
|
||||
groupmgr_init(&hmgr, 4, 4);
|
||||
groupmgr_init(&pmgr, 4, 4);
|
||||
|
||||
rc = build_limit(&limit, umgr, qmgr, hmgr, pmgr, &limit_config);
|
||||
TEST_ASSERT_TRUE(rc == 1);
|
||||
|
||||
memset(buf, 0x0, sizeof(buf));
|
||||
rc = fmt_limit_units(buf, sizeof(buf) - 1, limit, XCE_UNITS_HUMAN);
|
||||
TEST_ASSERT_TRUE(rc > 0);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(
|
||||
strstr(buf, "\nswp = 1.00 GB\n"),
|
||||
"swp's wire unit is MB, so fmt_limit_units() must apply the same "
|
||||
"MB-to-bytes conversion repl_format_numeric_resource_value() "
|
||||
"already applies before scaling for display");
|
||||
|
||||
kill_limit(limit);
|
||||
|
||||
destroy(pmgr);
|
||||
destroy(hmgr);
|
||||
destroy(qmgr);
|
||||
destroy(umgr);
|
||||
}
|
||||
|
||||
static void test_fmt_limit_wrapper_matches_fmt_limit_units_raw_mode(void)
|
||||
{
|
||||
var umgr;
|
||||
var qmgr;
|
||||
var hmgr;
|
||||
var pmgr;
|
||||
xce_limit_t* limit;
|
||||
int rc1, rc2;
|
||||
xce_limit_config_t limit_config = {
|
||||
.name = "limit_mem_wrapper",
|
||||
.per_user = 0,
|
||||
.per_queue = 0,
|
||||
.per_project = 0,
|
||||
.per_host = 0,
|
||||
.users = NULL,
|
||||
.queues = NULL,
|
||||
.hosts = NULL,
|
||||
.projects = NULL,
|
||||
.val
|
||||
= &(xce_limit_val_t) { .resname = "mem", .percent = 0,
|
||||
.val = 1073741824.0 }
|
||||
};
|
||||
char buf1[1024];
|
||||
char buf2[1024];
|
||||
|
||||
groupmgr_init(&umgr, 4, 4);
|
||||
groupmgr_init(&qmgr, 4, 4);
|
||||
groupmgr_init(&hmgr, 4, 4);
|
||||
groupmgr_init(&pmgr, 4, 4);
|
||||
|
||||
rc1 = build_limit(&limit, umgr, qmgr, hmgr, pmgr, &limit_config);
|
||||
TEST_ASSERT_TRUE(rc1 == 1);
|
||||
|
||||
memset(buf1, 0x0, sizeof(buf1));
|
||||
memset(buf2, 0x0, sizeof(buf2));
|
||||
|
||||
rc1 = fmt_limit(buf1, sizeof(buf1) - 1, limit);
|
||||
rc2 = fmt_limit_units(buf2, sizeof(buf2) - 1, limit, XCE_UNITS_RAW);
|
||||
|
||||
TEST_ASSERT_TRUE(rc1 > 0);
|
||||
TEST_ASSERT_TRUE(rc2 > 0);
|
||||
TEST_ASSERT_EQUAL_STRING_MESSAGE(
|
||||
buf2, buf1,
|
||||
"fmt_limit() must remain a byte-identical XCE_UNITS_RAW wrapper "
|
||||
"around fmt_limit_units()");
|
||||
|
||||
kill_limit(limit);
|
||||
|
||||
destroy(pmgr);
|
||||
destroy(hmgr);
|
||||
destroy(qmgr);
|
||||
destroy(umgr);
|
||||
}
|
||||
|
||||
void test_limit(void)
|
||||
{
|
||||
printf("\n>> limit test\n\n");
|
||||
|
||||
xstring_table_init(139);
|
||||
|
||||
|
||||
RUN_TEST(test_build_consumer1);
|
||||
RUN_TEST(test_build_consumer2);
|
||||
RUN_TEST(test_build_consumer_empty_config_defaults_to_all);
|
||||
|
|
@ -374,6 +661,12 @@ void test_limit(void)
|
|||
RUN_TEST(test_match_limit_ok2);
|
||||
RUN_TEST(test_match_limit1);
|
||||
RUN_TEST(test_match_limit2);
|
||||
RUN_TEST(test_fmt_limit_units_byte_valued_resource_renders_per_mode);
|
||||
RUN_TEST(test_fmt_limit_units_percent_limit_ignores_units_mode);
|
||||
RUN_TEST(
|
||||
test_fmt_limit_units_non_byte_valued_resource_ignores_units_mode);
|
||||
RUN_TEST(test_fmt_limit_units_swp_resource_applies_mb_to_bytes_conversion);
|
||||
RUN_TEST(test_fmt_limit_wrapper_matches_fmt_limit_units_raw_mode);
|
||||
|
||||
xstring_table_fini();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -151,7 +151,37 @@ void kill_consumer(xce_consumer_t* c)
|
|||
FREEUP(c);
|
||||
}
|
||||
|
||||
int fmt_limit(char* buf, int len, xce_limit_t* limit)
|
||||
/*
|
||||
* fmt_limit_units() -- scheduler-sim-diagnostics spec, Requirement 6, task
|
||||
* 8.1. Sibling of fmt_limit() carrying one extra parameter, following this
|
||||
* codebase's established "add a sibling rather than break an existing
|
||||
* signature" pattern (fmt_jobid()/fmt_jobid_ex(),
|
||||
* limit_seed_config()/limit_seed_config_impl()).
|
||||
*
|
||||
* The consumer-dump loop is IDENTICAL to fmt_limit()'s -- untouched. Only
|
||||
* the final configured-value line's formatting differs:
|
||||
* - percent != 0: always the bare "%f%%" rendering, regardless of
|
||||
* units_mode -- a percentage is never byte-scaled (Requirement 6 AC3).
|
||||
* - percent == 0, resource is byte-valued (xce_is_bytes_valued_resource())
|
||||
* or the "swp" special case (its wire unit is MB, not bytes -- the same
|
||||
* check repl_format_numeric_resource_value() already applies, converted
|
||||
* via *1024.0*1024.0 before formatting): render via
|
||||
* xce_format_units_value(units_mode, bytes_value, ...).
|
||||
* - otherwise (non-byte-valued, e.g. "slots"): keep today's exact "%f"
|
||||
* output unconditionally -- units_mode has zero effect (Requirement 6
|
||||
* AC3).
|
||||
*
|
||||
* fmt_limit() itself is a one-line wrapper around this function in
|
||||
* XCE_UNITS_RAW mode. That is provably byte-identical to fmt_limit()'s own
|
||||
* pre-existing output for every percent/non-percent, byte-valued/
|
||||
* non-byte-valued shape: xce_format_units_value(XCE_UNITS_RAW, v, ...)
|
||||
* always renders the bare "%f" of v (see its own implementation), matching
|
||||
* what the old inline fmt_limit() body produced. This is why
|
||||
* test_limit.c's five pre-existing fmt_limit() call sites needed zero
|
||||
* changes -- they are the regression proof.
|
||||
*/
|
||||
int fmt_limit_units(char* buf, int len, xce_limit_t* limit,
|
||||
xce_units_mode_t units_mode)
|
||||
{
|
||||
int pos = 0;
|
||||
int i, rc;
|
||||
|
|
@ -181,8 +211,28 @@ int fmt_limit(char* buf, int len, xce_limit_t* limit)
|
|||
}
|
||||
}
|
||||
|
||||
rc = snprintf(buf + pos, len - pos, "\n%s = %f%s\n", limit->val.resname,
|
||||
limit->val.val, (limit->val.percent != 0) ? "%" : "");
|
||||
if (limit->val.percent != 0) {
|
||||
rc = snprintf(buf + pos, len - pos, "\n%s = %f%%\n",
|
||||
limit->val.resname, limit->val.val);
|
||||
} else {
|
||||
bool is_swp = (limit->val.resname != NULL)
|
||||
&& (strcmp(limit->val.resname, "swp") == 0);
|
||||
|
||||
if (xce_is_bytes_valued_resource(limit->val.resname) || is_swp) {
|
||||
double bytes_value = is_swp
|
||||
? ((double)limit->val.val * 1024.0 * 1024.0)
|
||||
: (double)limit->val.val;
|
||||
char valbuf[128];
|
||||
|
||||
xce_format_units_value(units_mode, bytes_value, valbuf,
|
||||
sizeof(valbuf));
|
||||
rc = snprintf(buf + pos, len - pos, "\n%s = %s\n",
|
||||
limit->val.resname, valbuf);
|
||||
} else {
|
||||
rc = snprintf(buf + pos, len - pos, "\n%s = %f\n",
|
||||
limit->val.resname, limit->val.val);
|
||||
}
|
||||
}
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
|
|
@ -190,6 +240,11 @@ int fmt_limit(char* buf, int len, xce_limit_t* limit)
|
|||
return strlen(buf);
|
||||
}
|
||||
|
||||
int fmt_limit(char* buf, int len, xce_limit_t* limit)
|
||||
{
|
||||
return fmt_limit_units(buf, len, limit, XCE_UNITS_RAW);
|
||||
}
|
||||
|
||||
void kill_limit(xce_limit_t* limit)
|
||||
{
|
||||
int i;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue