myxce/src/scheduler/testsuite/test.c
Rongsong Shen 55173fca27
feat(scheduler-sim-live-allocation): make capacity checks core-safe (task 1.3)
Re-types sched_meet_host_selection(), sched_check_shared_satisfy()
(+its _ helper), sched_check_host_satisfy() (+its _ helper), and the
private unsatisfy_() helper they share, from xce_scheduler_t* to
xce_sched_core_t*, in resource.c.

Fixes every call site this forces (a C signature migration cannot
keep the build green otherwise): sched_try_alloc_on_host_() (2 sites,
already partially touched by task 1.2), host_iter.c's
sched_host_iter_next() (2 sites), and parjob_allocator.c/
seqjob_allocator.c's my_alloc() (1 site each) -- each fix is a minimal
&(sched->core) argument wrap, no algorithm change, preserving
host_iter/parjob/seqjob's own allocation algorithm exactly as-is.

Adds test_core_safe_allocation_chain_driver_parity, proving all three
functions produce identical grant/deny results whether given
normal-mode-shaped or simulation-mode-shaped core state.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RLSNJhNoULvVcUhXEFafQC
2026-09-18 12:42:11 +00:00

759 lines
26 KiB
C

#define UNIT_TEST 1
#include <argp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdatomic.h>
#include <unity/unity.h>
#include "helper.h"
#include "xstring.h"
/* Pull resource.c into this TU so static helpers are visible for white-box testing.
* This also brings in scheduler.h (with the real xce_scheduler_t) and all deps. */
#include "../src/resource.c"
/* scheduler-sim-live-allocation spec, task 1.3: sim.h only depends on
* core.h (+ stdbool/stdint/stdio, per its own header comment), so it is
* safe to pull in here just for xce_sched_sim_driver_t -- needed to build
* a simulation-mode-shaped container for the driver-parity test below. */
#include "sim.h"
/* Stubs for symbols used by resource.c functions not under test.
* sched_get_worker/sched_get_queue relocated to core.h (scheduler-core
* spec, task 4.1) -- now operate on xce_sched_core_t*, not
* xce_scheduler_t*. */
xce_sched_worker_t* sched_get_worker(xce_sched_core_t* s, char* n)
{
(void)s;
(void)n;
return NULL;
}
int plugin_prepare_allocation(xce_sched_core_t* s, xce_sched_ctx_t* c) { (void)s; (void)c; return 0; }
int sched_ctx_get_uncomit_host(xvec_t** v, xce_sched_ctx_t* c, int i) { (void)v; (void)c; (void)i; return 0; }
int plugin_queue_update(void* s, xce_sched_queue_t* q) { (void)s; (void)q; return 0; }
xce_sched_queue_t* sched_get_queue(xce_sched_core_t* s, char* n)
{
(void)s;
(void)n;
return NULL;
}
/* Stub: sched_update_existing_queue returns -EINVAL when updated is NULL,
* -ENOENT when queue not found (sched_get_queue stub returns NULL).
* Defined in schedobj.c; stubbed here for test linkage. */
int sched_update_existing_queue(xce_scheduler_t* sched, xce_queue_t* updated)
{
if (updated == NULL) {
return -EINVAL;
}
if (sched_get_queue(&(sched->core), updated->queue) == NULL) {
return -ENOENT;
}
return 0;
}
void setUp() { return; }
void tearDown(void)
{
printf("\n-----------------------------------------------------\n");
return;
}
static char doc[] = "sched_test";
static char args_doc[] = "";
#define MAX_TESTS (256)
struct arguments {
int ntests;
char* tests[MAX_TESTS];
};
static struct argp_option options[] = {
{ "run", 't', "STR", 0, "run test" },
{ 0 }
};
static error_t parse_opt(int key, char* arg, struct argp_state* state)
{
struct arguments* arguments = state->input;
switch (key) {
case 't':
if (arguments->ntests >= MAX_TESTS) {
fprintf(stderr, "Too many tests\n");
exit(-1);
}
arguments->tests[arguments->ntests] = arg;
arguments->ntests++;
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
static struct argp argp = { options, parse_opt, args_doc, doc };
struct arguments arguments;
static int check_test(struct arguments* args, char* x)
{
int i;
if (args->ntests == 0) {
return 1;
}
for (i = 0; i < args->ntests; i++) {
if (strcmp(x, args->tests[i]) == 0) {
return 1;
}
}
return 0;
}
static void
test_build_host_eval_ctx_float(void)
{
/* Worker with one float resource at res_idx=2 (value=4.0) */
xce_sched_worker_t w;
memset(&w, 0, sizeof(w));
zarray_init(&w.resvals, sizeof(xce_val_t*), NULL);
xce_val_t v;
memset(&v, 0, sizeof(v));
v.which = Value_u_fvalue;
v.u.fvalue = 4.0;
/* store xce_val_t* directly, matching production put_resvals behavior */
zarray_put(&w.resvals, 2, &v);
host_eval_ctx_t* ctx = build_host_eval_ctx(&w, 3);
TEST_ASSERT_NOT_NULL(ctx);
/* Unity is built without UNITY_INCLUDE_DOUBLE; compare via float cast */
TEST_ASSERT_EQUAL_FLOAT(4.0f, (float)ctx->numvals[2]);
TEST_ASSERT_NULL(ctx->strvals[2]);
TEST_ASSERT_EQUAL_INT(3, ctx->num_res);
free_host_eval_ctx(ctx);
zarray_fini(&w.resvals);
}
static void
test_scheduler_t_has_atomic_gc_fields(void)
{
xce_scheduler_t sched;
memset(&sched, 0, sizeof(sched));
TEST_ASSERT_EQUAL_INT(0, atomic_load(&sched.core.session_active));
TEST_ASSERT_EQUAL_INT(0, atomic_load(&sched.core.eval_in_progress));
TEST_ASSERT_EQUAL_INT(0, atomic_load(&sched.core.staging_merge_complete));
}
static void
test_sel_optimize_set_at_scheduler_startup(void)
{
/* sel_optimize_set(1) is called in sched_init.
* Since we can't call sched_init (it starts threads/network),
* verify the setter/getter contract directly. */
sel_optimize_set(1);
TEST_ASSERT_EQUAL_INT(1, sel_get_optimize_level());
/* reset for clean test isolation */
sel_optimize_test_reset();
}
static void
test_meet_host_selection_null_select_passes(void)
{
/* A NULL select pointer means no expression — always pass */
xce_scheduler_t sched;
memset(&sched, 0, sizeof(sched));
xce_sched_worker_t w;
memset(&w, 0, sizeof(w));
zarray_init(&w.resvals, 4, NULL);
int rc = sched_meet_host_selection(&(sched.core), &w, NULL);
TEST_ASSERT_EQUAL_INT(1, rc);
zarray_fini(&w.resvals);
}
static void
test_meet_host_selection_always_true_passes(void)
{
xce_scheduler_t sched;
memset(&sched, 0, sizeof(sched));
sel_optimize_set(1);
sel_node_table_init();
xce_sched_worker_t w;
memset(&w, 0, sizeof(w));
zarray_init(&w.resvals, 4, NULL);
/* SEL_TRUE root — must pass any host */
xce_select_node_t* root = makeSelectNode(SEL_TRUE, 0, 0);
TEST_ASSERT_NOT_NULL(root);
xce_select_t sel = { .root = root };
int rc = sched_meet_host_selection(&(sched.core), &w, &sel);
TEST_ASSERT_EQUAL_INT(1, rc);
sched_worker_release_host_ctx(&w);
zarray_fini(&w.resvals);
gc_clear_marks();
gc_sweep_select_nodes(NULL, 0);
sel_node_table_fini();
sel_optimize_test_reset();
}
static void
test_meet_host_selection_always_false_fails(void)
{
xce_scheduler_t sched;
memset(&sched, 0, sizeof(sched));
sel_optimize_set(1);
sel_node_table_init();
xce_sched_worker_t w;
memset(&w, 0, sizeof(w));
zarray_init(&w.resvals, 4, NULL);
/* SEL_FALSE root — must fail every host */
xce_select_node_t* root = makeSelectNode(SEL_FALSE, 0, 0);
TEST_ASSERT_NOT_NULL(root);
xce_select_t sel = { .root = root };
int rc = sched_meet_host_selection(&(sched.core), &w, &sel);
TEST_ASSERT_EQUAL_INT(0, rc);
sched_worker_release_host_ctx(&w);
zarray_fini(&w.resvals);
gc_clear_marks();
gc_sweep_select_nodes(NULL, 0);
sel_node_table_fini();
sel_optimize_test_reset();
}
static void
test_gc_predicates_idle_invariant(void)
{
xce_scheduler_t sched;
memset(&sched, 0, sizeof(sched));
/* staging_merge_complete=1 means GC is safe to run */
atomic_store(&sched.core.staging_merge_complete, 1);
sel_optimize_set(1);
sel_node_table_init();
/* Verify the atomic fields are readable — testing the predicate contract */
TEST_ASSERT_EQUAL_INT(0, atomic_load(&sched.core.session_active));
TEST_ASSERT_EQUAL_INT(0, atomic_load(&sched.core.eval_in_progress));
TEST_ASSERT_EQUAL_INT(1, atomic_load(&sched.core.staging_merge_complete));
/* GC operations on an empty node table must not crash */
gc_clear_marks();
gc_sweep_select_nodes(NULL, 0);
sel_node_table_fini();
sel_optimize_test_reset();
}
static void
test_staging_merge_complete_gc_guard(void)
{
/* Regression: sched_begin_session must leave staging_merge_complete=1 so
* sched_end_session's guard (!staging_merge_complete) does not fire.
*
* sched_begin_session: sets staging_merge_complete=0, does all session setup,
* then sets staging_merge_complete=1 (staging done).
* sched_end_session: guard skips GC when staging_merge_complete==0.
*
* Without the trailing set-to-1 in sched_begin_session, staging_merge_complete
* stays 0 forever and GC never runs — select-node memory leaks indefinitely. */
xce_scheduler_t sched;
memset(&sched, 0, sizeof(sched));
/* Simulate the post-begin_session state the fix produces */
atomic_store(&sched.core.session_active, 0); /* session ended */
atomic_store(&sched.core.eval_in_progress, 0);
atomic_store(&sched.core.staging_merge_complete,
1); /* fix: begin_session sets this */
/* The GC guard in sched_end_session: must NOT fire */
int guard_fires = atomic_load(&sched.core.eval_in_progress)
|| !atomic_load(&sched.core.staging_merge_complete);
TEST_ASSERT_EQUAL_INT(0, guard_fires);
/* Confirm the unfixed state (staging=0) would block GC */
atomic_store(&sched.core.staging_merge_complete, 0);
int guard_fires_unfixed = atomic_load(&sched.core.eval_in_progress)
|| !atomic_load(&sched.core.staging_merge_complete);
TEST_ASSERT_EQUAL_INT(1, guard_fires_unfixed);
}
static void
test_meet_host_selection_numeric_match(void)
{
/* Job requires: res[0] >= 4.
* Worker with res[0]=8.0 must match (rc=1).
* Worker with res[0]=2.0 must not match (rc=0). */
sel_optimize_set(1);
sel_node_table_init();
/* Build resreq AST: SEL_GE(SEL_RESNAME(idx=0, NUMERIC), SEL_NUM(4.0)) */
xce_select_node_t* resname = makeSelectNode(SEL_RESNAME, 0, RES_TYPE_NUMERIC);
double dval = 4.0;
uintptr_t bits;
memcpy(&bits, &dval, sizeof(bits));
xce_select_node_t* num4 = makeSelectNode(SEL_NUM, bits, 0);
xce_select_node_t* ge = makeSelectNode(SEL_GE,
(uintptr_t)resname,
(uintptr_t)num4);
TEST_ASSERT_NOT_NULL(ge);
xce_select_t sel = { .root = ge };
xce_scheduler_t sched;
memset(&sched, 0, sizeof(sched));
/* Worker with 8.0 at res_idx=0 — must pass */
xce_sched_worker_t w8;
memset(&w8, 0, sizeof(w8));
zarray_init(&w8.resvals, sizeof(xce_val_t*), NULL);
xce_val_t v8;
memset(&v8, 0, sizeof(v8));
v8.which = Value_u_fvalue;
v8.u.fvalue = 8.0f;
zarray_put(&w8.resvals, 0, &v8);
TEST_ASSERT_EQUAL_INT(1, sched_meet_host_selection(&(sched.core), &w8, &sel));
/* Worker with 2.0 at res_idx=0 — must fail */
xce_sched_worker_t w2;
memset(&w2, 0, sizeof(w2));
zarray_init(&w2.resvals, sizeof(xce_val_t*), NULL);
xce_val_t v2;
memset(&v2, 0, sizeof(v2));
v2.which = Value_u_fvalue;
v2.u.fvalue = 2.0f;
zarray_put(&w2.resvals, 0, &v2);
TEST_ASSERT_EQUAL_INT(0, sched_meet_host_selection(&(sched.core), &w2, &sel));
sched_worker_release_host_ctx(&w8);
sched_worker_release_host_ctx(&w2);
zarray_fini(&w8.resvals);
zarray_fini(&w2.resvals);
gc_clear_marks();
gc_sweep_select_nodes(NULL, 0);
sel_node_table_fini();
sel_optimize_test_reset();
}
static void
test_meet_host_selection_string_match(void)
{
/* Job requires: res[1] == "gpu".
* Worker with strval[1]="gpu" must match (rc=1).
* Worker with strval[1]="cpu" must not match (rc=0). */
sel_optimize_set(1);
xstring_table_init(139);
sel_node_table_init();
/* Build resreq AST: SEL_EQ(SEL_RESNAME(idx=1, STRING), SEL_STR("gpu")) */
xce_select_node_t* resname = makeSelectNode(SEL_RESNAME, 1, RES_TYPE_STRING);
char* gpu_str = makeString("gpu");
xce_select_node_t* str_node = makeSelectNode(SEL_STR, (uintptr_t)gpu_str, 0);
xce_select_node_t* eq = makeSelectNode(SEL_EQ,
(uintptr_t)resname,
(uintptr_t)str_node);
TEST_ASSERT_NOT_NULL(eq);
xce_select_t sel = { .root = eq };
xce_scheduler_t sched;
memset(&sched, 0, sizeof(sched));
/* Worker with "gpu" at res_idx=1 — must pass */
xce_sched_worker_t wgpu;
memset(&wgpu, 0, sizeof(wgpu));
zarray_init(&wgpu.resvals, sizeof(xce_val_t*), NULL);
xce_val_t vgpu;
memset(&vgpu, 0, sizeof(vgpu));
vgpu.which = Value_u_svalue;
vgpu.u.svalue = "gpu";
zarray_put(&wgpu.resvals, 1, &vgpu);
TEST_ASSERT_EQUAL_INT(1, sched_meet_host_selection(&(sched.core), &wgpu, &sel));
/* Worker with "cpu" at res_idx=1 — must fail */
xce_sched_worker_t wcpu;
memset(&wcpu, 0, sizeof(wcpu));
zarray_init(&wcpu.resvals, sizeof(xce_val_t*), NULL);
xce_val_t vcpu;
memset(&vcpu, 0, sizeof(vcpu));
vcpu.which = Value_u_svalue;
vcpu.u.svalue = "cpu";
zarray_put(&wcpu.resvals, 1, &vcpu);
TEST_ASSERT_EQUAL_INT(0, sched_meet_host_selection(&(sched.core), &wcpu, &sel));
sched_worker_release_host_ctx(&wgpu);
sched_worker_release_host_ctx(&wcpu);
zarray_fini(&wgpu.resvals);
zarray_fini(&wcpu.resvals);
gc_clear_marks();
gc_sweep_select_nodes(NULL, 0);
sel_node_table_fini();
xstring_table_fini();
sel_optimize_test_reset();
}
/* ── Task 9.1: xce_notify_event_name ─────────────────────────────────── */
static void
test_notify_event_name_known_values(void)
{
TEST_ASSERT_EQUAL_STRING("schedStartJob",
xce_notify_event_name(NotifyData_data_schedStartJob));
TEST_ASSERT_EQUAL_STRING("schedExitJob",
xce_notify_event_name(NotifyData_data_schedExitJob));
TEST_ASSERT_EQUAL_STRING("schedUpdateJob",
xce_notify_event_name(NotifyData_data_schedUpdateJob));
TEST_ASSERT_EQUAL_STRING("schedUpdateQueue",
xce_notify_event_name(NotifyData_data_schedUpdateQueue));
TEST_ASSERT_EQUAL_STRING("schedUpdateResvals",
xce_notify_event_name(NotifyData_data_schedUpdateResvals));
TEST_ASSERT_EQUAL_STRING("schedJobRusages",
xce_notify_event_name(NotifyData_data_schedJobRusages));
}
static void
test_notify_event_name_unknown_value(void)
{
char* s = xce_notify_event_name(9999);
TEST_ASSERT_NOT_NULL(s);
/* must start with "UNKNOWN(" */
TEST_ASSERT_EQUAL_INT(0, strncmp(s, "UNKNOWN(", 8));
}
/* ── Task 9.1 (enum completeness): new xce_sched_op_t values compile ─── */
static void
test_new_sched_op_enum_values_exist(void)
{
/* Compile-time presence check — if any enum value is missing, this
* translation unit will fail to compile. */
xce_sched_op_t ops[] = {
XCE_SCHED_OP_UPDATE_QUEUE,
XCE_SCHED_OP_UPDATE_RESOURCES,
XCE_SCHED_OP_UPDATE_RESVALS_FULL,
XCE_SCHED_OP_UPDATE_RESVALS,
XCE_SCHED_OP_JOB_RUSAGE,
};
TEST_ASSERT_EQUAL_INT(5, (int)(sizeof(ops) / sizeof(ops[0])));
}
/* ── Task 9.5: sched_update_existing_queue NULL guard ───────────────── */
static void
test_sched_update_existing_queue_null_payload(void)
{
xce_scheduler_t sched;
memset(&sched, 0, sizeof(sched));
/* NULL updated → -EINVAL without touching scheduler state */
int rc = sched_update_existing_queue(&sched, NULL);
TEST_ASSERT_EQUAL_INT(-EINVAL, rc);
}
/* ── Task 9.8: dead symbols absent ──────────────────────────────────── */
static void
test_dead_pushmsg_symbols_absent(void)
{
/* If sched_handle_jobpush or sched_handle_workerpush were present, they
* would be included via ../src/handler.c — but handler.c cannot be
* included here (requires generated controller.capnp.h). The build-time
* link step is the definitive check; this test documents the intent. */
TEST_PASS();
}
/* ── scheduler-sim-live-allocation task 1.3: Core-Safe Allocation Chain ── */
/*
* sched_meet_host_selection()/sched_check_shared_satisfy()/
* sched_check_host_satisfy() are re-typed to take xce_sched_core_t* core
* directly instead of a normal-mode-only xce_scheduler_t* sched, so they
* must be callable identically from either driver's core state --
* xce_scheduler_t (this file's own `sched` variables above) and
* xce_sched_sim_driver_t (sim.h) both embed xce_sched_core_t core as their
* first member (scheduler.h / sim.h). This test drives IDENTICAL inputs
* through all three functions against &(normal.core) and &(sim.core) and
* asserts identical results -- proving driver-agnosticism, not merely a
* retyped signature.
*
* RED (before this task): fails to compile -- the three functions take
* xce_scheduler_t*, so passing xce_sched_core_t* directly is an
* incompatible-pointer-type error (this toolchain treats
* -Wincompatible-pointer-types as fatal, not a warning).
*
* No sched_sim_driver_run() call here -- this fixture never spawns a
* thread against xce_sched_sim_driver_t, only reads/writes its embedded
* `core` synchronously within this function's own lifetime, so bug-1598's
* stack-use-after-return hazard (a stack-local sim driver outliving a
* never-joined thread) does not apply.
*/
static void init_test_resmgr(xce_sched_core_t* core)
{
kvmgr_params_t resmgr_params = { .key_type = KVMGR_KEY_STRING,
.nslots = 8,
.delta = 4,
.datakiller = NULL };
core->resmgr = makeInstance(&xkvmgr_type, &resmgr_params);
TEST_ASSERT_NOT_NULL(core->resmgr);
}
static void
test_core_safe_allocation_chain_driver_parity(void)
{
xce_scheduler_t sched_normal;
xce_sched_sim_driver_t sim_shape;
xce_sched_core_t* core_normal = &(sched_normal.core);
xce_sched_core_t* core_sim = &(sim_shape.core);
memset(&sched_normal, 0, sizeof(sched_normal));
memset(&sim_shape, 0, sizeof(sim_shape));
init_test_resmgr(core_normal);
init_test_resmgr(core_sim);
/* --- sched_meet_host_selection(): NULL select always passes -------- */
{
xce_sched_worker_t w;
int rc_normal, rc_sim;
memset(&w, 0, sizeof(w));
zarray_init(&w.resvals, 4, NULL);
rc_normal = sched_meet_host_selection(core_normal, &w, NULL);
rc_sim = sched_meet_host_selection(core_sim, &w, NULL);
TEST_ASSERT_EQUAL_INT(1, rc_normal);
TEST_ASSERT_EQUAL_INT_MESSAGE(
rc_normal, rc_sim,
"sched_meet_host_selection() must return identically for a "
"normal-mode-shaped and a simulation-mode-shaped core");
zarray_fini(&w.resvals);
}
/* --- sched_check_shared_satisfy(): identical grant/deny ------------- */
{
xvec_t uncomitted_n, uncomitted_s;
xvec_idval_t wanted_n, wanted_s, unmeet_n, unmeet_s;
xce_val_t total, inuse;
int rc_normal, rc_sim;
zarray_init(&core_normal->sharedInst, 4, NULL);
zarray_init(&core_normal->sharedInUse, 4, NULL);
zarray_init(&core_sim->sharedInst, 4, NULL);
zarray_init(&core_sim->sharedInUse, 4, NULL);
memset(&total, 0, sizeof(total));
total.which = Value_u_fvalue;
total.u.fvalue = 10.0f;
memset(&inuse, 0, sizeof(inuse));
inuse.which = Value_u_fvalue;
inuse.u.fvalue = 3.0f;
zarray_put(&core_normal->sharedInst, 0, &total);
zarray_put(&core_normal->sharedInUse, 0, &inuse);
zarray_put(&core_sim->sharedInst, 0, &total);
zarray_put(&core_sim->sharedInUse, 0, &inuse);
/* Grant: wants 5.0, only 3.0 in use out of 10.0 total. */
xvec_init(&uncomitted_n, 1, sizeof(float));
xvec_idval_init(&wanted_n, 1);
xvec_idval_store(&wanted_n, 0, 5.0f);
xvec_init(&uncomitted_s, 1, sizeof(float));
xvec_idval_init(&wanted_s, 1);
xvec_idval_store(&wanted_s, 0, 5.0f);
rc_normal = sched_check_shared_satisfy(core_normal, &uncomitted_n,
&wanted_n, &unmeet_n);
rc_sim = sched_check_shared_satisfy(core_sim, &uncomitted_s,
&wanted_s, &unmeet_s);
TEST_ASSERT_EQUAL_INT(1, rc_normal);
TEST_ASSERT_EQUAL_INT_MESSAGE(
rc_normal, rc_sim,
"sched_check_shared_satisfy() must grant identically for a "
"normal-mode-shaped and a simulation-mode-shaped core");
xvec_fini(&uncomitted_n);
xvec_idval_fini(&wanted_n);
xvec_idval_fini(&unmeet_n);
xvec_fini(&uncomitted_s);
xvec_idval_fini(&wanted_s);
xvec_idval_fini(&unmeet_s);
/* Deny: wants 8.0, only 7.0 of headroom remains (10.0 - 3.0). */
xvec_init(&uncomitted_n, 1, sizeof(float));
xvec_idval_init(&wanted_n, 1);
xvec_idval_store(&wanted_n, 0, 8.0f);
xvec_init(&uncomitted_s, 1, sizeof(float));
xvec_idval_init(&wanted_s, 1);
xvec_idval_store(&wanted_s, 0, 8.0f);
rc_normal = sched_check_shared_satisfy(core_normal, &uncomitted_n,
&wanted_n, &unmeet_n);
rc_sim = sched_check_shared_satisfy(core_sim, &uncomitted_s,
&wanted_s, &unmeet_s);
TEST_ASSERT_EQUAL_INT(0, rc_normal);
TEST_ASSERT_EQUAL_INT_MESSAGE(
rc_normal, rc_sim,
"sched_check_shared_satisfy() must deny identically for a "
"normal-mode-shaped and a simulation-mode-shaped core");
xvec_fini(&uncomitted_n);
xvec_idval_fini(&wanted_n);
xvec_idval_fini(&unmeet_n);
xvec_fini(&uncomitted_s);
xvec_idval_fini(&wanted_s);
xvec_idval_fini(&unmeet_s);
zarray_fini(&core_normal->sharedInst);
zarray_fini(&core_normal->sharedInUse);
zarray_fini(&core_sim->sharedInst);
zarray_fini(&core_sim->sharedInUse);
}
/* --- sched_check_host_satisfy(): identical grant/deny --------------- */
{
xce_sched_worker_t w_normal, w_sim;
xvec_t uncomitted_n, uncomitted_s;
xvec_idval_t wanted_n, wanted_s, unmeet_n, unmeet_s;
xce_val_t total, inuse;
int rc_normal, rc_sim;
memset(&w_normal, 0, sizeof(w_normal));
memset(&w_sim, 0, sizeof(w_sim));
zarray_init(&w_normal.resvals, 4, NULL);
zarray_init(&w_normal.resInUse, 4, NULL);
zarray_init(&w_sim.resvals, 4, NULL);
zarray_init(&w_sim.resInUse, 4, NULL);
memset(&total, 0, sizeof(total));
total.which = Value_u_fvalue;
total.u.fvalue = 10.0f;
memset(&inuse, 0, sizeof(inuse));
inuse.which = Value_u_fvalue;
inuse.u.fvalue = 3.0f;
zarray_put(&w_normal.resvals, 0, &total);
zarray_put(&w_normal.resInUse, 0, &inuse);
zarray_put(&w_sim.resvals, 0, &total);
zarray_put(&w_sim.resInUse, 0, &inuse);
/* Grant */
xvec_init(&uncomitted_n, 1, sizeof(float));
xvec_idval_init(&wanted_n, 1);
xvec_idval_store(&wanted_n, 0, 5.0f);
xvec_init(&uncomitted_s, 1, sizeof(float));
xvec_idval_init(&wanted_s, 1);
xvec_idval_store(&wanted_s, 0, 5.0f);
rc_normal = sched_check_host_satisfy(core_normal, &w_normal,
&uncomitted_n, &wanted_n,
&unmeet_n);
rc_sim = sched_check_host_satisfy(core_sim, &w_sim, &uncomitted_s,
&wanted_s, &unmeet_s);
TEST_ASSERT_EQUAL_INT(1, rc_normal);
TEST_ASSERT_EQUAL_INT_MESSAGE(
rc_normal, rc_sim,
"sched_check_host_satisfy() must grant identically for a "
"normal-mode-shaped and a simulation-mode-shaped core");
xvec_fini(&uncomitted_n);
xvec_idval_fini(&wanted_n);
xvec_idval_fini(&unmeet_n);
xvec_fini(&uncomitted_s);
xvec_idval_fini(&wanted_s);
xvec_idval_fini(&unmeet_s);
/* Deny */
xvec_init(&uncomitted_n, 1, sizeof(float));
xvec_idval_init(&wanted_n, 1);
xvec_idval_store(&wanted_n, 0, 8.0f);
xvec_init(&uncomitted_s, 1, sizeof(float));
xvec_idval_init(&wanted_s, 1);
xvec_idval_store(&wanted_s, 0, 8.0f);
rc_normal = sched_check_host_satisfy(core_normal, &w_normal,
&uncomitted_n, &wanted_n,
&unmeet_n);
rc_sim = sched_check_host_satisfy(core_sim, &w_sim, &uncomitted_s,
&wanted_s, &unmeet_s);
TEST_ASSERT_EQUAL_INT(0, rc_normal);
TEST_ASSERT_EQUAL_INT_MESSAGE(
rc_normal, rc_sim,
"sched_check_host_satisfy() must deny identically for a "
"normal-mode-shaped and a simulation-mode-shaped core");
xvec_fini(&uncomitted_n);
xvec_idval_fini(&wanted_n);
xvec_idval_fini(&unmeet_n);
xvec_fini(&uncomitted_s);
xvec_idval_fini(&wanted_s);
xvec_idval_fini(&unmeet_s);
zarray_fini(&w_normal.resvals);
zarray_fini(&w_normal.resInUse);
zarray_fini(&w_sim.resvals);
zarray_fini(&w_sim.resInUse);
}
destroy(core_normal->resmgr);
destroy(core_sim->resmgr);
}
#define TEST(x) \
do { \
if (check_test(&arguments, #x)) { \
RUN_TEST(x, 0); \
} \
} while (0)
int main(int argc, char* argv[])
{
argp_parse(&argp, argc, argv, 0, 0, &arguments);
UNITY_BEGIN();
TEST(test_build_host_eval_ctx_float);
TEST(test_scheduler_t_has_atomic_gc_fields);
TEST(test_sel_optimize_set_at_scheduler_startup);
TEST(test_meet_host_selection_null_select_passes);
TEST(test_meet_host_selection_always_true_passes);
TEST(test_meet_host_selection_always_false_fails);
TEST(test_gc_predicates_idle_invariant);
TEST(test_staging_merge_complete_gc_guard);
TEST(test_meet_host_selection_numeric_match);
TEST(test_meet_host_selection_string_match);
TEST(test_notify_event_name_known_values);
TEST(test_notify_event_name_unknown_value);
TEST(test_new_sched_op_enum_values_exist);
TEST(test_sched_update_existing_queue_null_payload);
TEST(test_dead_pushmsg_symbols_absent);
TEST(test_core_safe_allocation_chain_driver_parity);
return UNITY_END();
}