diff --git a/.kiro/specs/scheduler-sim-discrete-clock/tasks.md b/.kiro/specs/scheduler-sim-discrete-clock/tasks.md index 77d812c5..b555f2d1 100644 --- a/.kiro/specs/scheduler-sim-discrete-clock/tasks.md +++ b/.kiro/specs/scheduler-sim-discrete-clock/tasks.md @@ -20,7 +20,7 @@ Test-first throughout (red → green → refactor). Every sub-task leaves the bu - _Requirements: 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 4.2_ - _Boundary: SimClock_ - _Depends: 1.1_ -- [ ] 2.2 (P) Remove the REPL speed parameter +- [x] 2.2 (P) Remove the REPL speed parameter - Write the tests first: `set speed 7` and bare `set speed` print the removal message, return to the prompt and leave the clock and core speed setting unchanged; `help` and the `set` usage message do not mention speed; `info config` has no speed item and still shows output, verify, events processed, current timestamp and sessions; a scripted forked-child REPL run with `set speed 7`, `info config` and `quit` exits 0 and prints both the removal message and the config line; the other `set` parameters' existing tests still pass. - Handle `set speed` before the argument-count check, remove the speed setter, and drop speed from the usage, help and `info config` text (design.md, Components: ReplSet / ReplHelp / ReplInfo). - Replace the existing speed-setter test and update the help and `info config` tests, the comments about the `set speed` command in the REPL parameter, help and resource-formatting tests, and the REPL internal header's comments that mention `set speed` (comments about the simulator speed argument belong to 3.1). diff --git a/src/scheduler/src/repl.c b/src/scheduler/src/repl.c index 3a21722b..d2e15cd1 100644 --- a/src/scheduler/src/repl.c +++ b/src/scheduler/src/repl.c @@ -250,9 +250,9 @@ static void repl_print_banner(const xce_sched_sim_driver_t* sim) * flat, independently hand-padded list into four labeled sections -- * Execution Control, Breakpoints, State Inspection (now including `info * config`, moved here next to its `info` siblings per Req 7.3), and - * Snapshot & Parameters (now including `set units`, alongside `set speed`/ - * `set output`/`set verify`) -- each line rendered through the SAME - * `FIELD_FMT` fixed-field-width format, so a future command addition can't + * Snapshot & Parameters (now including `set units`, alongside `set output`/ + * `set verify`; `set speed` was removed by scheduler-sim-discrete-clock) + * -- each line rendered through the SAME `FIELD_FMT` fixed-field-width format, so a future command addition can't * silently break alignment the way `info config`'s old hand-counted append * did (Req 7.2). `FIELD_FMT`'s "%-38s" width is sized to the longest * command/argument field below ("break [key=value ...]", 34 @@ -311,7 +311,6 @@ static void repl_print_help(void) fprintf(stdout, "\nSnapshot & Parameters:\n"); fprintf(stdout, FIELD_FMT, "snapshot [FILE]", "Write a standalone snapshot."); - fprintf(stdout, FIELD_FMT, "set speed ", "Set simulation speed."); fprintf(stdout, FIELD_FMT, "set output |off", "Start/stop writing decisions."); fprintf(stdout, FIELD_FMT, "set verify on|off", diff --git a/src/scheduler/src/repl_info.c b/src/scheduler/src/repl_info.c index 93b0f23f..305e480d 100644 --- a/src/scheduler/src/repl_info.c +++ b/src/scheduler/src/repl_info.c @@ -20,8 +20,8 @@ * `info resource`/`info summary`/`info break`/`info config`, and the * `repl_cmd_info()` dispatcher repl.c's repl_dispatch() hands off to * (repl_internal.h's own extern declaration). `info config` is the - * read-only counterpart to repl_params.c's `set speed`/`set output`/ - * `set verify` -- grouped here with every other `info` dump rather than in + * read-only counterpart to repl_params.c's `set output`/`set verify` -- + * grouped here with every other `info` dump rather than in * repl_params.c, per this file's own domain-ownership scope. * * Every dump below reads core->jobs/queues/workers/resmgr the SAME way @@ -1768,14 +1768,12 @@ static void repl_info_session(xce_sched_sim_driver_t* sim) /* * repl_info_config - AC 22's "info config": "Show current sim parameters: - * speed=N, output=, verify=on|off, events_processed=N, - * current_ts=, sessions=N." Read-only counterpart to - * repl_params.c's `set speed`/`set output`/`set verify` (task 12.3) -- - * reads sim->sim_speed_num (the driver-level shadow copy sim.h's own doc - * comment says exists precisely "for the REPL/diagnostics to read without - * reaching into core", kept in sync with the real core.speed_num/speed_den - * atomics by every `set speed` call, repl_params.c's repl_set_speed()), - * sim->record_file/rs->output_path (repl_params.c's own output_path field + * output=, verify=on|off, events_processed=N, + * current_ts=, sessions=N." (The speed item was removed with + * the REPL speed parameter, scheduler-sim-discrete-clock Req 3.4: the + * simulator clock moves only at recorded timestamps.) Read-only + * counterpart to repl_params.c's `set output`/`set verify` (task 12.3) -- + * reads sim->record_file/rs->output_path (repl_params.c's own output_path field * doc comment explains the "path unknown" fallback for a --sim-output * stream this REPL session never itself opened), sim->verify, and the SAME * rs->event_index/rs->session_count `info session` already reports @@ -1798,9 +1796,9 @@ static void repl_info_config(xce_sched_sim_driver_t* sim) repl_format_ts_human(xce_core_now(&(sim->core)), ts_human, sizeof(ts_human)); - fprintf(stdout, "[sim] config: speed=%llu output=%s verify=%s " + fprintf(stdout, "[sim] config: output=%s verify=%s " "events_processed=%llu current_ts=%llu (%s) sessions=%llu\n", - (unsigned long long)sim->sim_speed_num, output_desc, + output_desc, sim->verify ? "on" : "off", (unsigned long long)((rs != NULL) ? rs->event_index : 0), (unsigned long long)xce_core_now(&(sim->core)), ts_human, diff --git a/src/scheduler/src/repl_internal.h b/src/scheduler/src/repl_internal.h index b04e0d88..bffac12f 100644 --- a/src/scheduler/src/repl_internal.h +++ b/src/scheduler/src/repl_internal.h @@ -21,7 +21,7 @@ * (CLAUDE.md/coding-style.md). Split along the same domain boundaries * task 8.1 already used for snapshot.c/snapshot_wire.c/snapshot_load.c * (that file's own "Three-file split" header section is the precedent). - * Task 12.3 (snapshot capture + `set speed`/`set output`/`set verify`) + * Task 12.3 (snapshot capture + `set output`/`set verify`) * adds a FOURTH file, repl_params.c, for the identical reason: neither * repl_break.c nor repl_info.c had enough remaining headroom under the * 800-line cap to absorb this task's own real chunk of new logic (both @@ -46,7 +46,7 @@ * domain-wise with every other `info` dump, matching this file's own * "Owns" doc comment). * - repl_params.c (task 12.3): the `snapshot [FILE]` and `set - * speed|output|verify ...` command handlers -- mutating the simulation + * output|verify|units ...` command handlers -- mutating the simulation * parameters `info config` (repl_info.c) reports. * * `struct xce_sched_repl_state_s` (the full definition of sim.h's opaque diff --git a/src/scheduler/src/repl_params.c b/src/scheduler/src/repl_params.c index 9f679385..3fd023af 100644 --- a/src/scheduler/src/repl_params.c +++ b/src/scheduler/src/repl_params.c @@ -19,7 +19,7 @@ * gets its own file, matching task 12.2's own precedent for the identical * problem. * - * Owns: the `snapshot [FILE]` and `set speed|output|verify ...` command + * Owns: the `snapshot [FILE]` and `set output|verify|units ...` command * handlers (repl_cmd_snapshot()/repl_cmd_set(), repl_internal.h's own * extern declarations; repl.c's repl_dispatch() hands off to both). `info * config` -- the read-only counterpart that reports the SAME parameters @@ -41,11 +41,10 @@ * own file header comment for the precedent this follows). Neither command * hand-rolls capnp encoding. * - * `set speed`'s atomic_store_explicit()/memory_order_release calls need no - * `#include ` of their own -- core.h (pulled in transitively - * via repl.h -> sim.h -> core.h) already includes it, the SAME reason - * sim.c's own sim_set_virtual_clock() (the function this file's - * repl_set_speed() mirrors) needs no direct include either. + * `set speed` is no longer a parameter (scheduler-sim-discrete-clock, Req + * 3.1-3.3): the simulator clock moves only at recorded timestamps, so + * repl_cmd_set() answers it with a fixed removal message and changes + * nothing. */ extern int sim_record_write_header_and_snapshot(FILE* f, xce_sched_core_t* core, @@ -168,64 +167,9 @@ bool repl_cmd_snapshot(xce_sched_sim_driver_t* sim, const char* line) } /* ------------------------------------------------------------------------ - * set speed|output|verify ... + * set output|verify|units ... * ---------------------------------------------------------------------- */ -/* - * repl_set_speed - AC 22's "set speed ": mutates BOTH - * sim->sim_speed_num/sim_speed_den (the driver-level shadow copy sim.h's - * own doc comment says exists "for the REPL/diagnostics to read without - * reaching into `core`" -- `info config`'s own speed= field, repl_info.c, - * reads these) AND sim->core.speed_num/speed_den -- the atomic pair - * core_clock.c's xce_core_now() actually consults, FRESH, on every single - * call (no caching) -- via the SAME two atomic_store_explicit() calls - * sim_set_virtual_clock() (sim.c, Req 5 AC 13 Step 4) already performs at - * startup for the identical pair. Mutating only the shadow copy would leave - * `info config` and reality disagreeing forever; sim_loop.c needs no new - * call site for this to take effect -- xce_core_now() is already called - * throughout its event loop on every iteration, so the very next call - * observes the new speed. - * - * `N` = 0 (freeze / "as-fast-as-possible", AC 22's own table wording) is - * accepted here, unlike `step`'s N (repl_cmd_step(), repl.c), which - * rejects N<=0 -- speed=0 is a legitimate, documented mode (core.h's own - * speed_num/speed_den field doc comment: "discrete-simulation freezes via - * speed_num=0"), not a user error. - * - * Deliberately does NOT re-anchor virtual_clock_base/real_clock_base: - * those are only ever re-anchored by sim_advance_virtual_clock() - * (sim_loop.c) at each event/session-boundary jump, and the REPL only ever - * regains control AT one of those jump points (the pre-replay pause, a - * post-session pause, or a breakpoint hit) -- so leaving them untouched - * here introduces no discontinuity in xce_core_now()'s reported value. - */ -static bool repl_set_speed(xce_sched_sim_driver_t* sim, const char* value) -{ - char* endptr; - uint64_t n; - - if (value[0] == '\0') { - fprintf(stdout, "[sim] set speed: usage: set speed \n"); - return false; - } - - n = strtoull(value, &endptr, 10); - if (*endptr != '\0') { - fprintf(stdout, "[sim] set speed: '%s' is not a valid non-negative integer\n", - value); - return false; - } - - atomic_store_explicit(&(sim->core.speed_num), n, memory_order_release); - atomic_store_explicit(&(sim->core.speed_den), 1, memory_order_release); - sim->sim_speed_num = n; - sim->sim_speed_den = 1; - - fprintf(stdout, "[sim] speed set to %llu\n", (unsigned long long)n); - - return false; -} - /* * repl_set_output_off - AC 22's "set output off": "Close the current * output file. info config confirms the path and event count at close @@ -345,8 +289,8 @@ static bool repl_set_output_file(xce_sched_sim_driver_t* sim, const char* path) * raw|human|both": selects the operator's resource-value display mode * (rs->units_mode), read by the (task 8, not yet implemented) `info * worker`/`info resource` table renderer. Same per-key - * usage-message-on-invalid-value pattern repl_set_speed()/`set verify` - * (above) already establish -- an unrecognized VALUE is rejected with a + * usage-message-on-invalid-value pattern `set verify` (in repl_cmd_set(), + * below) already establishes -- an unrecognized VALUE is rejected with a * units-specific usage message and leaves the current mode unchanged, never * the generic "unrecognized parameter" message repl_cmd_set() falls through * to below (that message fires only for an unrecognized KEY; `units` IS a @@ -386,26 +330,34 @@ static bool repl_set_units(xce_sched_sim_driver_t* sim, const char* value) } /* - * repl_cmd_set - Req 5 AC 22's "set ..." family: "set speed " / - * "set output |off" / "set verify on|off" / "set units - * raw|human|both" (the last added by scheduler-sim-repl-verbosity's task - * 7.2, Req 6.2/6.3). `line` re-tokenized via repl_split_tokens() (repl.c), - * same as break/delete/info. + * repl_cmd_set - Req 5 AC 22's "set ..." family: "set output |off" / + * "set verify on|off" / "set units raw|human|both" (the last added by + * scheduler-sim-repl-verbosity's task 7.2, Req 6.2/6.3). `line` + * re-tokenized via repl_split_tokens() (repl.c), same as break/delete/info. + * + * "set speed" (with or without a value) was removed by + * scheduler-sim-discrete-clock (Req 3.1-3.3): it is checked BEFORE the + * argument-count check so a bare `set speed` gets the removal message, not + * the usage text, and it returns to the prompt without touching the clock + * or the core speed setting. */ bool repl_cmd_set(xce_sched_sim_driver_t* sim, const char* line) { char tokens[XCE_REPL_MAX_TOKENS][XCE_REPL_TOKEN_LEN]; int ntok = repl_split_tokens(line, tokens, XCE_REPL_MAX_TOKENS); + if ((ntok >= 2) && (strcmp(tokens[1], "speed") == 0)) { + fprintf(stdout, "[sim] set speed: the speed parameter was removed; " + "the simulator clock moves only at recorded timestamps\n"); + return false; + } + if (ntok < 3) { - fprintf(stdout, "[sim] set: usage: set speed | set output |off | " + fprintf(stdout, "[sim] set: usage: set output |off | " "set verify on|off | set units raw|human|both\n"); return false; } - if (strcmp(tokens[1], "speed") == 0) { - return repl_set_speed(sim, tokens[2]); - } if (strcmp(tokens[1], "output") == 0) { if (strcmp(tokens[2], "off") == 0) { return repl_set_output_off(sim); diff --git a/src/scheduler/testsuite/test_repl_help.c b/src/scheduler/testsuite/test_repl_help.c index 910c0ffe..b8645406 100644 --- a/src/scheduler/testsuite/test_repl_help.c +++ b/src/scheduler/testsuite/test_repl_help.c @@ -5,8 +5,8 @@ * into four labeled sections, aligns every command's description to one * consistent column position throughout (not independently hand-padded per * line), lists `info config` together with the other `info` subcommands in - * the state-inspection section, lists `set units` alongside `set speed`/ - * `set output`/`set verify` in the snapshot-and-parameters section, and + * the state-inspection section, lists `set units` alongside `set output`/ + * `set verify` in the snapshot-and-parameters section, and * enumerates the complete set of valid `break ` values on its * `break` line. `break`'s own unrecognized-event-type error message * (Requirement 7.5) is NOT covered here -- that lives in the sibling @@ -552,8 +552,9 @@ static const char* BP_KIND_TOKENS[] = { /* command's line falling within its correct section's bounds -- including */ /* `info config` landing inside State Inspection next to its `info` */ /* siblings (Requirement 7.3), and `set units` landing inside Snapshot & */ -/* Parameters next to `set speed`/`set output`/`set verify` (Requirement */ -/* 7.1/7.4's section-organization half). */ +/* Parameters next to `set output`/`set verify` (Requirement 7.1/7.4's */ +/* section-organization half), and no `set speed` line anywhere (the REPL */ +/* speed parameter was removed, scheduler-sim-discrete-clock Req 3.3). */ /* ---------------------------------------------------------------------- */ static void test_help_groups_commands_into_four_labeled_sections_in_order( @@ -646,13 +647,13 @@ static void test_help_groups_commands_into_four_labeled_sections_in_order( } } - /* Snapshot & Parameters: snapshot/set speed/set output/set verify/set + /* Snapshot & Parameters: snapshot/set output/set verify/set * units, all after Snapshot & Parameters's own label (last section, so * only a lower bound applies). `set units`'s exact argument spelling is * deliberately not pinned here -- only its existence and placement. */ { const char* fields[] = { - "snapshot [FILE]", "set speed ", "set output |off", + "snapshot [FILE]", "set output |off", "set verify on|off", "set units", }; size_t i; @@ -669,6 +670,11 @@ static void test_help_groups_commands_into_four_labeled_sections_in_order( } } + TEST_ASSERT_NULL_MESSAGE(strstr(captured, "set speed"), + "help must not list the removed `set speed` " + "parameter (scheduler-sim-discrete-clock Req " + "3.3)"); + free(captured); printf("test_help_groups_commands_into_four_labeled_sections_in_order: " @@ -708,7 +714,6 @@ static void test_help_uses_one_consistent_column_width_throughout(void) "info summary", "info config", "snapshot [FILE]", - "set speed ", "set output |off", "set verify on|off", }; diff --git a/src/scheduler/testsuite/test_repl_resource_formatting.c b/src/scheduler/testsuite/test_repl_resource_formatting.c index f1d1db9e..e821d852 100644 --- a/src/scheduler/testsuite/test_repl_resource_formatting.c +++ b/src/scheduler/testsuite/test_repl_resource_formatting.c @@ -13,7 +13,7 @@ * definitions, this same file (unmodified) becomes the GREEN-phase proof. * `set units` parsing (also part of Requirement 6.2/6.3) is covered * separately in test_repl_snapshot_params.c, alongside that file's existing - * `set speed`/`set output`/`set verify` tests -- this file covers ONLY the + * `set output`/`set verify` tests -- this file covers ONLY the * two pure classification/scaling functions. * * Declaring the functions under test: same rationale as diff --git a/src/scheduler/testsuite/test_repl_snapshot_params.c b/src/scheduler/testsuite/test_repl_snapshot_params.c index e725aaa6..e29f9090 100644 --- a/src/scheduler/testsuite/test_repl_snapshot_params.c +++ b/src/scheduler/testsuite/test_repl_snapshot_params.c @@ -15,18 +15,19 @@ * reloaded core state (snapshot_ts, the pushed job) matches what the * child's session actually produced. * - * The remaining tests cover `set speed`/`set output`/`set verify`/ - * `info config` and the auto-generated `snapshot` filename, at the same + * The remaining tests cover `set output`/`set verify`/`info config`, the + * removed `set speed` parameter (scheduler-sim-discrete-clock, Req 3.1-3.5: + * it prints a removal message and changes nothing) and the auto-generated + * `snapshot` filename, at the same * "unit-level precision where possible, integration-level proof for the * one Observable that needs it" split test_repl_execution_control.c's own * header comment establishes. * * `set units raw|human|both` (scheduler-sim-repl-verbosity spec, task 7.1, - * Requirement 6.2/6.3) is added below, alongside the existing `set speed`/ - * `set output`/`set verify` tests, per design.md's File Structure Plan + * Requirement 6.2/6.3) is added below, alongside the existing `set output`/ + * `set verify` tests, per that spec's design.md File Structure Plan * ("test_repl_snapshot_params.c -- extended with `set units raw|human|both` - * parsing/validation tests ... alongside its existing `set speed`/`set - * output`/`set verify` tests"). THIS SLICE IS RED-PHASE ONLY: neither + * parsing/validation tests ... alongside its existing `set ...` tests"). THIS SLICE IS RED-PHASE ONLY: neither * xce_sched_repl_state_s's `units_mode` field nor repl_cmd_set()'s `units` * branch exists yet (task 7.2, a separate later task, adds both) -- see * that test group's own header comment further below for the exact @@ -880,42 +881,195 @@ static void test_snapshot_auto_generates_filename_from_event_index(void) } /* ---------------------------------------------------------------------- */ -/* Test: `set speed ` mutates BOTH the driver-level shadow copy AND the */ -/* atomic core.speed_num/speed_den pair xce_core_now() actually reads. */ +/* Test: `set speed` (with or without a value) prints the removal message, */ +/* hands control back to the prompt and leaves the simulation clock and */ +/* the core speed atoms untouched (scheduler-sim-discrete-clock, Req 3.1, */ +/* 3.2, 1.5). */ /* ---------------------------------------------------------------------- */ -static void test_set_speed_updates_shadow_and_core_atomics(void) +#define SET_SPEED_REMOVED_MSG \ + "[sim] set speed: the speed parameter was removed; the simulator " \ + "clock moves only at recorded timestamps" + +static xce_sched_sim_driver_t g_speed_sim; +static bool g_speed_handled; +static const char* g_speed_line; + +static void run_set_speed_line(void) +{ + g_speed_handled = repl_cmd_set(&g_speed_sim, g_speed_line); +} + +static void assert_set_speed_removed(const char* line) +{ + char* captured; + + memset(&g_speed_sim, 0, sizeof(g_speed_sim)); + atomic_store_explicit(&(g_speed_sim.core.virtual_clock_base), 5000ULL, + memory_order_release); + atomic_store_explicit(&(g_speed_sim.core.real_clock_base), 0ULL, + memory_order_release); + atomic_store_explicit(&(g_speed_sim.core.speed_num), 0ULL, + memory_order_release); + atomic_store_explicit(&(g_speed_sim.core.speed_den), 1ULL, + memory_order_release); + + g_speed_line = line; + g_speed_handled = true; + captured = capture_stdout_around(run_set_speed_line); + + TEST_ASSERT_FALSE_MESSAGE(g_speed_handled, + "`set speed` must return to the prompt"); + TEST_ASSERT_NOT_NULL_MESSAGE(strstr(captured, SET_SPEED_REMOVED_MSG), + "`set speed` must print the removal " + "message"); + TEST_ASSERT_NULL_MESSAGE(strstr(captured, "usage"), + "`set speed` must not fall through to the " + "`set` usage message"); + TEST_ASSERT_NULL_MESSAGE(strstr(captured, "speed set to"), + "`set speed` must not report a new speed"); + TEST_ASSERT_EQUAL_UINT64_MESSAGE(5000ULL, + xce_core_now(&(g_speed_sim.core)), + "`set speed` must not move the " + "simulation clock"); + TEST_ASSERT_EQUAL_UINT64( + 0ULL, atomic_load_explicit(&(g_speed_sim.core.speed_num), + memory_order_acquire)); + TEST_ASSERT_EQUAL_UINT64( + 1ULL, atomic_load_explicit(&(g_speed_sim.core.speed_den), + memory_order_acquire)); + TEST_ASSERT_EQUAL_UINT64( + 5000ULL, atomic_load_explicit(&(g_speed_sim.core.virtual_clock_base), + memory_order_acquire)); + free(captured); +} + +static void test_set_speed_prints_removal_message_and_changes_nothing(void) +{ + assert_set_speed_removed("set speed 7"); + assert_set_speed_removed("set speed"); + + printf("test_set_speed_prints_removal_message_and_changes_nothing: " + "PASS\n"); +} + +/* ---------------------------------------------------------------------- */ +/* Test: the bare `set` usage message no longer offers a speed setting */ +/* (Req 3.3) and still lists output/verify/units (Req 3.5). */ +/* ---------------------------------------------------------------------- */ + +static xce_sched_sim_driver_t g_usage_sim; + +static void run_set_bare(void) { (void)repl_cmd_set(&g_usage_sim, "set"); } + +static void test_set_usage_omits_speed(void) +{ + char* captured; + + memset(&g_usage_sim, 0, sizeof(g_usage_sim)); + captured = capture_stdout_around(run_set_bare); + + TEST_ASSERT_NOT_NULL(strstr(captured, "[sim] set: usage: " + "set output |off | " + "set verify on|off | " + "set units raw|human|both")); + TEST_ASSERT_NULL_MESSAGE(strstr(captured, "speed"), + "the `set` usage message must not mention " + "speed"); + free(captured); + + printf("test_set_usage_omits_speed: PASS\n"); +} + +/* ---------------------------------------------------------------------- */ +/* Test: a scripted interactive REPL run (`set speed 7`, `info config`, */ +/* `quit`) keeps reading after `set speed`, prints the removal message and */ +/* the config line, and exits 0 through `quit` (Req 3.1, 3.2, 3.4). */ +/* ---------------------------------------------------------------------- */ + +static void child_run_repl_script(const char* replay_path, + const char* script_path, + const char* capture_path) { xce_sched_sim_driver_t sim; - bool handled; + sched_sim_args_t args; + + if (freopen(script_path, "r", stdin) == NULL) { + _exit(120); + } + if (freopen(capture_path, "w", stdout) == NULL) { + _exit(121); + } memset(&sim, 0, sizeof(sim)); + memset(&args, 0, sizeof(args)); + args.replay_path = replay_path; + args.sim_start_ts = SCHED_SIM_START_TS_LATEST; + args.interactive = true; - handled = repl_cmd_set(&sim, "set speed 7"); - TEST_ASSERT_FALSE_MESSAGE(handled, "`set` never hands control back"); - TEST_ASSERT_EQUAL_UINT64(7ULL, sim.sim_speed_num); - TEST_ASSERT_EQUAL_UINT64(1ULL, sim.sim_speed_den); - TEST_ASSERT_EQUAL_UINT64( - 7ULL, atomic_load_explicit(&(sim.core.speed_num), memory_order_acquire)); - TEST_ASSERT_EQUAL_UINT64( - 1ULL, atomic_load_explicit(&(sim.core.speed_den), memory_order_acquire)); + (void)sched_sim_driver_run(&sim, &args); - /* speed=0 (freeze / as-fast-as-possible) is a legitimate value, unlike - * `step`'s N (which rejects <= 0). */ - handled = repl_cmd_set(&sim, "set speed 0"); - TEST_ASSERT_FALSE(handled); - TEST_ASSERT_EQUAL_UINT64(0ULL, sim.sim_speed_num); - TEST_ASSERT_EQUAL_UINT64( - 0ULL, atomic_load_explicit(&(sim.core.speed_num), memory_order_acquire)); + /* Unreachable in a correct implementation -- "quit" calls exit(0). */ + fflush(stdout); + _exit(111); +} - /* Malformed input must be rejected AND leave state unchanged. */ - handled = repl_cmd_set(&sim, "set speed abc"); - TEST_ASSERT_FALSE(handled); - TEST_ASSERT_EQUAL_UINT64_MESSAGE(0ULL, sim.sim_speed_num, - "a malformed `set speed` argument must " - "not change the current speed"); +static void test_scripted_repl_set_speed_then_info_config_then_quit(void) +{ + char replay_path[PATH_MAX]; + char aux_dir[PATH_MAX]; + char script_path[PATH_MAX]; + char capture_path[PATH_MAX]; + FILE* f; + pid_t pid; + int status; + char* captured; + char* cfg_line; - printf("test_set_speed_updates_shadow_and_core_atomics: PASS\n"); + f = write_header_and_snapshot(replay_path, sizeof(replay_path), + "_setspeedscript"); + TEST_ASSERT_NOT_NULL(f); + append_queue_update_event(f, 1000ULL, "normal"); + append_replay_session(f, 2000ULL); + append_end_of_stream_marker(f); + fclose(f); + + make_tmp_dir(aux_dir, sizeof(aux_dir), "_setspeedscriptaux"); + snprintf(script_path, sizeof(script_path), "%s/script.txt", aux_dir); + snprintf(capture_path, sizeof(capture_path), "%s/capture.out", aux_dir); + write_text_file(script_path, "set speed 7\ninfo config\nquit\n"); + + pid = fork(); + TEST_ASSERT_TRUE_MESSAGE(pid >= 0, "fork() must succeed"); + + if (pid == 0) { + child_run_repl_script(replay_path, script_path, capture_path); + _exit(112); /* unreachable */ + } + + TEST_ASSERT_EQUAL_INT_MESSAGE(pid, waitpid(pid, &status, 0), + "waitpid must reap the REPL child"); + TEST_ASSERT_TRUE_MESSAGE(WIFEXITED(status), + "the REPL child must exit normally via quit's " + "exit(0)"); + TEST_ASSERT_EQUAL_INT_MESSAGE(0, WEXITSTATUS(status), + "quit's exit(0) is the only correct exit " + "path"); + + captured = read_whole_file(capture_path); + TEST_ASSERT_NOT_NULL_MESSAGE(strstr(captured, SET_SPEED_REMOVED_MSG), + "the scripted `set speed 7` must print the " + "removal message"); + cfg_line = strstr(captured, "[sim] config:"); + TEST_ASSERT_NOT_NULL_MESSAGE(cfg_line, + "the REPL must keep reading after `set " + "speed` and run `info config`"); + TEST_ASSERT_NULL_MESSAGE(strstr(cfg_line, "speed="), + "`info config` must not report a speed item"); + free(captured); + + printf("test_scripted_repl_set_speed_then_info_config_then_quit: " + "PASS\n"); } /* ---------------------------------------------------------------------- */ @@ -1041,15 +1195,13 @@ static void test_set_output_opens_writes_preamble_and_off_closes(void) /* */ /* Direct struct inspection (rs->units_mode) observes the setting rather */ /* than routing through `info config`: today `info config` reports only */ -/* speed/output/verify/events_processed/current_ts/sessions +/* output/verify/events_processed/current_ts/sessions * (test_info_config_reports_current_parameters below), it does not report * units_mode, and design.md's Traceability table never lists `info config` * as a Resource Value Formatter dependency -- extending it is out of this * task's scope (that belongs to task 8, the table-layout task). Direct - * struct access is the same technique - * test_set_speed_updates_shadow_and_core_atomics/ - * test_set_verify_toggles_flag already use for their own driver-state - * fields (sim.sim_speed_num, sim.verify) above. + * struct access is the same technique test_set_verify_toggles_flag + * already uses for its own driver-state field (sim.verify) above. */ static void test_set_units_raw_human_both_update_display_mode(void) @@ -1122,8 +1274,7 @@ static void test_set_units_invalid_value_rejected_like_existing_pattern(void) TEST_ASSERT_NOT_NULL_MESSAGE( strstr(captured, "set units"), "an unrecognized `set units` value must be rejected with a " - "units-specific usage message -- matching `set speed`'s " - "\"[sim] set speed: usage: set speed \" and `set verify`'s " + "units-specific usage message -- matching `set verify`'s " "\"[sim] set verify: usage: set verify on|off\" per-key " "usage-error pattern (repl_params.c), NOT the generic \"[sim] set: " "unrecognized parameter 'units'\" message that fires today for an " @@ -1132,8 +1283,8 @@ static void test_set_units_invalid_value_rejected_like_existing_pattern(void) TEST_ASSERT_EQUAL_MESSAGE( XCE_REPL_UNITS_BOTH, g_units_invalid_sim.repl_state->units_mode, "a rejected `set units` value must leave the current display mode " - "unchanged -- same discipline `set speed`/`set verify`'s own " - "malformed-input tests already establish for their own fields"); + "unchanged -- same discipline `set verify`'s own " + "malformed-input test already establishes for its own field"); free(captured); printf("test_set_units_invalid_value_rejected_like_existing_pattern: " @@ -1141,9 +1292,9 @@ static void test_set_units_invalid_value_rejected_like_existing_pattern(void) } /* ---------------------------------------------------------------------- */ -/* Test: `info config` reports speed/output/verify/events_processed/ */ -/* current_ts/sessions, both while output is off and while it is active */ -/* with a known path. */ +/* Test: `info config` reports output/verify/events_processed/current_ts/ */ +/* sessions and no speed item (scheduler-sim-discrete-clock, Req 3.4), */ +/* both while output is off and while it is active with a known path. */ /* ---------------------------------------------------------------------- */ static xce_sched_sim_driver_t g_cfg_sim; @@ -1169,7 +1320,9 @@ static void test_info_config_reports_current_parameters(void) g_cfg_sim.record_file = NULL; captured = capture_stdout_around(run_info_config); - TEST_ASSERT_NOT_NULL(strstr(captured, "speed=3")); + TEST_ASSERT_NULL_MESSAGE(strstr(captured, "speed="), + "info config must not report a speed item " + "(the REPL speed parameter was removed)"); TEST_ASSERT_NOT_NULL(strstr(captured, "output=off")); TEST_ASSERT_NOT_NULL(strstr(captured, "verify=on")); TEST_ASSERT_NOT_NULL(strstr(captured, "events_processed=7")); @@ -1209,7 +1362,8 @@ static void test_help_lists_snapshot_and_set_commands(void) char* captured = capture_stdout_around(run_print_help); TEST_ASSERT_NOT_NULL(strstr(captured, "snapshot")); - TEST_ASSERT_NOT_NULL(strstr(captured, "set speed")); + TEST_ASSERT_NULL_MESSAGE(strstr(captured, "speed"), + "help must not offer a speed setting"); TEST_ASSERT_NOT_NULL(strstr(captured, "set output")); TEST_ASSERT_NOT_NULL(strstr(captured, "set verify")); TEST_ASSERT_NOT_NULL(strstr(captured, "info config")); @@ -1224,7 +1378,9 @@ int main(void) RUN_TEST(test_snapshot_written_file_is_loadable_by_fresh_sim_start); RUN_TEST(test_snapshot_auto_generates_filename_from_event_index); - RUN_TEST(test_set_speed_updates_shadow_and_core_atomics); + RUN_TEST(test_set_speed_prints_removal_message_and_changes_nothing); + RUN_TEST(test_set_usage_omits_speed); + RUN_TEST(test_scripted_repl_set_speed_then_info_config_then_quit); RUN_TEST(test_set_verify_toggles_flag); RUN_TEST(test_set_output_opens_writes_preamble_and_off_closes); RUN_TEST(test_set_units_raw_human_both_update_display_mode);