feat: fill scheduler controller message gaps

Wire up 6 previously-stubbed message handlers in the scheduler:
resource dataset, resval dataset, job update notify, queue update
notify, resval update notify, and job rusage notify. Remove the
unreachable pushmsg branch (vtask_entry rewrites all pushmsg to
datamsg before the handler fires).

Changes by component:
- helper.h/helper.c: add xce_notify_event_name() for symbolic log names
- scheduler.h: add 5 new xce_sched_op_t values and matching union fields
- plugin.h/plugin.c: add plugin_job_rusage_hook_t and plugin_job_rusage_update()
- schedobj.c: add sched_update_existing_queue() with NULL/-ENOENT guards
- scheduler.c: wire 5 new dequeue cases; add sched_update_existing_job()
  and static sched_op_name(); emit WARN log on handler failure
- handler.c: implement sched_handle_resources/resvals (dataset) and
  sched_handle_sched_update_{job,queue,resvals}/job_rusage (notify);
  remove dead sched_handle_jobpush/workerpush and pushmsg branch;
  update TRACE logs to use symbolic name helpers
- test.c: 5 new unit tests covering notify event names, enum values,
  and queue update NULL guard (15/15 pass)
This commit is contained in:
Rongsong Shen 2026-05-12 07:01:52 +00:00
parent f80839cd99
commit 3e652d2d7e
10 changed files with 534 additions and 39 deletions

View file

@ -19,5 +19,6 @@ extern void* xce_notify_data_ptr(xce_notify_data_t*);
extern void* xce_jobctrl_data_ptr(xce_jobctrl_t*);
extern void* xce_reply_data_ptr(xce_reply_t*);
extern char* xce_dataset_table(int);
extern char* xce_notify_event_name(int);
#endif

View file

@ -1,4 +1,5 @@
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include "controller.capnp.h"
@ -330,3 +331,55 @@ char* xce_dataset_table(int table)
return "unknown";
}
char* xce_notify_event_name(int event)
{
static char buf[32];
switch (event) {
case NotifyData_data_jobPidInfo:
return "jobPidInfo";
case NotifyData_data_jobFailInfo:
return "jobFailInfo";
case NotifyData_data_jobDispatchFail:
return "jobDispatchFail";
case NotifyData_data_jobFinishInfo:
return "jobFinishInfo";
case NotifyData_data_schedStartJob:
return "schedStartJob";
case NotifyData_data_schedExitJob:
return "schedExitJob";
case NotifyData_data_schedFailJob:
return "schedFailJob";
case NotifyData_data_schedUpdateJob:
return "schedUpdateJob";
case NotifyData_data_schedUpdateResvals:
return "schedUpdateResvals";
case NotifyData_data_stoppedJob:
return "stoppedJob";
case NotifyData_data_resumedJob:
return "resumedJob";
case NotifyData_data_ckptinfo:
return "ckptinfo";
case NotifyData_data_schedUpdateQueue:
return "schedUpdateQueue";
case NotifyData_data_taskPidInfo:
return "taskPidInfo";
case NotifyData_data_taskFailInfo:
return "taskFailInfo";
case NotifyData_data_taskFinish:
return "taskFinish";
case NotifyData_data_postDoneJob:
return "postDoneJob";
case NotifyData_data_schedJobRusages:
return "schedJobRusages";
case NotifyData_data_updateJob:
return "updateJob";
default:
break;
}
snprintf(buf, sizeof(buf), "UNKNOWN(%d)", event);
return buf;
}

View file

@ -21,6 +21,8 @@ typedef int (*plugin_job_state_change_hook_t)(void*, xce_sched_job_t*,
typedef int (*plugin_commit_decision_hook_t)(void*, xce_decision_t*);
typedef int (*plugin_job_election_hook_t)(void*, xce_sched_job_t*);
typedef int (*plugin_prepare_allocation_hook_t)(void*, xce_sched_ctx_t*);
typedef int (*plugin_job_rusage_hook_t)(void*, xce_sched_job_t*,
xce_job_rusages_t*);
/* initialize function of plugin */
typedef int (*plugin_init_fn_t)(void*);
@ -48,6 +50,7 @@ typedef struct {
plugin_commit_decision_hook_t commit_hook;
plugin_job_election_hook_t job_election_hook;
plugin_prepare_allocation_hook_t prepare_allocation_hook;
plugin_job_rusage_hook_t rusage_hook;
} plugin_hooks_t;
typedef struct {
@ -76,6 +79,7 @@ extern int plugin_end_session(void*);
extern int plugin_commit_decision(void*, xce_decision_t*);
extern int plugin_prepare_allocation(void*, xce_sched_ctx_t*);
extern int plugin_job_election(void*, xce_sched_job_t*);
extern int plugin_job_rusage_update(void*, xce_sched_job_t*, xce_job_rusages_t*);
extern int register_fcfs(void*);
extern int register_limit(void*);
extern int register_seqjob_allocator(void*);

View file

@ -26,7 +26,12 @@ typedef enum {
XCE_SCHED_OP_UPDATE_CONFIG,
XCE_SCHED_OP_UPDATE_JOB,
XCE_SCHED_OP_JSTATE_CHANGE,
XCE_SCHED_OP_UPDATE_WORKER
XCE_SCHED_OP_UPDATE_WORKER,
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
} xce_sched_op_t;
typedef struct {
@ -46,6 +51,23 @@ typedef struct {
uint64_t jobid;
uint32_t state;
} job_state_change;
struct {
xce_queue_t* queue;
} queueupdate;
struct {
int nresources;
xce_resource_t** resources;
} resourceupdate;
struct {
int nvals;
xce_resval_t** vals;
} resvalfullupdate;
struct {
xce_resval_list_t* resvals;
} resvalupdate;
struct {
xce_job_rusages_t* rusages;
} job_rusage;
} u;
} xce_sched_update_request_t;
@ -105,6 +127,7 @@ extern xce_sched_user_t* sched_get_user(xce_scheduler_t*, char*);
extern xce_sched_project_t* sched_get_project(xce_scheduler_t*, char*);
extern int sched_add_job(xce_scheduler_t*, xce_sched_job_t*);
extern int sched_add_queue(xce_scheduler_t*, xce_sched_queue_t*);
extern int sched_update_existing_queue(xce_scheduler_t*, xce_queue_t*);
extern int sched_add_user(xce_scheduler_t*, xce_sched_user_t*);
extern int sched_add_worker(xce_scheduler_t*, xce_sched_worker_t*);
extern int sched_add_project(xce_scheduler_t*, xce_sched_project_t*);

View file

@ -5,6 +5,7 @@
#include "controller.capnp.h"
#include "dataq.h"
#include "helper.h"
#include "xce/types.h"
#include "scheduler.h"
@ -18,21 +19,6 @@ static void enq_pend_update(xce_scheduler_t* sched,
mylock_unlock(&(sched->lock));
}
static int sched_handle_jobpush(xce_scheduler_t* sched, xce_jobs_data_t* jobs)
{
TRACE("%s...", __func__);
return 0;
}
static int sched_handle_workerpush(xce_scheduler_t* sched,
xce_workers_data_t* workers)
{
TRACE("%s...", __func__);
return 0;
}
static int sched_on_jobs(xce_scheduler_t* sched, int njobs, xce_job_t** jobs)
{
xce_sched_update_request_t* req;
@ -70,11 +56,57 @@ static int sched_on_workers(xce_scheduler_t* sched, int nworkers,
return 0;
}
static int sched_handle_resources(xce_scheduler_t* sched, int count,
xce_resource_t** resources)
{
xce_sched_update_request_t* req;
if (resources == NULL || count == 0) {
return 0;
}
req = (xce_sched_update_request_t*)calloc(1, sizeof(*req));
if (req == NULL) {
return -ENOMEM;
}
req->op = XCE_SCHED_OP_UPDATE_RESOURCES;
req->u.resourceupdate.nresources = count;
req->u.resourceupdate.resources = resources;
enq_pend_update(sched, req);
return 0;
}
static int sched_handle_resvals(xce_scheduler_t* sched, int count,
xce_resval_t** vals)
{
xce_sched_update_request_t* req;
if (vals == NULL || count == 0) {
return 0;
}
req = (xce_sched_update_request_t*)calloc(1, sizeof(*req));
if (req == NULL) {
return -ENOMEM;
}
req->op = XCE_SCHED_OP_UPDATE_RESVALS_FULL;
req->u.resvalfullupdate.nvals = count;
req->u.resvalfullupdate.vals = vals;
enq_pend_update(sched, req);
return 0;
}
static int sched_handle_dataset(xce_scheduler_t* sched, dataset_t* dataset)
{
int rc = 0;
TRACE("%s table = %d", __func__, dataset->table);
TRACE("%s table = %s", __func__, xce_dataset_table(dataset->table));
switch (dataset->table) {
case Data_data_schedjobs:
@ -96,8 +128,20 @@ static int sched_handle_dataset(xce_scheduler_t* sched, dataset_t* dataset)
}
break;
case Data_data_resources:
rc = sched_handle_resources(sched, dataset->nrows,
(xce_resource_t**)dataset->rows);
if (rc >= 0) {
dataset->nrows = 0;
dataset->rows = NULL;
}
break;
case Data_data_resvals:
rc = sched_handle_resvals(sched, dataset->nrows,
(xce_resval_t**)dataset->rows);
if (rc >= 0) {
dataset->nrows = 0;
dataset->rows = NULL;
}
break;
}
@ -126,10 +170,113 @@ static int sched_job_state_change(xce_scheduler_t* sched, uint64_t jobid,
return 0;
}
static int sched_handle_sched_update_job(xce_scheduler_t* sched,
xce_job_t* job)
{
xce_sched_update_request_t* req;
xce_job_t** job_arr;
char jobidString[128];
if (job == NULL) {
return -EINVAL;
}
fmt_jobid(jobidString, sizeof(jobidString) - 1, job->jobid);
TRACE("%s %s updated", __func__, jobidString);
job_arr = (xce_job_t**)malloc(sizeof(xce_job_t*));
if (job_arr == NULL) {
return -ENOMEM;
}
job_arr[0] = job;
req = (xce_sched_update_request_t*)calloc(1, sizeof(*req));
if (req == NULL) {
free(job_arr);
return -ENOMEM;
}
req->op = XCE_SCHED_OP_UPDATE_JOB;
req->u.jobupdate.njobs = 1;
req->u.jobupdate.jobs = job_arr;
enq_pend_update(sched, req);
return 0;
}
static int sched_handle_sched_update_queue(xce_scheduler_t* sched,
xce_queue_t* queue)
{
xce_sched_update_request_t* req;
if (queue == NULL) {
return -EINVAL;
}
req = (xce_sched_update_request_t*)calloc(1, sizeof(*req));
if (req == NULL) {
return -ENOMEM;
}
req->op = XCE_SCHED_OP_UPDATE_QUEUE;
req->u.queueupdate.queue = queue;
enq_pend_update(sched, req);
return 0;
}
static int sched_handle_sched_update_resvals(xce_scheduler_t* sched,
xce_resval_list_t* resvals)
{
xce_sched_update_request_t* req;
if (resvals == NULL || resvals->count == 0) {
return 0;
}
req = (xce_sched_update_request_t*)calloc(1, sizeof(*req));
if (req == NULL) {
return -ENOMEM;
}
req->op = XCE_SCHED_OP_UPDATE_RESVALS;
req->u.resvalupdate.resvals = resvals;
enq_pend_update(sched, req);
return 0;
}
static int sched_handle_sched_job_rusage(xce_scheduler_t* sched,
xce_job_rusages_t* rusages)
{
xce_sched_update_request_t* req;
if (rusages == NULL) {
return -EINVAL;
}
req = (xce_sched_update_request_t*)calloc(1, sizeof(*req));
if (req == NULL) {
return -ENOMEM;
}
req->op = XCE_SCHED_OP_JOB_RUSAGE;
req->u.job_rusage.rusages = rusages;
enq_pend_update(sched, req);
return 0;
}
int sched_handle_notify(xce_scheduler_t* sched, xce_notify_data_t* notify)
{
int rc = 0;
char jobidString[128];
TRACE("%s event = %s", __func__, xce_notify_event_name(notify->event));
switch (notify->event) {
case NotifyData_data_schedStartJob:
@ -145,15 +292,20 @@ int sched_handle_notify(xce_scheduler_t* sched, xce_notify_data_t* notify)
JOB_STAT_EXIT);
break;
case NotifyData_data_schedUpdateJob:
fmt_jobid(jobidString, sizeof(jobidString) - 1,
notify->data.sched_update_job->jobid);
TRACE("%s %s updated", __func__, jobidString);
rc = sched_handle_sched_update_job(sched,
notify->data.sched_update_job);
break;
case NotifyData_data_schedUpdateQueue:
rc = sched_handle_sched_update_queue(sched,
notify->data.sched_update_queue);
break;
case NotifyData_data_schedUpdateResvals:
rc = sched_handle_sched_update_resvals(
sched, notify->data.sched_update_resvals);
break;
case NotifyData_data_schedJobRusages:
rc = sched_handle_sched_job_rusage(sched,
notify->data.sched_job_rusages);
break;
default:
rc = -EINVAL;
@ -162,11 +314,14 @@ int sched_handle_notify(xce_scheduler_t* sched, xce_notify_data_t* notify)
return rc;
}
int scheduler_req_handler(xce_request_t* request, void* args)
{
int rc = 0;
xce_scheduler_t* sched = (xce_scheduler_t*)args;
TRACE("%s op = %s", __func__, xce_request_op_name(request));
switch (request->opcode) {
case OPCode_push:
switch (request->body_which) {
@ -181,21 +336,6 @@ int scheduler_req_handler(xce_request_t* request, void* args)
request->body.configpushmsg->data.conf4sched = NULL;
}
break;
case Request_body_pushmsg:
/* data arriving */
switch (request->body.pushmsg->table) {
case Data_data_schedjobs:
/* job arriving */
rc = sched_handle_jobpush(sched,
request->body.pushmsg->data.jobs);
break;
case Data_data_schedworkers:
/* workers arriving */
rc = sched_handle_workerpush(
sched, request->body.pushmsg->data.workers);
break;
}
break;
case Request_body_datamsg:
rc = sched_handle_dataset(sched, request->body.datamsg);
break;

View file

@ -358,6 +358,44 @@ int plugin_job_state_change(void* arg, xce_sched_job_t* job, uint32_t state)
return rc;
}
int plugin_job_rusage_update(void* arg, xce_sched_job_t* job,
xce_job_rusages_t* rusages)
{
xce_scheduler_t* sched = (xce_scheduler_t*)arg;
iterator_t it;
int rc;
if (sched == NULL) {
return -EINVAL;
}
rc = iter_make(&it, sched->plugins);
if (rc < 0) {
return rc;
}
for (;;) {
xce_sched_plugin_t* plugin = NULL;
rc = iter_next(&it, (void**)&plugin);
if ((rc < 0) || (rc == RTTI_ITER_NO_DATA)) {
break;
}
if (plugin->hooks.rusage_hook != NULL) {
plugin->hooks.rusage_hook(plugin, job, rusages);
}
}
if (rc == RTTI_ITER_NO_DATA) {
rc = 0;
}
iter_destroy(&it);
return rc;
}
int plugin_job_election(void* arg, xce_sched_job_t* job)
{
xce_scheduler_t* sched = (xce_scheduler_t*)arg;

View file

@ -469,3 +469,31 @@ int sched_ctx_get_uncomit_host(xvec_t** host, xce_sched_ctx_t* ctx, int idx)
*host = v;
return 1;
}
int sched_update_existing_queue(xce_scheduler_t* sched, xce_queue_t* updated)
{
xce_sched_queue_t* q;
if (updated == NULL) {
return -EINVAL;
}
q = sched_get_queue(sched, updated->queue);
if (q == NULL) {
return -ENOENT;
}
q->status = updated->status;
q->priority = updated->priority;
q->accept_interval = updated->accept_interval;
q->sched_delay = updated->sched_delay;
if (updated->resreq != NULL) {
free(q->resreq);
q->resreq = makeString(updated->resreq);
}
plugin_queue_update(sched, q);
return 0;
}

View file

@ -307,6 +307,16 @@ static void sched_pend_update_killer(data_item_t* it)
break;
case XCE_SCHED_OP_JSTATE_CHANGE:
break;
case XCE_SCHED_OP_UPDATE_QUEUE:
break;
case XCE_SCHED_OP_UPDATE_RESOURCES:
break;
case XCE_SCHED_OP_UPDATE_RESVALS_FULL:
break;
case XCE_SCHED_OP_UPDATE_RESVALS:
break;
case XCE_SCHED_OP_JOB_RUSAGE:
break;
default:
break;
}
@ -610,6 +620,48 @@ static int sched_update_workers(xce_scheduler_t* sched, int nworkers,
return 0;
}
static char* sched_op_name(xce_sched_op_t op)
{
static char buf[32];
switch (op) {
case XCE_SCHED_OP_NONE: return "NONE";
case XCE_SCHED_OP_UPDATE_CONFIG: return "UPDATE_CONFIG";
case XCE_SCHED_OP_UPDATE_JOB: return "UPDATE_JOB";
case XCE_SCHED_OP_JSTATE_CHANGE: return "JSTATE_CHANGE";
case XCE_SCHED_OP_UPDATE_WORKER: return "UPDATE_WORKER";
case XCE_SCHED_OP_UPDATE_QUEUE: return "UPDATE_QUEUE";
case XCE_SCHED_OP_UPDATE_RESOURCES: return "UPDATE_RESOURCES";
case XCE_SCHED_OP_UPDATE_RESVALS_FULL: return "UPDATE_RESVALS_FULL";
case XCE_SCHED_OP_UPDATE_RESVALS: return "UPDATE_RESVALS";
case XCE_SCHED_OP_JOB_RUSAGE: return "JOB_RUSAGE";
default: break;
}
snprintf(buf, sizeof(buf), "UNKNOWN(%d)", (int)op);
return buf;
}
static int sched_update_existing_job(xce_scheduler_t* sched, xce_job_t* updated)
{
xce_sched_job_t* job;
if (updated == NULL) {
return -EINVAL;
}
job = sched_get_job(sched, updated->jobid);
if (job == NULL) {
return -ENOENT;
}
job->state = updated->state;
plugin_job_update(sched, job);
return 0;
}
int sched_update(xce_scheduler_t* sched)
{
dataq_t* q = NULL;
@ -650,6 +702,70 @@ int sched_update(xce_scheduler_t* sched)
rc = sched_job_state_change(sched, req->u.job_state_change.jobid,
req->u.job_state_change.state);
break;
case XCE_SCHED_OP_UPDATE_QUEUE:
rc = sched_update_existing_queue(sched, req->u.queueupdate.queue);
break;
case XCE_SCHED_OP_UPDATE_RESOURCES: {
int i;
for (i = 0; i < req->u.resourceupdate.nresources; i++) {
xce_resource_t* r = req->u.resourceupdate.resources[i];
if (r == NULL) {
continue;
}
makeResource(sched->resmgr, r);
}
rc = 0;
break;
}
case XCE_SCHED_OP_UPDATE_RESVALS_FULL: {
int i;
for (i = 0; i < req->u.resvalfullupdate.nvals; i++) {
xce_resval_t* rv = req->u.resvalfullupdate.vals[i];
xce_resource_t* r = NULL;
if (rv == NULL) {
continue;
}
if (key_find(sched->resmgr, rv->name, (void**)&r) < 0
|| r == NULL) {
WARN("%s unknown resource %s in resvals update", __func__,
rv->name);
continue;
}
zarray_put(&sched->sharedInst, r->idx, rv->val);
}
rc = 0;
break;
}
case XCE_SCHED_OP_UPDATE_RESVALS: {
int i;
xce_resval_list_t* resvals = req->u.resvalupdate.resvals;
for (i = 0; i < resvals->count; i++) {
xce_resval_t* rv = resvals->vals[i];
xce_sched_worker_t* w = NULL;
if (rv == NULL) {
continue;
}
w = sched_get_worker(sched, rv->host);
if (w == NULL) {
continue;
}
zarray_put(&w->resvals, 0, rv->val);
}
rc = 0;
break;
}
case XCE_SCHED_OP_JOB_RUSAGE: {
xce_sched_job_t* job = NULL;
xce_job_rusages_t* rusages = req->u.job_rusage.rusages;
job = sched_get_job(sched, rusages->jobid);
if (job == NULL) {
rc = -ENOENT;
} else {
plugin_job_rusage_update(sched, job, rusages);
rc = 0;
}
break;
}
default:
rc = -EINVAL;
break;
@ -658,7 +774,8 @@ int sched_update(xce_scheduler_t* sched)
sched_pend_update_killer(it);
if (rc < 0) {
/* something wrong? */
WARN("%s: op %s failed: %s (%d)", __func__,
sched_op_name(req->op), strerror(-rc), rc);
}
}

View file

@ -1,5 +1,5 @@
scheduler_test_src = [
'test.c'
'test.c',
]
scheduler_test_deps = [libxce_deps, librtti_dep, unity_dep, libxce_dep]

View file

@ -5,6 +5,7 @@
#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.
@ -15,6 +16,22 @@
xce_sched_worker_t* sched_get_worker(xce_scheduler_t* s, char* n) { (void)s; (void)n; return NULL; }
int plugin_prepare_allocation(void* 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_scheduler_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, updated->queue) == NULL) {
return -ENOENT;
}
return 0;
}
void setUp() { return; }
@ -371,6 +388,75 @@ test_meet_host_selection_string_match(void)
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();
}
#define TEST(x) \
do { \
if (check_test(&arguments, #x)) { \
@ -394,6 +480,11 @@ int main(int argc, char* argv[])
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);
return UNITY_END();
}