sched_sim_driver_start() now registers the core's tlog format function and marks its core active for logging right after the virtual clock is seeded (xce_core_register_tlog_format()/xce_core_set_active_for_logging()) -- every log line for the rest of the run shows frozen/scaled replay time instead of real wall-clock time, matching what an operator debugging against a replay's own timeline needs. sched_sim_driver_run() deactivates on all 7 of its return paths, since there's no sim-driver teardown function and g_active_core_for_logging would otherwise dangle past the caller's stack frame. Also fixes a genuine pre-existing ASAN stack-use-after-return (bug-1598) the new deactivation test exposed: sim_picker_thread_fn is spawned but, by design, never joined/cancelled, so a stack-local `xce_sched_sim_driver_t sim` in test_sim_event_loop.c was always a latent UAF once its owning test function returned -- just not previously triggered by any prior test ordering. Switched the three affected fixtures in that file to `static` storage (safe: sched_sim_driver_start() memsets the whole struct on entry, and none of the three call sites are re-entrant).
548 lines
20 KiB
C
548 lines
20 KiB
C
/*
|
|
* test_sim_startup.c - contract test for simulation-mode startup
|
|
* (scheduler-core spec, task 11.1, Req 5 AC 13).
|
|
*
|
|
* Observable under test (task 11.1's own text):
|
|
* - starting simulation against a valid fixture file reaches a
|
|
* ready-to-run state with the virtual clock reporting the snapshot's
|
|
* timestamp.
|
|
* - starting against a file missing its initial snapshot entry fails with
|
|
* a clear error before any event is processed.
|
|
*
|
|
* Fixture strategy: rather than hand-constructing capnp bytes, this file
|
|
* builds REAL, valid `.replay` fixtures via the SAME production code path
|
|
* normal-mode recording already uses (sched_record_start()/
|
|
* sched_record_stop(), task 9.2) against a small real xce_scheduler_t
|
|
* fixture -- same technique test_record_lifecycle.c already establishes.
|
|
* The "missing initial snapshot entry" fixture is then derived from a valid
|
|
* one by truncating it to exactly its first message (the ReplayHeader),
|
|
* discovered by decoding that one message and reading the resulting file
|
|
* offset -- no hand-rolled capnp byte construction needed for that case
|
|
* either. The "first message is not a valid ReplayHeader" and "first data
|
|
* message is not a SnapshotEntry" fixtures DO hand-construct one wrong-
|
|
* shaped message directly (same technique test_e2e_capnp.c/
|
|
* test_record_lifecycle.c already use for building capnp messages by hand),
|
|
* since there is no production code path that ever writes a malformed
|
|
* stream to reuse.
|
|
*
|
|
* White-box #include of ../src/core.c, ../src/core_clock.c (task 11.1's own
|
|
* code under test, sim.c, needs both -- xce_sched_core_init/fini and
|
|
* xce_core_now()) PLUS ../src/snapshot.c, ../src/snapshot_wire.c,
|
|
* ../src/snapshot_load.c (task 8.1's real save/load, needed both to BUILD
|
|
* the fixture via sched_record_start() and to exercise sim.c's own
|
|
* xce_sched_core_load_snapshot() call) PLUS ../src/recording.c (real
|
|
* sched_record_start()/sched_record_stop(), used ONLY to build fixtures --
|
|
* this file never asserts anything about recording.c's own behavior, that
|
|
* is test_record_lifecycle.c's job) PLUS ../src/sim.c (the code under
|
|
* test). No driver.c/plugin.c/core_events.c/core_session.c/decision.c: this
|
|
* file never drives a real scheduling session (task 11.1 stops at
|
|
* "ready-to-run", not the event loop), so none of those are needed.
|
|
*
|
|
* Stubs below mirror the exact same link-satisfying set
|
|
* test_record_lifecycle.c/test_driver_session_ordering.c already establish
|
|
* for this core.c/core_clock.c/snapshot.c/snapshot_wire.c/snapshot_load.c/
|
|
* recording.c combination (kill_sched_*, sched_resolve_job_picker's real
|
|
* reimplementation -- schedobj.c is not linked).
|
|
*/
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <unity/unity.h>
|
|
|
|
#include "controller.capnp.h"
|
|
#include "kvmgr.h"
|
|
#include "scheduler.h"
|
|
#include "xstring.h"
|
|
|
|
/* Pull in the real functions under test into this TU (white-box #include,
|
|
* same convention as test_record_lifecycle.c). */
|
|
#include "../src/core.c"
|
|
#include "../src/core_clock.c"
|
|
#include "../src/recording.c"
|
|
#include "../src/sim.c"
|
|
#include "../src/snapshot.c"
|
|
#include "../src/snapshot_load.c"
|
|
#include "../src/snapshot_wire.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; }
|
|
|
|
/* plugin.c is not linked into this TU (no session is ever driven, so no
|
|
* real plugin unregistration ever happens) -- same stub every other
|
|
* core.c-including test file in this directory establishes for
|
|
* xce_sched_core_init()'s kvmgr datakiller. */
|
|
void plugin_unregister(void* arg) { (void)arg; }
|
|
|
|
/* Real reimplementation of schedobj.c's sched_resolve_job_picker -- same
|
|
* rationale/algorithm as test_record_lifecycle.c's own copy (schedobj.c is
|
|
* not linked). Both the FIXTURE core (built via a real xce_scheduler_t,
|
|
* needs a resolvable picker for sched_record_start()'s own snapshot to be
|
|
* well-formed) and sim.c's own core (which never registers a picker, per
|
|
* sim.h's documented scope boundary -- this will legitimately return
|
|
* -EINVAL/-ENOENT for that core, which xce_sched_core_load_snapshot()
|
|
* gracefully WARNs and skips) share this one implementation. */
|
|
int sched_resolve_job_picker(xce_sched_core_t* core, xce_sched_plugin_t** out)
|
|
{
|
|
xce_sched_plugin_t* plugin = NULL;
|
|
int rc = 0;
|
|
|
|
if ((core == NULL) || (out == NULL)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
*out = NULL;
|
|
|
|
if (core->job_pick_plugin == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
lock(core->plugins, RTTI_LOCK_READ);
|
|
rc = key_find(core->plugins, core->job_pick_plugin, (void**)&plugin);
|
|
unlock(core->plugins);
|
|
|
|
if (rc <= 0) {
|
|
return (rc < 0) ? rc : -ENOENT;
|
|
}
|
|
|
|
if ((plugin == NULL) || (plugin->picker_iface == NULL)
|
|
|| (plugin->picker_iface->next_job == NULL)
|
|
|| (plugin->picker_iface->begin_session == NULL)
|
|
|| (plugin->picker_iface->end_session == NULL)
|
|
|| (plugin->picker_iface->on_dispatch == NULL)
|
|
|| (plugin->picker_iface->on_pend == NULL)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
*out = plugin;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void setUp(void) { return; }
|
|
void tearDown(void) { return; }
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
/* Fixture helpers */
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
static void make_tmp_dir(char* out, size_t out_len, const char* suffix)
|
|
{
|
|
char tmpl[PATH_MAX];
|
|
|
|
snprintf(tmpl, sizeof(tmpl), "/tmp/sched_simstart%s_XXXXXX", suffix);
|
|
TEST_ASSERT_NOT_NULL(mkdtemp(tmpl));
|
|
TEST_ASSERT_TRUE(strlen(tmpl) < out_len);
|
|
strcpy(out, tmpl);
|
|
}
|
|
|
|
static int stub_picker_begin_session(void* picker)
|
|
{
|
|
(void)picker;
|
|
return 0;
|
|
}
|
|
|
|
static xce_sched_job_t* stub_picker_next_job(void* picker)
|
|
{
|
|
(void)picker;
|
|
return NULL;
|
|
}
|
|
|
|
static int stub_picker_end_session(void* picker)
|
|
{
|
|
(void)picker;
|
|
return 0;
|
|
}
|
|
|
|
static int stub_picker_jobid_noop(void* picker, uint64_t jobid)
|
|
{
|
|
(void)picker;
|
|
(void)jobid;
|
|
return 0;
|
|
}
|
|
|
|
static int stub_picker_queue_update_noop(void* picker, xce_sched_queue_t* q)
|
|
{
|
|
(void)picker;
|
|
(void)q;
|
|
return 0;
|
|
}
|
|
|
|
static int stub_picker_job_noop(void* picker, xce_sched_job_t* job)
|
|
{
|
|
(void)picker;
|
|
(void)job;
|
|
return 0;
|
|
}
|
|
|
|
static int stub_picker_job_state_change_noop(void* picker, xce_sched_job_t* job,
|
|
uint32_t state)
|
|
{
|
|
(void)picker;
|
|
(void)job;
|
|
(void)state;
|
|
return 0;
|
|
}
|
|
|
|
static xce_job_picker_iface_t g_stub_picker_iface
|
|
= { .init = NULL,
|
|
.fini = NULL,
|
|
.begin_session = stub_picker_begin_session,
|
|
.next_job = stub_picker_next_job,
|
|
.end_session = stub_picker_end_session,
|
|
.on_dispatch = stub_picker_jobid_noop,
|
|
.on_pend = stub_picker_jobid_noop,
|
|
.on_queue_update = stub_picker_queue_update_noop,
|
|
.on_job_push = stub_picker_job_noop,
|
|
.on_job_state_change = stub_picker_job_state_change_noop,
|
|
.on_job_update = stub_picker_job_noop,
|
|
.on_config = NULL,
|
|
.on_job_rusage = NULL };
|
|
|
|
static xce_sched_plugin_t g_stub_picker_plugin
|
|
= { .name = "test_sim_startup_picker",
|
|
.picker_iface = &g_stub_picker_iface };
|
|
|
|
/* Builds a small, real xce_scheduler_t fixture (same pattern
|
|
* test_record_lifecycle.c's fixture_init() establishes) purely to drive
|
|
* sched_record_start()/sched_record_stop() and produce a REAL, valid
|
|
* `.replay` file this test's OWN code under test (sim.c) then consumes. */
|
|
static void build_fixture_scheduler(xce_scheduler_t* sched, char* record_dir)
|
|
{
|
|
int rc;
|
|
|
|
memset(sched, 0, sizeof(*sched));
|
|
mylock_init(&(sched->record_lock));
|
|
|
|
rc = xce_sched_core_init(&(sched->core));
|
|
TEST_ASSERT_EQUAL_INT(0, rc);
|
|
|
|
lock(sched->core.plugins, RTTI_LOCK_WRITE);
|
|
rc = key_add(sched->core.plugins, (void*)g_stub_picker_plugin.name,
|
|
(void*)&g_stub_picker_plugin);
|
|
TEST_ASSERT_GREATER_OR_EQUAL(0, rc);
|
|
unlock(sched->core.plugins);
|
|
|
|
sched->core.job_pick_plugin = g_stub_picker_plugin.name;
|
|
sched->record_dir = record_dir;
|
|
}
|
|
|
|
/* Produces a valid `.replay` fixture (ReplayHeader + initial SnapshotEntry)
|
|
* at `out_path` via the real sched_record_start()/sched_record_stop(); the
|
|
* chosen snapshot timestamp is returned via `out_ts`. */
|
|
static void write_valid_fixture(char* out_path, size_t out_path_len,
|
|
uint64_t* out_ts, const char* suffix)
|
|
{
|
|
char record_dir[PATH_MAX];
|
|
xce_scheduler_t sched;
|
|
int rc;
|
|
|
|
make_tmp_dir(record_dir, sizeof(record_dir), suffix);
|
|
build_fixture_scheduler(&sched, record_dir);
|
|
|
|
rc = sched_record_start(&sched, "fixture.replay");
|
|
TEST_ASSERT_EQUAL_INT(0, rc);
|
|
TEST_ASSERT_TRUE(strlen(sched.record_file_path) < out_path_len);
|
|
strcpy(out_path, sched.record_file_path);
|
|
*out_ts = sched.record_start_ts;
|
|
|
|
rc = sched_record_stop(&sched);
|
|
TEST_ASSERT_EQUAL_INT(0, rc);
|
|
}
|
|
|
|
/* Truncates `src_path` down to just its first capnp message (the
|
|
* ReplayHeader) and writes the result to `dst_path` -- simulates a replay
|
|
* file whose stream ends immediately after the header, i.e. "missing
|
|
* initial snapshot entry". */
|
|
static void truncate_to_header_only(const char* src_path, const char* dst_path)
|
|
{
|
|
FILE* src;
|
|
FILE* dst;
|
|
struct capn c;
|
|
long header_end;
|
|
char* buf;
|
|
size_t n;
|
|
|
|
src = fopen(src_path, "r");
|
|
TEST_ASSERT_NOT_NULL(src);
|
|
TEST_ASSERT_EQUAL_INT(0, capn_init_fp(&c, src, 0));
|
|
capn_free(&c);
|
|
header_end = ftell(src);
|
|
TEST_ASSERT_TRUE(header_end > 0);
|
|
rewind(src);
|
|
|
|
buf = (char*)malloc((size_t)header_end);
|
|
TEST_ASSERT_NOT_NULL(buf);
|
|
n = fread(buf, 1, (size_t)header_end, src);
|
|
TEST_ASSERT_EQUAL_UINT((size_t)header_end, n);
|
|
fclose(src);
|
|
|
|
dst = fopen(dst_path, "w");
|
|
TEST_ASSERT_NOT_NULL(dst);
|
|
TEST_ASSERT_EQUAL_UINT((size_t)header_end,
|
|
fwrite(buf, 1, (size_t)header_end, dst));
|
|
fclose(dst);
|
|
free(buf);
|
|
}
|
|
|
|
/* Hand-builds a single capnp message shaped like `SnapshotEntry`
|
|
* (word_count=1, pointer_count=1) but with all-default/empty field
|
|
* contents, and writes it standalone to `path` -- used to simulate a
|
|
* malformed "first message is not ReplayHeader" fixture (ReplayHeader is
|
|
* shaped (1,5); a lone (1,1)-shaped message can never be mistaken for one
|
|
* by sim.c's shape check). */
|
|
static void write_lone_snapshotentry_message(const char* path)
|
|
{
|
|
struct capn c;
|
|
struct capn_segment* cs;
|
|
SnapshotData_ptr sdata;
|
|
SnapshotEntry_ptr snap;
|
|
FILE* f;
|
|
int rc;
|
|
|
|
capn_init_malloc(&c);
|
|
cs = capn_root(&c).seg;
|
|
|
|
sdata = new_SnapshotData(cs);
|
|
snap = new_SnapshotEntry(cs);
|
|
SnapshotEntry_set_ts(snap, 42ULL);
|
|
SnapshotEntry_set_data(snap, sdata);
|
|
capn_setp(capn_root(&c), 0, snap.p);
|
|
|
|
f = fopen(path, "w");
|
|
TEST_ASSERT_NOT_NULL(f);
|
|
rc = write_capnp_message(f, &c);
|
|
TEST_ASSERT_EQUAL_INT(0, rc);
|
|
fclose(f);
|
|
capn_free(&c);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
/* Test 1: starting simulation against a valid fixture file reaches a */
|
|
/* ready-to-run state with the virtual clock reporting the snapshot's */
|
|
/* timestamp -- this task's own primary Observable. */
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
static void
|
|
test_valid_fixture_reaches_ready_state_with_frozen_virtual_clock(void)
|
|
{
|
|
char path[PATH_MAX];
|
|
uint64_t snapshot_ts = 0;
|
|
sched_sim_args_t args;
|
|
xce_sched_sim_driver_t sim;
|
|
int rc;
|
|
|
|
write_valid_fixture(path, sizeof(path), &snapshot_ts, "_valid");
|
|
TEST_ASSERT_TRUE(snapshot_ts > 0);
|
|
|
|
memset(&args, 0, sizeof(args));
|
|
args.replay_path = path;
|
|
args.sim_start_ts = SCHED_SIM_START_TS_LATEST;
|
|
args.sim_speed = 0; /* fast/frozen -- xce_core_now() stays pinned to
|
|
* virtual_clock_base regardless of real elapsed
|
|
* time, giving this test a deterministic,
|
|
* non-flaky assertion. */
|
|
|
|
rc = sched_sim_driver_start(&sim, &args);
|
|
TEST_ASSERT_EQUAL_INT(0, rc);
|
|
|
|
/* Step 4's Observable: the virtual clock reports the snapshot's own
|
|
* timestamp, exactly (frozen -- speed_num=0). */
|
|
TEST_ASSERT_EQUAL_UINT64(snapshot_ts, xce_core_now(&(sim.core)));
|
|
|
|
/* Direct inspection of the anchors Step 4 documents setting. */
|
|
TEST_ASSERT_EQUAL_UINT64(
|
|
snapshot_ts, atomic_load_explicit(&(sim.core.virtual_clock_base),
|
|
memory_order_acquire));
|
|
TEST_ASSERT_EQUAL_UINT64(0ULL, atomic_load_explicit(&(sim.core.speed_num),
|
|
memory_order_acquire));
|
|
TEST_ASSERT_EQUAL_UINT64(1ULL, atomic_load_explicit(&(sim.core.speed_den),
|
|
memory_order_acquire));
|
|
TEST_ASSERT_EQUAL_UINT64(0ULL, sim.sim_speed_num);
|
|
TEST_ASSERT_EQUAL_UINT64(1ULL, sim.sim_speed_den);
|
|
|
|
/* "Ready-to-run" -- no session is active, load_snapshot()'s own
|
|
* precondition would otherwise have been violated. */
|
|
TEST_ASSERT_EQUAL_INT(0, atomic_load(&(sim.core.session_active)));
|
|
|
|
TEST_ASSERT_NOT_NULL(sim.replay_file);
|
|
|
|
printf("test_valid_fixture_reaches_ready_state_with_frozen_virtual_clock:"
|
|
" PASS\n");
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
/* Test 1b (design review follow-up, Req 5 AC 3): sched_sim_driver_start() */
|
|
/* must activate the core's virtual clock for tlog logging -- otherwise */
|
|
/* every log line emitted during a --sim/--interactive run shows real */
|
|
/* wall-clock time instead of the frozen/virtual replay timestamp, */
|
|
/* defeating the whole point of debugging against a replay's own timeline. */
|
|
/* g_active_core_for_logging is core_clock.c's module-static (white-box */
|
|
/* #include above puts it in this TU) -- direct inspection is simpler and */
|
|
/* more precise than round-tripping through tlog's own formatter, which */
|
|
/* test_clock.c already covers at the unit level. */
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
static void test_valid_fixture_activates_virtual_clock_for_tlog_logging(void)
|
|
{
|
|
char path[PATH_MAX];
|
|
uint64_t snapshot_ts = 0;
|
|
sched_sim_args_t args;
|
|
xce_sched_sim_driver_t sim;
|
|
int rc;
|
|
|
|
/* Defensive reset -- other tests in this same process may have left
|
|
* g_active_core_for_logging pointing at a since-destroyed core. */
|
|
xce_core_set_active_for_logging(NULL);
|
|
|
|
write_valid_fixture(path, sizeof(path), &snapshot_ts, "_tlogactivate");
|
|
|
|
memset(&args, 0, sizeof(args));
|
|
args.replay_path = path;
|
|
args.sim_start_ts = SCHED_SIM_START_TS_LATEST;
|
|
args.sim_speed = 0;
|
|
|
|
rc = sched_sim_driver_start(&sim, &args);
|
|
TEST_ASSERT_EQUAL_INT(0, rc);
|
|
|
|
TEST_ASSERT_EQUAL_PTR(&(sim.core), g_active_core_for_logging);
|
|
|
|
xce_core_set_active_for_logging(NULL); /* leave clean for later tests */
|
|
|
|
printf("test_valid_fixture_activates_virtual_clock_for_tlog_logging:"
|
|
" PASS\n");
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
/* Test 2: starting against a file missing its initial snapshot entry */
|
|
/* fails with a clear error before any event is processed -- this task's */
|
|
/* own second Observable. */
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
static void test_missing_initial_snapshot_entry_fails_cleanly(void)
|
|
{
|
|
char valid_path[PATH_MAX];
|
|
char header_only_path[PATH_MAX + 32];
|
|
uint64_t snapshot_ts = 0;
|
|
sched_sim_args_t args;
|
|
xce_sched_sim_driver_t sim;
|
|
int rc;
|
|
|
|
write_valid_fixture(valid_path, sizeof(valid_path), &snapshot_ts,
|
|
"_hdronly_src");
|
|
snprintf(header_only_path, sizeof(header_only_path),
|
|
"%s.header_only.replay", valid_path);
|
|
truncate_to_header_only(valid_path, header_only_path);
|
|
|
|
memset(&args, 0, sizeof(args));
|
|
args.replay_path = header_only_path;
|
|
args.sim_start_ts = SCHED_SIM_START_TS_LATEST;
|
|
args.sim_speed = 1;
|
|
|
|
rc = sched_sim_driver_start(&sim, &args);
|
|
TEST_ASSERT_TRUE(rc < 0);
|
|
|
|
printf("test_missing_initial_snapshot_entry_fails_cleanly: PASS\n");
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
/* Test 3: a replay file whose FIRST message is not a valid ReplayHeader */
|
|
/* is rejected (Req 5 AC 13 step 1) -- distinct code path from test 2. */
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
static void test_first_message_not_replayheader_fails_cleanly(void)
|
|
{
|
|
char dir[PATH_MAX];
|
|
char path[PATH_MAX + 32];
|
|
sched_sim_args_t args;
|
|
xce_sched_sim_driver_t sim;
|
|
int rc;
|
|
|
|
make_tmp_dir(dir, sizeof(dir), "_badhdr");
|
|
snprintf(path, sizeof(path), "%s/bad_header.replay", dir);
|
|
write_lone_snapshotentry_message(path);
|
|
|
|
memset(&args, 0, sizeof(args));
|
|
args.replay_path = path;
|
|
args.sim_start_ts = SCHED_SIM_START_TS_LATEST;
|
|
args.sim_speed = 1;
|
|
|
|
rc = sched_sim_driver_start(&sim, &args);
|
|
TEST_ASSERT_TRUE(rc < 0);
|
|
|
|
printf("test_first_message_not_replayheader_fails_cleanly: PASS\n");
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
/* Test 4: a missing replay file fails with a clear error (Req 5 AC 13 */
|
|
/* step 1, "the file is missing, unreadable"). */
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
static void test_missing_replay_file_fails_cleanly(void)
|
|
{
|
|
sched_sim_args_t args;
|
|
xce_sched_sim_driver_t sim;
|
|
int rc;
|
|
|
|
memset(&args, 0, sizeof(args));
|
|
args.replay_path = "/nonexistent/path/does_not_exist.replay";
|
|
args.sim_start_ts = SCHED_SIM_START_TS_LATEST;
|
|
args.sim_speed = 1;
|
|
|
|
rc = sched_sim_driver_start(&sim, &args);
|
|
TEST_ASSERT_TRUE(rc < 0);
|
|
|
|
printf("test_missing_replay_file_fails_cleanly: PASS\n");
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
/* Test 5: NULL argument guards. */
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
static void test_null_arguments_return_einval(void)
|
|
{
|
|
xce_sched_sim_driver_t sim;
|
|
sched_sim_args_t args;
|
|
|
|
memset(&args, 0, sizeof(args));
|
|
args.replay_path = "/tmp/does-not-matter.replay";
|
|
|
|
TEST_ASSERT_EQUAL_INT(-EINVAL, sched_sim_driver_start(NULL, &args));
|
|
TEST_ASSERT_EQUAL_INT(-EINVAL, sched_sim_driver_start(&sim, NULL));
|
|
|
|
memset(&args, 0, sizeof(args));
|
|
args.replay_path = NULL;
|
|
TEST_ASSERT_EQUAL_INT(-EINVAL, sched_sim_driver_start(&sim, &args));
|
|
|
|
printf("test_null_arguments_return_einval: PASS\n");
|
|
}
|
|
|
|
/* Test 6 (sched_sim_driver_run() -- design.md's literal entry point --
|
|
* calling start() then failing loudly with -ENOSYS pending the event loop)
|
|
* was removed here: the event loop now exists (scheduler-core spec, task
|
|
* 11.2), and sched_sim_driver_run() itself moved to sim_loop.c (a much
|
|
* heavier white-box dependency set this file's own header comment
|
|
* deliberately stays without -- "this task stops at 'ready-to-run'"). Its
|
|
* successor lives in test_sim_event_loop.c, alongside every other
|
|
* sched_sim_driver_run()-level test. */
|
|
|
|
int main(void)
|
|
{
|
|
UNITY_BEGIN();
|
|
|
|
RUN_TEST(test_valid_fixture_reaches_ready_state_with_frozen_virtual_clock);
|
|
RUN_TEST(test_valid_fixture_activates_virtual_clock_for_tlog_logging);
|
|
RUN_TEST(test_missing_initial_snapshot_entry_fails_cleanly);
|
|
RUN_TEST(test_first_message_not_replayheader_fails_cleanly);
|
|
RUN_TEST(test_missing_replay_file_fails_cleanly);
|
|
RUN_TEST(test_null_arguments_return_einval);
|
|
|
|
return UNITY_END();
|
|
}
|