feat(scheduler-sim-repl-verbosity): task 7.1 - RED-phase resource formatting tests

Adds 5 pure-function tests in a new test_repl_resource_formatting.c
for repl_is_bytes_valued_resource()/repl_format_bytes_human()
(classification, adaptive B/KB/MB/GB/TB/PB scaling including exact
boundary values, swp's MB->bytes conversion), and 3 tests in
test_repl_snapshot_params.c for set units raw|human|both parsing
(Requirements 6.1-6.3). All fail as expected: link failure for the
not-yet-existing helpers, compile failure for the not-yet-existing
units_mode field/enum.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YAWMhfRJFnmmnG8h97yUDd
This commit is contained in:
Rongsong Shen 2026-09-02 16:45:59 +00:00
parent da6ba96eb4
commit 11fe6194ce
No known key found for this signature in database
GPG key ID: 5BCDAAE0123E9FE2
4 changed files with 495 additions and 1 deletions

View file

@ -72,7 +72,7 @@
- _Boundary: REPL Prompt Context_ - _Boundary: REPL Prompt Context_
- [ ] 7. (P) Resource value classification, human-readable scaling, and the `set units` option - [ ] 7. (P) Resource value classification, human-readable scaling, and the `set units` option
- [ ] 7.1 (P) Write failing tests for byte/megabyte classification, adaptive scaling, and `set units` parsing - [x] 7.1 (P) Write failing tests for byte/megabyte classification, adaptive scaling, and `set units` parsing
- Cover: the bytes-valued classifier returns true only for `mem`/`tmp`/`maxmem`/`maxtmp`/`maxswap` and false for `swp`, unitless resources, and unknown names; the scaling helper adaptively renders across B/KB/MB/GB/TB, including exact unit-boundary values; `swp`'s value is converted MB→bytes before scaling; `set units raw`/`human`/`both` each update the display-mode setting, defaulting to `both`; an unrecognized `set units` value is rejected the same way other unrecognized `set` parameters already are - Cover: the bytes-valued classifier returns true only for `mem`/`tmp`/`maxmem`/`maxtmp`/`maxswap` and false for `swp`, unitless resources, and unknown names; the scaling helper adaptively renders across B/KB/MB/GB/TB, including exact unit-boundary values; `swp`'s value is converted MB→bytes before scaling; `set units raw`/`human`/`both` each update the display-mode setting, defaulting to `both`; an unrecognized `set units` value is rejected the same way other unrecognized `set` parameters already are
- Observable: the new test cases fail against the current codebase, which has neither the classifier/scaling helpers nor a `units` setting - Observable: the new test cases fail against the current codebase, which has neither the classifier/scaling helpers nor a `units` setting
- _Requirements: 6.1, 6.2, 6.3_ - _Requirements: 6.1, 6.2, 6.3_

View file

@ -1126,6 +1126,36 @@ sched_repl_output_compression_test = executable(
dependencies: [scheduler_deps, unity_dep], dependencies: [scheduler_deps, unity_dep],
install: false) install: false)
# Contract test for the REPL's resource-value byte/megabyte classification
# and adaptive human-scaling helpers (scheduler-sim-repl-verbosity spec, task
# 7.1, Requirement 6.1/6.2 "Human-Readable Resource Value Formatting").
# repl_is_bytes_valued_resource()/repl_format_bytes_human() are pure,
# self-contained formatters (design.md's "Resource Value Formatter"
# component) that -- like repl_fmt_jobid_range() above -- do not yet exist
# anywhere in this codebase (task 7.2, a separate later task, implements
# them); this test file declares them as plain externs rather than
# #include-ing repl.c, same rationale as
# repl_output_compression_test_srcs above (repl.c's own dependency surface
# pulls in undefined cross-references to sibling files this test does not
# need). Same "link the real scheduler_srcs object list instead of textually
# including one" technique, for the identical reason: task 7.2 has not
# landed yet, so this target is expected to fail at LINK time (undefined
# reference to both symbols) until it does -- that failure is task 7.1's own
# Observable evidence.
repl_resource_formatting_test_srcs = ['test_repl_resource_formatting.c']
foreach f : scheduler_srcs
if f != 'src/main.c'
repl_resource_formatting_test_srcs += ['../' + f]
endif
endforeach
sched_repl_resource_formatting_test = executable(
'sched_repl_resource_formatting_test',
repl_resource_formatting_test_srcs,
include_directories:[incdir],
dependencies: [scheduler_deps, unity_dep],
install: false)
# Tests for the scheduler's unified candidate-list connect/rejoin pipeline # Tests for the scheduler's unified candidate-list connect/rejoin pipeline
# (component-failover-adoption, task 3.1, boundary: SchedulerControllerClient). # (component-failover-adoption, task 3.1, boundary: SchedulerControllerClient).
# Unlike the white-box #include-the-.c-file tests above, this target links # Unlike the white-box #include-the-.c-file tests above, this target links
@ -1491,6 +1521,9 @@ test('scheduler repl equivalence test',
test('scheduler repl output compression test', test('scheduler repl output compression test',
sched_repl_output_compression_test, suite: 'scheduler', sched_repl_output_compression_test, suite: 'scheduler',
env: ['ASAN_OPTIONS=detect_leaks=0']) env: ['ASAN_OPTIONS=detect_leaks=0'])
test('scheduler repl resource formatting test',
sched_repl_resource_formatting_test, suite: 'scheduler',
env: ['ASAN_OPTIONS=detect_leaks=0'])
test('scheduler connect pipeline test', test('scheduler connect pipeline test',
sched_scheduler_connect_test, suite: 'scheduler', sched_scheduler_connect_test, suite: 'scheduler',
env: ['ASAN_OPTIONS=detect_leaks=0']) env: ['ASAN_OPTIONS=detect_leaks=0'])

View file

@ -0,0 +1,327 @@
/*
* test_repl_resource_formatting.c - RED-phase contract test for the REPL's
* resource-value byte/megabyte classification and adaptive human-scaling
* helpers (scheduler-sim-repl-verbosity spec, task 7.1, Requirement 6
* "Human-Readable Resource Value Formatting", ACs 6.1, 6.2, 6.3).
*
* THIS TASK (7.1) IS RED-PHASE ONLY: neither `repl_is_bytes_valued_resource()`
* nor `repl_format_bytes_human()` exists anywhere in this codebase yet --
* task 7.2 (a separate, later task) implements them. Building this file is
* therefore expected to FAIL AT LINK TIME (undefined reference to both
* symbols), not to fail a runtime assertion -- that link failure IS this
* task's Observable evidence per tasks.md. Once task 7.2 lands real
* 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
* two pure classification/scaling functions.
*
* Declaring the functions under test: same rationale as
* test_repl_output_compression.c's own header comment (task 1.1 precedent,
* cited by design.md's Testing Strategy section as this file's pattern to
* mirror). design.md's "Resource Value Formatter" component documents both
* functions as pure, self-contained formatters with "no dependency on
* sim/core/repl state" (Contracts: Service [x] / State [x] -- the [x] on
* State refers to the SEPARATE `units_mode` field these two functions never
* touch, not to any state of their own). Rather than white-box #include-ing
* repl.c (which would drag in repl_internal.h's other extern
* cross-references to repl_cmd_break/repl_cmd_info/repl_cmd_set/... in
* sibling files, producing a pile of unrelated undefined-reference errors
* that would bury the two this task actually wants to prove), this file
* declares both functions locally as plain `extern`s with the exact
* signatures design.md's Service Interface gives. Once task 7.2 gives them a
* shared declaration in repl_internal.h, these local prototypes become
* redundant but harmless (identical signatures) -- meson.build links this
* test against the real scheduler_srcs object list (not a textual
* #include), the same technique test_repl_output_compression.c's own
* meson.build entry already established for this exact "real production
* dependency surface, avoid textual inclusion" situation.
*
* Evidence for the classification list and the swp special case (design.md
* cites these as the exact grounding for its "confirmed canonical-bytes"/
* "confirmed canonical-megabytes" claims, re-verified directly against
* source for this task):
* - src/worker/src/node_information.c: node_info_get_mem()'s doc comment
* ("physical memory in bytes (canonical internal/wire unit -- same
* convention as the maxmem host attribute)"), node_info_get_tmp()'s doc
* comment (same canonical-bytes convention), and node_info_get_swap()'s
* doc comment ("currently available virtual memory (swap space) in MB"
* -- megabytes, NOT bytes, the one deliberate exception).
* - src/worker/src/xcecd.c's init_host_attrs() bug-1554 comment (line
* ~353): "host_attrs.maxswap"/"host_attrs.maxtmp" are set in the SAME
* canonical unit as "maxmem, mem, tmp" -- i.e. bytes -- confirming
* maxswap/maxtmp belong in the bytes-valued list alongside maxmem/mem/
* tmp, unlike their un-prefixed `swp` load-index counterpart.
* - src/controller/testsuite/test.c's is_bytes_valued_resource()/
* format_bytes_human() (design.md's cited precedent): same 5-name list
* { "maxmem", "mem", "tmp", "maxswap", "maxtmp" }, same "divide by 1024
* while >= 1024.0, cap at PB (6 units)" adaptive-scaling algorithm, same
* "%.2f %s" render format (e.g. "20.56 GB") -- this file's expected
* values below are computed against that identical algorithm.
*
* Assumed output contract (design.md's Service Interface documents the
* classification list and the scaling algorithm, but pins one literal
* example only -- "20.56 GB" -- this header comment fixes the rest of the
* literal format these tests rely on, for task 7.2 to satisfy):
* - repl_is_bytes_valued_resource(name) returns true for EXACTLY "mem",
* "tmp", "maxmem", "maxtmp", "maxswap" and false for every other name,
* including "swp" (special-cased separately, see below), an unrelated
* unitless resource such as "r1m" (a 1-minute load average -- cited by
* test.c's own is_bytes_valued_resource() doc comment as a deliberate
* non-member alongside cpuf/r15s), and an unknown/custom name.
* - repl_format_bytes_human(bytes, buf, bufsz) writes "<val> <UNIT>" with
* `val` rendered via "%.2f" (always exactly two decimal places, e.g.
* "1.00 KB", never "1 KB" or "1.0 KB") and `UNIT` one of B/KB/MB/GB/TB/
* PB, choosing the smallest unit `bytes` fits in a value < 1024 for (or
* PB once no larger unit remains, per test.c's own cap-at-PB behavior).
* - `swp`'s wire value is in megabytes (node_info_get_swap()'s own doc
* comment); to render it on the SAME adaptive B/KB/MB/GB/TB/PB scale a
* byte-native resource uses, design.md's own "Resource Value Formatter"
* Responsibilities section requires converting it to bytes (x 1024.0 x
* 1024.0) BEFORE calling repl_format_bytes_human() -- exercised directly
* below via the same two public pure functions, since the conversion
* itself is a one-line caller-side multiplication, not a third helper
* function design.md declares anywhere.
*/
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include <unity/unity.h>
/*
* repl_is_bytes_valued_resource / repl_format_bytes_human (task 7.2, NOT YET
* IMPLEMENTED -- see this file's own header comment above for why these are
* local externs rather than a white-box #include). Signatures copied
* verbatim from design.md's "Resource Value Formatter" Service Interface.
*/
extern bool repl_is_bytes_valued_resource(const char* name);
extern void repl_format_bytes_human(double bytes, char* buf, size_t bufsz);
void setUp(void) { return; }
void tearDown(void) { return; }
/* ---------------------------------------------------------------------- */
/* Test 1 (AC 6.1): the classifier returns true for EXACTLY the five */
/* confirmed canonical-bytes resource names. */
/* ---------------------------------------------------------------------- */
static void test_bytes_valued_classifier_true_for_canonical_five(void)
{
TEST_ASSERT_TRUE_MESSAGE(repl_is_bytes_valued_resource("mem"),
"mem's wire unit is bytes "
"(node_info_get_mem() doc comment)");
TEST_ASSERT_TRUE_MESSAGE(repl_is_bytes_valued_resource("tmp"),
"tmp's wire unit is bytes "
"(node_info_get_tmp() doc comment)");
TEST_ASSERT_TRUE_MESSAGE(repl_is_bytes_valued_resource("maxmem"),
"maxmem's wire unit is bytes "
"(xcecd.c init_host_attrs() bug-1554 comment)");
TEST_ASSERT_TRUE_MESSAGE(repl_is_bytes_valued_resource("maxtmp"),
"maxtmp's wire unit is bytes "
"(xcecd.c init_host_attrs() bug-1554 comment)");
TEST_ASSERT_TRUE_MESSAGE(repl_is_bytes_valued_resource("maxswap"),
"maxswap's wire unit is bytes -- unlike its "
"un-prefixed `swp` load-index counterpart "
"(xcecd.c init_host_attrs() bug-1554 comment)");
printf("test_bytes_valued_classifier_true_for_canonical_five: PASS\n");
}
/* ---------------------------------------------------------------------- */
/* Test 2 (AC 6.1): the classifier returns false for `swp` (a deliberate */
/* special case, megabytes not bytes), an unrelated unitless resource, and */
/* an unknown/custom name. */
/* ---------------------------------------------------------------------- */
static void test_bytes_valued_classifier_false_for_swp_unitless_and_unknown(
void)
{
TEST_ASSERT_FALSE_MESSAGE(
repl_is_bytes_valued_resource("swp"),
"swp is deliberately NOT in the bytes-valued list -- its wire unit "
"is megabytes (node_info_get_swap() doc comment), handled via a "
"caller-side MB->bytes conversion instead (see test 5 below)");
TEST_ASSERT_FALSE_MESSAGE(
repl_is_bytes_valued_resource("r1m"),
"r1m (1-minute load average) is a unitless factor, not a "
"byte-denominated resource (test.c's own is_bytes_valued_resource() "
"doc comment names it as a deliberate non-member)");
TEST_ASSERT_FALSE_MESSAGE(
repl_is_bytes_valued_resource("gpu_slots"),
"an unknown/custom resource name must not be misclassified as "
"bytes-valued -- this is a small, explicit, hardcoded list, not a "
"heuristic");
printf("test_bytes_valued_classifier_false_for_swp_unitless_and_unknown: "
"PASS\n");
}
/* ---------------------------------------------------------------------- */
/* Test 3 (AC 6.2): adaptive scaling across the full B/KB/MB/GB/TB/PB unit */
/* range, at exact unit boundaries -- the 1024-byte boundary must render as */
/* "1.00 KB", never "1024.00 B" (design.md's own literal example of the */
/* algorithm's intent). */
/* ---------------------------------------------------------------------- */
static void test_format_bytes_human_scales_across_full_unit_range_at_boundaries(
void)
{
char buf[64];
memset(buf, 0, sizeof(buf));
repl_format_bytes_human(0.0, buf, sizeof(buf));
TEST_ASSERT_EQUAL_STRING("0.00 B", buf);
memset(buf, 0, sizeof(buf));
repl_format_bytes_human(1023.0, buf, sizeof(buf));
TEST_ASSERT_EQUAL_STRING_MESSAGE(
"1023.00 B", buf, "just below the KB boundary must stay in B");
memset(buf, 0, sizeof(buf));
repl_format_bytes_human(1024.0, buf, sizeof(buf));
TEST_ASSERT_EQUAL_STRING_MESSAGE(
"1.00 KB", buf,
"exactly 1024 bytes must render as '1.00 KB', not '1024.00 B'");
memset(buf, 0, sizeof(buf));
repl_format_bytes_human(1024.0 * 1024.0, buf, sizeof(buf));
TEST_ASSERT_EQUAL_STRING("1.00 MB", buf);
memset(buf, 0, sizeof(buf));
repl_format_bytes_human(1024.0 * 1024.0 * 1024.0, buf, sizeof(buf));
TEST_ASSERT_EQUAL_STRING("1.00 GB", buf);
memset(buf, 0, sizeof(buf));
repl_format_bytes_human(1024.0 * 1024.0 * 1024.0 * 1024.0, buf,
sizeof(buf));
TEST_ASSERT_EQUAL_STRING("1.00 TB", buf);
memset(buf, 0, sizeof(buf));
repl_format_bytes_human(1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0, buf,
sizeof(buf));
TEST_ASSERT_EQUAL_STRING("1.00 PB", buf);
/* PB is the largest unit -- a value that would nominally scale past PB
* stays capped there instead of inventing a further unit (test.c's own
* "cap at PB" precedent, design.md's "cap at PB" algorithm text). */
memset(buf, 0, sizeof(buf));
repl_format_bytes_human(
1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0 * 1024.0, buf,
sizeof(buf));
TEST_ASSERT_EQUAL_STRING_MESSAGE(
"1024.00 PB", buf,
"a value past the PB range must stay capped at PB, not invent a "
"further unit");
printf("test_format_bytes_human_scales_across_full_unit_range_at_"
"boundaries: PASS\n");
}
/* ---------------------------------------------------------------------- */
/* Test 4 (AC 6.2): non-boundary, fractional values render with exactly two */
/* decimal places within their chosen unit (design.md's own literal example */
/* is a non-boundary value: "20.56 GB"). */
/* ---------------------------------------------------------------------- */
static void test_format_bytes_human_fractional_values_render_two_decimals(
void)
{
char buf[64];
/* 1.5 MB, exactly representable in double (1.5 * 1048576 = 1572864.0) --
* a clean fractional value within one unit, not a unit-boundary value. */
memset(buf, 0, sizeof(buf));
repl_format_bytes_human(1572864.0, buf, sizeof(buf));
TEST_ASSERT_EQUAL_STRING("1.50 MB", buf);
/* 500 bytes -- fractional-free but still exercises the fixed "%.2f"
* two-decimal render at the smallest (B) unit. */
memset(buf, 0, sizeof(buf));
repl_format_bytes_human(500.0, buf, sizeof(buf));
TEST_ASSERT_EQUAL_STRING("500.00 B", buf);
/* 5,000,000,000 bytes / 1024^3 = 4.656612873077393... GB, rounds via
* "%.2f" to "4.66 GB" -- an arbitrary, non-power-of-2 value exercising
* real rounding, not just clean multiples of 1024. */
memset(buf, 0, sizeof(buf));
repl_format_bytes_human(5000000000.0, buf, sizeof(buf));
TEST_ASSERT_EQUAL_STRING("4.66 GB", buf);
printf("test_format_bytes_human_fractional_values_render_two_decimals: "
"PASS\n");
}
/* ---------------------------------------------------------------------- */
/* Test 5 (AC 6.1): swp's value, converted MB->bytes by the caller, renders */
/* on the SAME adaptive scale a byte-native resource of equivalent */
/* magnitude would -- and skipping the conversion (feeding the raw MB */
/* number straight to the byte-scaling formatter) produces a visibly wrong */
/* result, proving the conversion step is load-bearing. */
/* ---------------------------------------------------------------------- */
static void test_swp_value_converted_mb_to_bytes_renders_on_same_adaptive_scale(
void)
{
char buf_converted[64];
char buf_byte_native[64];
char buf_unconverted[64];
double swp_raw_mb = 2048.0; /* swp's wire value, in MB */
double swp_bytes = swp_raw_mb * 1024.0 * 1024.0; /* == 2 GiB in bytes */
double byte_native_equivalent = 2.0 * 1024.0 * 1024.0 * 1024.0;
TEST_ASSERT_FALSE_MESSAGE(
repl_is_bytes_valued_resource("swp"),
"swp must never be classified bytes-valued -- the MB->bytes "
"conversion is a caller-side step taken BECAUSE the classifier "
"correctly says false, not a classifier behavior itself");
memset(buf_converted, 0, sizeof(buf_converted));
repl_format_bytes_human(swp_bytes, buf_converted, sizeof(buf_converted));
TEST_ASSERT_EQUAL_STRING_MESSAGE(
"2.00 GB", buf_converted,
"swp's 2048 MB, converted to bytes (x 1024.0 x 1024.0) before "
"scaling, must render as '2.00 GB'");
/* A byte-native resource of the identical magnitude (e.g. maxmem at
* exactly 2 GiB) must render IDENTICALLY -- same function, same units,
* no swp-specific quirk in the scaling output itself. */
memset(buf_byte_native, 0, sizeof(buf_byte_native));
repl_format_bytes_human(byte_native_equivalent, buf_byte_native,
sizeof(buf_byte_native));
TEST_ASSERT_EQUAL_STRING_MESSAGE(
buf_byte_native, buf_converted,
"swp's converted rendering must be indistinguishable from a "
"byte-native resource of the same magnitude");
/* Contrast: feeding swp's RAW megabyte number straight into the
* byte-scaling formatter without converting first (the bug this
* conversion step exists to prevent) produces a materially different,
* wrong result -- 2048 misread as bytes is only 2 KB, not 2 GB. */
memset(buf_unconverted, 0, sizeof(buf_unconverted));
repl_format_bytes_human(swp_raw_mb, buf_unconverted,
sizeof(buf_unconverted));
TEST_ASSERT_EQUAL_STRING_MESSAGE(
"2.00 KB", buf_unconverted,
"sanity check: without the MB->bytes conversion, swp's raw value "
"misreads three orders of magnitude too small -- confirms the "
"conversion in the test above is load-bearing, not incidental");
printf("test_swp_value_converted_mb_to_bytes_renders_on_same_adaptive_"
"scale: PASS\n");
}
int main(void)
{
UNITY_BEGIN();
RUN_TEST(test_bytes_valued_classifier_true_for_canonical_five);
RUN_TEST(test_bytes_valued_classifier_false_for_swp_unitless_and_unknown);
RUN_TEST(
test_format_bytes_human_scales_across_full_unit_range_at_boundaries);
RUN_TEST(test_format_bytes_human_fractional_values_render_two_decimals);
RUN_TEST(
test_swp_value_converted_mb_to_bytes_renders_on_same_adaptive_scale);
return UNITY_END();
}

View file

@ -21,6 +21,17 @@
* one Observable that needs it" split test_repl_execution_control.c's own * one Observable that needs it" split test_repl_execution_control.c's own
* header comment establishes. * 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
* ("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
* 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
* build-failure shape this is expected to produce today.
*
* Fixture strategy and white-box #include set: identical to * Fixture strategy and white-box #include set: identical to
* test_repl_execution_control.c/test_repl_breakpoints_inspection.c (see * test_repl_execution_control.c/test_repl_breakpoints_inspection.c (see
* either file's own header comment) PLUS the new ../src/repl_params.c * either file's own header comment) PLUS the new ../src/repl_params.c
@ -776,6 +787,126 @@ static void test_set_output_opens_writes_preamble_and_off_closes(void)
printf("test_set_output_opens_writes_preamble_and_off_closes: PASS\n"); printf("test_set_output_opens_writes_preamble_and_off_closes: PASS\n");
} }
/* ---------------------------------------------------------------------- */
/* Tests: `set units raw|human|both` (scheduler-sim-repl-verbosity spec, */
/* task 7.1, Requirement 6.2/6.3). */
/* */
/* THESE THREE TESTS ARE RED-PHASE ONLY: neither xce_sched_repl_state_s's */
/* `units_mode` field, the XCE_REPL_UNITS_RAW/HUMAN/BOTH enum constants, */
/* nor repl_cmd_set()'s "units" branch exist anywhere in this codebase yet */
/* -- task 7.2 (a separate, later task) adds all three. Referencing */
/* `sim.repl_state->units_mode` and the enum constants below is therefore */
/* expected to fail this file's COMPILATION (no member named 'units_mode', */
/* undeclared identifiers) -- a build failure for the whole translation */
/* unit, not merely a runtime assertion failure. That build failure IS this */
/* slice's Observable evidence per tasks.md (task 7.1's own text: "the new */
/* test cases fail against the current codebase, which has neither the */
/* classifier/scaling helpers nor a `units` setting" -- a compile error is */
/* a valid, and in this case the actual, form of "fail"). Once task 7.2 */
/* lands the field/enum/branch, these same tests (unmodified) become the */
/* GREEN-phase proof. */
/* */
/* 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
* (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.
*/
static void test_set_units_raw_human_both_update_display_mode(void)
{
xce_sched_sim_driver_t sim;
bool handled;
memset(&sim, 0, sizeof(sim));
sim.repl_state = repl_state_ensure(&sim);
TEST_ASSERT_NOT_NULL(sim.repl_state);
handled = repl_cmd_set(&sim, "set units raw");
TEST_ASSERT_FALSE_MESSAGE(handled, "`set` never hands control back");
TEST_ASSERT_EQUAL_MESSAGE(XCE_REPL_UNITS_RAW, sim.repl_state->units_mode,
"`set units raw` must select the raw-only "
"display mode");
handled = repl_cmd_set(&sim, "set units human");
TEST_ASSERT_FALSE(handled);
TEST_ASSERT_EQUAL_MESSAGE(
XCE_REPL_UNITS_HUMAN, sim.repl_state->units_mode,
"`set units human` must select the human-only display mode");
handled = repl_cmd_set(&sim, "set units both");
TEST_ASSERT_FALSE(handled);
TEST_ASSERT_EQUAL_MESSAGE(
XCE_REPL_UNITS_BOTH, sim.repl_state->units_mode,
"`set units both` must select the combined raw+human display mode");
printf("test_set_units_raw_human_both_update_display_mode: PASS\n");
}
static void test_set_units_defaults_to_both_without_explicit_command(void)
{
xce_sched_sim_driver_t sim;
memset(&sim, 0, sizeof(sim));
sim.repl_state = repl_state_ensure(&sim);
TEST_ASSERT_NOT_NULL(sim.repl_state);
TEST_ASSERT_EQUAL_MESSAGE(
XCE_REPL_UNITS_BOTH, sim.repl_state->units_mode,
"absent an explicit `set units` command, units_mode must default "
"to `both` (Requirement 6.3) -- design.md's own State Management "
"section calls this out explicitly as the one field in this struct "
"that cannot rely on calloc's zero-initialization alone (unless "
"`both` is deliberately given enum value 0), so repl_state_ensure() "
"must set it explicitly");
printf(
"test_set_units_defaults_to_both_without_explicit_command: PASS\n");
}
static xce_sched_sim_driver_t g_units_invalid_sim;
static void run_set_units_bogus(void)
{
repl_cmd_set(&g_units_invalid_sim, "set units bogus");
}
static void test_set_units_invalid_value_rejected_like_existing_pattern(void)
{
char* captured;
memset(&g_units_invalid_sim, 0, sizeof(g_units_invalid_sim));
g_units_invalid_sim.repl_state = repl_state_ensure(&g_units_invalid_sim);
TEST_ASSERT_NOT_NULL(g_units_invalid_sim.repl_state);
captured = capture_stdout_around(run_set_units_bogus);
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 <N>\" and `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 "
"unrecognized KEY (units IS a recognized key once task 7.2 lands; "
"only its VALUE here is invalid)");
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");
free(captured);
printf("test_set_units_invalid_value_rejected_like_existing_pattern: "
"PASS\n");
}
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
/* Test: `info config` reports speed/output/verify/events_processed/ */ /* Test: `info config` reports speed/output/verify/events_processed/ */
/* current_ts/sessions, both while output is off and while it is active */ /* current_ts/sessions, both while output is off and while it is active */
@ -863,6 +994,9 @@ int main(void)
RUN_TEST(test_set_speed_updates_shadow_and_core_atomics); RUN_TEST(test_set_speed_updates_shadow_and_core_atomics);
RUN_TEST(test_set_verify_toggles_flag); RUN_TEST(test_set_verify_toggles_flag);
RUN_TEST(test_set_output_opens_writes_preamble_and_off_closes); RUN_TEST(test_set_output_opens_writes_preamble_and_off_closes);
RUN_TEST(test_set_units_raw_human_both_update_display_mode);
RUN_TEST(test_set_units_defaults_to_both_without_explicit_command);
RUN_TEST(test_set_units_invalid_value_rejected_like_existing_pattern);
RUN_TEST(test_info_config_reports_current_parameters); RUN_TEST(test_info_config_reports_current_parameters);
RUN_TEST(test_help_lists_snapshot_and_set_commands); RUN_TEST(test_help_lists_snapshot_and_set_commands);