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:
parent
da6ba96eb4
commit
11fe6194ce
4 changed files with 495 additions and 1 deletions
|
|
@ -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_
|
||||||
|
|
|
||||||
|
|
@ -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'])
|
||||||
|
|
|
||||||
327
src/scheduler/testsuite/test_repl_resource_formatting.c
Normal file
327
src/scheduler/testsuite/test_repl_resource_formatting.c
Normal 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();
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue