feat(scheduler-core): implement builtin_fcfs state-change hooks (task 2.2)
Adds on_queue_update, on_job_push, on_job_state_change, and on_job_update to builtin_fcfs, driving the picker's private ordq_t candidate sets directly (no core/sched queries), matching fcfs.c's existing ordering semantics on the new finer-grained hook interface. Requirements: 3.2
This commit is contained in:
parent
f7921b961a
commit
b7214ebe88
3 changed files with 854 additions and 1 deletions
|
|
@ -209,6 +209,527 @@ static void test_builtin_fcfs_fini_releases_populated_queues_and_jobs(void)
|
|||
TEST_ASSERT_NULL(plugin.data);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Task 2.2: state-change hooks (Req 3 AC 2's on_queue_update/on_job_push/
|
||||
* on_job_state_change/on_job_update rows). Every test below drives the
|
||||
* hooks directly against a real init()'d picker and then inspects the
|
||||
* underlying ordq_t structures (priv->queues, and each fcfs_queue_t's
|
||||
* pendJobs) by walking ->head chains -- next_job() doesn't exist yet
|
||||
* (task 2.3), so this is the only way to observe candidate-set order this
|
||||
* task can prove.
|
||||
* RED: before task 2.2, builtin_fcfs_on_queue_update/on_job_push/
|
||||
* on_job_state_change/on_job_update do not exist -- this file
|
||||
* fails to compile (implicit declaration / undefined symbols).
|
||||
* GREEN: the hooks upsert/insert/remove/reposition exactly as Req 3 AC 2
|
||||
* specifies, verified by walking the resulting ordq_t chains.
|
||||
* ---------------------------------------------------------------------- */
|
||||
|
||||
/* Walks an ordq_t of fcfs_queue_t entries and collects queue names in list
|
||||
* order, so a test can assert on priv->queues' actual ordering (descending
|
||||
* queue->priority, fcfs_queue_cmp) without depending on next_job(). */
|
||||
static size_t collect_queue_names(ordq_t* q, const char** out, size_t max)
|
||||
{
|
||||
ordq_item_t* it = q->head;
|
||||
size_t n = 0;
|
||||
|
||||
while ((it != NULL) && (n < max)) {
|
||||
fcfs_queue_t* fq = (fcfs_queue_t*)it->data;
|
||||
|
||||
out[n++] = fq->queue->name;
|
||||
it = it->next;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/* Walks an ordq_t of fcfs_job_t entries and collects jobids in list order,
|
||||
* so a test can assert on a queue's pendJobs actual ordering (priority
|
||||
* desc, queue_time asc, fcfs_job_cmp). */
|
||||
static size_t collect_jobids(ordq_t* q, uint64_t* out, size_t max)
|
||||
{
|
||||
ordq_item_t* it = q->head;
|
||||
size_t n = 0;
|
||||
|
||||
while ((it != NULL) && (n < max)) {
|
||||
fcfs_job_t* fj = (fcfs_job_t*)it->data;
|
||||
|
||||
out[n++] = fj->job->jobid;
|
||||
it = it->next;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/* Req 3 AC 2 "on_queue_update" row, unseen-name branch: allocates a new
|
||||
* fcfs_queue_t pointing at `queue`, with an empty pendJobs initialized on
|
||||
* fcfs_job_cmp/fcfs_job_killer -- ready for a later on_job_push, task 2.1's
|
||||
* own comparators reused rather than re-implemented. */
|
||||
static void test_on_queue_update_inserts_new_queue_with_empty_pendjobs(void)
|
||||
{
|
||||
xce_sched_plugin_t plugin;
|
||||
fcfs_plugin_data_t* priv;
|
||||
xce_sched_queue_t q1 = { .name = "q1", .priority = 5 };
|
||||
fcfs_queue_t* fq;
|
||||
|
||||
fixture_init(&plugin);
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_init(&plugin, NULL));
|
||||
priv = (fcfs_plugin_data_t*)plugin.data;
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_queue_update(&plugin, &q1));
|
||||
|
||||
TEST_ASSERT_NOT_NULL(priv->queues.head);
|
||||
fq = (fcfs_queue_t*)priv->queues.head->data;
|
||||
TEST_ASSERT_EQUAL_PTR(&q1, fq->queue);
|
||||
TEST_ASSERT_NULL(fq->pendJobs.head);
|
||||
TEST_ASSERT_EQUAL_PTR(fcfs_job_cmp, fq->pendJobs.cmp);
|
||||
TEST_ASSERT_EQUAL_PTR(fcfs_job_killer, fq->pendJobs.killer);
|
||||
|
||||
builtin_fcfs_fini(&plugin);
|
||||
}
|
||||
|
||||
/* Req 3 AC 2 "on_queue_update" row + Req 3 AC 4 "inter-queue by descending
|
||||
* queue->priority": three queues pushed out of priority order must come
|
||||
* back out of priv->queues sorted highest-priority-first. */
|
||||
static void test_on_queue_update_orders_queues_by_priority_desc(void)
|
||||
{
|
||||
xce_sched_plugin_t plugin;
|
||||
xce_sched_queue_t qlow = { .name = "low", .priority = 1 };
|
||||
xce_sched_queue_t qhigh = { .name = "high", .priority = 9 };
|
||||
xce_sched_queue_t qmid = { .name = "mid", .priority = 5 };
|
||||
const char* names[3];
|
||||
|
||||
fixture_init(&plugin);
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_init(&plugin, NULL));
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_queue_update(&plugin, &qlow));
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_queue_update(&plugin, &qhigh));
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_queue_update(&plugin, &qmid));
|
||||
|
||||
TEST_ASSERT_EQUAL_UINT(
|
||||
3, collect_queue_names(&(((fcfs_plugin_data_t*)plugin.data)->queues),
|
||||
names, 3));
|
||||
TEST_ASSERT_EQUAL_STRING("high", names[0]);
|
||||
TEST_ASSERT_EQUAL_STRING("mid", names[1]);
|
||||
TEST_ASSERT_EQUAL_STRING("low", names[2]);
|
||||
|
||||
builtin_fcfs_fini(&plugin);
|
||||
}
|
||||
|
||||
/* Req 3 AC 2 "on_queue_update" row, known-name branch: "If queue exists,
|
||||
* update its priority and reorder." Raising q1's priority above q2's (by
|
||||
* mutating the same xce_sched_queue_t in place, the way
|
||||
* sched_update_existing_queue does) and re-calling on_queue_update must
|
||||
* move q1 ahead of q2 -- without allocating a second fcfs_queue_t for
|
||||
* "q1" (checked by identity: the head entry's pointer is still &q1). */
|
||||
static void
|
||||
test_on_queue_update_reorders_existing_queue_on_priority_change(void)
|
||||
{
|
||||
xce_sched_plugin_t plugin;
|
||||
fcfs_plugin_data_t* priv;
|
||||
xce_sched_queue_t q1 = { .name = "q1", .priority = 1 };
|
||||
xce_sched_queue_t q2 = { .name = "q2", .priority = 5 };
|
||||
const char* names[2];
|
||||
|
||||
fixture_init(&plugin);
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_init(&plugin, NULL));
|
||||
priv = (fcfs_plugin_data_t*)plugin.data;
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_queue_update(&plugin, &q1));
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_queue_update(&plugin, &q2));
|
||||
TEST_ASSERT_EQUAL_UINT(2, collect_queue_names(&(priv->queues), names, 2));
|
||||
TEST_ASSERT_EQUAL_STRING("q2", names[0]); /* q2 (5) ahead of q1 (1) */
|
||||
|
||||
q1.priority = 9; /* in-place mutation, mirrors the live core's update */
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_queue_update(&plugin, &q1));
|
||||
|
||||
TEST_ASSERT_EQUAL_UINT(2, collect_queue_names(&(priv->queues), names, 2));
|
||||
TEST_ASSERT_EQUAL_STRING("q1", names[0]); /* now q1 (9) ahead of q2 (5) */
|
||||
TEST_ASSERT_EQUAL_STRING("q2", names[1]);
|
||||
|
||||
builtin_fcfs_fini(&plugin);
|
||||
}
|
||||
|
||||
/* Defensive-argument contract: NULL picker, NULL queue, an unnamed queue,
|
||||
* and a picker that was never init()'d must all report -EINVAL rather than
|
||||
* crash -- same convention as builtin_fcfs_init's own NULL check. */
|
||||
static void test_on_queue_update_rejects_invalid_args(void)
|
||||
{
|
||||
xce_sched_plugin_t plugin;
|
||||
xce_sched_queue_t q = { .name = "q1", .priority = 1 };
|
||||
xce_sched_queue_t noname = { .name = NULL, .priority = 1 };
|
||||
|
||||
fixture_init(&plugin); /* plugin.data == NULL, never init()'d */
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(-EINVAL, builtin_fcfs_on_queue_update(NULL, &q));
|
||||
TEST_ASSERT_EQUAL_INT(-EINVAL, builtin_fcfs_on_queue_update(&plugin, NULL));
|
||||
TEST_ASSERT_EQUAL_INT(-EINVAL,
|
||||
builtin_fcfs_on_queue_update(&plugin, &noname));
|
||||
TEST_ASSERT_EQUAL_INT(-EINVAL, builtin_fcfs_on_queue_update(&plugin, &q));
|
||||
}
|
||||
|
||||
/* Req 3 AC 2 "on_job_push" row: a pending job is wrapped and inserted into
|
||||
* its own queue's pendJobs (found via job->queue->name). */
|
||||
static void test_on_job_push_adds_pending_job_to_its_queue(void)
|
||||
{
|
||||
xce_sched_plugin_t plugin;
|
||||
fcfs_plugin_data_t* priv;
|
||||
xce_sched_queue_t q1 = { .name = "q1", .priority = 1 };
|
||||
xce_sched_job_t j1 = { .jobid = 42,
|
||||
.priority = 3,
|
||||
.queue_time = 100,
|
||||
.state = JOB_STAT_PEND,
|
||||
.queue = &q1 };
|
||||
fcfs_queue_t* fq;
|
||||
uint64_t jobids[1];
|
||||
|
||||
fixture_init(&plugin);
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_init(&plugin, NULL));
|
||||
priv = (fcfs_plugin_data_t*)plugin.data;
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_queue_update(&plugin, &q1));
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_job_push(&plugin, &j1));
|
||||
|
||||
fq = (fcfs_queue_t*)priv->queues.head->data;
|
||||
TEST_ASSERT_EQUAL_UINT(1, collect_jobids(&(fq->pendJobs), jobids, 1));
|
||||
TEST_ASSERT_EQUAL_UINT64(42, jobids[0]);
|
||||
|
||||
builtin_fcfs_fini(&plugin);
|
||||
}
|
||||
|
||||
/* Req 3 AC 2 "on_job_push" row + Req 3 AC 4 "intra-queue by priority desc,
|
||||
* queue_time asc": three pending jobs pushed out of order come back sorted
|
||||
* by priority desc, then (for the priority tie) queue_time asc. */
|
||||
static void test_on_job_push_orders_jobs_priority_desc_then_queue_time_asc(void)
|
||||
{
|
||||
xce_sched_plugin_t plugin;
|
||||
fcfs_plugin_data_t* priv;
|
||||
xce_sched_queue_t q1 = { .name = "q1", .priority = 1 };
|
||||
/* tie on priority (5): j_tie_late (queue_time 200) must sort AFTER
|
||||
* j_tie_early (queue_time 100) despite being pushed first. */
|
||||
xce_sched_job_t j_tie_late = { .jobid = 1,
|
||||
.priority = 5,
|
||||
.queue_time = 200,
|
||||
.state = JOB_STAT_PEND,
|
||||
.queue = &q1 };
|
||||
xce_sched_job_t j_low = { .jobid = 2,
|
||||
.priority = 1,
|
||||
.queue_time = 50,
|
||||
.state = JOB_STAT_PEND,
|
||||
.queue = &q1 };
|
||||
xce_sched_job_t j_tie_early = { .jobid = 3,
|
||||
.priority = 5,
|
||||
.queue_time = 100,
|
||||
.state = JOB_STAT_PEND,
|
||||
.queue = &q1 };
|
||||
fcfs_queue_t* fq;
|
||||
uint64_t jobids[3];
|
||||
|
||||
fixture_init(&plugin);
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_init(&plugin, NULL));
|
||||
priv = (fcfs_plugin_data_t*)plugin.data;
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_queue_update(&plugin, &q1));
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_job_push(&plugin, &j_tie_late));
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_job_push(&plugin, &j_low));
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_job_push(&plugin, &j_tie_early));
|
||||
|
||||
fq = (fcfs_queue_t*)priv->queues.head->data;
|
||||
TEST_ASSERT_EQUAL_UINT(3, collect_jobids(&(fq->pendJobs), jobids, 3));
|
||||
TEST_ASSERT_EQUAL_UINT64(3, jobids[0]); /* j_tie_early: prio 5, t=100 */
|
||||
TEST_ASSERT_EQUAL_UINT64(1, jobids[1]); /* j_tie_late: prio 5, t=200 */
|
||||
TEST_ASSERT_EQUAL_UINT64(2, jobids[2]); /* j_low: prio 1 */
|
||||
|
||||
builtin_fcfs_fini(&plugin);
|
||||
}
|
||||
|
||||
/* Req 3 AC 2 "on_job_push" row's JOB_STAT_PEND guard: a non-pending job
|
||||
* (state 0, no JOB_STAT_PEND bit) must not be added to any candidate set. */
|
||||
static void test_on_job_push_skips_non_pending_job(void)
|
||||
{
|
||||
xce_sched_plugin_t plugin;
|
||||
fcfs_plugin_data_t* priv;
|
||||
xce_sched_queue_t q1 = { .name = "q1", .priority = 1 };
|
||||
xce_sched_job_t j1 = { .jobid = 1,
|
||||
.priority = 3,
|
||||
.queue_time = 100,
|
||||
.state = 0, /* not pending */
|
||||
.queue = &q1 };
|
||||
fcfs_queue_t* fq;
|
||||
|
||||
fixture_init(&plugin);
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_init(&plugin, NULL));
|
||||
priv = (fcfs_plugin_data_t*)plugin.data;
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_queue_update(&plugin, &q1));
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_job_push(&plugin, &j1));
|
||||
|
||||
fq = (fcfs_queue_t*)priv->queues.head->data;
|
||||
TEST_ASSERT_NULL(fq->pendJobs.head);
|
||||
|
||||
builtin_fcfs_fini(&plugin);
|
||||
}
|
||||
|
||||
/* A pending job whose queue was never registered via on_queue_update has
|
||||
* nowhere to go -- -EINVAL, not a crash or a silent drop into the wrong
|
||||
* queue. */
|
||||
static void test_on_job_push_rejects_unknown_queue(void)
|
||||
{
|
||||
xce_sched_plugin_t plugin;
|
||||
xce_sched_queue_t unregistered = { .name = "ghost", .priority = 1 };
|
||||
xce_sched_job_t j1 = { .jobid = 1,
|
||||
.priority = 3,
|
||||
.queue_time = 100,
|
||||
.state = JOB_STAT_PEND,
|
||||
.queue = &unregistered };
|
||||
|
||||
fixture_init(&plugin);
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_init(&plugin, NULL));
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(-EINVAL, builtin_fcfs_on_job_push(&plugin, &j1));
|
||||
|
||||
builtin_fcfs_fini(&plugin);
|
||||
}
|
||||
|
||||
/* A duplicate JOBS_PUSH delivery for the same still-pending job must not
|
||||
* create a second candidate entry. */
|
||||
static void test_on_job_push_ignores_duplicate_push_of_same_job(void)
|
||||
{
|
||||
xce_sched_plugin_t plugin;
|
||||
fcfs_plugin_data_t* priv;
|
||||
xce_sched_queue_t q1 = { .name = "q1", .priority = 1 };
|
||||
xce_sched_job_t j1 = { .jobid = 7,
|
||||
.priority = 3,
|
||||
.queue_time = 100,
|
||||
.state = JOB_STAT_PEND,
|
||||
.queue = &q1 };
|
||||
fcfs_queue_t* fq;
|
||||
uint64_t jobids[2];
|
||||
|
||||
fixture_init(&plugin);
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_init(&plugin, NULL));
|
||||
priv = (fcfs_plugin_data_t*)plugin.data;
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_queue_update(&plugin, &q1));
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_job_push(&plugin, &j1));
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_job_push(&plugin, &j1));
|
||||
|
||||
fq = (fcfs_queue_t*)priv->queues.head->data;
|
||||
TEST_ASSERT_EQUAL_UINT(1, collect_jobids(&(fq->pendJobs), jobids, 2));
|
||||
|
||||
builtin_fcfs_fini(&plugin);
|
||||
}
|
||||
|
||||
/* Req 3 AC 2 "on_job_state_change" row: a job leaving pending state
|
||||
* (EXIT/FAIL/DONE) is removed from its queue's candidate set. */
|
||||
static void test_on_job_state_change_removes_job_when_leaving_pending(void)
|
||||
{
|
||||
xce_sched_plugin_t plugin;
|
||||
fcfs_plugin_data_t* priv;
|
||||
xce_sched_queue_t q1 = { .name = "q1", .priority = 1 };
|
||||
xce_sched_job_t j1 = { .jobid = 1,
|
||||
.priority = 3,
|
||||
.queue_time = 100,
|
||||
.state = JOB_STAT_PEND,
|
||||
.queue = &q1 };
|
||||
fcfs_queue_t* fq;
|
||||
|
||||
fixture_init(&plugin);
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_init(&plugin, NULL));
|
||||
priv = (fcfs_plugin_data_t*)plugin.data;
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_queue_update(&plugin, &q1));
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_job_push(&plugin, &j1));
|
||||
|
||||
j1.state = JOB_STAT_DONE;
|
||||
TEST_ASSERT_EQUAL_INT(
|
||||
0, builtin_fcfs_on_job_state_change(&plugin, &j1, JOB_STAT_DONE));
|
||||
|
||||
fq = (fcfs_queue_t*)priv->queues.head->data;
|
||||
TEST_ASSERT_NULL(fq->pendJobs.head);
|
||||
|
||||
builtin_fcfs_fini(&plugin);
|
||||
}
|
||||
|
||||
/* Req 3 AC 2 "on_job_state_change" row: a job returning to PEND is re-added
|
||||
* to its queue's candidate set, in its correctly ordered position among
|
||||
* jobs already there. */
|
||||
static void
|
||||
test_on_job_state_change_readds_job_in_order_when_returning_to_pending(void)
|
||||
{
|
||||
xce_sched_plugin_t plugin;
|
||||
fcfs_plugin_data_t* priv;
|
||||
xce_sched_queue_t q1 = { .name = "q1", .priority = 1 };
|
||||
xce_sched_job_t jlow = { .jobid = 1,
|
||||
.priority = 1,
|
||||
.queue_time = 100,
|
||||
.state = JOB_STAT_PEND,
|
||||
.queue = &q1 };
|
||||
xce_sched_job_t jresurrected = { .jobid = 2,
|
||||
.priority = 9,
|
||||
.queue_time = 50,
|
||||
.state = JOB_STAT_DONE,
|
||||
.queue = &q1 };
|
||||
fcfs_queue_t* fq;
|
||||
uint64_t jobids[2];
|
||||
|
||||
fixture_init(&plugin);
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_init(&plugin, NULL));
|
||||
priv = (fcfs_plugin_data_t*)plugin.data;
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_queue_update(&plugin, &q1));
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_job_push(&plugin, &jlow));
|
||||
/* jresurrected starts DONE -- never pushed, not a candidate yet. */
|
||||
|
||||
jresurrected.state = JOB_STAT_PEND;
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_job_state_change(
|
||||
&plugin, &jresurrected, JOB_STAT_PEND));
|
||||
|
||||
fq = (fcfs_queue_t*)priv->queues.head->data;
|
||||
TEST_ASSERT_EQUAL_UINT(2, collect_jobids(&(fq->pendJobs), jobids, 2));
|
||||
TEST_ASSERT_EQUAL_UINT64(2, jobids[0]); /* higher priority first */
|
||||
TEST_ASSERT_EQUAL_UINT64(1, jobids[1]);
|
||||
|
||||
builtin_fcfs_fini(&plugin);
|
||||
}
|
||||
|
||||
/* Idempotency: on_job_state_change(..., PEND) called twice in a row for a
|
||||
* job that is already a candidate must not create a duplicate entry. */
|
||||
static void test_on_job_state_change_is_idempotent_when_already_pending(void)
|
||||
{
|
||||
xce_sched_plugin_t plugin;
|
||||
fcfs_plugin_data_t* priv;
|
||||
xce_sched_queue_t q1 = { .name = "q1", .priority = 1 };
|
||||
xce_sched_job_t j1 = { .jobid = 1,
|
||||
.priority = 3,
|
||||
.queue_time = 100,
|
||||
.state = JOB_STAT_PEND,
|
||||
.queue = &q1 };
|
||||
fcfs_queue_t* fq;
|
||||
uint64_t jobids[2];
|
||||
|
||||
fixture_init(&plugin);
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_init(&plugin, NULL));
|
||||
priv = (fcfs_plugin_data_t*)plugin.data;
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_queue_update(&plugin, &q1));
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_job_push(&plugin, &j1));
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(
|
||||
0, builtin_fcfs_on_job_state_change(&plugin, &j1, JOB_STAT_PEND));
|
||||
|
||||
fq = (fcfs_queue_t*)priv->queues.head->data;
|
||||
TEST_ASSERT_EQUAL_UINT(1, collect_jobids(&(fq->pendJobs), jobids, 2));
|
||||
|
||||
builtin_fcfs_fini(&plugin);
|
||||
}
|
||||
|
||||
/* Req 3 AC 2 "on_job_update" row, priority-change case: raising j1's
|
||||
* priority above j2's (mutated in place, mirroring the live core) and
|
||||
* calling on_job_update must move j1 ahead of j2 within their shared
|
||||
* queue. */
|
||||
static void
|
||||
test_on_job_update_repositions_job_within_same_queue_on_priority_change(void)
|
||||
{
|
||||
xce_sched_plugin_t plugin;
|
||||
fcfs_plugin_data_t* priv;
|
||||
xce_sched_queue_t q1 = { .name = "q1", .priority = 1 };
|
||||
xce_sched_job_t j1 = { .jobid = 1,
|
||||
.priority = 1,
|
||||
.queue_time = 100,
|
||||
.state = JOB_STAT_PEND,
|
||||
.queue = &q1 };
|
||||
xce_sched_job_t j2 = { .jobid = 2,
|
||||
.priority = 5,
|
||||
.queue_time = 100,
|
||||
.state = JOB_STAT_PEND,
|
||||
.queue = &q1 };
|
||||
fcfs_queue_t* fq;
|
||||
uint64_t jobids[2];
|
||||
|
||||
fixture_init(&plugin);
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_init(&plugin, NULL));
|
||||
priv = (fcfs_plugin_data_t*)plugin.data;
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_queue_update(&plugin, &q1));
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_job_push(&plugin, &j1));
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_job_push(&plugin, &j2));
|
||||
|
||||
fq = (fcfs_queue_t*)priv->queues.head->data;
|
||||
TEST_ASSERT_EQUAL_UINT(2, collect_jobids(&(fq->pendJobs), jobids, 2));
|
||||
TEST_ASSERT_EQUAL_UINT64(2, jobids[0]); /* j2 (5) ahead of j1 (1) */
|
||||
|
||||
j1.priority = 9; /* in-place mutation, mirrors sched_update_existing_job */
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_job_update(&plugin, &j1));
|
||||
|
||||
TEST_ASSERT_EQUAL_UINT(2, collect_jobids(&(fq->pendJobs), jobids, 2));
|
||||
TEST_ASSERT_EQUAL_UINT64(1, jobids[0]); /* now j1 (9) ahead of j2 (5) */
|
||||
TEST_ASSERT_EQUAL_UINT64(2, jobids[1]);
|
||||
|
||||
builtin_fcfs_fini(&plugin);
|
||||
}
|
||||
|
||||
/* Req 3 AC 2 "on_job_update" row, queue-reassignment case: moving a job's
|
||||
* `queue` field to a different registered queue (mutated in place) and
|
||||
* calling on_job_update must remove it from the old queue's pendJobs and
|
||||
* insert it into the new queue's pendJobs. */
|
||||
static void
|
||||
test_on_job_update_moves_job_to_new_queue_on_queue_reassignment(void)
|
||||
{
|
||||
xce_sched_plugin_t plugin;
|
||||
fcfs_plugin_data_t* priv;
|
||||
xce_sched_queue_t qa = { .name = "qa", .priority = 1 };
|
||||
xce_sched_queue_t qb = { .name = "qb", .priority = 1 };
|
||||
xce_sched_job_t j1 = { .jobid = 1,
|
||||
.priority = 3,
|
||||
.queue_time = 100,
|
||||
.state = JOB_STAT_PEND,
|
||||
.queue = &qa };
|
||||
fcfs_queue_t* fqa;
|
||||
fcfs_queue_t* fqb;
|
||||
uint64_t jobids[1];
|
||||
|
||||
fixture_init(&plugin);
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_init(&plugin, NULL));
|
||||
priv = (fcfs_plugin_data_t*)plugin.data;
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_queue_update(&plugin, &qa));
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_queue_update(&plugin, &qb));
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_job_push(&plugin, &j1));
|
||||
|
||||
j1.queue = &qb; /* in-place reassignment */
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_job_update(&plugin, &j1));
|
||||
|
||||
fqa = fcfs_find_queue(priv, "qa");
|
||||
fqb = fcfs_find_queue(priv, "qb");
|
||||
TEST_ASSERT_NULL(fqa->pendJobs.head); /* gone from the old queue */
|
||||
TEST_ASSERT_EQUAL_UINT(1, collect_jobids(&(fqb->pendJobs), jobids, 1));
|
||||
TEST_ASSERT_EQUAL_UINT64(1, jobids[0]); /* present in the new queue */
|
||||
|
||||
builtin_fcfs_fini(&plugin);
|
||||
}
|
||||
|
||||
/* on_job_update on a job that is not (or no longer) pending must not add
|
||||
* it to any queue's candidate set -- pendJobs holds only pending jobs. */
|
||||
static void test_on_job_update_is_noop_for_non_pending_job(void)
|
||||
{
|
||||
xce_sched_plugin_t plugin;
|
||||
fcfs_plugin_data_t* priv;
|
||||
xce_sched_queue_t q1 = { .name = "q1", .priority = 1 };
|
||||
xce_sched_job_t j1 = { .jobid = 1,
|
||||
.priority = 3,
|
||||
.queue_time = 100,
|
||||
.state = JOB_STAT_RUN, /* not pending */
|
||||
.queue = &q1 };
|
||||
fcfs_queue_t* fq;
|
||||
|
||||
fixture_init(&plugin);
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_init(&plugin, NULL));
|
||||
priv = (fcfs_plugin_data_t*)plugin.data;
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_queue_update(&plugin, &q1));
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_job_update(&plugin, &j1));
|
||||
|
||||
fq = (fcfs_queue_t*)priv->queues.head->data;
|
||||
TEST_ASSERT_NULL(fq->pendJobs.head);
|
||||
|
||||
builtin_fcfs_fini(&plugin);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
UNITY_BEGIN();
|
||||
|
|
@ -218,5 +739,27 @@ int main(void)
|
|||
RUN_TEST(test_builtin_fcfs_fini_releases_empty_state);
|
||||
RUN_TEST(test_builtin_fcfs_fini_handles_null_and_unset_picker);
|
||||
RUN_TEST(test_builtin_fcfs_fini_releases_populated_queues_and_jobs);
|
||||
|
||||
RUN_TEST(test_on_queue_update_inserts_new_queue_with_empty_pendjobs);
|
||||
RUN_TEST(test_on_queue_update_orders_queues_by_priority_desc);
|
||||
RUN_TEST(test_on_queue_update_reorders_existing_queue_on_priority_change);
|
||||
RUN_TEST(test_on_queue_update_rejects_invalid_args);
|
||||
|
||||
RUN_TEST(test_on_job_push_adds_pending_job_to_its_queue);
|
||||
RUN_TEST(test_on_job_push_orders_jobs_priority_desc_then_queue_time_asc);
|
||||
RUN_TEST(test_on_job_push_skips_non_pending_job);
|
||||
RUN_TEST(test_on_job_push_rejects_unknown_queue);
|
||||
RUN_TEST(test_on_job_push_ignores_duplicate_push_of_same_job);
|
||||
|
||||
RUN_TEST(test_on_job_state_change_removes_job_when_leaving_pending);
|
||||
RUN_TEST(
|
||||
test_on_job_state_change_readds_job_in_order_when_returning_to_pending);
|
||||
RUN_TEST(test_on_job_state_change_is_idempotent_when_already_pending);
|
||||
|
||||
RUN_TEST(
|
||||
test_on_job_update_repositions_job_within_same_queue_on_priority_change);
|
||||
RUN_TEST(test_on_job_update_moves_job_to_new_queue_on_queue_reassignment);
|
||||
RUN_TEST(test_on_job_update_is_noop_for_non_pending_job);
|
||||
|
||||
return UNITY_END();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue