test_tlog_format_shows_frozen_virtual_time_not_infotime asserted that "1999" (info->time's year) appears nowhere in the formatted line, but the line also carries getpid(), so the test failed whenever its PID contained 1999. It now asserts the exact frozen timestamp field. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
517 lines
20 KiB
C
517 lines
20 KiB
C
/*
|
|
* test_clock.c - contract test for clock virtualization and session timing
|
|
* (scheduler-core spec, task 4.5, Req 5 AC 3 / AC 12).
|
|
*
|
|
* Observable under test:
|
|
* - xce_core_now() implements the two-anchor + speed formula (Req 5 AC 3)
|
|
* exactly, given fixed anchor values -- no sleep-based
|
|
* real-elapsed-time assertions. The product uses two settings of the
|
|
* speed atoms: 1/1 in normal mode and 0/1 (frozen) in simulation mode
|
|
* (scheduler-sim-discrete-clock: the simulator has no speed modes).
|
|
* Three further tests exercise the formula itself with an arbitrary
|
|
* virtual anchor and with ratios 1/1, N/1 and 1/N; they test the
|
|
* arithmetic, not any product mode. Where the ratio is non-zero the
|
|
* output tracks real wall-clock time, so the test brackets the result
|
|
* between two back-to-back CLOCK_MONOTONIC reads taken immediately
|
|
* before/after
|
|
* the call -- deterministic (time is monotonic, so the bracket always
|
|
* holds) and fast (nanoseconds of real work between the two reads), NOT
|
|
* the sleep-then-check-approximate-elapsed pattern this task forbids.
|
|
* Simulation mode (frozen, speed=0/1) collapses the bracket to an
|
|
* exact equality since elapsed*0/den is always 0 regardless of when the
|
|
* test runs.
|
|
* - xce_core_tlog_format() (core_clock.c's registered tlog_format_func)
|
|
* reads xce_core_now() of the module-level "active core for logging"
|
|
* when one is registered, IGNORING info->time (which tlog itself
|
|
* populates from real wall-clock time at the call site) -- so a log
|
|
* line formatted while the virtual clock is frozen shows the frozen
|
|
* simulated timestamp, not real wall-clock time. Falls back to
|
|
* info->time when no active core is registered (matching today's
|
|
* default formatter's behavior).
|
|
* - xce_core_register_tlog_format() really registers with the real
|
|
* tlog_reg_format_func() (proves xce_core_tlog_format's signature is
|
|
* genuinely tlog_format_func-compatible -- this file would fail to
|
|
* compile otherwise -- and that tlog accepts it at runtime).
|
|
* - session_start is stamped at the top of xce_core_go() and
|
|
* session_duration_ns is computed at the top of xce_core_end_session()
|
|
* as xce_core_now() - session_start (Req 5 AC 12), exercised through
|
|
* the REAL xce_core_go()/xce_core_end_session() (core_session.c) with
|
|
* the core's clock frozen (discrete-simulation mode) so both the
|
|
* zero-duration case (AC 12's simulation-mode bullet: the clock moves
|
|
* only at recorded timestamps, so a session's duration_ns is 0) and
|
|
* a nonzero case (clock advanced between go() and end_session(), the
|
|
* same anchor-update-between-events pattern a real driver follows)
|
|
* are fully deterministic.
|
|
*
|
|
* White-box #include of ../src/core_clock.c (xce_core_now(),
|
|
* xce_core_tlog_format(), g_active_core_for_logging,
|
|
* xce_core_register_tlog_format() -- same convention as every other
|
|
* white-box test in this directory) PLUS ../src/core.c, ../src/core_session.c,
|
|
* and ../src/decision.c (same set test_core_session.c already pulls in) so
|
|
* the session-timing tests exercise the REAL xce_core_go()/
|
|
* xce_core_end_session(), not a re-implementation. The kill_sched_ family,
|
|
* plugin_unregister, and sched_worker_release_host_ctx stubs below exist
|
|
* purely to satisfy the linker for xce_sched_core_init()'s
|
|
* kvmgr_params_t.datakiller fields and core_session.c's core_reset_workers()
|
|
* -- never actually invoked, same rationale test_core_session.c's own header
|
|
* comment documents.
|
|
*/
|
|
#include <stdarg.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
#include <unity/unity.h>
|
|
|
|
#include "preason.h"
|
|
|
|
/* Pull in the real xce_core_now()/xce_core_tlog_format()/
|
|
* xce_core_register_tlog_format()/xce_sched_core_init()/xce_core_go()/
|
|
* xce_core_end_session() under test into this TU (white-box #include). */
|
|
#include "../src/core.c"
|
|
#include "../src/core_clock.c"
|
|
#include "../src/core_session.c"
|
|
#include "../src/decision.c"
|
|
|
|
void kill_sched_job(void* arg) { (void)arg; }
|
|
void kill_sched_queue(void* arg) { (void)arg; }
|
|
void kill_sched_worker(void* arg) { (void)arg; }
|
|
void kill_sched_user(void* arg) { (void)arg; }
|
|
void kill_sched_project(void* arg) { (void)arg; }
|
|
void kill_sched_resval(xce_val_t* val) { (void)val; }
|
|
void plugin_unregister(void* arg) { (void)arg; }
|
|
void sched_worker_release_host_ctx(xce_sched_worker_t* w) { (void)w; }
|
|
|
|
void setUp(void) { return; }
|
|
void tearDown(void) { return; }
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
/* Helpers */
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
#define TEST_NSEC_PER_SEC 1000000000ULL
|
|
|
|
static uint64_t test_monotonic_ns(void)
|
|
{
|
|
struct timespec ts;
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
return ((uint64_t)ts.tv_sec * TEST_NSEC_PER_SEC) + (uint64_t)ts.tv_nsec;
|
|
}
|
|
|
|
static void set_clock_atoms(xce_sched_core_t* core, uint64_t vbase,
|
|
uint64_t rbase, uint64_t num, uint64_t den)
|
|
{
|
|
atomic_store_explicit(&(core->virtual_clock_base), vbase,
|
|
memory_order_release);
|
|
atomic_store_explicit(&(core->real_clock_base), rbase,
|
|
memory_order_release);
|
|
atomic_store_explicit(&(core->speed_num), num, memory_order_release);
|
|
atomic_store_explicit(&(core->speed_den), den, memory_order_release);
|
|
}
|
|
|
|
static void fixture_init(xce_sched_core_t* core)
|
|
{
|
|
int rc = xce_sched_core_init(core);
|
|
TEST_ASSERT_EQUAL_INT(0, rc);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
/* xce_core_now() -- two-anchor + speed formula (Req 5 AC 3) */
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
static void test_xce_core_now_null_core_returns_zero(void)
|
|
{
|
|
TEST_ASSERT_EQUAL_UINT64(0, xce_core_now(NULL));
|
|
}
|
|
|
|
static void test_xce_core_now_normal_mode_matches_wall_clock(void)
|
|
{
|
|
/* Normal mode (AC 3's table): virtual_clock_base=0, real_clock_base=0,
|
|
* speed=1/1 -- degraded to real wall clock: 0 + (wall-0)*1/1 = wall. */
|
|
xce_sched_core_t core;
|
|
uint64_t before, result, after;
|
|
|
|
memset(&core, 0, sizeof(core));
|
|
set_clock_atoms(&core, 0, 0, 1, 1);
|
|
|
|
before = test_monotonic_ns();
|
|
result = xce_core_now(&core);
|
|
after = test_monotonic_ns();
|
|
|
|
TEST_ASSERT_TRUE(result >= before);
|
|
TEST_ASSERT_TRUE(result <= after);
|
|
}
|
|
|
|
static void test_xce_core_now_discrete_simulation_frozen_at_event_ts(void)
|
|
{
|
|
/* Simulation discrete (AC 3's table): virtual_clock_base=event ts,
|
|
* real_clock_base=wall at load, speed=0/1 -- frozen at ts regardless of
|
|
* real elapsed time (elapsed * 0 / 1 == 0 always). Exact equality, no
|
|
* bracket needed -- the whole point of this mode. */
|
|
xce_sched_core_t core;
|
|
const uint64_t EVENT_TS = 1700000000123456789ULL;
|
|
uint64_t result;
|
|
|
|
memset(&core, 0, sizeof(core));
|
|
set_clock_atoms(&core, EVENT_TS, test_monotonic_ns(), 0, 1);
|
|
|
|
result = xce_core_now(&core);
|
|
TEST_ASSERT_EQUAL_UINT64(EVENT_TS, result);
|
|
|
|
/* Re-reading later still returns the identical frozen value -- proves
|
|
* "time does not advance between events", not a one-shot coincidence. */
|
|
result = xce_core_now(&core);
|
|
TEST_ASSERT_EQUAL_UINT64(EVENT_TS, result);
|
|
}
|
|
|
|
static void
|
|
test_xce_core_now_unit_ratio_tracks_wall_clock_from_virtual_anchor(void)
|
|
{
|
|
/* Formula test: an arbitrary virtual_clock_base, real_clock_base=wall
|
|
* now, speed=1/1 -- the result advances with the wall clock from the
|
|
* virtual anchor: it lies in [anchor, anchor + (after-before)]. */
|
|
xce_sched_core_t core;
|
|
const uint64_t SNAPSHOT_TS = 500000000000ULL;
|
|
uint64_t before, result, after;
|
|
|
|
memset(&core, 0, sizeof(core));
|
|
before = test_monotonic_ns();
|
|
set_clock_atoms(&core, SNAPSHOT_TS, before, 1, 1);
|
|
|
|
result = xce_core_now(&core);
|
|
after = test_monotonic_ns();
|
|
|
|
TEST_ASSERT_TRUE(result >= SNAPSHOT_TS);
|
|
TEST_ASSERT_TRUE(result <= SNAPSHOT_TS + (after - before));
|
|
}
|
|
|
|
static void test_xce_core_now_ratio_above_one_scales_elapsed_up(void)
|
|
{
|
|
/* Formula test: speed=N/1 -- elapsed wall time is multiplied by N.
|
|
* Bracket scaled by N. */
|
|
xce_sched_core_t core;
|
|
const uint64_t SNAPSHOT_TS = 1000ULL;
|
|
const uint64_t N = 1000ULL;
|
|
uint64_t before, result, after;
|
|
|
|
memset(&core, 0, sizeof(core));
|
|
before = test_monotonic_ns();
|
|
set_clock_atoms(&core, SNAPSHOT_TS, before, N, 1);
|
|
|
|
result = xce_core_now(&core);
|
|
after = test_monotonic_ns();
|
|
|
|
TEST_ASSERT_TRUE(result >= SNAPSHOT_TS);
|
|
TEST_ASSERT_TRUE(result <= SNAPSHOT_TS + (after - before) * N);
|
|
}
|
|
|
|
static void test_xce_core_now_ratio_below_one_scales_elapsed_down(void)
|
|
{
|
|
/* Formula test: speed=1/N -- elapsed wall time is divided by N.
|
|
* Bracket scaled down by N (+1 for integer-division rounding slack). */
|
|
xce_sched_core_t core;
|
|
const uint64_t SNAPSHOT_TS = 1000ULL;
|
|
const uint64_t N = 1000ULL;
|
|
uint64_t before, result, after;
|
|
|
|
memset(&core, 0, sizeof(core));
|
|
before = test_monotonic_ns();
|
|
set_clock_atoms(&core, SNAPSHOT_TS, before, 1, N);
|
|
|
|
result = xce_core_now(&core);
|
|
after = test_monotonic_ns();
|
|
|
|
TEST_ASSERT_TRUE(result >= SNAPSHOT_TS);
|
|
TEST_ASSERT_TRUE(result <= SNAPSHOT_TS + (after - before) / N + 1);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
/* Clock virtualization is atomically updated as a group (Req 5 AC 3) */
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
static void test_xce_sched_core_init_seeds_normal_mode_defaults(void)
|
|
{
|
|
/* xce_sched_core_init() (core.c, task 4.5) must seed the clock atoms
|
|
* to AC 3's Normal-mode row -- preserving today's real-wall-clock
|
|
* default until a simulation driver reconfigures them. */
|
|
xce_sched_core_t core;
|
|
|
|
fixture_init(&core);
|
|
|
|
TEST_ASSERT_EQUAL_UINT64(0, atomic_load_explicit(&(core.virtual_clock_base),
|
|
memory_order_acquire));
|
|
TEST_ASSERT_EQUAL_UINT64(
|
|
0, atomic_load_explicit(&(core.real_clock_base), memory_order_acquire));
|
|
TEST_ASSERT_EQUAL_UINT64(
|
|
1, atomic_load_explicit(&(core.speed_num), memory_order_acquire));
|
|
TEST_ASSERT_EQUAL_UINT64(
|
|
1, atomic_load_explicit(&(core.speed_den), memory_order_acquire));
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
/* xce_core_set_wallclock_anchor() -- normal-mode epoch seeding (bug-1584) */
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
static uint64_t test_epoch_ns(void)
|
|
{
|
|
struct timespec ts;
|
|
|
|
clock_gettime(CLOCK_REALTIME, &ts);
|
|
return ((uint64_t)ts.tv_sec * TEST_NSEC_PER_SEC) + (uint64_t)ts.tv_nsec;
|
|
}
|
|
|
|
static void test_xce_core_set_wallclock_anchor_seeds_real_epoch_time(void)
|
|
{
|
|
/* bug-1584: xce_sched_core_init() alone leaves xce_core_now() returning
|
|
* raw CLOCK_MONOTONIC-since-boot nanoseconds (see
|
|
* test_xce_sched_core_init_seeds_normal_mode_defaults above) -- NOT
|
|
* epoch time, which every real consumer of a normal-mode timestamp
|
|
* needs (recording.c's record_start_ts, SchedAdminReply.startedAt,
|
|
* documented in controller.capnp as "nanoseconds since epoch").
|
|
* xce_core_set_wallclock_anchor() is the fix: bracket its effect on
|
|
* xce_core_now() between two back-to-back CLOCK_REALTIME reads taken
|
|
* immediately before/after the call -- deterministic (real time only
|
|
* moves forward) and immune to sleep-based flakiness. Before this fix,
|
|
* `result` would be a small monotonic-since-boot value, far below
|
|
* `before` (a real epoch value ~1.7e18 ns as of 2026) -- this bracket
|
|
* fails loudly on that regression rather than passing by coincidence. */
|
|
xce_sched_core_t core;
|
|
uint64_t before, result, after;
|
|
|
|
fixture_init(&core);
|
|
|
|
before = test_epoch_ns();
|
|
xce_core_set_wallclock_anchor(&core);
|
|
result = xce_core_now(&core);
|
|
after = test_epoch_ns();
|
|
|
|
TEST_ASSERT_TRUE(result >= before);
|
|
TEST_ASSERT_TRUE(result <= after);
|
|
}
|
|
|
|
static void test_xce_core_set_wallclock_anchor_null_core_is_a_safe_noop(void)
|
|
{
|
|
/* Mirrors xce_core_now(NULL)'s defensive contract -- no crash. */
|
|
xce_core_set_wallclock_anchor(NULL);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
/* tlog format integration (Req 5 AC 3) */
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
static int call_format(char* buff, int maxlen, struct tlog_loginfo* info,
|
|
void* userptr, const char* fmt, ...)
|
|
{
|
|
va_list ap;
|
|
int rc;
|
|
|
|
va_start(ap, fmt);
|
|
rc = xce_core_tlog_format(buff, maxlen, info, userptr, fmt, ap);
|
|
va_end(ap);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static void test_tlog_format_shows_frozen_virtual_time_not_infotime(void)
|
|
{
|
|
/* Freeze the active core's virtual clock at a known simulated instant
|
|
* (2033-05-18 03:04:05 UTC) and set info->time to a deliberately
|
|
* DIFFERENT, real-looking wall-clock value (1999-01-01) -- proving the
|
|
* formatter reads the frozen simulated time, not info->time. */
|
|
xce_sched_core_t core;
|
|
struct tlog_loginfo info;
|
|
char buff[256];
|
|
int rc;
|
|
const uint64_t FROZEN_NS = 2000000000ULL * TEST_NSEC_PER_SEC;
|
|
/* 2033-05-18 03:33:20 UTC -- asserted exactly below. */
|
|
|
|
memset(&core, 0, sizeof(core));
|
|
set_clock_atoms(&core, FROZEN_NS, test_monotonic_ns(), 0, 1);
|
|
|
|
memset(&info, 0, sizeof(info));
|
|
info.level = TLOG_INFO;
|
|
info.file = "test.c";
|
|
info.func = "test_func";
|
|
info.line = 42;
|
|
info.time.year = 1999;
|
|
info.time.mon = 1;
|
|
info.time.mday = 1;
|
|
info.time.hour = 0;
|
|
info.time.min = 0;
|
|
info.time.sec = 0;
|
|
info.time.usec = 0;
|
|
|
|
xce_core_set_active_for_logging(&core);
|
|
|
|
rc = call_format(buff, sizeof(buff), &info, NULL, "hello %d", 7);
|
|
TEST_ASSERT_TRUE(rc > 0);
|
|
TEST_ASSERT_NOT_NULL(strstr(buff, "hello 7"));
|
|
/* Only the timestamp field is checked: the rest of the line carries
|
|
* getpid(), which itself contains "1999" for some PIDs (bug-1924).
|
|
* FROZEN_NS is 2000000000 s after the epoch, formatted with gmtime_r
|
|
* (core_clock.c ns_to_tlog_time), so the field is exact. */
|
|
TEST_ASSERT_EQUAL_STRING_LEN_MESSAGE(
|
|
"[2033-05-18 03:33:20,000] ", buff, 26,
|
|
"formatted log line must show the frozen virtual time, not "
|
|
"info->time's wall-clock time");
|
|
|
|
xce_core_set_active_for_logging(NULL);
|
|
}
|
|
|
|
static void test_tlog_format_frozen_time_is_stable_across_calls(void)
|
|
{
|
|
/* Formatting twice, with no change to the frozen anchors, must produce
|
|
* an IDENTICAL timestamp both times -- the direct proof that the shown
|
|
* timestamp is the frozen simulated ts, not whatever the real clock
|
|
* happens to read at each call. */
|
|
xce_sched_core_t core;
|
|
struct tlog_loginfo info;
|
|
char buff1[256];
|
|
char buff2[256];
|
|
char* ts_end1;
|
|
char* ts_end2;
|
|
const uint64_t FROZEN_NS = 1234567890ULL * TEST_NSEC_PER_SEC;
|
|
|
|
memset(&core, 0, sizeof(core));
|
|
set_clock_atoms(&core, FROZEN_NS, test_monotonic_ns(), 0, 1);
|
|
|
|
memset(&info, 0, sizeof(info));
|
|
info.level = TLOG_INFO;
|
|
info.func = "test_func";
|
|
|
|
xce_core_set_active_for_logging(&core);
|
|
|
|
call_format(buff1, sizeof(buff1), &info, NULL, "first");
|
|
call_format(buff2, sizeof(buff2), &info, NULL, "second");
|
|
|
|
xce_core_set_active_for_logging(NULL);
|
|
|
|
/* Both lines share the same "[YYYY-MM-DD HH:MM:SS,mmm]" prefix -- find
|
|
* the closing bracket of the timestamp portion and compare up to it. */
|
|
ts_end1 = strchr(buff1, ']');
|
|
ts_end2 = strchr(buff2, ']');
|
|
TEST_ASSERT_NOT_NULL(ts_end1);
|
|
TEST_ASSERT_NOT_NULL(ts_end2);
|
|
TEST_ASSERT_EQUAL_INT(ts_end1 - buff1, ts_end2 - buff2);
|
|
TEST_ASSERT_TRUE(strncmp(buff1, buff2, (size_t)(ts_end1 - buff1)) == 0);
|
|
}
|
|
|
|
static void test_tlog_format_falls_back_to_infotime_when_no_active_core(void)
|
|
{
|
|
/* No active core registered (NULL, the module's own default) --
|
|
* xce_core_tlog_format() must fall back to info->time, matching
|
|
* src/lib/utils.c's my_tlog_format() default behavior exactly. */
|
|
struct tlog_loginfo info;
|
|
char buff[256];
|
|
int rc;
|
|
|
|
xce_core_set_active_for_logging(NULL);
|
|
|
|
memset(&info, 0, sizeof(info));
|
|
info.level = TLOG_INFO;
|
|
info.func = "test_func";
|
|
info.time.year = 2077;
|
|
info.time.mon = 6;
|
|
info.time.mday = 15;
|
|
info.time.hour = 12;
|
|
info.time.min = 30;
|
|
info.time.sec = 45;
|
|
info.time.usec = 0;
|
|
|
|
rc = call_format(buff, sizeof(buff), &info, NULL, "fallback");
|
|
TEST_ASSERT_TRUE(rc > 0);
|
|
TEST_ASSERT_NOT_NULL(strstr(buff, "2077-06-15"));
|
|
}
|
|
|
|
static void test_xce_core_register_tlog_format_succeeds(void)
|
|
{
|
|
int rc = xce_core_register_tlog_format();
|
|
TEST_ASSERT_EQUAL_INT(0, rc);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
/* Session timing (Req 5 AC 12) */
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
static void test_session_duration_zero_when_clock_frozen_across_session(void)
|
|
{
|
|
/* AC 12's simulation-mode bullet: the virtual clock moves only when
|
|
* the replay sets it to a recorded timestamp, none is reached while a
|
|
* session is in progress, so duration_ns is 0. Simulation mode
|
|
* (speed=0/1) reproduces exactly that: xce_core_now() returns the same
|
|
* frozen value at both core_go() and core_end_session(). */
|
|
xce_sched_core_t core;
|
|
int rc;
|
|
int go = 0;
|
|
const uint64_t FROZEN_TS = 42424242ULL;
|
|
|
|
fixture_init(&core);
|
|
set_clock_atoms(&core, FROZEN_TS, test_monotonic_ns(), 0, 1);
|
|
|
|
rc = xce_core_go(&core);
|
|
TEST_ASSERT_EQUAL_INT(0, rc);
|
|
TEST_ASSERT_EQUAL_UINT64(FROZEN_TS, core.session_start);
|
|
|
|
cbuf_tryget(&(core.wbuf), &go); /* drain the signal, not under test here */
|
|
|
|
rc = xce_core_end_session(&core);
|
|
TEST_ASSERT_EQUAL_INT(0, rc);
|
|
TEST_ASSERT_EQUAL_UINT64(0, core.session_duration_ns);
|
|
}
|
|
|
|
static void
|
|
test_session_duration_reflects_clock_advance_between_go_and_end(void)
|
|
{
|
|
/* Models a real driver updating the clock atoms between events (AC 3:
|
|
* "the driver atomically updates all four fields before each event
|
|
* dispatch") -- the virtual clock advances by a known, fixed amount
|
|
* between core_go() and core_end_session(), fully deterministic (no
|
|
* sleep, no real elapsed time involved). */
|
|
xce_sched_core_t core;
|
|
int rc;
|
|
int go = 0;
|
|
const uint64_t START_TS = 1000000ULL;
|
|
const uint64_t ADVANCE = 250000ULL;
|
|
|
|
fixture_init(&core);
|
|
set_clock_atoms(&core, START_TS, test_monotonic_ns(), 0, 1);
|
|
|
|
rc = xce_core_go(&core);
|
|
TEST_ASSERT_EQUAL_INT(0, rc);
|
|
TEST_ASSERT_EQUAL_UINT64(START_TS, core.session_start);
|
|
|
|
cbuf_tryget(&(core.wbuf), &go);
|
|
|
|
/* Driver advances virtual_clock_base before the next dispatch -- still
|
|
* frozen (speed 0/1) at the new value, so end_session() sees exactly
|
|
* START_TS + ADVANCE, deterministically. */
|
|
atomic_store_explicit(&(core.virtual_clock_base), START_TS + ADVANCE,
|
|
memory_order_release);
|
|
|
|
rc = xce_core_end_session(&core);
|
|
TEST_ASSERT_EQUAL_INT(0, rc);
|
|
TEST_ASSERT_EQUAL_UINT64(ADVANCE, core.session_duration_ns);
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
UNITY_BEGIN();
|
|
RUN_TEST(test_xce_core_now_null_core_returns_zero);
|
|
RUN_TEST(test_xce_core_now_normal_mode_matches_wall_clock);
|
|
RUN_TEST(test_xce_core_now_discrete_simulation_frozen_at_event_ts);
|
|
RUN_TEST(
|
|
test_xce_core_now_unit_ratio_tracks_wall_clock_from_virtual_anchor);
|
|
RUN_TEST(test_xce_core_now_ratio_above_one_scales_elapsed_up);
|
|
RUN_TEST(test_xce_core_now_ratio_below_one_scales_elapsed_down);
|
|
RUN_TEST(test_xce_sched_core_init_seeds_normal_mode_defaults);
|
|
RUN_TEST(test_xce_core_set_wallclock_anchor_seeds_real_epoch_time);
|
|
RUN_TEST(test_xce_core_set_wallclock_anchor_null_core_is_a_safe_noop);
|
|
RUN_TEST(test_tlog_format_shows_frozen_virtual_time_not_infotime);
|
|
RUN_TEST(test_tlog_format_frozen_time_is_stable_across_calls);
|
|
RUN_TEST(test_tlog_format_falls_back_to_infotime_when_no_active_core);
|
|
RUN_TEST(test_xce_core_register_tlog_format_succeeds);
|
|
RUN_TEST(test_session_duration_zero_when_clock_frozen_across_session);
|
|
RUN_TEST(test_session_duration_reflects_clock_advance_between_go_and_end);
|
|
return UNITY_END();
|
|
}
|