From d3df8fb29f7774b9dd60d6ad46b42e2b07566a1e Mon Sep 17 00:00:00 2001 From: Rongsong Shen Date: Thu, 17 Sep 2026 09:53:59 +0000 Subject: [PATCH] feat(scheduler-sim-diagnostics): implement info limit name/all resolution repl_info_limit() (repl_info.c, not yet wired into repl_cmd_info() -- that's task 4) resolves "all" vs a specific name against limit_get_configured_limits(): a NULL/empty handle on the all path reports "no limits configured" (Req 1.7); a NULL handle or key_find() miss on the named path reports "no such limit ''" (Req 1.3) and never collapses into the all-only message. New white-box test_repl_limit_table.c exercises both paths directly, including a dedicated regression guard for the NULL-scoping rule. Making repl_info.c genuinely call limit_get_configured_limits() broke the link for ~24 other white-box test files that #include repl_info.c without also linking plugins/limit.c -- fixed with the same link-satisfying-stub idiom this file family already uses elsewhere (all return NULL, verified behavior- preserving since none of these fixtures register the limit plugin). Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01RLSNJhNoULvVcUhXEFafQC --- .../specs/scheduler-sim-diagnostics/tasks.md | 3 +- src/scheduler/src/repl_info.c | 78 +++ src/scheduler/testsuite/meson.build | 37 ++ .../test_maxtasks_wiring_sim_driver_parity.c | 15 + .../test_repl_breakpoints_inspection.c | 15 + .../testsuite/test_repl_equivalence.c | 15 + .../testsuite/test_repl_event_echo.c | 15 + .../testsuite/test_repl_execution_control.c | 15 + src/scheduler/testsuite/test_repl_help.c | 15 + .../testsuite/test_repl_limit_table.c | 621 ++++++++++++++++++ .../testsuite/test_repl_prompt_context.c | 15 + .../testsuite/test_repl_resource_table.c | 15 + .../testsuite/test_repl_round_rollup.c | 15 + .../testsuite/test_repl_snapshot_params.c | 15 + .../testsuite/test_repl_step_verbosity.c | 15 + src/scheduler/testsuite/test_sim_alt_events.c | 15 + .../testsuite/test_sim_contention_parity.c | 15 + .../test_sim_e2e_original_motivation.c | 15 + .../testsuite/test_sim_equivalence.c | 15 + src/scheduler/testsuite/test_sim_event_loop.c | 15 + .../testsuite/test_sim_print_decision_gate.c | 15 + .../test_sim_reconcile_unaccounted.c | 15 + .../testsuite/test_sim_record_output.c | 15 + .../testsuite/test_sim_resource_contention.c | 15 + .../test_sim_round_boundary_reconciliation.c | 15 + .../testsuite/test_sim_session_ordering.c | 15 + .../test_sim_snapshot_reconcile_gap.c | 15 + src/scheduler/testsuite/test_sim_verify.c | 15 + 28 files changed, 1098 insertions(+), 1 deletion(-) create mode 100644 src/scheduler/testsuite/test_repl_limit_table.c diff --git a/.kiro/specs/scheduler-sim-diagnostics/tasks.md b/.kiro/specs/scheduler-sim-diagnostics/tasks.md index e0e12332..99755f29 100644 --- a/.kiro/specs/scheduler-sim-diagnostics/tasks.md +++ b/.kiro/specs/scheduler-sim-diagnostics/tasks.md @@ -9,7 +9,7 @@ - _Boundary: Limit Plugin Accessor_ - [ ] 2. Core: Limit lookup and configured-setting display -- [ ] 2.1 Implement name/all resolution with correct absence messaging +- [x] 2.1 Implement name/all resolution with correct absence messaging - New white-box test file `test_repl_limit_table.c` created (sibling of `test_repl_resource_table.c`, own `#include` set covering `plugins/limit.c` and `resource.c` for real — not stubbed — per `design.md`'s build-wiring risk note citing bug-1762's precedent) - `repl_info_limit(sim, arg)`: `arg == "all"` iterates `limit_get_configured_limits()`; `NULL`/empty prints "no limits configured" (Requirement 1.7); `arg != "all"` does a `key_find()` by name (not a scan) on the same handle, printing "no such limit ''" (Requirement 1.3) for a `NULL` handle or a miss — a `NULL` handle on this path is never treated as "no limits configured," matching that message's own `all`-only scoping - Wired as a direct call for now (`repl_dispatch()`/`repl_cmd_info()` wiring itself deferred to task 4); tests in this task call `repl_info_limit()` directly, not through `repl_dispatch()` @@ -64,3 +64,4 @@ No `(P)` markers are used anywhere in this plan. Tasks 2.1–2.3 and 3.1–3.2 a ## Implementation Notes - **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.** diff --git a/src/scheduler/src/repl_info.c b/src/scheduler/src/repl_info.c index f6f7114f..0695e32a 100644 --- a/src/scheduler/src/repl_info.c +++ b/src/scheduler/src/repl_info.c @@ -1154,6 +1154,84 @@ static void repl_info_resource(xce_sched_sim_driver_t* sim, const char* arg) } } +/* + * repl_info_limit - `info limit ` 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 + * test_repl_limit_table.c per this task's own scope. + * + * Requirement 1.7's "no limits configured" message is reserved for the + * `all` path specifically -- its own acceptance criterion scopes it to + * "when an operator requests information for all limits". On the named + * path (`arg != "all"`), a NULL limit_get_configured_limits() handle (the + * limit plugin was never registered -- a simulation with nothing + * configured never registers it) reports Requirement 1.3's "no such limit + * ''" instead: absence of ANY configuration and absence of THIS ONE + * name collapse to the same "no such limit" message on the named path, + * per design.md's REPL Limit Info Command Responsibilities and Error + * Handling's "Limit plugin not registered/initialized" category. A NULL + * handle must never fall through to "no limits configured" on this path. + */ +static void repl_info_limit(xce_sched_sim_driver_t* sim, const char* arg) +{ + var limits = limit_get_configured_limits(&(sim->core)); + + if (strcmp(arg, "all") == 0) { + iterator_t it; + xce_limit_t* limit = NULL; + int rc; + int n_shown = 0; + + if (limits == NULL) { + fprintf(stdout, "[sim] no limits configured\n"); + return; + } + + rc = iter_make(&it, limits); + if (rc < 0) { + fprintf(stdout, "[sim] no limits configured\n"); + return; + } + for (;;) { + rc = iter_next(&it, (void**)&limit); + 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. */ + n_shown++; + } + iter_destroy(&it); + + if (n_shown == 0) { + fprintf(stdout, "[sim] no limits configured\n"); + } + return; + } + + { + xce_limit_t* match = NULL; + int rc = (limits != NULL) + ? key_find(limits, (void*)arg, (void**)&match) + : -1; + + if ((limits == NULL) || (rc <= 0) || (match == NULL)) { + fprintf(stdout, "[sim] info limit: no such limit '%s'\n", 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. */ + } +} + /* * repl_info_summary - AC 22's "info summary": "Quick overview: N_jobs (M * pending, K running), N_workers (M online), N_queues, N_resources." Four diff --git a/src/scheduler/testsuite/meson.build b/src/scheduler/testsuite/meson.build index c3136858..c21bcbf6 100644 --- a/src/scheduler/testsuite/meson.build +++ b/src/scheduler/testsuite/meson.build @@ -1103,6 +1103,40 @@ sched_repl_resource_table_test = executable( dependencies: [scheduler_test_deps, libedit_dep], install: false) +# Contract test for the interactive simulation REPL's `info limit ` +# name/all resolution and absence messaging (scheduler-sim-diagnostics spec, +# Requirement Set 1, task 2.1, Requirements 1.3, 1.7, 4.1, 4.2). Split into +# its own file rather than added to test_repl_resource_table.c, per +# design.md's own explicit build-wiring risk note: that file's white-box +# #include set deliberately does NOT link plugins/limit.c or resource.c +# ("resource.c, not linked here"), and this task needs the REAL +# plugins/limit.c (for the real limit_get_configured_limits()/limit_init()/ +# limit_update_config() its own fixtures and repl_info_limit() call) -- +# not stubbed, the same bug-1762 precedent design.md cites for a different +# scheduler-sim-repl-verbosity command that hit the identical undefined- +# reference link-failure class. Same white-box #include pattern as +# sched_repl_resource_table_test above (core.c, core_clock.c, core_events.c, +# core_session.c, decision.c, plugin.c, plugins/builtin_fcfs.c, recording.c, +# recording_session.c, sim.c, sim_loop.c, sim_picker.c, sim_record.c, +# sim_verify.c, snapshot.c/snapshot_wire.c/snapshot_load.c, repl.c, +# repl_fmt.c, repl_rollup.c, repl_break.c, repl_info.c, repl_params.c) PLUS +# the new ../src/plugins/limit.c under test. task 2.1's own scope never +# reads live usage/resource accounting (that's task 3), so resource.c is +# NOT linked here -- the same minimal link-satisfying stubs +# test_limit_plugin.c already establishes for limit.c's unused +# limit_job_election()/limit_prepare_allocation() hook bodies +# (reset_sched_obj()/get_resvals()/put_resvals()) are reused verbatim, +# confirmed sufficient by actually attempting the build and reading the +# resulting undefined-reference errors rather than guessing blindly. +# `repl_info_limit()` is not yet wired into repl_cmd_info() (task 4) -- every +# test in this file calls it directly, not through repl_dispatch(). +sched_repl_limit_table_test = executable( + 'sched_repl_limit_table_test', + ['test_repl_limit_table.c'], + include_directories:[incdir], + dependencies: [scheduler_test_deps, libedit_dep], + install: false) + # Contract test for the interactive simulation REPL's Help & Breakpoint # Discoverability component (scheduler-sim-repl-verbosity spec, task 9.1, # Requirements 7.1-7.4): `help`'s listing groups commands into four labeled @@ -1828,6 +1862,9 @@ test('scheduler repl breakpoints inspection test', test('scheduler repl resource table test', sched_repl_resource_table_test, suite: 'scheduler', env: ['ASAN_OPTIONS=detect_leaks=0']) +test('scheduler repl limit table test', + sched_repl_limit_table_test, suite: 'scheduler', + env: ['ASAN_OPTIONS=detect_leaks=0']) test('scheduler repl help test', sched_repl_help_test, suite: 'scheduler', env: ['ASAN_OPTIONS=detect_leaks=0']) diff --git a/src/scheduler/testsuite/test_maxtasks_wiring_sim_driver_parity.c b/src/scheduler/testsuite/test_maxtasks_wiring_sim_driver_parity.c index f6520cf8..1a8a9b94 100644 --- a/src/scheduler/testsuite/test_maxtasks_wiring_sim_driver_parity.c +++ b/src/scheduler/testsuite/test_maxtasks_wiring_sim_driver_parity.c @@ -165,6 +165,21 @@ void kill_sched_user(void* arg) { (void)arg; } void kill_sched_project(void* arg) { (void)arg; } void kill_sched_resval(xce_val_t* val) { (void)val; } +/* repl_info.c (scheduler-sim-diagnostics spec, task 2.1) now calls + * limit_get_configured_limits() (plugins/limit.c, not part of this TU) to + * resolve `info limit`'s name/all argument -- link-satisfying stub only, + * same rationale as kill_sched_job()/kill_sched_resval() above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_configured_limits()'s own documented "plugin absent" return) + * is also the behaviorally correct answer here, not just a link + * placeholder. repl_info_limit() itself is not yet wired into + * repl_cmd_info() (task 4), so no test in this file can reach it anyway. */ +var limit_get_configured_limits(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + int sched_reconcile_job_resource_usage(xce_sched_core_t* c, xce_job_account_t* a, xce_job_rusages_t* r) diff --git a/src/scheduler/testsuite/test_repl_breakpoints_inspection.c b/src/scheduler/testsuite/test_repl_breakpoints_inspection.c index 4e4e758f..fef5054b 100644 --- a/src/scheduler/testsuite/test_repl_breakpoints_inspection.c +++ b/src/scheduler/testsuite/test_repl_breakpoints_inspection.c @@ -101,6 +101,21 @@ void kill_sched_user(void* arg) { (void)arg; } void kill_sched_project(void* arg) { (void)arg; } void kill_sched_resval(xce_val_t* val) { (void)val; } +/* repl_info.c (scheduler-sim-diagnostics spec, task 2.1) now calls + * limit_get_configured_limits() (plugins/limit.c, not part of this TU) to + * resolve `info limit`'s name/all argument -- link-satisfying stub only, + * same rationale as kill_sched_job()/kill_sched_resval() above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_configured_limits()'s own documented "plugin absent" return) + * is also the behaviorally correct answer here, not just a link + * placeholder. repl_info_limit() itself is not yet wired into + * repl_cmd_info() (task 4), so no test in this file can reach it anyway. */ +var limit_get_configured_limits(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts != diff --git a/src/scheduler/testsuite/test_repl_equivalence.c b/src/scheduler/testsuite/test_repl_equivalence.c index 415c6f35..ce8e837d 100644 --- a/src/scheduler/testsuite/test_repl_equivalence.c +++ b/src/scheduler/testsuite/test_repl_equivalence.c @@ -150,6 +150,21 @@ void kill_sched_user(void* arg) { (void)arg; } void kill_sched_project(void* arg) { (void)arg; } void kill_sched_resval(xce_val_t* val) { (void)val; } +/* repl_info.c (scheduler-sim-diagnostics spec, task 2.1) now calls + * limit_get_configured_limits() (plugins/limit.c, not part of this TU) to + * resolve `info limit`'s name/all argument -- link-satisfying stub only, + * same rationale as kill_sched_job()/kill_sched_resval() above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_configured_limits()'s own documented "plugin absent" return) + * is also the behaviorally correct answer here, not just a link + * placeholder. repl_info_limit() itself is not yet wired into + * repl_cmd_info() (task 4), so no test in this file can reach it anyway. */ +var limit_get_configured_limits(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts != diff --git a/src/scheduler/testsuite/test_repl_event_echo.c b/src/scheduler/testsuite/test_repl_event_echo.c index f1202757..978441a4 100644 --- a/src/scheduler/testsuite/test_repl_event_echo.c +++ b/src/scheduler/testsuite/test_repl_event_echo.c @@ -118,6 +118,21 @@ void kill_sched_user(void* arg) { (void)arg; } void kill_sched_project(void* arg) { (void)arg; } void kill_sched_resval(xce_val_t* val) { (void)val; } +/* repl_info.c (scheduler-sim-diagnostics spec, task 2.1) now calls + * limit_get_configured_limits() (plugins/limit.c, not part of this TU) to + * resolve `info limit`'s name/all argument -- link-satisfying stub only, + * same rationale as kill_sched_job()/kill_sched_resval() above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_configured_limits()'s own documented "plugin absent" return) + * is also the behaviorally correct answer here, not just a link + * placeholder. repl_info_limit() itself is not yet wired into + * repl_cmd_info() (task 4), so no test in this file can reach it anyway. */ +var limit_get_configured_limits(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts != diff --git a/src/scheduler/testsuite/test_repl_execution_control.c b/src/scheduler/testsuite/test_repl_execution_control.c index 4d610eb5..b4af286e 100644 --- a/src/scheduler/testsuite/test_repl_execution_control.c +++ b/src/scheduler/testsuite/test_repl_execution_control.c @@ -115,6 +115,21 @@ void kill_sched_user(void* arg) { (void)arg; } void kill_sched_project(void* arg) { (void)arg; } void kill_sched_resval(xce_val_t* val) { (void)val; } +/* repl_info.c (scheduler-sim-diagnostics spec, task 2.1) now calls + * limit_get_configured_limits() (plugins/limit.c, not part of this TU) to + * resolve `info limit`'s name/all argument -- link-satisfying stub only, + * same rationale as kill_sched_job()/kill_sched_resval() above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_configured_limits()'s own documented "plugin absent" return) + * is also the behaviorally correct answer here, not just a link + * placeholder. repl_info_limit() itself is not yet wired into + * repl_cmd_info() (task 4), so no test in this file can reach it anyway. */ +var limit_get_configured_limits(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts != diff --git a/src/scheduler/testsuite/test_repl_help.c b/src/scheduler/testsuite/test_repl_help.c index a5e7fbb3..78581b4c 100644 --- a/src/scheduler/testsuite/test_repl_help.c +++ b/src/scheduler/testsuite/test_repl_help.c @@ -110,6 +110,21 @@ void kill_sched_user(void* arg) { (void)arg; } void kill_sched_project(void* arg) { (void)arg; } void kill_sched_resval(xce_val_t* val) { (void)val; } +/* repl_info.c (scheduler-sim-diagnostics spec, task 2.1) now calls + * limit_get_configured_limits() (plugins/limit.c, not part of this TU) to + * resolve `info limit`'s name/all argument -- link-satisfying stub only, + * same rationale as kill_sched_job()/kill_sched_resval() above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_configured_limits()'s own documented "plugin absent" return) + * is also the behaviorally correct answer here, not just a link + * placeholder. repl_info_limit() itself is not yet wired into + * repl_cmd_info() (task 4), so no test in this file can reach it anyway. */ +var limit_get_configured_limits(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts != diff --git a/src/scheduler/testsuite/test_repl_limit_table.c b/src/scheduler/testsuite/test_repl_limit_table.c new file mode 100644 index 00000000..c4b46a7d --- /dev/null +++ b/src/scheduler/testsuite/test_repl_limit_table.c @@ -0,0 +1,621 @@ +/* + * test_repl_limit_table.c - contract test for the interactive simulation + * REPL's `info limit ` name/all resolution and absence messaging + * (scheduler-sim-diagnostics spec, Requirement Set 1, task 2.1, Req 1.3, + * 1.7, 4.1, 4.2). + * + * New white-box test file, sibling of test_repl_resource_table.c -- kept + * separate rather than added to that file, per design.md's own explicit + * build-wiring risk note (Implementation Notes, "REPL Limit Info Command"): + * test_repl_resource_table.c white-box #includes repl_info.c but + * deliberately does NOT link plugins/limit.c or resource.c ("resource.c, + * not linked here"). This file needs the REAL plugins/limit.c (for the + * real limit_get_configured_limits()/limit_init()/limit_update_config() + * repl_info_limit() and this file's own fixtures call) -- not stubbed, + * same bug-1762 precedent design.md cites for a different scheduler-sim- + * repl-verbosity command that hit the identical class of undefined- + * reference link failure. + * + * `repl_info_limit()` is NOT yet wired into repl_cmd_info() (that's task + * 4) -- every test below calls it directly, not through repl_dispatch(). + * + * White-box #include set: identical to test_repl_resource_table.c's own + * (core.c, core_clock.c, core_events.c, core_session.c, decision.c, + * plugin.c, plugins/builtin_fcfs.c, recording.c, recording_session.c, + * repl.c, repl_fmt.c, repl_rollup.c, repl_break.c, repl_info.c, + * repl_params.c, sim.c, sim_loop.c, sim_picker.c, sim_record.c, + * sim_verify.c, snapshot.c, snapshot_load.c, snapshot_wire.c) PLUS the new + * ../src/plugins/limit.c under test. task 2.1's own scope never reads live + * usage/resource accounting (that's task 3), so resource.c is NOT linked + * here either -- the same minimal link-satisfying stubs + * test_limit_plugin.c already establishes for limit.c's unused + * limit_job_election()/limit_prepare_allocation() hook bodies + * (reset_sched_obj()/get_resvals()/put_resvals(), none of which task 2.1's + * own code path ever calls) are reused verbatim here, confirmed sufficient + * by actually attempting the build and reading the resulting undefined- + * reference errors rather than guessing blindly, per design.md's own risk + * note. + * + * Fixture strategy: unlike test_limit_plugin.c's own TU (which links + * neither core.c nor plugin.c, so its fixture_init()/ + * fixture_register_plugin_in_core() hand-build a standalone core->plugins + * kvmgr), this file links the REAL ../src/core.c, so xce_sched_core_init() + * already creates a real, usable sim->core.plugins kvmgr on its own -- + * register_limit_plugin() (below) key_add()s a real, limit_init()'d + * xce_sched_plugin_t directly into THAT kvmgr, and add_configured_limit() + * drives the real limit_update_config() (plugins/limit.c) against it, + * exactly the machinery Implementation Notes' "task 1" note predicted this + * file would have available and confirms here. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "controller.capnp.h" +#include "group.h" +#include "kvmgr.h" +#include "scheduler.h" +#include "xce/types.h" +#include "xcelimit.h" +#include "xstring.h" + +/* Pull in the real functions under test into this TU (white-box #include, + * same convention as test_repl_resource_table.c/test_repl_round_rollup.c/ + * test_repl_event_echo.c). */ +#include "../src/core.c" +#include "../src/core_clock.c" +#include "../src/core_events.c" +#include "../src/core_session.c" +#include "../src/decision.c" +#include "../src/plugin.c" +#include "../src/plugins/builtin_fcfs.c" +#include "../src/plugins/limit.c" +#include "../src/recording.c" +#include "../src/recording_session.c" +#include "../src/repl.c" +#include "../src/repl_fmt.c" +#include "../src/repl_rollup.c" +#include "../src/repl_break.c" +#include "../src/repl_info.c" +#include "../src/repl_params.c" +#include "../src/sim.c" +#include "../src/sim_loop.c" +#include "../src/sim_picker.c" /* bug-1567: sched_sim_driver_run() now calls sim_bootstrap_picker() (sim_loop.c-cap sibling) as an extern -- undefined reference at link time otherwise */ +#include "../src/sim_record.c" +#include "../src/sim_verify.c" +#include "../src/snapshot.c" +#include "../src/snapshot_load.c" +#include "../src/snapshot_wire.c" + +void kill_sched_job(void* arg) { (void)arg; } +void kill_sched_queue(void* arg) { (void)arg; } +void kill_sched_worker(void* arg) { (void)arg; } +void kill_sched_user(void* arg) { (void)arg; } +void kill_sched_project(void* arg) { (void)arg; } +void kill_sched_resval(xce_val_t* val) { (void)val; } + +/* resource-accounting.md's reconciliation (resource.c, not linked here -- + * this file only #includes core_events.c, same as test_repl_resource_table.c). + * core_events.c's XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever + * job->accounts != NULL; link-satisfying stub only, same rationale as every + * other stub in this block. */ +int sched_reconcile_job_resource_usage(xce_sched_core_t* c, + xce_job_account_t* a, + xce_job_rusages_t* r) +{ + (void)c; (void)a; (void)r; return 0; +} + +void sched_worker_release_host_ctx(xce_sched_worker_t* w) { (void)w; } + +int sched_fill_limit_requests(xce_sched_alloc_req_t* alloc_req, + xlist_t* limits, xlist_t* rusages, int nhosts, + int nslots) +{ + (void)alloc_req; + (void)limits; + (void)rusages; + (void)nhosts; + (void)nslots; + return 0; +} + +int sched_account_jobs(xce_sched_core_t* core) +{ + (void)core; + return 0; +} + +/* Real reimplementation of schedobj.c's sched_resolve_job_picker -- same + * copy test_repl_resource_table.c/test_repl_execution_control.c/ + * test_sim_alt_events.c/etc. each carry (schedobj.c is not linked here). */ +int sched_resolve_job_picker(xce_sched_core_t* core, xce_sched_plugin_t** out) +{ + xce_sched_plugin_t* plugin = NULL; + int rc = 0; + + if ((core == NULL) || (out == NULL)) { + return -EINVAL; + } + + *out = NULL; + + if (core->job_pick_plugin == NULL) { + return -EINVAL; + } + + lock(core->plugins, RTTI_LOCK_READ); + rc = key_find(core->plugins, core->job_pick_plugin, (void**)&plugin); + unlock(core->plugins); + + if (rc <= 0) { + return (rc < 0) ? rc : -ENOENT; + } + + if ((plugin == NULL) || (plugin->picker_iface == NULL) + || (plugin->picker_iface->next_job == NULL) + || (plugin->picker_iface->begin_session == NULL) + || (plugin->picker_iface->end_session == NULL) + || (plugin->picker_iface->on_dispatch == NULL) + || (plugin->picker_iface->on_pend == NULL)) { + return -EINVAL; + } + + *out = plugin; + + return 0; +} + +/* plugins/limit.c's limit_job_election()/limit_prepare_allocation() (never + * exercised by any test in this file -- task 2.1's own scope never touches + * usage/accounting) reference reset_sched_obj() (../src/schedobj.c) and + * get_resvals()/put_resvals() (../src/resource.c), neither of which is part + * of this white-box TU. Stubbed as no-ops purely to satisfy the linker -- + * same pattern test_limit_plugin.c already establishes for the identical + * symbols. */ +void reset_sched_obj(xce_sched_obj_t* obj) +{ + (void)obj; +} + +int get_resvals(zarray_t* array, int nres, int* idxs, float* vals) +{ + (void)array; + (void)nres; + (void)idxs; + (void)vals; + return 0; +} + +int put_resvals(zarray_t* array, int nres, int* idxs, float* vals) +{ + (void)array; + (void)nres; + (void)idxs; + (void)vals; + return 0; +} + +void setUp(void) { return; } +void tearDown(void) { return; } + +/* ---------------------------------------------------------------------- */ +/* stdout-capture helper -- duplicated from test_repl_resource_table.c */ +/* (same dup()/dup2()-around-STDOUT_FILENO technique that file's own */ +/* header comment establishes), per this file family's own "duplicate, */ +/* don't share" convention. */ +/* ---------------------------------------------------------------------- */ +static char* capture_stdout_around(void (*fn)(void)) +{ + char tmp[] = "/tmp/xce_repllimtbl_stdoutcap_XXXXXX"; + int fd = mkstemp(tmp); + int saved_stdout; + off_t len; + char* buf; + + TEST_ASSERT_TRUE_MESSAGE(fd >= 0, "mkstemp for stdout capture must " + "succeed"); + + fflush(stdout); + saved_stdout = dup(STDOUT_FILENO); + TEST_ASSERT_TRUE_MESSAGE(saved_stdout >= 0, "dup(STDOUT_FILENO) must " + "succeed"); + TEST_ASSERT_TRUE_MESSAGE(dup2(fd, STDOUT_FILENO) >= 0, + "dup2 redirect of STDOUT_FILENO must succeed"); + + fn(); + + fflush(stdout); + dup2(saved_stdout, STDOUT_FILENO); + close(saved_stdout); + + len = lseek(fd, 0, SEEK_CUR); + TEST_ASSERT_TRUE(len >= 0); + + buf = (char*)calloc(1, (size_t)len + 1); + TEST_ASSERT_NOT_NULL(buf); + TEST_ASSERT_TRUE(lseek(fd, 0, SEEK_SET) == 0); + if (len > 0) { + ssize_t nread = read(fd, buf, (size_t)len); + TEST_ASSERT_TRUE(nread == len); + } + close(fd); + unlink(tmp); + + return buf; +} + +/* Captures the output of calling repl_info_limit(sim, arg) DIRECTLY -- + * task 2.1's own scope calls it directly, not through repl_dispatch() + * (that wiring is task 4's job). */ +static xce_sched_sim_driver_t* g_direct_sim; +static const char* g_direct_arg; + +static void run_info_limit_for_capture(void) +{ + repl_info_limit(g_direct_sim, g_direct_arg); +} + +static char* capture_info_limit(xce_sched_sim_driver_t* sim, const char* arg) +{ + g_direct_sim = sim; + g_direct_arg = arg; + return capture_stdout_around(run_info_limit_for_capture); +} + +/* ---------------------------------------------------------------------- */ +/* Limit configuration fixture helpers */ +/* ---------------------------------------------------------------------- */ + +/* + * register_limit_plugin - registers a real, limit_init()'d xce_sched_plugin_t + * directly into `sim`'s own core->plugins kvmgr (xce_sched_core_init() -- + * already called on `sim` by every test below -- creates that kvmgr for + * real, since this TU links ../src/core.c), so + * limit_get_configured_limits(&(sim->core))'s key_find(core->plugins, + * "xce_sched_limit", ...) genuinely finds it -- the exact real lookup path + * repl_info_limit() drives. + * + * `plugin`/`group_sched` are caller-owned (stack) storage, not static + * globals, so each test gets its own independent fixture. `group_sched` is + * a throwaway xce_scheduler_t existing purely so limit_update_config() + * (called separately by add_configured_limit() below) has real group + * managers to resolve each consumer's "all"-group fallback from + * (build_limit()/build_consumer(), xcelimit.c) -- the built xce_limit_t/ + * xce_group_t values it produces are owned by plugin->data's own + * privdata->limits kvmgr, not by group_sched, so group_sched need not be + * (and is not) sim's own core. Same minimal group-manager technique + * test_limit_plugin.c's own fixture_init() already establishes + * (groupmgr_init() x4), reused here rather than re-derived, per + * Implementation Notes' own "task 1" note. + */ +static void register_limit_plugin(xce_sched_sim_driver_t* sim, + xce_sched_plugin_t* plugin, + xce_scheduler_t* group_sched) +{ + int rc; + + memset(group_sched, 0, sizeof(*group_sched)); + groupmgr_init(&(group_sched->core.ugmgr), 4, 4); + groupmgr_init(&(group_sched->core.qgmgr), 4, 4); + groupmgr_init(&(group_sched->core.pgmgr), 4, 4); + groupmgr_init(&(group_sched->core.hgmgr), 4, 4); + TEST_ASSERT_NOT_NULL(group_sched->core.ugmgr); + TEST_ASSERT_NOT_NULL(group_sched->core.qgmgr); + TEST_ASSERT_NOT_NULL(group_sched->core.pgmgr); + TEST_ASSERT_NOT_NULL(group_sched->core.hgmgr); + + memset(plugin, 0, sizeof(*plugin)); + plugin->name = "xce_sched_limit"; + plugin->sched = group_sched; + + rc = limit_init(plugin); + TEST_ASSERT_EQUAL_INT(0, rc); + TEST_ASSERT_NOT_NULL(plugin->data); + + rc = key_add(sim->core.plugins, plugin->name, (void*)plugin); + TEST_ASSERT_TRUE(rc >= 0); +} + +/* A minimal, heap-allocated xce_limit_config_t -- identical shape to + * test_limit_plugin.c's own make_limit_config()/free_limit_config() + * (duplicated here per this file family's own "duplicate, don't share" + * convention): no per-consumer groups, so build_limit()'s build_consumer() + * falls back to get_group(mgr, "all") for every consumer. `name` is + * expected to be a string literal (build_limit() strdup()s it). */ +static xce_limit_config_t* make_limit_config(const char* name) +{ + xce_limit_config_t* cfg = (xce_limit_config_t*)calloc(1, sizeof(*cfg)); + xce_limit_val_t* val = (xce_limit_val_t*)calloc(1, sizeof(*val)); + + TEST_ASSERT_NOT_NULL(cfg); + TEST_ASSERT_NOT_NULL(val); + + val->resname = "mem"; + val->percent = 0; + val->val = 10.0; + + cfg->name = (char*)name; + cfg->val = val; + + return cfg; +} + +static void free_limit_config(xce_limit_config_t* cfg) +{ + FREEUP(cfg->val); + FREEUP(cfg); +} + +/* Drives the real limit_update_config() (plugins/limit.c) to configure ONE + * limit named `name` on `plugin` -- the exact production entry point a + * scheduler config update reaches this plugin through, not a hand-rolled + * xce_limit_t. */ +static void add_configured_limit(xce_sched_plugin_t* plugin, const char* name) +{ + xce_limit_config_t* limits[1]; + xce_conf4sched_t config; + int rc; + + limits[0] = make_limit_config(name); + + memset(&config, 0, sizeof(config)); + config.nlimits = 1; + config.limits = limits; + + rc = limit_update_config(plugin, &config); + TEST_ASSERT_EQUAL_INT(0, rc); + + free_limit_config(limits[0]); +} + +/* ---------------------------------------------------------------------- */ +/* Tests */ +/* ---------------------------------------------------------------------- */ + +/* + * Requirement 1.7: `info limit all` against a fixture with zero configured + * limits (the limit plugin was never registered at all -- the realistic + * "nothing configured" scenario per design.md's Error Handling) must + * report that no limits are configured. + */ +static void +test_info_limit_all_reports_no_limits_configured_when_plugin_never_registered( + void) +{ + xce_sched_sim_driver_t sim; + char* captured; + int rc; + + memset(&sim, 0, sizeof(sim)); + rc = xce_sched_core_init(&(sim.core)); + TEST_ASSERT_EQUAL_INT(0, rc); + + captured = capture_info_limit(&sim, "all"); + + TEST_ASSERT_NOT_NULL_MESSAGE( + strstr(captured, "no limits configured"), + "Requirement 1.7: 'info limit all' against a fixture with zero " + "configured limits must report that no limits are configured"); + + free(captured); + + printf("test_info_limit_all_reports_no_limits_configured_when_plugin_" + "never_registered: PASS\n"); +} + +/* + * Requirement 1.7 / design.md's Error Handling section: a REGISTERED + * plugin with zero configured limits (privdata->limits is a real, non-NULL, + * empty kvmgr) must ALSO collapse to "no limits configured" on the `all` + * path -- not an empty listing with no explanation. + */ +static void +test_info_limit_all_reports_no_limits_configured_when_plugin_registered_with_zero_limits( + 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); + /* Deliberately no add_configured_limit() call -- plugin registered, + * privdata->limits real but empty. */ + + captured = capture_info_limit(&sim, "all"); + + TEST_ASSERT_NOT_NULL_MESSAGE( + strstr(captured, "no limits configured"), + "a registered plugin with zero configured limits must also report " + "'no limits configured' on the 'all' path"); + + free(captured); + + printf("test_info_limit_all_reports_no_limits_configured_when_plugin_" + "registered_with_zero_limits: PASS\n"); +} + +/* + * Positive-path proof for the `all` resolution: once at least one limit is + * genuinely configured, the Requirement 1.7 message must NOT appear -- + * proves the all-path iteration reaches the real, non-empty handle rather + * than misfiring the empty-case message unconditionally. + */ +static void +test_info_limit_all_does_not_report_no_limits_configured_when_limits_are_configured( + 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, "ug1_slots"); + + captured = capture_info_limit(&sim, "all"); + + TEST_ASSERT_NULL_MESSAGE( + strstr(captured, "no limits configured"), + "'info limit all' must NOT report 'no limits configured' once at " + "least one limit is genuinely configured"); + + free(captured); + + printf("test_info_limit_all_does_not_report_no_limits_configured_when_" + "limits_are_configured: PASS\n"); +} + +/* + * Requirement 1.3 (this task's own Observable): a named lookup for a limit + * name that is NOT configured, against a fixture with OTHER limits + * configured, must report "no such limit" -- and must never report + * Requirement 1.7's all-only message. + */ +static void +test_info_limit_named_reports_no_such_limit_for_unconfigured_name_against_fixture_with_other_limits( + 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, "ug1_slots"); + + captured = capture_info_limit(&sim, "does_not_exist"); + + TEST_ASSERT_NOT_NULL_MESSAGE( + strstr(captured, "no such limit 'does_not_exist'"), + "Requirement 1.3: a named lookup for an unconfigured name, against " + "a fixture with other limits configured, must report 'no such " + "limit'"); + TEST_ASSERT_NULL_MESSAGE( + strstr(captured, "no limits configured"), + "Requirement 1.7's message is reserved for the 'all' path -- a " + "named-lookup miss must never say 'no limits configured'"); + + free(captured); + + printf("test_info_limit_named_reports_no_such_limit_for_unconfigured_" + "name_against_fixture_with_other_limits: PASS\n"); +} + +/* + * THE central scoping-regression guard (design.md's own explicit + * correction, called out by this task's brief): a NULL + * limit_get_configured_limits() handle on the NAMED path (the limit plugin + * was never registered at all) must report Requirement 1.3's "no such + * limit ''" -- and must NEVER fall through to Requirement 1.7's + * all-only "no limits configured" message, even though both conditions + * stem from the identical underlying NULL handle. + */ +static void +test_info_limit_named_reports_no_such_limit_when_plugin_never_registered_not_no_limits_configured( + void) +{ + xce_sched_sim_driver_t sim; + char* captured; + int rc; + + memset(&sim, 0, sizeof(sim)); + rc = xce_sched_core_init(&(sim.core)); + TEST_ASSERT_EQUAL_INT(0, rc); + + captured = capture_info_limit(&sim, "foo"); + + TEST_ASSERT_NOT_NULL_MESSAGE( + strstr(captured, "no such limit 'foo'"), + "a NULL limit_get_configured_limits() handle on the NAMED path " + "(plugin never registered) must report 'no such limit', never " + "Requirement 1.7's all-only message"); + TEST_ASSERT_NULL_MESSAGE( + strstr(captured, "no limits configured"), + "a NULL handle on the named path must never be treated as 'no " + "limits configured' -- that message is exclusively the 'all' " + "path's, per its own acceptance criterion's scoping"); + + free(captured); + + printf("test_info_limit_named_reports_no_such_limit_when_plugin_never_" + "registered_not_no_limits_configured: PASS\n"); +} + +/* + * Positive-path proof for the named-lookup resolution: a name that IS + * genuinely configured must never trigger the "no such limit" message -- + * proves key_find() resolves a real hit, not a false miss (would fail if + * the resolution logic were broken or reversed). + */ +static void +test_info_limit_named_does_not_report_no_such_limit_for_configured_name(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, "ug1_slots"); + + captured = capture_info_limit(&sim, "ug1_slots"); + + TEST_ASSERT_NULL_MESSAGE( + strstr(captured, "no such limit"), + "a named lookup for a limit that genuinely IS configured must " + "never report 'no such limit'"); + + free(captured); + + printf("test_info_limit_named_does_not_report_no_such_limit_for_" + "configured_name: PASS\n"); +} + +int main(void) +{ + UNITY_BEGIN(); + RUN_TEST( + test_info_limit_all_reports_no_limits_configured_when_plugin_never_registered); + RUN_TEST( + test_info_limit_all_reports_no_limits_configured_when_plugin_registered_with_zero_limits); + RUN_TEST( + test_info_limit_all_does_not_report_no_limits_configured_when_limits_are_configured); + RUN_TEST( + test_info_limit_named_reports_no_such_limit_for_unconfigured_name_against_fixture_with_other_limits); + RUN_TEST( + 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); + return UNITY_END(); +} diff --git a/src/scheduler/testsuite/test_repl_prompt_context.c b/src/scheduler/testsuite/test_repl_prompt_context.c index 7497157c..59a24267 100644 --- a/src/scheduler/testsuite/test_repl_prompt_context.c +++ b/src/scheduler/testsuite/test_repl_prompt_context.c @@ -114,6 +114,21 @@ void kill_sched_user(void* arg) { (void)arg; } void kill_sched_project(void* arg) { (void)arg; } void kill_sched_resval(xce_val_t* val) { (void)val; } +/* repl_info.c (scheduler-sim-diagnostics spec, task 2.1) now calls + * limit_get_configured_limits() (plugins/limit.c, not part of this TU) to + * resolve `info limit`'s name/all argument -- link-satisfying stub only, + * same rationale as kill_sched_job()/kill_sched_resval() above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_configured_limits()'s own documented "plugin absent" return) + * is also the behaviorally correct answer here, not just a link + * placeholder. repl_info_limit() itself is not yet wired into + * repl_cmd_info() (task 4), so no test in this file can reach it anyway. */ +var limit_get_configured_limits(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* Link-satisfying stubs -- same rationale as every sibling in this file * family (see test_repl_execution_control.c's own copies for the full * per-stub doc comments). */ diff --git a/src/scheduler/testsuite/test_repl_resource_table.c b/src/scheduler/testsuite/test_repl_resource_table.c index ba0c2902..ed14ef5c 100644 --- a/src/scheduler/testsuite/test_repl_resource_table.c +++ b/src/scheduler/testsuite/test_repl_resource_table.c @@ -81,6 +81,21 @@ void kill_sched_user(void* arg) { (void)arg; } void kill_sched_project(void* arg) { (void)arg; } void kill_sched_resval(xce_val_t* val) { (void)val; } +/* repl_info.c (scheduler-sim-diagnostics spec, task 2.1) now calls + * limit_get_configured_limits() (plugins/limit.c, not part of this TU) to + * resolve `info limit`'s name/all argument -- link-satisfying stub only, + * same rationale as kill_sched_job()/kill_sched_resval() above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_configured_limits()'s own documented "plugin absent" return) + * is also the behaviorally correct answer here, not just a link + * placeholder. repl_info_limit() itself is not yet wired into + * repl_cmd_info() (task 4), so no test in this file can reach it anyway. */ +var limit_get_configured_limits(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts != diff --git a/src/scheduler/testsuite/test_repl_round_rollup.c b/src/scheduler/testsuite/test_repl_round_rollup.c index dae51e3d..dacfc545 100644 --- a/src/scheduler/testsuite/test_repl_round_rollup.c +++ b/src/scheduler/testsuite/test_repl_round_rollup.c @@ -77,6 +77,21 @@ void kill_sched_user(void* arg) { (void)arg; } void kill_sched_project(void* arg) { (void)arg; } void kill_sched_resval(xce_val_t* val) { (void)val; } +/* repl_info.c (scheduler-sim-diagnostics spec, task 2.1) now calls + * limit_get_configured_limits() (plugins/limit.c, not part of this TU) to + * resolve `info limit`'s name/all argument -- link-satisfying stub only, + * same rationale as kill_sched_job()/kill_sched_resval() above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_configured_limits()'s own documented "plugin absent" return) + * is also the behaviorally correct answer here, not just a link + * placeholder. repl_info_limit() itself is not yet wired into + * repl_cmd_info() (task 4), so no test in this file can reach it anyway. */ +var limit_get_configured_limits(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts != diff --git a/src/scheduler/testsuite/test_repl_snapshot_params.c b/src/scheduler/testsuite/test_repl_snapshot_params.c index 39e36437..8ae3b2a0 100644 --- a/src/scheduler/testsuite/test_repl_snapshot_params.c +++ b/src/scheduler/testsuite/test_repl_snapshot_params.c @@ -90,6 +90,21 @@ void kill_sched_user(void* arg) { (void)arg; } void kill_sched_project(void* arg) { (void)arg; } void kill_sched_resval(xce_val_t* val) { (void)val; } +/* repl_info.c (scheduler-sim-diagnostics spec, task 2.1) now calls + * limit_get_configured_limits() (plugins/limit.c, not part of this TU) to + * resolve `info limit`'s name/all argument -- link-satisfying stub only, + * same rationale as kill_sched_job()/kill_sched_resval() above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_configured_limits()'s own documented "plugin absent" return) + * is also the behaviorally correct answer here, not just a link + * placeholder. repl_info_limit() itself is not yet wired into + * repl_cmd_info() (task 4), so no test in this file can reach it anyway. */ +var limit_get_configured_limits(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts != diff --git a/src/scheduler/testsuite/test_repl_step_verbosity.c b/src/scheduler/testsuite/test_repl_step_verbosity.c index 12699d8f..4c526ff3 100644 --- a/src/scheduler/testsuite/test_repl_step_verbosity.c +++ b/src/scheduler/testsuite/test_repl_step_verbosity.c @@ -131,6 +131,21 @@ void kill_sched_user(void* arg) { (void)arg; } void kill_sched_project(void* arg) { (void)arg; } void kill_sched_resval(xce_val_t* val) { (void)val; } +/* repl_info.c (scheduler-sim-diagnostics spec, task 2.1) now calls + * limit_get_configured_limits() (plugins/limit.c, not part of this TU) to + * resolve `info limit`'s name/all argument -- link-satisfying stub only, + * same rationale as kill_sched_job()/kill_sched_resval() above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_configured_limits()'s own documented "plugin absent" return) + * is also the behaviorally correct answer here, not just a link + * placeholder. repl_info_limit() itself is not yet wired into + * repl_cmd_info() (task 4), so no test in this file can reach it anyway. */ +var limit_get_configured_limits(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts != diff --git a/src/scheduler/testsuite/test_sim_alt_events.c b/src/scheduler/testsuite/test_sim_alt_events.c index 78913484..a3d54216 100644 --- a/src/scheduler/testsuite/test_sim_alt_events.c +++ b/src/scheduler/testsuite/test_sim_alt_events.c @@ -108,6 +108,21 @@ void kill_sched_user(void* arg) { (void)arg; } void kill_sched_project(void* arg) { (void)arg; } void kill_sched_resval(xce_val_t* val) { (void)val; } +/* repl_info.c (scheduler-sim-diagnostics spec, task 2.1) now calls + * limit_get_configured_limits() (plugins/limit.c, not part of this TU) to + * resolve `info limit`'s name/all argument -- link-satisfying stub only, + * same rationale as kill_sched_job()/kill_sched_resval() above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_configured_limits()'s own documented "plugin absent" return) + * is also the behaviorally correct answer here, not just a link + * placeholder. repl_info_limit() itself is not yet wired into + * repl_cmd_info() (task 4), so no test in this file can reach it anyway. */ +var limit_get_configured_limits(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + void sched_worker_release_host_ctx(xce_sched_worker_t* w) { (void)w; } /* resource-accounting.md's reconciliation (resource.c, not linked here -- diff --git a/src/scheduler/testsuite/test_sim_contention_parity.c b/src/scheduler/testsuite/test_sim_contention_parity.c index aea20e2f..bb2db615 100644 --- a/src/scheduler/testsuite/test_sim_contention_parity.c +++ b/src/scheduler/testsuite/test_sim_contention_parity.c @@ -193,6 +193,21 @@ void kill_sched_user(void* arg) { (void)arg; } void kill_sched_project(void* arg) { (void)arg; } void kill_sched_resval(xce_val_t* val) { (void)val; } +/* repl_info.c (scheduler-sim-diagnostics spec, task 2.1) now calls + * limit_get_configured_limits() (plugins/limit.c, not part of this TU) to + * resolve `info limit`'s name/all argument -- link-satisfying stub only, + * same rationale as kill_sched_job()/kill_sched_resval() above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_configured_limits()'s own documented "plugin absent" return) + * is also the behaviorally correct answer here, not just a link + * placeholder. repl_info_limit() itself is not yet wired into + * repl_cmd_info() (task 4), so no test in this file can reach it anyway. */ +var limit_get_configured_limits(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* xce_sched_driver_on_decision()'s (driver.c) publish-side linker * dependencies -- never actually invoked: this file never registers a * decision callback. Same rationale test_sim_resource_contention.c's own diff --git a/src/scheduler/testsuite/test_sim_e2e_original_motivation.c b/src/scheduler/testsuite/test_sim_e2e_original_motivation.c index d9a4974b..abeb60f9 100644 --- a/src/scheduler/testsuite/test_sim_e2e_original_motivation.c +++ b/src/scheduler/testsuite/test_sim_e2e_original_motivation.c @@ -216,6 +216,21 @@ void kill_sched_user(void* arg) { (void)arg; } void kill_sched_project(void* arg) { (void)arg; } void kill_sched_resval(xce_val_t* val) { (void)val; } +/* repl_info.c (scheduler-sim-diagnostics spec, task 2.1) now calls + * limit_get_configured_limits() (plugins/limit.c, not part of this TU) to + * resolve `info limit`'s name/all argument -- link-satisfying stub only, + * same rationale as kill_sched_job()/kill_sched_resval() above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_configured_limits()'s own documented "plugin absent" return) + * is also the behaviorally correct answer here, not just a link + * placeholder. repl_info_limit() itself is not yet wired into + * repl_cmd_info() (task 4), so no test in this file can reach it anyway. */ +var limit_get_configured_limits(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* resource.c's sched_try_alloc_on_host_()'s own link-time dependency -- * never actually invoked (this file calls sched_check_host_satisfy()/ * sched_charge_account()/sched_commit_job_alloc() directly). Same stub diff --git a/src/scheduler/testsuite/test_sim_equivalence.c b/src/scheduler/testsuite/test_sim_equivalence.c index 1833306a..88ba575f 100644 --- a/src/scheduler/testsuite/test_sim_equivalence.c +++ b/src/scheduler/testsuite/test_sim_equivalence.c @@ -177,6 +177,21 @@ void kill_sched_user(void* arg) { (void)arg; } void kill_sched_project(void* arg) { (void)arg; } void kill_sched_resval(xce_val_t* val) { (void)val; } +/* repl_info.c (scheduler-sim-diagnostics spec, task 2.1) now calls + * limit_get_configured_limits() (plugins/limit.c, not part of this TU) to + * resolve `info limit`'s name/all argument -- link-satisfying stub only, + * same rationale as kill_sched_job()/kill_sched_resval() above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_configured_limits()'s own documented "plugin absent" return) + * is also the behaviorally correct answer here, not just a link + * placeholder. repl_info_limit() itself is not yet wired into + * repl_cmd_info() (task 4), so no test in this file can reach it anyway. */ +var limit_get_configured_limits(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts != diff --git a/src/scheduler/testsuite/test_sim_event_loop.c b/src/scheduler/testsuite/test_sim_event_loop.c index 7842ca0e..9d13d8db 100644 --- a/src/scheduler/testsuite/test_sim_event_loop.c +++ b/src/scheduler/testsuite/test_sim_event_loop.c @@ -124,6 +124,21 @@ void kill_sched_user(void* arg) { (void)arg; } void kill_sched_project(void* arg) { (void)arg; } void kill_sched_resval(xce_val_t* val) { (void)val; } +/* repl_info.c (scheduler-sim-diagnostics spec, task 2.1) now calls + * limit_get_configured_limits() (plugins/limit.c, not part of this TU) to + * resolve `info limit`'s name/all argument -- link-satisfying stub only, + * same rationale as kill_sched_job()/kill_sched_resval() above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_configured_limits()'s own documented "plugin absent" return) + * is also the behaviorally correct answer here, not just a link + * placeholder. repl_info_limit() itself is not yet wired into + * repl_cmd_info() (task 4), so no test in this file can reach it anyway. */ +var limit_get_configured_limits(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts != diff --git a/src/scheduler/testsuite/test_sim_print_decision_gate.c b/src/scheduler/testsuite/test_sim_print_decision_gate.c index 30368cf1..05d2d890 100644 --- a/src/scheduler/testsuite/test_sim_print_decision_gate.c +++ b/src/scheduler/testsuite/test_sim_print_decision_gate.c @@ -136,6 +136,21 @@ void kill_sched_user(void* arg) { (void)arg; } void kill_sched_project(void* arg) { (void)arg; } void kill_sched_resval(xce_val_t* val) { (void)val; } +/* repl_info.c (scheduler-sim-diagnostics spec, task 2.1) now calls + * limit_get_configured_limits() (plugins/limit.c, not part of this TU) to + * resolve `info limit`'s name/all argument -- link-satisfying stub only, + * same rationale as kill_sched_job()/kill_sched_resval() above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_configured_limits()'s own documented "plugin absent" return) + * is also the behaviorally correct answer here, not just a link + * placeholder. repl_info_limit() itself is not yet wired into + * repl_cmd_info() (task 4), so no test in this file can reach it anyway. */ +var limit_get_configured_limits(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts != diff --git a/src/scheduler/testsuite/test_sim_reconcile_unaccounted.c b/src/scheduler/testsuite/test_sim_reconcile_unaccounted.c index 2e4ce618..e0514836 100644 --- a/src/scheduler/testsuite/test_sim_reconcile_unaccounted.c +++ b/src/scheduler/testsuite/test_sim_reconcile_unaccounted.c @@ -139,6 +139,21 @@ void kill_sched_user(void* arg) { (void)arg; } void kill_sched_project(void* arg) { (void)arg; } void kill_sched_resval(xce_val_t* val) { (void)val; } +/* repl_info.c (scheduler-sim-diagnostics spec, task 2.1) now calls + * limit_get_configured_limits() (plugins/limit.c, not part of this TU) to + * resolve `info limit`'s name/all argument -- link-satisfying stub only, + * same rationale as kill_sched_job()/kill_sched_resval() above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_configured_limits()'s own documented "plugin absent" return) + * is also the behaviorally correct answer here, not just a link + * placeholder. repl_info_limit() itself is not yet wired into + * repl_cmd_info() (task 4), so no test in this file can reach it anyway. */ +var limit_get_configured_limits(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* sched_worker_release_host_ctx() is now a REAL (non-stub) symbol pulled in * from resource.c (task 2.1 addition, below) -- no local stand-in needed or * permitted (a local definition here would collide at link time). Unused by diff --git a/src/scheduler/testsuite/test_sim_record_output.c b/src/scheduler/testsuite/test_sim_record_output.c index 33f24fbc..f608a561 100644 --- a/src/scheduler/testsuite/test_sim_record_output.c +++ b/src/scheduler/testsuite/test_sim_record_output.c @@ -95,6 +95,21 @@ void kill_sched_user(void* arg) { (void)arg; } void kill_sched_project(void* arg) { (void)arg; } void kill_sched_resval(xce_val_t* val) { (void)val; } +/* repl_info.c (scheduler-sim-diagnostics spec, task 2.1) now calls + * limit_get_configured_limits() (plugins/limit.c, not part of this TU) to + * resolve `info limit`'s name/all argument -- link-satisfying stub only, + * same rationale as kill_sched_job()/kill_sched_resval() above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_configured_limits()'s own documented "plugin absent" return) + * is also the behaviorally correct answer here, not just a link + * placeholder. repl_info_limit() itself is not yet wired into + * repl_cmd_info() (task 4), so no test in this file can reach it anyway. */ +var limit_get_configured_limits(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts != diff --git a/src/scheduler/testsuite/test_sim_resource_contention.c b/src/scheduler/testsuite/test_sim_resource_contention.c index 75cb4f60..d9cc572d 100644 --- a/src/scheduler/testsuite/test_sim_resource_contention.c +++ b/src/scheduler/testsuite/test_sim_resource_contention.c @@ -160,6 +160,21 @@ void kill_sched_user(void* arg) { (void)arg; } void kill_sched_project(void* arg) { (void)arg; } void kill_sched_resval(xce_val_t* val) { (void)val; } +/* repl_info.c (scheduler-sim-diagnostics spec, task 2.1) now calls + * limit_get_configured_limits() (plugins/limit.c, not part of this TU) to + * resolve `info limit`'s name/all argument -- link-satisfying stub only, + * same rationale as kill_sched_job()/kill_sched_resval() above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_configured_limits()'s own documented "plugin absent" return) + * is also the behaviorally correct answer here, not just a link + * placeholder. repl_info_limit() itself is not yet wired into + * repl_cmd_info() (task 4), so no test in this file can reach it anyway. */ +var limit_get_configured_limits(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* xce_sched_driver_run_normal_mode_round()'s (driver.c) linker dependency -- * a no-op stand-in, same rationale test_sim_equivalence.c's own copy * documents (this file drives its own resource accounting directly via diff --git a/src/scheduler/testsuite/test_sim_round_boundary_reconciliation.c b/src/scheduler/testsuite/test_sim_round_boundary_reconciliation.c index 158c1746..a2ef678a 100644 --- a/src/scheduler/testsuite/test_sim_round_boundary_reconciliation.c +++ b/src/scheduler/testsuite/test_sim_round_boundary_reconciliation.c @@ -128,6 +128,21 @@ void kill_sched_user(void* arg) { (void)arg; } void kill_sched_project(void* arg) { (void)arg; } void kill_sched_resval(xce_val_t* val) { (void)val; } +/* repl_info.c (scheduler-sim-diagnostics spec, task 2.1) now calls + * limit_get_configured_limits() (plugins/limit.c, not part of this TU) to + * resolve `info limit`'s name/all argument -- link-satisfying stub only, + * same rationale as kill_sched_job()/kill_sched_resval() above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_configured_limits()'s own documented "plugin absent" return) + * is also the behaviorally correct answer here, not just a link + * placeholder. repl_info_limit() itself is not yet wired into + * repl_cmd_info() (task 4), so no test in this file can reach it anyway. */ +var limit_get_configured_limits(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* resource.c's sched_try_alloc_on_host_()'s own link-time dependency -- * never actually invoked (this file calls sched_check_host_satisfy() * directly, never sched_try_alloc_on_hosts()/sched_try_alloc_on_host_() diff --git a/src/scheduler/testsuite/test_sim_session_ordering.c b/src/scheduler/testsuite/test_sim_session_ordering.c index 11f199d4..79a1b489 100644 --- a/src/scheduler/testsuite/test_sim_session_ordering.c +++ b/src/scheduler/testsuite/test_sim_session_ordering.c @@ -160,6 +160,21 @@ void kill_sched_user(void* arg) { (void)arg; } void kill_sched_project(void* arg) { (void)arg; } void kill_sched_resval(xce_val_t* val) { (void)val; } +/* repl_info.c (scheduler-sim-diagnostics spec, task 2.1) now calls + * limit_get_configured_limits() (plugins/limit.c, not part of this TU) to + * resolve `info limit`'s name/all argument -- link-satisfying stub only, + * same rationale as kill_sched_job()/kill_sched_resval() above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_configured_limits()'s own documented "plugin absent" return) + * is also the behaviorally correct answer here, not just a link + * placeholder. repl_info_limit() itself is not yet wired into + * repl_cmd_info() (task 4), so no test in this file can reach it anyway. */ +var limit_get_configured_limits(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* core_reset_workers()'s (core_session.c) linker dependency -- never * invoked (no worker is ever added here). */ void sched_worker_release_host_ctx(xce_sched_worker_t* w) { (void)w; } diff --git a/src/scheduler/testsuite/test_sim_snapshot_reconcile_gap.c b/src/scheduler/testsuite/test_sim_snapshot_reconcile_gap.c index 38c42891..126d7570 100644 --- a/src/scheduler/testsuite/test_sim_snapshot_reconcile_gap.c +++ b/src/scheduler/testsuite/test_sim_snapshot_reconcile_gap.c @@ -114,6 +114,21 @@ void kill_sched_user(void* arg) { (void)arg; } void kill_sched_project(void* arg) { (void)arg; } void kill_sched_resval(xce_val_t* val) { (void)val; } +/* repl_info.c (scheduler-sim-diagnostics spec, task 2.1) now calls + * limit_get_configured_limits() (plugins/limit.c, not part of this TU) to + * resolve `info limit`'s name/all argument -- link-satisfying stub only, + * same rationale as kill_sched_job()/kill_sched_resval() above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_configured_limits()'s own documented "plugin absent" return) + * is also the behaviorally correct answer here, not just a link + * placeholder. repl_info_limit() itself is not yet wired into + * repl_cmd_info() (task 4), so no test in this file can reach it anyway. */ +var limit_get_configured_limits(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* core_reset_workers()'s (core_session.c) linker dependency -- same stub * test_sim_verify.c already establishes; never invoked (no worker is ever * added here). */ diff --git a/src/scheduler/testsuite/test_sim_verify.c b/src/scheduler/testsuite/test_sim_verify.c index e879bf27..6a3bf81d 100644 --- a/src/scheduler/testsuite/test_sim_verify.c +++ b/src/scheduler/testsuite/test_sim_verify.c @@ -115,6 +115,21 @@ void kill_sched_user(void* arg) { (void)arg; } void kill_sched_project(void* arg) { (void)arg; } void kill_sched_resval(xce_val_t* val) { (void)val; } +/* repl_info.c (scheduler-sim-diagnostics spec, task 2.1) now calls + * limit_get_configured_limits() (plugins/limit.c, not part of this TU) to + * resolve `info limit`'s name/all argument -- link-satisfying stub only, + * same rationale as kill_sched_job()/kill_sched_resval() above; this + * file's own fixtures never register the limit plugin, so NULL + * (limit_get_configured_limits()'s own documented "plugin absent" return) + * is also the behaviorally correct answer here, not just a link + * placeholder. repl_info_limit() itself is not yet wired into + * repl_cmd_info() (task 4), so no test in this file can reach it anyway. */ +var limit_get_configured_limits(xce_sched_core_t* core) +{ + (void)core; + return NULL; +} + /* resource-accounting.md's reconciliation (resource.c, not linked here -- * this file only #includes core_events.c). core_events.c's * XCE_CORE_EVENT_JOB_RUSAGE handler calls this whenever job->accounts !=