feat(scheduler-core): implement REPL snapshot capture and simulation parameter commands (task 12.3)
Adds `snapshot [FILE]` and `set speed|output|verify ...`/`info config` (Req 5 AC 22 "Snapshots"/"Simulation parameters") to the interactive simulation REPL. `snapshot` reuses sim_record.c's existing sim_record_write_header_and_ snapshot() (task 11.5) rather than reimplementing capnp encoding, so the produced file is genuinely loadable via a fresh --sim invocation -- the task's own Observable. `set speed`/`set output`/`set verify` mutate fields sim_loop.c and core_clock.c already read fresh on every event/session, so no new hook points were needed and sim_loop.c stays untouched at exactly 800/800 lines (this project's hard per-file cap). New repl_params.c is a fourth sibling in the repl.c/repl_break.c/ repl_info.c split (task 12.2's precedent) -- neither existing file had headroom left under the 800-line cap for this task's own logic. Scheduler suite: 36/36 clean (was 35/35). Review independently reproduced the RED-phase probe and additionally verified the snapshot round-trip Observable is non-vacuous. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JPPXeKSUXUqcqvzNXY1rzJ
This commit is contained in:
parent
2fecea3592
commit
2329dbf2a8
18 changed files with 2755 additions and 110 deletions
|
|
@ -5,22 +5,24 @@
|
|||
* from the simulation driver's compiled object" -- plus the AC's own
|
||||
* "confirmed by the struct definition itself, not runtime checks alone"
|
||||
* clause. Scope: sim.h + sim.c + sim_loop.c + sim_record.c + repl.c/repl.h/
|
||||
* repl_break.c/repl_info.c/repl_internal.h together (task 11.1 + task
|
||||
* 11.2's sibling file + task 11.5's sibling file, the --sim-output writer +
|
||||
* task 12.1's REPL files + task 12.2's own repl_break.c/repl_info.c/
|
||||
* repl_internal.h, its 800-line-cap split of repl.c -- see repl_internal.h's
|
||||
* own "Why a split at all" section) -- NOT a re-audit of core.c (task 4.1's
|
||||
* own territory, test_core_isolation.c) and NOT plugin.c/builtin_fcfs.c
|
||||
* (shared normal-mode infra, out of this task's boundary). sim_verify.c
|
||||
* (task 11.4) is likewise out of scope here -- it is never compiled into
|
||||
* repl_break.c/repl_info.c/repl_internal.h/repl_params.c together (task
|
||||
* 11.1 + task 11.2's sibling file + task 11.5's sibling file, the
|
||||
* --sim-output writer + task 12.1's REPL files + task 12.2's own
|
||||
* repl_break.c/repl_info.c/repl_internal.h + task 12.3's own repl_params.c,
|
||||
* each an 800-line-cap split of repl.c -- see repl_internal.h's own "Why a
|
||||
* split at all" section) -- NOT a re-audit of core.c (task 4.1's own
|
||||
* territory, test_core_isolation.c) and NOT plugin.c/builtin_fcfs.c (shared
|
||||
* normal-mode infra, out of this task's boundary). sim_verify.c (task 11.4)
|
||||
* is likewise out of scope here -- it is never compiled into
|
||||
* sched_sim_isolation_lib below and carries no isolation-sensitive logic of
|
||||
* its own (pure struct comparison, no I/O); extending this guard to it was
|
||||
* not part of task 11.4's own text and is left as-is. repl.c/repl.h were
|
||||
* added per task 12.1's own text (extending this exact 3-file precedent to
|
||||
* a 5-file one); repl_break.c/repl_info.c/repl_internal.h extend it again
|
||||
* (task 12.2, to an 8-file one) -- all touch xce_sched_sim_driver_t
|
||||
* directly or read/write stdin/stdout, the same category of driver-level
|
||||
* code this guard already covers.
|
||||
* a 5-file one); repl_break.c/repl_info.c/repl_internal.h extended it again
|
||||
* (task 12.2, to an 8-file one); repl_params.c extends it once more (task
|
||||
* 12.3, to a 9-file one) -- all touch xce_sched_sim_driver_t directly or
|
||||
* read/write stdin/stdout, the same category of driver-level code this
|
||||
* guard already covers.
|
||||
*
|
||||
* Three layers, extending test_core_isolation.c's two-layer pattern (same
|
||||
* genre: source-text scans of the real, current on-disk files, not a frozen
|
||||
|
|
@ -112,9 +114,13 @@
|
|||
#error \
|
||||
"XCE_SIM_REPL_INTERNAL_H_PATH must be defined by meson.build to repl_internal.h's absolute path"
|
||||
#endif
|
||||
#ifndef XCE_SIM_REPL_PARAMS_C_PATH
|
||||
#error \
|
||||
"XCE_SIM_REPL_PARAMS_C_PATH must be defined by meson.build to repl_params.c's absolute path"
|
||||
#endif
|
||||
#ifndef XCE_SIM_LIB_PATH
|
||||
#error \
|
||||
"XCE_SIM_LIB_PATH must be defined by meson.build to the absolute path of sim.c+sim_loop.c+sim_record.c+repl.c+repl_break.c+repl_info.c's isolated static_library() archive"
|
||||
"XCE_SIM_LIB_PATH must be defined by meson.build to the absolute path of sim.c+sim_loop.c+sim_record.c+repl.c+repl_break.c+repl_info.c+repl_params.c's isolated static_library() archive"
|
||||
#endif
|
||||
|
||||
/* Forbidden headers (design.md "Allowed Dependencies", same list
|
||||
|
|
@ -384,15 +390,16 @@ void tearDown(void) { return; }
|
|||
* failure mode this whole test exists to avoid). */
|
||||
static void test_sim_source_paths_are_readable(void)
|
||||
{
|
||||
char* h = read_whole_file(XCE_SIM_H_PATH);
|
||||
char* c = read_whole_file(XCE_SIM_C_PATH);
|
||||
char* loop = read_whole_file(XCE_SIM_LOOP_C_PATH);
|
||||
char* record = read_whole_file(XCE_SIM_RECORD_C_PATH);
|
||||
char* repl_c = read_whole_file(XCE_SIM_REPL_C_PATH);
|
||||
char* repl_h = read_whole_file(XCE_SIM_REPL_H_PATH);
|
||||
char* repl_break_c = read_whole_file(XCE_SIM_REPL_BREAK_C_PATH);
|
||||
char* repl_info_c = read_whole_file(XCE_SIM_REPL_INFO_C_PATH);
|
||||
char* repl_int_h = read_whole_file(XCE_SIM_REPL_INTERNAL_H_PATH);
|
||||
char* h = read_whole_file(XCE_SIM_H_PATH);
|
||||
char* c = read_whole_file(XCE_SIM_C_PATH);
|
||||
char* loop = read_whole_file(XCE_SIM_LOOP_C_PATH);
|
||||
char* record = read_whole_file(XCE_SIM_RECORD_C_PATH);
|
||||
char* repl_c = read_whole_file(XCE_SIM_REPL_C_PATH);
|
||||
char* repl_h = read_whole_file(XCE_SIM_REPL_H_PATH);
|
||||
char* repl_break_c = read_whole_file(XCE_SIM_REPL_BREAK_C_PATH);
|
||||
char* repl_info_c = read_whole_file(XCE_SIM_REPL_INFO_C_PATH);
|
||||
char* repl_int_h = read_whole_file(XCE_SIM_REPL_INTERNAL_H_PATH);
|
||||
char* repl_params_c = read_whole_file(XCE_SIM_REPL_PARAMS_C_PATH);
|
||||
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(h, XCE_SIM_H_PATH);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(c, XCE_SIM_C_PATH);
|
||||
|
|
@ -403,6 +410,7 @@ static void test_sim_source_paths_are_readable(void)
|
|||
TEST_ASSERT_NOT_NULL_MESSAGE(repl_break_c, XCE_SIM_REPL_BREAK_C_PATH);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(repl_info_c, XCE_SIM_REPL_INFO_C_PATH);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(repl_int_h, XCE_SIM_REPL_INTERNAL_H_PATH);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(repl_params_c, XCE_SIM_REPL_PARAMS_C_PATH);
|
||||
|
||||
free(h);
|
||||
free(c);
|
||||
|
|
@ -413,19 +421,21 @@ static void test_sim_source_paths_are_readable(void)
|
|||
free(repl_break_c);
|
||||
free(repl_info_c);
|
||||
free(repl_int_h);
|
||||
free(repl_params_c);
|
||||
}
|
||||
|
||||
static void test_sim_never_includes_forbidden_headers(void)
|
||||
{
|
||||
char* h_raw = read_whole_file(XCE_SIM_H_PATH);
|
||||
char* c_raw = read_whole_file(XCE_SIM_C_PATH);
|
||||
char* loop_raw = read_whole_file(XCE_SIM_LOOP_C_PATH);
|
||||
char* record_raw = read_whole_file(XCE_SIM_RECORD_C_PATH);
|
||||
char* repl_c_raw = read_whole_file(XCE_SIM_REPL_C_PATH);
|
||||
char* repl_h_raw = read_whole_file(XCE_SIM_REPL_H_PATH);
|
||||
char* repl_break_c_raw = read_whole_file(XCE_SIM_REPL_BREAK_C_PATH);
|
||||
char* repl_info_c_raw = read_whole_file(XCE_SIM_REPL_INFO_C_PATH);
|
||||
char* repl_int_h_raw = read_whole_file(XCE_SIM_REPL_INTERNAL_H_PATH);
|
||||
char* h_raw = read_whole_file(XCE_SIM_H_PATH);
|
||||
char* c_raw = read_whole_file(XCE_SIM_C_PATH);
|
||||
char* loop_raw = read_whole_file(XCE_SIM_LOOP_C_PATH);
|
||||
char* record_raw = read_whole_file(XCE_SIM_RECORD_C_PATH);
|
||||
char* repl_c_raw = read_whole_file(XCE_SIM_REPL_C_PATH);
|
||||
char* repl_h_raw = read_whole_file(XCE_SIM_REPL_H_PATH);
|
||||
char* repl_break_c_raw = read_whole_file(XCE_SIM_REPL_BREAK_C_PATH);
|
||||
char* repl_info_c_raw = read_whole_file(XCE_SIM_REPL_INFO_C_PATH);
|
||||
char* repl_int_h_raw = read_whole_file(XCE_SIM_REPL_INTERNAL_H_PATH);
|
||||
char* repl_params_c_raw = read_whole_file(XCE_SIM_REPL_PARAMS_C_PATH);
|
||||
char* h;
|
||||
char* c;
|
||||
char* loop;
|
||||
|
|
@ -435,6 +445,7 @@ static void test_sim_never_includes_forbidden_headers(void)
|
|||
char* repl_break_c;
|
||||
char* repl_info_c;
|
||||
char* repl_int_h;
|
||||
char* repl_params_c;
|
||||
|
||||
TEST_ASSERT_NOT_NULL(h_raw);
|
||||
TEST_ASSERT_NOT_NULL(c_raw);
|
||||
|
|
@ -445,16 +456,18 @@ static void test_sim_never_includes_forbidden_headers(void)
|
|||
TEST_ASSERT_NOT_NULL(repl_break_c_raw);
|
||||
TEST_ASSERT_NOT_NULL(repl_info_c_raw);
|
||||
TEST_ASSERT_NOT_NULL(repl_int_h_raw);
|
||||
TEST_ASSERT_NOT_NULL(repl_params_c_raw);
|
||||
|
||||
h = strip_c_comments(h_raw);
|
||||
c = strip_c_comments(c_raw);
|
||||
loop = strip_c_comments(loop_raw);
|
||||
record = strip_c_comments(record_raw);
|
||||
repl_c = strip_c_comments(repl_c_raw);
|
||||
repl_h = strip_c_comments(repl_h_raw);
|
||||
repl_break_c = strip_c_comments(repl_break_c_raw);
|
||||
repl_info_c = strip_c_comments(repl_info_c_raw);
|
||||
repl_int_h = strip_c_comments(repl_int_h_raw);
|
||||
h = strip_c_comments(h_raw);
|
||||
c = strip_c_comments(c_raw);
|
||||
loop = strip_c_comments(loop_raw);
|
||||
record = strip_c_comments(record_raw);
|
||||
repl_c = strip_c_comments(repl_c_raw);
|
||||
repl_h = strip_c_comments(repl_h_raw);
|
||||
repl_break_c = strip_c_comments(repl_break_c_raw);
|
||||
repl_info_c = strip_c_comments(repl_info_c_raw);
|
||||
repl_int_h = strip_c_comments(repl_int_h_raw);
|
||||
repl_params_c = strip_c_comments(repl_params_c_raw);
|
||||
TEST_ASSERT_NOT_NULL(h);
|
||||
TEST_ASSERT_NOT_NULL(c);
|
||||
TEST_ASSERT_NOT_NULL(loop);
|
||||
|
|
@ -464,6 +477,7 @@ static void test_sim_never_includes_forbidden_headers(void)
|
|||
TEST_ASSERT_NOT_NULL(repl_break_c);
|
||||
TEST_ASSERT_NOT_NULL(repl_info_c);
|
||||
TEST_ASSERT_NOT_NULL(repl_int_h);
|
||||
TEST_ASSERT_NOT_NULL(repl_params_c);
|
||||
|
||||
assert_no_forbidden_includes(h, "sim.h");
|
||||
assert_no_forbidden_includes(c, "sim.c");
|
||||
|
|
@ -474,6 +488,7 @@ static void test_sim_never_includes_forbidden_headers(void)
|
|||
assert_no_forbidden_includes(repl_break_c, "repl_break.c");
|
||||
assert_no_forbidden_includes(repl_info_c, "repl_info.c");
|
||||
assert_no_forbidden_includes(repl_int_h, "repl_internal.h");
|
||||
assert_no_forbidden_includes(repl_params_c, "repl_params.c");
|
||||
|
||||
free(h_raw);
|
||||
free(c_raw);
|
||||
|
|
@ -484,6 +499,7 @@ static void test_sim_never_includes_forbidden_headers(void)
|
|||
free(repl_break_c_raw);
|
||||
free(repl_info_c_raw);
|
||||
free(repl_int_h_raw);
|
||||
free(repl_params_c_raw);
|
||||
free(h);
|
||||
free(c);
|
||||
free(loop);
|
||||
|
|
@ -493,19 +509,21 @@ static void test_sim_never_includes_forbidden_headers(void)
|
|||
free(repl_break_c);
|
||||
free(repl_info_c);
|
||||
free(repl_int_h);
|
||||
free(repl_params_c);
|
||||
}
|
||||
|
||||
static void test_sim_never_calls_forbidden_symbols(void)
|
||||
{
|
||||
char* h_raw = read_whole_file(XCE_SIM_H_PATH);
|
||||
char* c_raw = read_whole_file(XCE_SIM_C_PATH);
|
||||
char* loop_raw = read_whole_file(XCE_SIM_LOOP_C_PATH);
|
||||
char* record_raw = read_whole_file(XCE_SIM_RECORD_C_PATH);
|
||||
char* repl_c_raw = read_whole_file(XCE_SIM_REPL_C_PATH);
|
||||
char* repl_h_raw = read_whole_file(XCE_SIM_REPL_H_PATH);
|
||||
char* repl_break_c_raw = read_whole_file(XCE_SIM_REPL_BREAK_C_PATH);
|
||||
char* repl_info_c_raw = read_whole_file(XCE_SIM_REPL_INFO_C_PATH);
|
||||
char* repl_int_h_raw = read_whole_file(XCE_SIM_REPL_INTERNAL_H_PATH);
|
||||
char* h_raw = read_whole_file(XCE_SIM_H_PATH);
|
||||
char* c_raw = read_whole_file(XCE_SIM_C_PATH);
|
||||
char* loop_raw = read_whole_file(XCE_SIM_LOOP_C_PATH);
|
||||
char* record_raw = read_whole_file(XCE_SIM_RECORD_C_PATH);
|
||||
char* repl_c_raw = read_whole_file(XCE_SIM_REPL_C_PATH);
|
||||
char* repl_h_raw = read_whole_file(XCE_SIM_REPL_H_PATH);
|
||||
char* repl_break_c_raw = read_whole_file(XCE_SIM_REPL_BREAK_C_PATH);
|
||||
char* repl_info_c_raw = read_whole_file(XCE_SIM_REPL_INFO_C_PATH);
|
||||
char* repl_int_h_raw = read_whole_file(XCE_SIM_REPL_INTERNAL_H_PATH);
|
||||
char* repl_params_c_raw = read_whole_file(XCE_SIM_REPL_PARAMS_C_PATH);
|
||||
char* h;
|
||||
char* c;
|
||||
char* loop;
|
||||
|
|
@ -515,6 +533,7 @@ static void test_sim_never_calls_forbidden_symbols(void)
|
|||
char* repl_break_c;
|
||||
char* repl_info_c;
|
||||
char* repl_int_h;
|
||||
char* repl_params_c;
|
||||
|
||||
TEST_ASSERT_NOT_NULL(h_raw);
|
||||
TEST_ASSERT_NOT_NULL(c_raw);
|
||||
|
|
@ -525,16 +544,18 @@ static void test_sim_never_calls_forbidden_symbols(void)
|
|||
TEST_ASSERT_NOT_NULL(repl_break_c_raw);
|
||||
TEST_ASSERT_NOT_NULL(repl_info_c_raw);
|
||||
TEST_ASSERT_NOT_NULL(repl_int_h_raw);
|
||||
TEST_ASSERT_NOT_NULL(repl_params_c_raw);
|
||||
|
||||
h = strip_c_comments(h_raw);
|
||||
c = strip_c_comments(c_raw);
|
||||
loop = strip_c_comments(loop_raw);
|
||||
record = strip_c_comments(record_raw);
|
||||
repl_c = strip_c_comments(repl_c_raw);
|
||||
repl_h = strip_c_comments(repl_h_raw);
|
||||
repl_break_c = strip_c_comments(repl_break_c_raw);
|
||||
repl_info_c = strip_c_comments(repl_info_c_raw);
|
||||
repl_int_h = strip_c_comments(repl_int_h_raw);
|
||||
h = strip_c_comments(h_raw);
|
||||
c = strip_c_comments(c_raw);
|
||||
loop = strip_c_comments(loop_raw);
|
||||
record = strip_c_comments(record_raw);
|
||||
repl_c = strip_c_comments(repl_c_raw);
|
||||
repl_h = strip_c_comments(repl_h_raw);
|
||||
repl_break_c = strip_c_comments(repl_break_c_raw);
|
||||
repl_info_c = strip_c_comments(repl_info_c_raw);
|
||||
repl_int_h = strip_c_comments(repl_int_h_raw);
|
||||
repl_params_c = strip_c_comments(repl_params_c_raw);
|
||||
TEST_ASSERT_NOT_NULL(h);
|
||||
TEST_ASSERT_NOT_NULL(c);
|
||||
TEST_ASSERT_NOT_NULL(loop);
|
||||
|
|
@ -544,6 +565,7 @@ static void test_sim_never_calls_forbidden_symbols(void)
|
|||
TEST_ASSERT_NOT_NULL(repl_break_c);
|
||||
TEST_ASSERT_NOT_NULL(repl_info_c);
|
||||
TEST_ASSERT_NOT_NULL(repl_int_h);
|
||||
TEST_ASSERT_NOT_NULL(repl_params_c);
|
||||
|
||||
assert_no_forbidden_symbols(h, "sim.h");
|
||||
assert_no_forbidden_symbols(c, "sim.c");
|
||||
|
|
@ -554,6 +576,7 @@ static void test_sim_never_calls_forbidden_symbols(void)
|
|||
assert_no_forbidden_symbols(repl_break_c, "repl_break.c");
|
||||
assert_no_forbidden_symbols(repl_info_c, "repl_info.c");
|
||||
assert_no_forbidden_symbols(repl_int_h, "repl_internal.h");
|
||||
assert_no_forbidden_symbols(repl_params_c, "repl_params.c");
|
||||
|
||||
free(h_raw);
|
||||
free(c_raw);
|
||||
|
|
@ -564,6 +587,7 @@ static void test_sim_never_calls_forbidden_symbols(void)
|
|||
free(repl_break_c_raw);
|
||||
free(repl_info_c_raw);
|
||||
free(repl_int_h_raw);
|
||||
free(repl_params_c_raw);
|
||||
free(h);
|
||||
free(c);
|
||||
free(loop);
|
||||
|
|
@ -573,6 +597,7 @@ static void test_sim_never_calls_forbidden_symbols(void)
|
|||
free(repl_break_c);
|
||||
free(repl_info_c);
|
||||
free(repl_int_h);
|
||||
free(repl_params_c);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -617,11 +642,12 @@ static void test_sim_driver_struct_has_no_forbidden_fields(void)
|
|||
/*
|
||||
* test_sim_object_has_no_forbidden_undefined_symbols - the literal
|
||||
* build-time/link-time proof: sim.c + sim_loop.c + sim_record.c + repl.c +
|
||||
* repl_break.c + repl_info.c, compiled together in isolation into
|
||||
* sched_sim_isolation_lib (meson.build), must not reference (as an
|
||||
* undefined external symbol in the compiled archive) anything matching the
|
||||
* forbidden prefixes above. Independent of what any header transitively
|
||||
* declares -- reflects only what this driver's compiled code actually
|
||||
* repl_break.c + repl_info.c + repl_params.c, compiled together in
|
||||
* isolation into sched_sim_isolation_lib (meson.build), must not reference
|
||||
* (as an undefined external symbol in the compiled archive) anything
|
||||
* matching the forbidden prefixes above. Independent of what any header
|
||||
* transitively declares -- reflects only what this driver's compiled code
|
||||
* actually
|
||||
* calls. Same technique and same any_undefined_seen vacuous-pass guard as
|
||||
* test_core_isolation.c's test_core_object_has_no_forbidden_undefined_symbols().
|
||||
*/
|
||||
|
|
@ -646,9 +672,9 @@ static void test_sim_object_has_no_forbidden_undefined_symbols(void)
|
|||
/* posix format for an undefined symbol: "<name> U" (no
|
||||
* value/size columns). Skip anything that doesn't parse as
|
||||
* exactly that -- e.g. a blank line, or an archive member header
|
||||
* some nm versions print (this archive has six member objects,
|
||||
* some nm versions print (this archive has seven member objects,
|
||||
* sim.c.o, sim_loop.c.o, sim_record.c.o, repl.c.o, repl_break.c.o,
|
||||
* and repl_info.c.o). */
|
||||
* repl_info.c.o, and repl_params.c.o). */
|
||||
if (sscanf(line, "%511s %c", symbol, &type) != 2) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue