feat(scheduler-core): implement builtin_fcfs session and post-allocation hooks (task 2.3)
Adds begin_session/next_job/end_session/on_dispatch/on_pend, driving a dual-cursor walk (queue then job) over builtin_fcfs's private ordq_t state. next_job() returns candidates in (queue priority desc, job priority desc, queue_time asc) order and NULL once exhausted; on_dispatch removes permanently, on_pend is a true no-op. Review flagged an unsynchronized cross-thread access risk between next_job() (picker thread) and on_dispatch/on_pend (allocator thread) once wired live in task 2.4 — recorded in tasks.md Implementation Notes for that task to close before registering builtin_fcfs as the default. Requirements: 3.2, 3.4, 3.6
This commit is contained in:
parent
b7214ebe88
commit
fcb32a8b30
5 changed files with 658 additions and 8 deletions
|
|
@ -104,6 +104,20 @@ sched_picker_fcfs_test = executable('sched_picker_fcfs_test',
|
|||
dependencies: [scheduler_test_deps],
|
||||
install: false)
|
||||
|
||||
# White-box test for builtin_fcfs's session hooks (begin_session/next_job/
|
||||
# end_session) and post-allocation hooks (on_dispatch/on_pend) (scheduler-core
|
||||
# spec, task 2.3). Split out of test_picker_fcfs.c rather than appended to it
|
||||
# -- that file was already ~765 lines after tasks 2.1/2.2, against this
|
||||
# project's 800-line ceiling. Also #includes ../src/plugins/builtin_fcfs.c
|
||||
# directly (same white-box pattern as sched_picker_fcfs_test), and supplies
|
||||
# its own sched_queue_is_ready()/sched_job_is_ready() test doubles since this
|
||||
# translation unit does not link scheduler.c (see the file header comment).
|
||||
sched_picker_fcfs_session_test = executable('sched_picker_fcfs_session_test',
|
||||
['test_picker_fcfs_session.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',
|
||||
|
|
@ -124,3 +138,5 @@ test('scheduler picker config fields test', sched_picker_config_fields_test, sui
|
|||
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
||||
test('scheduler picker fcfs test', sched_picker_fcfs_test, suite: 'scheduler',
|
||||
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
||||
test('scheduler picker fcfs session test', sched_picker_fcfs_session_test, suite: 'scheduler',
|
||||
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
||||
|
|
|
|||
|
|
@ -32,6 +32,27 @@
|
|||
#include "plugin.h"
|
||||
#include "sched/types.h"
|
||||
|
||||
/* builtin_fcfs.c's next_job() (task 2.3) calls sched_queue_is_ready()/
|
||||
* sched_job_is_ready() (declared in scheduler.h, defined in scheduler.c),
|
||||
* which this translation unit does not link -- same as every other
|
||||
* white-box picker test in this directory that #includes builtin_fcfs.c
|
||||
* directly. This file's own tests (tasks 2.1/2.2) never call next_job(), so
|
||||
* a simple always-ready stub (mirroring scheduler.c's own current stub,
|
||||
* scheduler.c:1267/1270) is enough to satisfy the linker;
|
||||
* test_picker_fcfs_session.c is where the real skip-when-not-ready behavior
|
||||
* is exercised, via controllable test doubles. */
|
||||
int sched_queue_is_ready(xce_sched_queue_t* q)
|
||||
{
|
||||
(void)q;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int sched_job_is_ready(xce_sched_job_t* j)
|
||||
{
|
||||
(void)j;
|
||||
return 1;
|
||||
}
|
||||
|
||||
#include "../src/plugins/builtin_fcfs.c"
|
||||
|
||||
void setUp(void) { return; }
|
||||
|
|
|
|||
396
src/scheduler/testsuite/test_picker_fcfs_session.c
Normal file
396
src/scheduler/testsuite/test_picker_fcfs_session.c
Normal file
|
|
@ -0,0 +1,396 @@
|
|||
/*
|
||||
* test_picker_fcfs_session.c - white-box tests for builtin_fcfs's session
|
||||
* hooks (begin_session/next_job/end_session) and post-allocation hooks
|
||||
* (on_dispatch/on_pend) -- scheduler-core spec, Req 3 AC 2's corresponding
|
||||
* hook-table rows, Req 3 AC 4/AC 6.
|
||||
*
|
||||
* Split out of test_picker_fcfs.c (task 2.3, tasks.md): that file already
|
||||
* sits at ~765 lines after tasks 2.1/2.2 (lifecycle + state-change hooks),
|
||||
* against this project's 800-line ceiling (typical 200-400) -- adding this
|
||||
* task's session/post-allocation coverage on top would push meaningfully
|
||||
* past 800. This file is therefore a separate, cohesive translation unit
|
||||
* covering exactly task 2.3's hooks, registered as its own meson test
|
||||
* executable (testsuite/meson.build) alongside (not instead of)
|
||||
* test_picker_fcfs.c.
|
||||
*
|
||||
* RED: before task 2.3, plugins/builtin_fcfs.c did not define
|
||||
* builtin_fcfs_begin_session/builtin_fcfs_next_job/
|
||||
* builtin_fcfs_end_session/builtin_fcfs_on_dispatch/
|
||||
* builtin_fcfs_on_pend -- this file failed to compile (implicit
|
||||
* declaration of every symbol under test).
|
||||
* GREEN: the hooks implement the walking/cursor/no-op contract Req 3 AC 2
|
||||
* specifies, verified here by driving them directly (no live
|
||||
* core) and asserting on next_job()'s actual return sequence.
|
||||
*
|
||||
* #include's the real plugins/builtin_fcfs.c (white-box, same convention as
|
||||
* test_picker_fcfs.c) so this test exercises the real cursor logic and the
|
||||
* real private struct layout, not a re-implementation of either.
|
||||
*
|
||||
* builtin_fcfs.c's next_job() calls sched_queue_is_ready()/
|
||||
* sched_job_is_ready() (declared in scheduler.h, defined in scheduler.c --
|
||||
* design.md: "called as-is by both today's fcfs_elect_job and the new
|
||||
* builtin_fcfs"). This translation unit does not link scheduler.c (same as
|
||||
* test_picker_fcfs.c, which never needed these symbols since tasks 2.1/2.2
|
||||
* never call them), so this file supplies its own test-double definitions
|
||||
* below -- which doubles as the only way to unit-test the "skip a not-ready
|
||||
* queue/job" branch, since scheduler.c's real definitions are permanently
|
||||
* `return 1;` stubs (scheduler.c:1267/1270, FIXME) that could never
|
||||
* exercise the skip path.
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <unity/unity.h>
|
||||
|
||||
#include "plugin.h"
|
||||
#include "sched/types.h"
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Test doubles for sched_queue_is_ready()/sched_job_is_ready() -- see file
|
||||
* header. Each test that needs to force a skip points the relevant global
|
||||
* at the queue/job under test before calling next_job(), and resets it
|
||||
* (via fixture_init, below) before the next test runs. */
|
||||
static xce_sched_queue_t* g_not_ready_queue = NULL;
|
||||
static xce_sched_job_t* g_not_ready_job = NULL;
|
||||
|
||||
int sched_queue_is_ready(xce_sched_queue_t* q)
|
||||
{
|
||||
return (q != g_not_ready_queue);
|
||||
}
|
||||
|
||||
int sched_job_is_ready(xce_sched_job_t* j) { return (j != g_not_ready_job); }
|
||||
|
||||
#include "../src/plugins/builtin_fcfs.c"
|
||||
|
||||
void setUp(void) { return; }
|
||||
void tearDown(void) { return; }
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Fixture -- same convention as test_picker_fcfs.c's fixture_init: "picker"
|
||||
* is passed to every hook as this struct's address. Also resets the
|
||||
* sched_queue_is_ready()/sched_job_is_ready() test doubles to "everything
|
||||
* ready", so a skip forced by one test never leaks into the next. Nothing
|
||||
* is torn down between tests (suite convention, see meson.build:
|
||||
* detect_leaks=0). */
|
||||
static void fixture_init(xce_sched_plugin_t* plugin)
|
||||
{
|
||||
memset(plugin, 0, sizeof(*plugin));
|
||||
g_not_ready_queue = NULL;
|
||||
g_not_ready_job = NULL;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Task 2.3 Observable: "a full begin_session -> repeated next_job ->
|
||||
* end_session cycle against a fixed candidate set returns jobs in (queue
|
||||
* priority desc, job priority desc, queue_time asc) order and returns NULL
|
||||
* exactly once all candidates are exhausted."
|
||||
*
|
||||
* Fixture: three queues (mid=5, high=9, low=1, pushed out of priority
|
||||
* order) each with two-or-three jobs, including a same-queue priority tie
|
||||
* broken by queue_time asc. Expected next_job() sequence, fully spelled
|
||||
* out against the documented ordering rule:
|
||||
* high queue (prio 9): hA (prio 9), hB (prio 3)
|
||||
* mid queue (prio 5): mTieEarly (prio 4, t=100), mTieLate (prio 4,
|
||||
* t=200), mLow (prio 1)
|
||||
* low queue (prio 1): lOnly (prio 1)
|
||||
*/
|
||||
static void test_full_session_cycle_returns_documented_order_then_null(void)
|
||||
{
|
||||
xce_sched_plugin_t plugin;
|
||||
xce_sched_queue_t qmid = { .name = "mid", .priority = 5 };
|
||||
xce_sched_queue_t qhigh = { .name = "high", .priority = 9 };
|
||||
xce_sched_queue_t qlow = { .name = "low", .priority = 1 };
|
||||
|
||||
xce_sched_job_t hA = { .jobid = 101,
|
||||
.priority = 9,
|
||||
.queue_time = 10,
|
||||
.state = JOB_STAT_PEND,
|
||||
.queue = &qhigh };
|
||||
xce_sched_job_t hB = { .jobid = 102,
|
||||
.priority = 3,
|
||||
.queue_time = 20,
|
||||
.state = JOB_STAT_PEND,
|
||||
.queue = &qhigh };
|
||||
|
||||
xce_sched_job_t mTieLate = { .jobid = 201,
|
||||
.priority = 4,
|
||||
.queue_time = 200,
|
||||
.state = JOB_STAT_PEND,
|
||||
.queue = &qmid };
|
||||
xce_sched_job_t mLow = { .jobid = 202,
|
||||
.priority = 1,
|
||||
.queue_time = 5,
|
||||
.state = JOB_STAT_PEND,
|
||||
.queue = &qmid };
|
||||
xce_sched_job_t mTieEarly = { .jobid = 203,
|
||||
.priority = 4,
|
||||
.queue_time = 100,
|
||||
.state = JOB_STAT_PEND,
|
||||
.queue = &qmid };
|
||||
|
||||
xce_sched_job_t lOnly = { .jobid = 301,
|
||||
.priority = 1,
|
||||
.queue_time = 1,
|
||||
.state = JOB_STAT_PEND,
|
||||
.queue = &qlow };
|
||||
|
||||
uint64_t expected[6] = { 101, 102, 203, 201, 202, 301 };
|
||||
size_t i;
|
||||
xce_sched_job_t* got;
|
||||
|
||||
fixture_init(&plugin);
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_init(&plugin, NULL));
|
||||
|
||||
/* pushed out of priority order on purpose */
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_queue_update(&plugin, &qmid));
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_queue_update(&plugin, &qhigh));
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_queue_update(&plugin, &qlow));
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_job_push(&plugin, &hA));
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_job_push(&plugin, &hB));
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_job_push(&plugin, &mTieLate));
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_job_push(&plugin, &mLow));
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_job_push(&plugin, &mTieEarly));
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_job_push(&plugin, &lOnly));
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_begin_session(&plugin));
|
||||
|
||||
for (i = 0; i < 6; i++) {
|
||||
got = builtin_fcfs_next_job(&plugin);
|
||||
TEST_ASSERT_NOT_NULL(got);
|
||||
TEST_ASSERT_EQUAL_UINT64(expected[i], got->jobid);
|
||||
}
|
||||
|
||||
/* NULL-on-exhaustion, and stays NULL on a repeated call (task
|
||||
* Observable: "returns NULL exactly once all candidates are
|
||||
* exhausted"). */
|
||||
TEST_ASSERT_NULL(builtin_fcfs_next_job(&plugin));
|
||||
TEST_ASSERT_NULL(builtin_fcfs_next_job(&plugin));
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_end_session(&plugin));
|
||||
|
||||
builtin_fcfs_fini(&plugin);
|
||||
}
|
||||
|
||||
/* begin_session() with no queues registered at all: next_job() must return
|
||||
* NULL immediately, not crash walking a NULL priv->queues.head. */
|
||||
static void test_next_job_returns_null_when_no_queues_registered(void)
|
||||
{
|
||||
xce_sched_plugin_t plugin;
|
||||
|
||||
fixture_init(&plugin);
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_init(&plugin, NULL));
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_begin_session(&plugin));
|
||||
TEST_ASSERT_NULL(builtin_fcfs_next_job(&plugin));
|
||||
|
||||
builtin_fcfs_fini(&plugin);
|
||||
}
|
||||
|
||||
/* A registered queue with an empty pendJobs must be walked past (not
|
||||
* returned as a job, not crashed on) straight to NULL. */
|
||||
static void test_next_job_returns_null_when_queue_has_no_jobs(void)
|
||||
{
|
||||
xce_sched_plugin_t plugin;
|
||||
xce_sched_queue_t q1 = { .name = "q1", .priority = 1 };
|
||||
|
||||
fixture_init(&plugin);
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_init(&plugin, NULL));
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_queue_update(&plugin, &q1));
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_begin_session(&plugin));
|
||||
TEST_ASSERT_NULL(builtin_fcfs_next_job(&plugin));
|
||||
|
||||
builtin_fcfs_fini(&plugin);
|
||||
}
|
||||
|
||||
/* Req 3 AC 2 "next_job" row: "skip if sched_queue_is_ready() returns
|
||||
* false". Two queues, one forced not-ready via the test double -- its job
|
||||
* must never be returned; the other queue's job still is. */
|
||||
static void test_next_job_skips_not_ready_queue(void)
|
||||
{
|
||||
xce_sched_plugin_t plugin;
|
||||
xce_sched_queue_t qready = { .name = "ready", .priority = 1 };
|
||||
xce_sched_queue_t qnotready = { .name = "notready", .priority = 9 };
|
||||
xce_sched_job_t jready = { .jobid = 1,
|
||||
.priority = 1,
|
||||
.queue_time = 1,
|
||||
.state = JOB_STAT_PEND,
|
||||
.queue = &qready };
|
||||
xce_sched_job_t jnotready = { .jobid = 2,
|
||||
.priority = 1,
|
||||
.queue_time = 1,
|
||||
.state = JOB_STAT_PEND,
|
||||
.queue = &qnotready };
|
||||
|
||||
fixture_init(&plugin);
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_init(&plugin, NULL));
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_queue_update(&plugin, &qready));
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_queue_update(&plugin, &qnotready));
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_job_push(&plugin, &jready));
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_job_push(&plugin, &jnotready));
|
||||
|
||||
g_not_ready_queue = &qnotready; /* higher priority, would sort first */
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_begin_session(&plugin));
|
||||
|
||||
TEST_ASSERT_EQUAL_UINT64(1, builtin_fcfs_next_job(&plugin)->jobid);
|
||||
TEST_ASSERT_NULL(builtin_fcfs_next_job(&plugin));
|
||||
|
||||
builtin_fcfs_fini(&plugin);
|
||||
}
|
||||
|
||||
/* Req 3 AC 2 "next_job" row: "Return first job where sched_job_is_ready()
|
||||
* returns true" -- a single not-ready job within an otherwise-ready queue
|
||||
* must be skipped, not stop iteration of the rest of that queue's jobs. */
|
||||
static void test_next_job_skips_not_ready_job(void)
|
||||
{
|
||||
xce_sched_plugin_t plugin;
|
||||
xce_sched_queue_t q1 = { .name = "q1", .priority = 1 };
|
||||
/* jskip outranks jok on priority, so it would be returned FIRST if it
|
||||
* weren't forced not-ready. */
|
||||
xce_sched_job_t jskip = { .jobid = 1,
|
||||
.priority = 9,
|
||||
.queue_time = 1,
|
||||
.state = JOB_STAT_PEND,
|
||||
.queue = &q1 };
|
||||
xce_sched_job_t jok = { .jobid = 2,
|
||||
.priority = 1,
|
||||
.queue_time = 1,
|
||||
.state = JOB_STAT_PEND,
|
||||
.queue = &q1 };
|
||||
|
||||
fixture_init(&plugin);
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_init(&plugin, NULL));
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_queue_update(&plugin, &q1));
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_job_push(&plugin, &jskip));
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_job_push(&plugin, &jok));
|
||||
|
||||
g_not_ready_job = &jskip;
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_begin_session(&plugin));
|
||||
|
||||
TEST_ASSERT_EQUAL_UINT64(2, builtin_fcfs_next_job(&plugin)->jobid);
|
||||
TEST_ASSERT_NULL(builtin_fcfs_next_job(&plugin));
|
||||
|
||||
builtin_fcfs_fini(&plugin);
|
||||
}
|
||||
|
||||
/* Defensive-argument contract, matching every other hook in this file: NULL
|
||||
* or never-init()'d pickers report -EINVAL/NULL rather than crash. */
|
||||
static void test_session_hooks_reject_invalid_picker(void)
|
||||
{
|
||||
xce_sched_plugin_t plugin;
|
||||
|
||||
fixture_init(&plugin); /* plugin.data == NULL, never init()'d */
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(-EINVAL, builtin_fcfs_begin_session(NULL));
|
||||
TEST_ASSERT_EQUAL_INT(-EINVAL, builtin_fcfs_begin_session(&plugin));
|
||||
TEST_ASSERT_NULL(builtin_fcfs_next_job(NULL));
|
||||
TEST_ASSERT_NULL(builtin_fcfs_next_job(&plugin));
|
||||
TEST_ASSERT_EQUAL_INT(-EINVAL, builtin_fcfs_end_session(NULL));
|
||||
TEST_ASSERT_EQUAL_INT(-EINVAL, builtin_fcfs_end_session(&plugin));
|
||||
TEST_ASSERT_EQUAL_INT(-EINVAL, builtin_fcfs_on_dispatch(NULL, 1));
|
||||
TEST_ASSERT_EQUAL_INT(-EINVAL, builtin_fcfs_on_dispatch(&plugin, 1));
|
||||
TEST_ASSERT_EQUAL_INT(-EINVAL, builtin_fcfs_on_pend(NULL, 1));
|
||||
TEST_ASSERT_EQUAL_INT(-EINVAL, builtin_fcfs_on_pend(&plugin, 1));
|
||||
}
|
||||
|
||||
/* Req 3 AC 2 "on_dispatch" row: a dispatched job is permanently gone -- a
|
||||
* later session (begin_session -> next_job) never returns it again, while
|
||||
* the queue's remaining candidate still comes back. */
|
||||
static void test_on_dispatch_removes_job_permanently(void)
|
||||
{
|
||||
xce_sched_plugin_t plugin;
|
||||
xce_sched_queue_t q1 = { .name = "q1", .priority = 1 };
|
||||
xce_sched_job_t j1 = { .jobid = 1,
|
||||
.priority = 5,
|
||||
.queue_time = 1,
|
||||
.state = JOB_STAT_PEND,
|
||||
.queue = &q1 };
|
||||
xce_sched_job_t j2 = { .jobid = 2,
|
||||
.priority = 1,
|
||||
.queue_time = 1,
|
||||
.state = JOB_STAT_PEND,
|
||||
.queue = &q1 };
|
||||
|
||||
fixture_init(&plugin);
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_init(&plugin, NULL));
|
||||
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));
|
||||
|
||||
/* session 1: j1 elected (higher priority), allocator dispatches it */
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_begin_session(&plugin));
|
||||
TEST_ASSERT_EQUAL_UINT64(1, builtin_fcfs_next_job(&plugin)->jobid);
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_dispatch(&plugin, 1));
|
||||
TEST_ASSERT_EQUAL_UINT64(2, builtin_fcfs_next_job(&plugin)->jobid);
|
||||
TEST_ASSERT_NULL(builtin_fcfs_next_job(&plugin));
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_end_session(&plugin));
|
||||
|
||||
/* session 2: j1 must not reappear; j2 (never dispatched) still does */
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_begin_session(&plugin));
|
||||
TEST_ASSERT_EQUAL_UINT64(2, builtin_fcfs_next_job(&plugin)->jobid);
|
||||
TEST_ASSERT_NULL(builtin_fcfs_next_job(&plugin));
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_end_session(&plugin));
|
||||
|
||||
builtin_fcfs_fini(&plugin);
|
||||
}
|
||||
|
||||
/* Req 3 AC 2 "on_pend" row: a true no-op -- the job reappears in the SAME
|
||||
* position on the very next session (not moved, not dropped, not
|
||||
* duplicated). */
|
||||
static void test_on_pend_is_true_noop_job_reappears_same_position(void)
|
||||
{
|
||||
xce_sched_plugin_t plugin;
|
||||
xce_sched_queue_t q1 = { .name = "q1", .priority = 1 };
|
||||
xce_sched_job_t j1 = { .jobid = 1,
|
||||
.priority = 5,
|
||||
.queue_time = 1,
|
||||
.state = JOB_STAT_PEND,
|
||||
.queue = &q1 };
|
||||
xce_sched_job_t j2 = { .jobid = 2,
|
||||
.priority = 1,
|
||||
.queue_time = 1,
|
||||
.state = JOB_STAT_PEND,
|
||||
.queue = &q1 };
|
||||
|
||||
fixture_init(&plugin);
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_init(&plugin, NULL));
|
||||
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));
|
||||
|
||||
/* session 1: j1 elected first, allocator can't place it, pends it */
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_begin_session(&plugin));
|
||||
TEST_ASSERT_EQUAL_UINT64(1, builtin_fcfs_next_job(&plugin)->jobid);
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_on_pend(&plugin, 1));
|
||||
TEST_ASSERT_EQUAL_UINT64(2, builtin_fcfs_next_job(&plugin)->jobid);
|
||||
TEST_ASSERT_NULL(builtin_fcfs_next_job(&plugin));
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_end_session(&plugin));
|
||||
|
||||
/* session 2: j1 still comes back first, in the same position */
|
||||
TEST_ASSERT_EQUAL_INT(0, builtin_fcfs_begin_session(&plugin));
|
||||
TEST_ASSERT_EQUAL_UINT64(1, builtin_fcfs_next_job(&plugin)->jobid);
|
||||
TEST_ASSERT_EQUAL_UINT64(2, builtin_fcfs_next_job(&plugin)->jobid);
|
||||
TEST_ASSERT_NULL(builtin_fcfs_next_job(&plugin));
|
||||
|
||||
builtin_fcfs_fini(&plugin);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(test_full_session_cycle_returns_documented_order_then_null);
|
||||
RUN_TEST(test_next_job_returns_null_when_no_queues_registered);
|
||||
RUN_TEST(test_next_job_returns_null_when_queue_has_no_jobs);
|
||||
RUN_TEST(test_next_job_skips_not_ready_queue);
|
||||
RUN_TEST(test_next_job_skips_not_ready_job);
|
||||
RUN_TEST(test_session_hooks_reject_invalid_picker);
|
||||
RUN_TEST(test_on_dispatch_removes_job_permanently);
|
||||
RUN_TEST(test_on_pend_is_true_noop_job_reappears_same_position);
|
||||
|
||||
return UNITY_END();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue