test(scheduler-controller-msg-gap): real handler/applier unit tests (bug-765 G-6)
The Task 9 tests were marked [x] but were stub/absent. Convert the correctness-critical ones to real white-box tests, registered as meson tests under --suite scheduler (leaks-off per convention): - test_handlers.c (new) — #include ../src/handler.c; covers 9.2/9.3: each notify/dataset handler enqueues the correct op / skips NULL-empty / -EINVAL. Asserts the 5.6 fix (update_job -> UPDATE_JOB_FIELDS, not bulk UPDATE_JOB). - test_job_update.c — add 9.5 (real sched_update_existing_queue, replacing the former local stub); already covers 9.4 (job) and 9.7 (rusage). - meson.build — build + register all three scheduler test executables. Suite GREEN: sched_test 15/15, sched_job_update_test 10/10, sched_handlers_test 6/6; ASAN-clean. Honest remaining: 9.6 dequeue worker-resvals APPLY is PARTIAL (enqueue tested; apply is a dormant path, R-3); 9.8 is a documented compile-time absence check. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
080ff78a80
commit
276e26c37b
4 changed files with 270 additions and 7 deletions
|
|
@ -20,3 +20,21 @@ sched_job_update_test = executable('sched_job_update_test',
|
|||
include_directories:[incdir],
|
||||
dependencies: [scheduler_test_deps],
|
||||
install: false)
|
||||
|
||||
# White-box test for the notify/dataset enqueue handlers. It #includes
|
||||
# ../src/handler.c directly (enq_pend_update + the static handlers), so it is a
|
||||
# standalone TU with its own main() — built as a separate executable.
|
||||
sched_handlers_test = executable('sched_handlers_test',
|
||||
['test_handlers.c'],
|
||||
include_directories:[incdir],
|
||||
dependencies: [scheduler_test_deps],
|
||||
install: false)
|
||||
|
||||
# Register as meson tests so `meson test --suite scheduler` runs them all.
|
||||
# detect_leaks=0 matches the suite convention (fixtures are not torn down).
|
||||
test('scheduler test', scheduler_test, suite: 'scheduler',
|
||||
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
||||
test('scheduler job-update test', sched_job_update_test, suite: 'scheduler',
|
||||
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
||||
test('scheduler handlers test', sched_handlers_test, suite: 'scheduler',
|
||||
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
||||
|
|
|
|||
190
src/scheduler/testsuite/test_handlers.c
Normal file
190
src/scheduler/testsuite/test_handlers.c
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
/*
|
||||
* test_handlers.c - white-box tests for the scheduler notify/dataset enqueue
|
||||
* handlers (bug-765 G-6, tasks 9.2 + 9.3). Pulls the real (static) handlers in
|
||||
* handler.c into this TU; enq_pend_update is local to handler.c, so we drive a
|
||||
* real sched->pend_updates dataq and assert the correct op was enqueued (or
|
||||
* skipped / -EINVAL) per each handler's contract.
|
||||
*/
|
||||
#define UNIT_TEST 1
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <unity/unity.h>
|
||||
|
||||
#include "dataq.h"
|
||||
#include "mylock.h"
|
||||
#include "scheduler.h"
|
||||
|
||||
/* Pull the real handlers under test into this TU. handler.c is self-contained
|
||||
* for the enqueue path: enq_pend_update and sched_job_state_change are both
|
||||
* static within it; the dispatchers call lib helpers (linked). The only
|
||||
* scheduler-internal extern handler.c references (in the config-init request
|
||||
* path, not exercised by these enqueue tests) is sched_init_config — stubbed. */
|
||||
#include "../src/handler.c"
|
||||
|
||||
int sched_init_config(xce_scheduler_t* s, xce_conf4sched_t* c)
|
||||
{
|
||||
(void)s; (void)c; return 0;
|
||||
}
|
||||
|
||||
void setUp(void) { return; }
|
||||
void tearDown(void)
|
||||
{
|
||||
printf("\n-----------------------------------------------------\n");
|
||||
return;
|
||||
}
|
||||
|
||||
static void fixture_init(xce_scheduler_t* sched)
|
||||
{
|
||||
memset(sched, 0, sizeof(*sched));
|
||||
mylock_init(&(sched->lock));
|
||||
sched->pend_updates[0] = (dataq_t*)calloc(1, sizeof(dataq_t));
|
||||
dataq_init(sched->pend_updates[0], NULL);
|
||||
sched->pend_updates[1] = (dataq_t*)calloc(1, sizeof(dataq_t));
|
||||
dataq_init(sched->pend_updates[1], NULL);
|
||||
}
|
||||
|
||||
/* Dequeue one pending request (NULL if the queue is empty) and return its op
|
||||
* via *op_out; frees the request shell (payloads are test-owned/leaked under
|
||||
* the suite's leaks-off convention). Returns 1 if an item was present. */
|
||||
static int pop_op(xce_scheduler_t* sched, xce_sched_op_t* op_out)
|
||||
{
|
||||
data_item_t* it = dataq_deq(sched->pend_updates[0], NULL);
|
||||
xce_sched_update_request_t* req;
|
||||
|
||||
if (it == NULL) {
|
||||
return 0;
|
||||
}
|
||||
req = ITPTR_TO_UPDATE_REQ(it);
|
||||
if (op_out != NULL) {
|
||||
*op_out = req->op;
|
||||
}
|
||||
free(req);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ── 9.3: notify single-item handlers (NULL -> -EINVAL, valid -> enqueue) ── */
|
||||
|
||||
static void test_handle_update_job_enqueues_job_fields(void)
|
||||
{
|
||||
xce_scheduler_t sched;
|
||||
xce_sched_op_t op = (xce_sched_op_t)-1;
|
||||
fixture_init(&sched);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(-EINVAL, sched_handle_sched_update_job(&sched, NULL));
|
||||
TEST_ASSERT_EQUAL_INT(0, pop_op(&sched, NULL)); /* NULL enqueued nothing */
|
||||
|
||||
xce_job_t* job = (xce_job_t*)calloc(1, sizeof(*job));
|
||||
job->jobid = 42;
|
||||
TEST_ASSERT_EQUAL_INT(0, sched_handle_sched_update_job(&sched, job));
|
||||
TEST_ASSERT_EQUAL_INT(1, pop_op(&sched, &op));
|
||||
/* 5.6: notify job update takes the in-place UPDATE_JOB_FIELDS path, NOT
|
||||
* the dataset bulk-create UPDATE_JOB path. */
|
||||
TEST_ASSERT_EQUAL_INT(XCE_SCHED_OP_UPDATE_JOB_FIELDS, op);
|
||||
free(job);
|
||||
}
|
||||
|
||||
static void test_handle_update_queue_enqueues_queue(void)
|
||||
{
|
||||
xce_scheduler_t sched;
|
||||
xce_sched_op_t op = (xce_sched_op_t)-1;
|
||||
fixture_init(&sched);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(-EINVAL,
|
||||
sched_handle_sched_update_queue(&sched, NULL));
|
||||
|
||||
xce_queue_t* q = (xce_queue_t*)calloc(1, sizeof(*q));
|
||||
TEST_ASSERT_EQUAL_INT(0, sched_handle_sched_update_queue(&sched, q));
|
||||
TEST_ASSERT_EQUAL_INT(1, pop_op(&sched, &op));
|
||||
TEST_ASSERT_EQUAL_INT(XCE_SCHED_OP_UPDATE_QUEUE, op);
|
||||
free(q);
|
||||
}
|
||||
|
||||
static void test_handle_job_rusage_enqueues_rusage(void)
|
||||
{
|
||||
xce_scheduler_t sched;
|
||||
xce_sched_op_t op = (xce_sched_op_t)-1;
|
||||
fixture_init(&sched);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(-EINVAL,
|
||||
sched_handle_sched_job_rusage(&sched, NULL));
|
||||
|
||||
xce_job_rusages_t* ru = (xce_job_rusages_t*)calloc(1, sizeof(*ru));
|
||||
ru->jobid = 7;
|
||||
TEST_ASSERT_EQUAL_INT(0, sched_handle_sched_job_rusage(&sched, ru));
|
||||
TEST_ASSERT_EQUAL_INT(1, pop_op(&sched, &op));
|
||||
TEST_ASSERT_EQUAL_INT(XCE_SCHED_OP_JOB_RUSAGE, op);
|
||||
free(ru);
|
||||
}
|
||||
|
||||
static void test_handle_update_resvals_skips_empty_enqueues_nonempty(void)
|
||||
{
|
||||
xce_scheduler_t sched;
|
||||
xce_sched_op_t op = (xce_sched_op_t)-1;
|
||||
xce_resval_list_t empty = { .count = 0, .vals = NULL };
|
||||
xce_resval_list_t full = { .count = 1, .vals = NULL };
|
||||
fixture_init(&sched);
|
||||
|
||||
/* NULL or count==0 -> skip (return 0, nothing enqueued) */
|
||||
TEST_ASSERT_EQUAL_INT(0, sched_handle_sched_update_resvals(&sched, NULL));
|
||||
TEST_ASSERT_EQUAL_INT(0,
|
||||
sched_handle_sched_update_resvals(&sched, &empty));
|
||||
TEST_ASSERT_EQUAL_INT(0, pop_op(&sched, NULL));
|
||||
|
||||
/* count>0 -> enqueue UPDATE_RESVALS */
|
||||
TEST_ASSERT_EQUAL_INT(0,
|
||||
sched_handle_sched_update_resvals(&sched, &full));
|
||||
TEST_ASSERT_EQUAL_INT(1, pop_op(&sched, &op));
|
||||
TEST_ASSERT_EQUAL_INT(XCE_SCHED_OP_UPDATE_RESVALS, op);
|
||||
}
|
||||
|
||||
/* ── 9.2: dataset handlers (skip on NULL/empty, enqueue on valid) ── */
|
||||
|
||||
static void test_handle_resources_skips_empty_enqueues_nonempty(void)
|
||||
{
|
||||
xce_scheduler_t sched;
|
||||
xce_sched_op_t op = (xce_sched_op_t)-1;
|
||||
xce_resource_t* one[1];
|
||||
fixture_init(&sched);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, sched_handle_resources(&sched, 0, NULL));
|
||||
TEST_ASSERT_EQUAL_INT(0, pop_op(&sched, NULL));
|
||||
|
||||
one[0] = (xce_resource_t*)calloc(1, sizeof(xce_resource_t));
|
||||
TEST_ASSERT_EQUAL_INT(0, sched_handle_resources(&sched, 1, one));
|
||||
TEST_ASSERT_EQUAL_INT(1, pop_op(&sched, &op));
|
||||
TEST_ASSERT_EQUAL_INT(XCE_SCHED_OP_UPDATE_RESOURCES, op);
|
||||
free(one[0]);
|
||||
}
|
||||
|
||||
static void test_handle_resvals_full_skips_empty_enqueues_nonempty(void)
|
||||
{
|
||||
xce_scheduler_t sched;
|
||||
xce_sched_op_t op = (xce_sched_op_t)-1;
|
||||
xce_resval_t* one[1];
|
||||
fixture_init(&sched);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, sched_handle_resvals(&sched, 0, NULL));
|
||||
TEST_ASSERT_EQUAL_INT(0, pop_op(&sched, NULL));
|
||||
|
||||
one[0] = (xce_resval_t*)calloc(1, sizeof(xce_resval_t));
|
||||
TEST_ASSERT_EQUAL_INT(0, sched_handle_resvals(&sched, 1, one));
|
||||
TEST_ASSERT_EQUAL_INT(1, pop_op(&sched, &op));
|
||||
TEST_ASSERT_EQUAL_INT(XCE_SCHED_OP_UPDATE_RESVALS_FULL, op);
|
||||
free(one[0]);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
(void)argc; (void)argv;
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(test_handle_update_job_enqueues_job_fields, 0);
|
||||
RUN_TEST(test_handle_update_queue_enqueues_queue, 0);
|
||||
RUN_TEST(test_handle_job_rusage_enqueues_rusage, 0);
|
||||
RUN_TEST(test_handle_update_resvals_skips_empty_enqueues_nonempty, 0);
|
||||
RUN_TEST(test_handle_resources_skips_empty_enqueues_nonempty, 0);
|
||||
RUN_TEST(test_handle_resvals_full_skips_empty_enqueues_nonempty, 0);
|
||||
return UNITY_END();
|
||||
}
|
||||
|
|
@ -295,6 +295,57 @@ test_rusage_null_returns_einval(void)
|
|||
TEST_ASSERT_EQUAL_INT(-EINVAL, sched_apply_job_rusage(&sched, NULL));
|
||||
}
|
||||
|
||||
/* Queue in-place update (Req 4 / task 9.5 — REAL, exercises the linked
|
||||
* schedobj.c sched_update_existing_queue, not a local stub) */
|
||||
|
||||
static void
|
||||
test_update_existing_queue_real(void)
|
||||
{
|
||||
xce_scheduler_t sched;
|
||||
fixture_init(&sched);
|
||||
|
||||
xce_sched_queue_t* q = make_test_queue("q1", 1);
|
||||
|
||||
xce_queue_t upd;
|
||||
memset(&upd, 0, sizeof(upd));
|
||||
upd.queue = makeString("q1");
|
||||
upd.status = 2;
|
||||
upd.priority = 9;
|
||||
upd.accept_interval = 5;
|
||||
upd.sched_delay = 3;
|
||||
upd.resreq = NULL; /* skip the resreq makeString branch */
|
||||
|
||||
int rc = sched_update_existing_queue(&sched, &upd);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
TEST_ASSERT_EQUAL_UINT(2, q->status);
|
||||
TEST_ASSERT_EQUAL_INT(9, q->priority);
|
||||
TEST_ASSERT_EQUAL_INT(5, q->accept_interval);
|
||||
TEST_ASSERT_EQUAL_INT(3, q->sched_delay);
|
||||
}
|
||||
|
||||
static void
|
||||
test_update_unknown_queue_returns_enoent(void)
|
||||
{
|
||||
xce_scheduler_t sched;
|
||||
fixture_init(&sched);
|
||||
|
||||
xce_queue_t upd;
|
||||
memset(&upd, 0, sizeof(upd));
|
||||
upd.queue = makeString("nope"); /* no such queue seeded */
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(-ENOENT, sched_update_existing_queue(&sched, &upd));
|
||||
}
|
||||
|
||||
static void
|
||||
test_update_queue_null_returns_einval(void)
|
||||
{
|
||||
xce_scheduler_t sched;
|
||||
fixture_init(&sched);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(-EINVAL, sched_update_existing_queue(&sched, NULL));
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
(void)argc; (void)argv;
|
||||
|
|
@ -306,5 +357,8 @@ int main(int argc, char* argv[])
|
|||
RUN_TEST(test_rusage_replace_frees_prior, 0);
|
||||
RUN_TEST(test_rusage_unknown_job_returns_enoent, 0);
|
||||
RUN_TEST(test_rusage_null_returns_einval, 0);
|
||||
RUN_TEST(test_update_existing_queue_real, 0);
|
||||
RUN_TEST(test_update_unknown_queue_returns_enoent, 0);
|
||||
RUN_TEST(test_update_queue_null_returns_einval, 0);
|
||||
return UNITY_END();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue