From 4f60f148fb11d6df83fbf967c7206234f7c20a02 Mon Sep 17 00:00:00 2001 From: Rongsong Shen Date: Thu, 17 Sep 2026 13:22:12 +0000 Subject: [PATCH] feat(scheduler-sim-diagnostics): display per-host usage for host-scoped limits repl_print_limit_usage_per_host() fans a single host-scoped xce_vres_t out across every worker with a populated capacity entry at that vres's shared resource index (w->resvals[idx]/w->resInUse[idx]), rather than constructing any per-host vres object -- matching design.md's (xce_vres_t, worker) pair cardinality model exactly. A worker with no populated capacity entry produces no line at all (not a fabricated zero); a worker with capacity but no charged usage shows accounted=0. Uses the corrected idx<0 + .caps-only bounds-check pattern task 3.1 established, avoiding bug-1824's latent imprecision. This closes out all of Requirement 3 (3.1-3.4). Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01RLSNJhNoULvVcUhXEFafQC --- .../specs/scheduler-sim-diagnostics/tasks.md | 4 +- src/scheduler/src/repl_info.c | 132 +++++++- .../testsuite/test_repl_limit_table.c | 298 ++++++++++++++++++ 3 files changed, 424 insertions(+), 10 deletions(-) diff --git a/.kiro/specs/scheduler-sim-diagnostics/tasks.md b/.kiro/specs/scheduler-sim-diagnostics/tasks.md index 196a9967..867bc560 100644 --- a/.kiro/specs/scheduler-sim-diagnostics/tasks.md +++ b/.kiro/specs/scheduler-sim-diagnostics/tasks.md @@ -36,7 +36,7 @@ - _Requirements: 1.4, 2.1, 2.2, 2.3, 3.3_ - _Depends: 1_ -- [ ] 3.2 Display per-host usage for host-scoped limit instances +- [x] 3.2 Display per-host usage for host-scoped limit instances - For a host-scoped `vres` (explicit or implicit per 2.3), iterate `core.workers`; include a worker only if it has a populated capacity entry (`w->resvals[idx]` non-`NULL`) — read capacity from `w->resvals[idx]` and accounted usage from `w->resInUse[idx]` (same existence-vs-zero-usage distinction as 3.1, applied per worker); label each instance with its scope values (user/queue/project from the vresname, plus host) - A limit varying by more than one scope dimension at once (e.g. per-user and per-host together) shows each distinct combination as its own labeled instance - 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 @@ -65,3 +65,5 @@ No `(P)` markers are used anywhere in this plan. Tasks 2.1–2.3 and 3.1–3.2 a - **Task 1**: `test_limit_plugin.c`'s pre-existing `fixture_init()` never populated `core->plugins` (`plugin_register()` is stubbed to a no-op in that white-box TU). Closed with two new test-local fixture helpers that hand-build a real `core->plugins` kvmgr and `key_add()` the plugin under its real `"xce_sched_limit"` name — necessary to exercise the accessors' real `key_find()` path, not a workaround. `test_repl_limit_table.c` (tasks 2.1+) is a *different* white-box TU with its own include set (per design.md's build-wiring risk note) — it should link real `plugin.c`/`core.c` machinery where `register_limit()`/`xce_sched_core_init()` actually populate `core->plugins`, so it likely does NOT need this same hand-built-kvmgr workaround; confirm this when starting 2.1 rather than assuming the same gap exists there. - **Task 2.1**: Making `repl_info.c` genuinely call `limit_get_configured_limits()` broke the link for ~24 pre-existing white-box test files that `#include "../src/repl_info.c"` without also linking `plugins/limit.c` — this codebase's white-box test convention means any new cross-file call from an included production file ripples to every file that includes the caller but not the callee. Fixed with an identical 5-line link-satisfying stub (`return NULL`) added to each, matching the established "duplicate, don't share" stub idiom already used throughout this file family; verified behavior-preserving since none of those 24 fixtures register the limit plugin (the real function would also return `NULL` there). **Task 3 (which calls `limit_get_usage_instances()`) will very likely hit the identical ripple across an overlapping or identical file set — expect it, and apply the same fix pattern rather than treating it as a surprise.** +- **Task 3.1**: found and fixed a real latent bounds-check imprecision (negative/stale `idx` not safely rejected by a `.count`-based pre-check) while writing the new GLOBAL/SHARED usage-read code — logged as `bug-1824` in `.wolf/buglog.json` rather than fixed in the pre-existing `repl_info_resource_one()` (out of this task's boundary); the new code uses the corrected pattern instead. Task 3.2 confirmed it did NOT repeat the same mistake in its own per-host reads. +- **Task 3.2**: reviewer approved with one non-blocking suggestion — no test directly exercises Requirement 3.1's "per-user and per-host together" combined-dimension case end-to-end (structurally supported by the code, per independent review inspection, just not covered by a dedicated fixture). Worth adding if this area gets touched again; not required before shipping. diff --git a/src/scheduler/src/repl_info.c b/src/scheduler/src/repl_info.c index 650820f2..54133b58 100644 --- a/src/scheduler/src/repl_info.c +++ b/src/scheduler/src/repl_info.c @@ -1246,14 +1246,122 @@ static void repl_print_limit_host_scope_note_if_implicit( } /* - * repl_print_limit_usage - task 3.1, Requirement 1.4/2.1/2.2/2.3/3.3: - * prints `limit`'s live accounted usage against its configured capacity, - * one line per distinct GLOBAL/SHARED xce_vres_t instance found in - * limit_get_usage_instances() whose `->limit` pointer-equals `limit`. - * A host-scoped instance (`vres->vres` not flagged GLOBAL/SHARED) is - * task 3.2's job and is silently skipped here, per design.md's own - * per-instance filter ("skip -- do not display -- any per-host-scoped - * vres in this task"). + * repl_print_limit_usage_per_host - task 3.2, Requirement 1.4/2.1/2.2/2.3/ + * 3.1/3.2/3.4: for ONE host-scoped xce_vres_t (`vres->vres` NOT flagged + * GLOBAL/SHARED), prints one usage line per worker that has a populated + * capacity entry at `vres->vres->idx` in its OWN resvals zarray -- + * design.md's Data Models section: "a displayed 'instance' is therefore a + * (xce_vres_t, worker) pair when host-scoped." There is exactly ONE + * xce_vres_t per matched (user, queue, project) combination regardless of + * host-scoping (build_vres_name(), xcelimit.c, never encodes host) -- this + * function does NOT create a vres per host; it fans a SINGLE vres out over + * every worker by reading each worker's own resvals/resInUse at the SAME + * vres->vres->idx. + * + * Existence-of-instance is gated ONLY on a worker's CAPACITY entry + * (w->resvals[idx]) being present -- never on its in-use entry + * (w->resInUse[idx]), the same existence-vs-zero-usage distinction + * repl_print_limit_usage()'s own GLOBAL/SHARED branch below establishes, + * applied per worker here: a worker with a populated capacity but no + * in-use entry yet gets a genuine accounted=0 line; a worker with no + * populated capacity entry at all (this limit's shadow resource has never + * been written for that host -- the host is configured but untouched) gets + * no line at all, never a fabricated zero (Requirement 2.2 at per-host + * granularity). + * + * Bounds safety mirrors repl_print_limit_usage()'s own corrected pattern + * (bug-1824 is the still-unfixed older mistake in repl_info_resource_one(), + * NOT repeated here): relies on zarray_get()/zarray_get_ex()'s own + * `.caps`-based bound rather than a `.count`-based pre-check (imprecise for + * a sparsely-populated zarray) -- no manual upper-bound pre-check is + * performed at all, matching the GLOBAL/SHARED branch's own omission of + * one. + * + * Returns the number of lines printed (0 if no worker has a populated + * capacity entry at this vres's idx). + */ +static int repl_print_limit_usage_per_host(xce_sched_sim_driver_t* sim, + xce_vres_t* vres) +{ + iterator_t it; + int rc; + int n_shown = 0; + + if (vres->vres->idx < 0) { + /* No capacity index allocated at all -- this vres has never + * actually been evaluated to a usable capacity on any host; do not + * fabricate a display for it (Requirement 2.2). */ + return 0; + } + + rc = iter_make(&it, sim->core.workers); + if (rc < 0) { + return 0; + } + + for (;;) { + xce_sched_worker_t* w = NULL; + xce_val_t* cap_v = NULL; + + rc = iter_next(&it, (void**)&w); + if ((rc < 0) || (rc == RTTI_ITER_NO_DATA) || (w == NULL)) { + break; + } + + cap_v = (xce_val_t*)zarray_get(&(w->resvals), vres->vres->idx); + if ((cap_v == NULL) || (cap_v->which != Value_u_fvalue)) { + /* This worker's capacity entry for this vres does not exist -- + * the limit has never been evaluated against a job on this + * host. Must not appear as a line at all (Observable: "a third + * configured-but-untouched host shows no line at all"). */ + continue; + } + + { + xce_val_t* used_v = NULL; + double used = 0.0; + char value_str[80]; + char used_str[80]; + + used_v = (xce_val_t*)zarray_get(&(w->resInUse), vres->vres->idx); + if ((used_v != NULL) && (used_v->which == Value_u_fvalue)) { + used = used_v->u.fvalue; + } + /* used_v == NULL (or wrong-typed) here is genuine zero usage on + * this host, never "no instance" -- the capacity check above + * already gated existence for this worker; existence must not + * be re-derived from this read. */ + + repl_format_consumable_value_and_used( + repl_current_units_mode(sim), vres->vres->name, + cap_v->u.fvalue, used, value_str, sizeof(value_str), + used_str, sizeof(used_str)); + + /* Requirement 3.4: label with BOTH the scope values already + * encoded in the vres's own name (user/queue/project, via + * build_vres_name()) AND this worker's own hostname, so each + * per-host line is distinguishable from every other. */ + fprintf(stdout, + "[sim] usage %s host=%s: accounted=%s capacity=%s\n", + vres->vres->name, w->name, used_str, value_str); + } + + n_shown++; + } + iter_destroy(&it); + + return n_shown; +} + +/* + * repl_print_limit_usage - task 3.1/3.2, Requirement 1.4/2.1/2.2/2.3/3.1/ + * 3.2/3.3/3.4: prints `limit`'s live accounted usage against its configured + * capacity, one section per distinct xce_vres_t instance found in + * limit_get_usage_instances() whose `->limit` pointer-equals `limit`. A + * GLOBAL/SHARED vres prints exactly one line (this function's own inline + * logic below); a host-scoped vres (`vres->vres` not flagged GLOBAL/ + * SHARED) fans out into zero or more per-host lines via + * repl_print_limit_usage_per_host() above (task 3.2). * * Existence-of-instance is gated ONLY on the matched vres's CAPACITY entry * (core.sharedInst[idx]) being present -- never on the in-use entry @@ -1300,7 +1408,13 @@ static void repl_print_limit_usage(xce_sched_sim_driver_t* sim, } if ((vres->vres->flags & (XCE_RESF_GLOBAL | XCE_RESF_SHARED)) == 0) { - /* Host-scoped instance -- task 3.2's branch, not this one. */ + /* Host-scoped instance (task 3.2): fan out over every worker + * with a populated capacity entry at this vres's shared idx -- + * see repl_print_limit_usage_per_host()'s own doc comment. + * There is still exactly ONE xce_vres_t here; the fan-out + * happens entirely inside that helper by reading each worker's + * OWN resvals/resInUse zarrays. */ + n_shown += repl_print_limit_usage_per_host(sim, vres); continue; } diff --git a/src/scheduler/testsuite/test_repl_limit_table.c b/src/scheduler/testsuite/test_repl_limit_table.c index 1c9869df..046006c3 100644 --- a/src/scheduler/testsuite/test_repl_limit_table.c +++ b/src/scheduler/testsuite/test_repl_limit_table.c @@ -598,6 +598,97 @@ static void put_shared_fvalue(zarray_t* array, int idx, double val) TEST_ASSERT_TRUE(rc >= 0); } +/* + * instantiate_and_register_host_vres - task 3.2's own fixture, mirroring + * instantiate_and_register_global_vres() above but for a HOST-SCOPED + * xce_vres_t: drives the real, already-linked instantiate_limit() + * (xcelimit.c) against `matched_limit` the same way, then key_add()s it + * into `plugin`'s privdata->instances. + * + * `matched_limit` must be configured the way add_configured_percent_limit() + * shapes it (percent-based, on a resource register_per_host_resource() + * registered as non-GLOBAL/non-SHARED) so is_host_based_limit() returns > 0 + * and instantiate_limit()'s internal `shared` local stays 0 -- add_vres() + * then flags the resulting vres->vres XCE_RESF_DEFAULT_HOST (never GLOBAL/ + * SHARED), the exact case task 3.2's own scope covers. + */ +static xce_vres_t* instantiate_and_register_host_vres( + xce_sched_sim_driver_t* sim, xce_sched_plugin_t* plugin, + xce_limit_t* matched_limit) +{ + limit_plugin_data_t* privdata = (limit_plugin_data_t*)plugin->data; + xce_vres_t* vres = NULL; + int rc; + + rc = instantiate_limit(&vres, sim->core.resmgr, matched_limit, "", "", + ""); + TEST_ASSERT_TRUE(rc >= 0); + TEST_ASSERT_NOT_NULL(vres); + TEST_ASSERT_NOT_NULL(vres->vres); + TEST_ASSERT_TRUE(vres->vres->idx >= 0); + TEST_ASSERT_TRUE_MESSAGE( + (vres->vres->flags & (XCE_RESF_GLOBAL | XCE_RESF_SHARED)) == 0, + "fixture sanity: this helper only ever produces a host-scoped " + "(non-GLOBAL/non-SHARED) vres -- a GLOBAL/SHARED vres would mean " + "the fixture's own limit shape drifted from " + "add_configured_percent_limit()'s implicit-host-scope contract"); + + rc = key_add(privdata->instances, (void*)vres->vres->name, (void*)vres); + TEST_ASSERT_TRUE(rc >= 0); + + return vres; +} + +/* + * build_bare_worker - registers a real xce_sched_worker_t named `hostname` + * into `sim`'s core via the real XCE_CORE_EVENT_WORKERS_PUSH path + * (core_events.c's core_upsert_worker()/core_build_worker(), the exact + * production path test_repl_resource_table.c's own + * build_worker_with_one_numeric_resource() already drives) -- this + * zarray_init()s a real resvals/resInUse pair on the new worker, each with + * a real killer (kill_sched_resval, stubbed above), so this file's own + * put_shared_fvalue() can safely zarray_put() directly into a returned + * worker's OWN resvals/resInUse afterward (task 3.2's fan-out reads + * exactly those two zarrays per worker) without this fixture needing to + * drive the real put_resvals()/update_limit_res_value() (resource.c, + * deliberately not linked into this TU, see this file's own header + * comment). No wire resvals are carried on the push itself + * (nresvals_to_sched=0) -- this fixture only needs the worker to exist with + * its own zarrays ready for a direct put_shared_fvalue() write at the + * limit's own shadow-resource idx. + */ +static xce_sched_worker_t* build_bare_worker(xce_sched_sim_driver_t* sim, + const char* hostname) +{ + xce_worker_t* wire_worker; + xce_worker_t* worker_ptrs[1]; + xce_core_event_t events[1]; + int rc; + + wire_worker = (xce_worker_t*)calloc(1, sizeof(*wire_worker)); + TEST_ASSERT_NOT_NULL(wire_worker); + wire_worker->name = (char*)hostname; + wire_worker->status = 0; + wire_worker->nresvals_to_sched = 0; + wire_worker->resvals_to_sched = NULL; + worker_ptrs[0] = wire_worker; + + memset(events, 0, sizeof(events)); + events[0].type = XCE_CORE_EVENT_WORKERS_PUSH; + events[0].u.workers_push.nworkers = 1; + events[0].u.workers_push.workers = worker_ptrs; + + rc = xce_core_push_events(&(sim->core), events, 1); + TEST_ASSERT_EQUAL_INT(0, rc); + + { + xce_sched_worker_t* w + = sched_get_worker(&(sim->core), (char*)hostname); + TEST_ASSERT_NOT_NULL(w); + return w; + } +} + /* ---------------------------------------------------------------------- */ /* Tests */ /* ---------------------------------------------------------------------- */ @@ -1200,6 +1291,207 @@ test_info_limit_named_shows_accounted_zero_for_matched_but_never_charged_instanc "never_charged_instance: PASS\n"); } +/* + * Task 3.2's own central Observable (Requirement 3.1, 3.2, 3.4): a + * host-scoped limit matched against jobs on TWO distinct hosts shows TWO + * SEPARATE usage lines, each labeled with its own host -- and a THIRD + * configured-but-untouched host (registered as a real worker, but never + * given a populated capacity entry at this limit's shadow-resource idx) + * shows no line at all, proving existence is gated on a per-worker + * capacity read, not on the worker merely existing. + */ +static void +test_info_limit_named_shows_separate_per_host_usage_lines_for_touched_hosts_and_none_for_untouched_host( + 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; + xce_sched_worker_t* wA; + xce_sched_worker_t* wB; + char* captured; + int rc; + + memset(&sim, 0, sizeof(sim)); + rc = xce_sched_core_init(&(sim.core)); + TEST_ASSERT_EQUAL_INT(0, rc); + + register_per_host_resource(&sim, "mem"); + register_limit_plugin(&sim, &plugin, &group_sched); + add_configured_percent_limit(&plugin, "host_usage_limit", "mem"); + + limits_handle = limit_get_configured_limits(&(sim.core)); + rc = key_find(limits_handle, (void*)"host_usage_limit", (void**)&matched); + TEST_ASSERT_TRUE(rc > 0); + TEST_ASSERT_NOT_NULL(matched); + + vres = instantiate_and_register_host_vres(&sim, &plugin, matched); + + wA = build_bare_worker(&sim, "hostA"); + wB = build_bare_worker(&sim, "hostB"); + /* hostC is registered as a real worker but deliberately never given a + * populated capacity entry below -- configured, but untouched by this + * limit. */ + (void)build_bare_worker(&sim, "hostC"); + + put_shared_fvalue(&(wA->resvals), vres->vres->idx, 5.0); + put_shared_fvalue(&(wA->resInUse), vres->vres->idx, 2.0); + + put_shared_fvalue(&(wB->resvals), vres->vres->idx, 5.0); + put_shared_fvalue(&(wB->resInUse), vres->vres->idx, 3.0); + + captured = capture_info_limit(&sim, "host_usage_limit"); + + TEST_ASSERT_NOT_NULL_MESSAGE( + strstr(captured, "host=hostA"), + "Requirement 3.2/3.4: a host-scoped limit matched on hostA must " + "show a usage line labeled with hostA"); + TEST_ASSERT_NOT_NULL_MESSAGE( + strstr(captured, "host=hostB"), + "Requirement 3.2/3.4: a host-scoped limit matched on hostB must " + "show a SEPARATE usage line labeled with hostB"); + TEST_ASSERT_NULL_MESSAGE( + strstr(captured, "host=hostC"), + "Observable: a third configured-but-untouched host must show no " + "line at all, not a fabricated zero"); + TEST_ASSERT_NOT_NULL_MESSAGE( + strstr(captured, "accounted=2.00"), + "Requirement 2.1: hostA's own accounted usage must be shown"); + TEST_ASSERT_NOT_NULL_MESSAGE( + strstr(captured, "accounted=3.00"), + "Requirement 2.1: hostB's own accounted usage must be shown, " + "distinct from hostA's"); + + free(captured); + + printf("test_info_limit_named_shows_separate_per_host_usage_lines_for_" + "touched_hosts_and_none_for_untouched_host: PASS\n"); +} + +/* + * Task 3.2's own existence-vs-zero-usage distinction, applied per host + * (mirrors task 3.1's central Observable #3 for the GLOBAL/SHARED case): + * a worker with a genuinely populated CAPACITY entry at this limit's + * shadow-resource idx, but no in-use entry (never charged), must display a + * genuine accounted=0 line for that host -- not omit the host entirely. + */ +static void +test_info_limit_named_shows_accounted_zero_for_host_with_capacity_but_never_charged( + 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; + xce_sched_worker_t* wA; + char* captured; + int rc; + + memset(&sim, 0, sizeof(sim)); + rc = xce_sched_core_init(&(sim.core)); + TEST_ASSERT_EQUAL_INT(0, rc); + + register_per_host_resource(&sim, "mem"); + register_limit_plugin(&sim, &plugin, &group_sched); + add_configured_percent_limit(&plugin, "host_uncharged_limit", "mem"); + + limits_handle = limit_get_configured_limits(&(sim.core)); + rc = key_find(limits_handle, (void*)"host_uncharged_limit", + (void**)&matched); + TEST_ASSERT_TRUE(rc > 0); + TEST_ASSERT_NOT_NULL(matched); + + vres = instantiate_and_register_host_vres(&sim, &plugin, matched); + wA = build_bare_worker(&sim, "hostA"); + + put_shared_fvalue(&(wA->resvals), vres->vres->idx, 5.0); + /* Deliberately no put_shared_fvalue() into wA->resInUse -- a genuinely + * populated capacity entry, but nothing charged against it yet. */ + + captured = capture_info_limit(&sim, "host_uncharged_limit"); + + TEST_ASSERT_NOT_NULL_MESSAGE( + strstr(captured, "host=hostA"), + "hostA has a genuinely populated capacity entry and must appear as " + "a line, even though nothing has been charged against it yet"); + TEST_ASSERT_NOT_NULL_MESSAGE( + strstr(captured, "accounted=0.00"), + "the existence-vs-zero-usage distinction applies per host too: a " + "populated capacity entry with an absent in-use entry must display " + "a genuine accounted=0, not omit the host"); + + free(captured); + + printf("test_info_limit_named_shows_accounted_zero_for_host_with_" + "capacity_but_never_charged: PASS\n"); +} + +/* + * Requirement 2.2 applied to the host-scoped case as a whole: a host-scoped + * xce_vres_t that genuinely exists (instantiated, real capacity idx + * allocated) but for which NO worker has ever been given a populated + * capacity entry must report the same "no active usage instance yet" + * message the GLOBAL/SHARED case reports for an unmatched limit -- proving + * repl_print_limit_usage_per_host()'s own zero-lines-printed return value + * correctly threads through to repl_print_limit_usage()'s outer n_shown + * fallback rather than silently printing nothing with no explanation. + */ +static void +test_info_limit_named_shows_no_active_usage_instance_when_host_scoped_vres_exists_but_no_worker_populated( + 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; + char* captured; + int rc; + + memset(&sim, 0, sizeof(sim)); + rc = xce_sched_core_init(&(sim.core)); + TEST_ASSERT_EQUAL_INT(0, rc); + + register_per_host_resource(&sim, "mem"); + register_limit_plugin(&sim, &plugin, &group_sched); + add_configured_percent_limit(&plugin, "host_untouched_limit", "mem"); + + limits_handle = limit_get_configured_limits(&(sim.core)); + rc = key_find(limits_handle, (void*)"host_untouched_limit", + (void**)&matched); + TEST_ASSERT_TRUE(rc > 0); + TEST_ASSERT_NOT_NULL(matched); + + (void)instantiate_and_register_host_vres(&sim, &plugin, matched); + /* A worker exists, but its capacity entry at this vres's idx is + * deliberately never populated -- the limit's shadow resource has + * never actually been written for any host. */ + (void)build_bare_worker(&sim, "hostA"); + + captured = capture_info_limit(&sim, "host_untouched_limit"); + + TEST_ASSERT_NOT_NULL_MESSAGE( + strstr(captured, "no active usage instance yet"), + "Requirement 2.2: a host-scoped vres that exists but has no worker " + "with a populated capacity entry must report no active usage " + "instance, not a fabricated zero and not silence with no " + "explanation"); + TEST_ASSERT_NULL_MESSAGE( + strstr(captured, "host=hostA"), + "hostA has no populated capacity entry and must not appear as a " + "line"); + + free(captured); + + printf("test_info_limit_named_shows_no_active_usage_instance_when_host_" + "scoped_vres_exists_but_no_worker_populated: PASS\n"); +} + int main(void) { UNITY_BEGIN(); @@ -1230,5 +1522,11 @@ int main(void) test_info_limit_named_shows_no_active_usage_instance_for_never_matched_limit); RUN_TEST( test_info_limit_named_shows_accounted_zero_for_matched_but_never_charged_instance); + RUN_TEST( + test_info_limit_named_shows_separate_per_host_usage_lines_for_touched_hosts_and_none_for_untouched_host); + RUN_TEST( + 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); return UNITY_END(); }