From bf61f5954e2bea9ced04c09c2d48a380b894495f Mon Sep 17 00:00:00 2001 From: Rongsong Shen Date: Sun, 20 Sep 2026 08:08:21 +0000 Subject: [PATCH] test(scheduler-sim-diagnostics): close cross-cutting gaps for Requirement 6 (task 10.2) Final validation task for the Human-Readable Units increment (tasks 7-10.1). Adds three tests closing gaps no prior task's own tests covered: - Cross-surface consistency (Requirement 6.5): a single info limit request against a byte-valued limit with both a configured setting and a live usage instance shows the SAME units convention in both lines, across mode switches -- neither task 8.2's nor task 9's own tests combined both surfaces in one capture. - Non-byte-valued exclusion (6.3): a new "slots"-resourced fixture (the first non-"mem" fixture in this file) proves the entire combined display is byte-for-byte identical across all three units modes. - Percentage exclusion (6.3) at the REPL-dispatch level: a percent limit's own percentage value survives real repl_dispatch()/ set-units switching unchanged, complementing test_limit.c's existing library-level proof of the same invariant. Full regression sweep: lib suite 368/368, scheduler suite 85/85, including test_repl_limit_table.c (35/35), test_repl_resource_table.c, test_repl_resource_formatting.c, and test_limit_plugin.c all unaffected. No production code changed. This closes out Requirement 6 (ACs 6.1-6.5) and reconfirms Requirement 5 (ACs 5.1-5.3) end-to-end. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01RLSNJhNoULvVcUhXEFafQC --- .../specs/scheduler-sim-diagnostics/tasks.md | 2 +- .../testsuite/test_repl_limit_table.c | 313 ++++++++++++++++++ 2 files changed, 314 insertions(+), 1 deletion(-) diff --git a/.kiro/specs/scheduler-sim-diagnostics/tasks.md b/.kiro/specs/scheduler-sim-diagnostics/tasks.md index fa175de4..ac00791e 100644 --- a/.kiro/specs/scheduler-sim-diagnostics/tasks.md +++ b/.kiro/specs/scheduler-sim-diagnostics/tasks.md @@ -107,7 +107,7 @@ - _Requirements: 6.1, 6.3, 6.4_ - _Depends: 9_ -- [ ] 10.2 Confirm units consistency, exclusion rules, and zero regression end to end +- [x] 10.2 Confirm units consistency, exclusion rules, and zero regression end to end - A byte-valued limit's configured-setting display and its live usage display agree on units under every display mode, within one request for that limit's information - A limit whose constrained resource is not byte-valued, or whose own configured value is itself a percentage, is never subject to byte-unit scaling in either display - The complete pre-existing `info limit` test suite, and the complete pre-existing suite for every other `info` subcommand — including the two pre-existing, non-limit displays that share the same underlying formatting logic this requirement set retyped in task 7 — show zero output differences beyond what this requirement set intends diff --git a/src/scheduler/testsuite/test_repl_limit_table.c b/src/scheduler/testsuite/test_repl_limit_table.c index ee244244..682d46fa 100644 --- a/src/scheduler/testsuite/test_repl_limit_table.c +++ b/src/scheduler/testsuite/test_repl_limit_table.c @@ -3257,6 +3257,313 @@ test_limit_prepare_allocation_matches_host_limit_via_sim_registered_plugin( "registered_plugin: PASS\n"); } +/* ---------------------------------------------------------------------- */ +/* Task 10.2 (Requirement 6.1-6.5, 5.1-5.3): the cross-cutting validation */ +/* task closing out this entire increment. Every task above (7/8.1/8.2/9/ */ +/* 10.1) individually proved its own half of the units-consistency story: */ +/* task 8.2's own tests only ever set up a configured-setting line (no */ +/* vres/usage instance in play at all), and task 9's own tests only ever */ +/* exercise the usage line under the REPL's single default (BOTH) mode */ +/* (sim.repl_state is never assigned in either of those two tests, so */ +/* repl_current_units_mode() falls back to XCE_REPL_UNITS_BOTH and is */ +/* never switched). Neither proves, in one request, that BOTH surfaces */ +/* agree on units AND that the agreement holds across more than one mode */ +/* -- which is the literal content of Requirement 6.5 ("...consistently... */ +/* throughout a single request for limit information"). The test below */ +/* closes that gap directly. */ +/* ---------------------------------------------------------------------- */ + +/* + * THE central Requirement 6.5 proof: a single byte-valued ("mem") limit, + * configured with a real GLOBAL/SHARED usage instance so ONE `info limit + * ` dispatch prints both the configured-setting line ("mem = ...") + * and a live usage line ("capacity=... accounted=..."). Captures that one + * combined dump under `set units raw` and again under `set units human` + * (the real repl_params.c dispatch path, not a direct units_mode + * parameter), and asserts BOTH lines agree on the SAME units convention + * within each single capture: under RAW, neither line shows any + * human-readable unit suffix; under HUMAN, neither line shows the raw byte + * count. This is strictly stronger than task 8.2's or task 9's own tests, + * neither of which combines both surfaces in one request, and neither of + * which switches mode against a fixture that has both a configured setting + * AND a live usage instance at the same time. + */ +static void +test_info_limit_configured_and_usage_amounts_agree_on_units_within_one_request( + void) +{ + xce_sched_sim_driver_t sim; + xce_sched_plugin_t plugin; + xce_scheduler_t group_sched; + xce_limit_t* matched = NULL; + var limits_handle; + xce_vres_t* vres; + char* captured_raw; + char* captured_human; + bool handled; + int rc; + + memset(&sim, 0, sizeof(sim)); + rc = xce_sched_core_init(&(sim.core)); + TEST_ASSERT_EQUAL_INT(0, rc); + + sim.repl_state = repl_state_ensure(&sim); + TEST_ASSERT_NOT_NULL(sim.repl_state); + + register_per_host_resource(&sim, "mem"); + register_limit_plugin(&sim, &plugin, &group_sched); + add_configured_limit_with_value(&plugin, "cross_surface_limit", "mem", + 5368709120.0); + + limits_handle = limit_get_configured_limits(&(sim.core)); + rc = key_find(limits_handle, (void*)"cross_surface_limit", + (void**)&matched); + TEST_ASSERT_TRUE(rc > 0); + TEST_ASSERT_NOT_NULL(matched); + + vres = instantiate_and_register_global_vres(&sim, &plugin, matched); + put_shared_fvalue(&(sim.core.sharedInst), vres->vres->idx, 5368709120.0); + put_shared_fvalue(&(sim.core.sharedInUse), vres->vres->idx, 5000000000.0); + + /* --- RAW mode: both the configured-setting line and the usage line(s) + * must show the bare byte count, with no human-readable suffix + * anywhere in the combined dump. */ + handled = repl_dispatch(&sim, "set units raw"); + TEST_ASSERT_FALSE(handled); + captured_raw = capture_dispatch(&sim, "info limit cross_surface_limit"); + + TEST_ASSERT_NOT_NULL_MESSAGE( + strstr(captured_raw, "mem = 5368709120.000000"), + "Requirement 6.5 (RAW): the configured-setting line must render the " + "bare byte count"); + TEST_ASSERT_NOT_NULL_MESSAGE( + strstr(captured_raw, "capacity=5368709120.000000"), + "Requirement 6.5 (RAW): the usage line's capacity must ALSO render " + "the bare byte count -- same request, same mode, same convention " + "as the configured-setting line above"); + TEST_ASSERT_NOT_NULL_MESSAGE( + strstr(captured_raw, "accounted=5000000000.000000"), + "Requirement 6.5 (RAW): ... and the usage line's accounted amount " + "too"); + TEST_ASSERT_NULL_MESSAGE( + strstr(captured_raw, "GB"), + "Requirement 6.5 (RAW): NEITHER surface may show a human-readable " + "unit suffix under RAW mode -- a 'GB' anywhere in this combined " + "dump would mean the two surfaces disagree on units within the " + "same request"); + free(captured_raw); + + /* --- HUMAN mode: both the configured-setting line and the usage + * line(s) must switch to human-readable form together, with no raw + * byte count surviving anywhere in the combined dump. */ + handled = repl_dispatch(&sim, "set units human"); + TEST_ASSERT_FALSE(handled); + captured_human + = capture_dispatch(&sim, "info limit cross_surface_limit"); + + TEST_ASSERT_NOT_NULL_MESSAGE( + strstr(captured_human, "mem = 5.00 GB"), + "Requirement 6.1/6.2/6.5 (HUMAN): the configured-setting line must " + "switch to human-readable form"); + TEST_ASSERT_NOT_NULL_MESSAGE( + strstr(captured_human, "capacity=5.00 GB"), + "Requirement 6.1/6.5 (HUMAN): the usage line's capacity must ALSO " + "switch to human-readable form -- same request, same mode, same " + "convention as the configured-setting line above"); + TEST_ASSERT_NOT_NULL_MESSAGE( + strstr(captured_human, "accounted=4.66 GB"), + "Requirement 6.1/6.5 (HUMAN): ... and the usage line's accounted " + "amount too"); + TEST_ASSERT_NULL_MESSAGE( + strstr(captured_human, "5368709120"), + "Requirement 6.5 (HUMAN): NEITHER surface may show the raw byte " + "count under HUMAN mode -- a bare '5368709120' anywhere in this " + "combined dump would mean the two surfaces disagree on units " + "within the same request"); + TEST_ASSERT_NULL_MESSAGE( + strstr(captured_human, "5000000000"), + "Requirement 6.5 (HUMAN): ... the usage line's raw accounted " + "figure must not survive either"); + free(captured_human); + + printf( + "test_info_limit_configured_and_usage_amounts_agree_on_units_" + "within_one_request: PASS\n"); +} + +/* + * Requirement 6.3's non-byte-valued half, proven end-to-end through the + * REPL (not just fmt_limit_units()'s own direct unit tests in test_limit.c, + * which already cover this at the library level for the configured-setting + * formatter alone). No existing fixture in this file configures a limit on + * a genuinely non-byte-valued resource -- every add_configured_limit()/ + * add_configured_percent_limit() fixture above hardcodes resname "mem". + * This fixture uses "slots" instead, with both a configured setting AND a + * live GLOBAL usage instance, and asserts the ENTIRE combined dump (both + * lines together) is byte-for-byte IDENTICAL across all three units modes + * -- the strongest possible "completely unaffected" proof. + */ +static void +test_info_limit_named_non_byte_valued_limit_display_completely_unaffected_by_units_mode( + void) +{ + xce_sched_sim_driver_t sim; + xce_sched_plugin_t plugin; + xce_scheduler_t group_sched; + xce_limit_t* matched = NULL; + var limits_handle; + xce_vres_t* vres; + char* captured_raw; + char* captured_human; + char* captured_both; + bool handled; + int rc; + + memset(&sim, 0, sizeof(sim)); + rc = xce_sched_core_init(&(sim.core)); + TEST_ASSERT_EQUAL_INT(0, rc); + + sim.repl_state = repl_state_ensure(&sim); + TEST_ASSERT_NOT_NULL(sim.repl_state); + + /* "slots" -- a real, registered, genuinely non-byte-valued resource + * (not one of xce_is_bytes_valued_resource()'s five recognized names), + * required so instantiate_limit()'s own vres->res lookup succeeds (see + * xcelimit.c: a miss there is a hard -ENOENT, independent of any + * units-mode concern). */ + register_per_host_resource(&sim, "slots"); + register_limit_plugin(&sim, &plugin, &group_sched); + add_configured_limit_with_value(&plugin, "slots_limit", "slots", 3.0); + + limits_handle = limit_get_configured_limits(&(sim.core)); + rc = key_find(limits_handle, (void*)"slots_limit", (void**)&matched); + TEST_ASSERT_TRUE(rc > 0); + TEST_ASSERT_NOT_NULL(matched); + + vres = instantiate_and_register_global_vres(&sim, &plugin, matched); + put_shared_fvalue(&(sim.core.sharedInst), vres->vres->idx, 3.0); + put_shared_fvalue(&(sim.core.sharedInUse), vres->vres->idx, 1.0); + + handled = repl_dispatch(&sim, "set units raw"); + TEST_ASSERT_FALSE(handled); + captured_raw = capture_dispatch(&sim, "info limit slots_limit"); + TEST_ASSERT_NOT_NULL_MESSAGE( + strstr(captured_raw, "slots = 3.000000"), + "sanity: the configured-setting line must show the real value"); + TEST_ASSERT_NOT_NULL_MESSAGE( + strstr(captured_raw, "accounted=1.00 capacity=3.00"), + "sanity: the usage line must show the real accounted/capacity " + "amounts"); + + handled = repl_dispatch(&sim, "set units human"); + TEST_ASSERT_FALSE(handled); + captured_human = capture_dispatch(&sim, "info limit slots_limit"); + TEST_ASSERT_EQUAL_STRING_MESSAGE( + captured_raw, captured_human, + "Requirement 6.3: a non-byte-valued limit's ENTIRE display (both " + "the configured-setting line and the usage line together) must be " + "byte-for-byte unaffected by `set units human` -- any difference " + "here would mean byte-unit scaling leaked onto a resource that " + "isn't byte-valued"); + + handled = repl_dispatch(&sim, "set units both"); + TEST_ASSERT_FALSE(handled); + captured_both = capture_dispatch(&sim, "info limit slots_limit"); + TEST_ASSERT_EQUAL_STRING_MESSAGE( + captured_raw, captured_both, + "Requirement 6.3: ... and unaffected by `set units both` too -- " + "identical to the RAW-mode capture in every byte"); + + free(captured_raw); + free(captured_human); + free(captured_both); + + printf( + "test_info_limit_named_non_byte_valued_limit_display_completely_" + "unaffected_by_units_mode: PASS\n"); +} + +/* + * Requirement 6.3's percentage half, proven end-to-end through the REPL + * (fmt_limit_units()'s own test_fmt_limit_units_percent_limit_ignores_ + * units_mode() in test_limit.c already covers this at the library level + * directly; this closes the same gap at the REPL dispatch level, through + * the real `set units` command and repl_info_limit()'s real + * translate_units_mode() bridge -- neither of which the library-level test + * exercises). The limit is percent-based (val.percent=1, 50%) on the + * byte-valued resource "mem" -- deliberately so, since Requirement 6.3's + * own wording singles out "a limit expressed as a percentage of a resource + * whose own canonical unit is not bytes" as the case that must never be + * scaled REGARDLESS of whether the underlying resource is itself + * byte-valued. No usage instance is instantiated here -- this test's whole + * point is the raw percentage literal on the configured-setting line, which + * task 9's own per-host byte-valued-percent-limit test already covers for + * the DIFFERENT, RESOLVED-ABSOLUTE usage amount (Requirement 6.4) -- not + * re-litigated here. + */ +static void +test_info_limit_named_percent_limit_own_percentage_value_never_scaled_by_units_mode( + void) +{ + xce_sched_sim_driver_t sim; + xce_sched_plugin_t plugin; + xce_scheduler_t group_sched; + char* captured_raw; + char* captured_human; + char* captured_both; + bool handled; + int rc; + + memset(&sim, 0, sizeof(sim)); + rc = xce_sched_core_init(&(sim.core)); + TEST_ASSERT_EQUAL_INT(0, rc); + + sim.repl_state = repl_state_ensure(&sim); + TEST_ASSERT_NOT_NULL(sim.repl_state); + + register_per_host_resource(&sim, "mem"); + register_limit_plugin(&sim, &plugin, &group_sched); + add_configured_percent_limit(&plugin, "pct_never_scaled_limit", "mem"); + + handled = repl_dispatch(&sim, "set units raw"); + TEST_ASSERT_FALSE(handled); + captured_raw + = capture_dispatch(&sim, "info limit pct_never_scaled_limit"); + TEST_ASSERT_NOT_NULL_MESSAGE( + strstr(captured_raw, "mem = 50.000000%"), + "sanity: the configured-setting line must show the real percent " + "value under RAW mode"); + + handled = repl_dispatch(&sim, "set units human"); + TEST_ASSERT_FALSE(handled); + captured_human + = capture_dispatch(&sim, "info limit pct_never_scaled_limit"); + TEST_ASSERT_EQUAL_STRING_MESSAGE( + captured_raw, captured_human, + "Requirement 6.3: a percentage-based limit's OWN percentage value " + "must be byte-for-byte unaffected by `set units human` -- the " + "percent literal must never gain a human-readable byte-unit " + "suffix, even though its constrained resource ('mem') is itself " + "byte-valued"); + + handled = repl_dispatch(&sim, "set units both"); + TEST_ASSERT_FALSE(handled); + captured_both + = capture_dispatch(&sim, "info limit pct_never_scaled_limit"); + TEST_ASSERT_EQUAL_STRING_MESSAGE( + captured_raw, captured_both, + "Requirement 6.3: ... and unaffected by `set units both` too -- " + "identical to the RAW-mode capture in every byte"); + + free(captured_raw); + free(captured_human); + free(captured_both); + + printf( + "test_info_limit_named_percent_limit_own_percentage_value_never_" + "scaled_by_units_mode: PASS\n"); +} + int main(void) { UNITY_BEGIN(); @@ -3323,5 +3630,11 @@ int main(void) test_limit_job_election_matches_global_limit_via_sim_registered_plugin); RUN_TEST( test_limit_prepare_allocation_matches_host_limit_via_sim_registered_plugin); + RUN_TEST( + test_info_limit_configured_and_usage_amounts_agree_on_units_within_one_request); + RUN_TEST( + test_info_limit_named_non_byte_valued_limit_display_completely_unaffected_by_units_mode); + RUN_TEST( + test_info_limit_named_percent_limit_own_percentage_value_never_scaled_by_units_mode); return UNITY_END(); }