feat(scheduler-core): wire builtin_fcfs as registered default picker (task 2.4)
Registers builtin_fcfs at scheduler startup and makes it the default job_pick_plugin, replacing the hardcoded "xce_sched_fcfs" literal in sched_job_picker() with a fail-loud lookup through sched_resolve_job_picker(). The picker thread now branches legacy job_elect_fn plugins (delegated unchanged, preserving their self-contained wbuf-driven loop) from native xce_job_picker_iface_t pickers (driven by a new per-session loop), and the allocator now calls on_dispatch/on_pend for every picker as Req 1 AC 4 requires. Closes task 2.3's flagged picker/allocator concurrency race with a mutex on fcfs_plugin_data_t, and adds cancel points to next_job().
This commit is contained in:
parent
fcb32a8b30
commit
7ee9a16f53
11 changed files with 1036 additions and 54 deletions
|
|
@ -118,6 +118,22 @@ sched_picker_fcfs_session_test = executable('sched_picker_fcfs_session_test',
|
|||
dependencies: [scheduler_test_deps],
|
||||
install: false)
|
||||
|
||||
# White-box test for wiring builtin_fcfs in as the registered default picker
|
||||
# and removing the hardcoded "xce_sched_fcfs" picker string (scheduler-core
|
||||
# spec, task 2.4). It #includes ../src/plugin.c, ../src/schedobj.c, and
|
||||
# ../src/plugins/builtin_fcfs.c directly (same white-box pattern as the
|
||||
# other picker tests) so it exercises the real plugin_register(),
|
||||
# register_builtin_fcfs(), sched_set_default_job_pick_plugin(), and
|
||||
# sched_resolve_job_picker() together -- not a re-implementation of any of
|
||||
# them. See the file's own header comment for why sched_job_picker() itself
|
||||
# (scheduler.c) is covered at the resolved-plugin level rather than by
|
||||
# whole-file-including scheduler.c.
|
||||
sched_picker_wiring_test = executable('sched_picker_wiring_test',
|
||||
['test_picker_wiring.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',
|
||||
|
|
@ -140,3 +156,5 @@ 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'])
|
||||
test('scheduler picker wiring test', sched_picker_wiring_test, suite: 'scheduler',
|
||||
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
||||
|
|
|
|||
|
|
@ -53,6 +53,21 @@ int sched_job_is_ready(xce_sched_job_t* j)
|
|||
return 1;
|
||||
}
|
||||
|
||||
/* Task 2.4 added register_builtin_fcfs() (bottom of builtin_fcfs.c), which
|
||||
* calls plugin_register() (plugin.c, not linked into this white-box TU --
|
||||
* this file's own tests never registered anything before task 2.4 either).
|
||||
* Stub matching plugin.c's real signature/calling convention closely
|
||||
* enough to satisfy the linker; this file's tests (tasks 2.1/2.2) don't
|
||||
* exercise registration, so no behavior beyond "don't crash" is needed --
|
||||
* test_picker_wiring.c (task 2.4) is where register_builtin_fcfs() is
|
||||
* exercised against the REAL plugin_register(). */
|
||||
int plugin_register(void* arg1, void* arg2)
|
||||
{
|
||||
(void)arg1;
|
||||
(void)arg2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include "../src/plugins/builtin_fcfs.c"
|
||||
|
||||
void setUp(void) { return; }
|
||||
|
|
|
|||
|
|
@ -62,6 +62,19 @@ int sched_queue_is_ready(xce_sched_queue_t* q)
|
|||
|
||||
int sched_job_is_ready(xce_sched_job_t* j) { return (j != g_not_ready_job); }
|
||||
|
||||
/* Task 2.4 added register_builtin_fcfs() (bottom of builtin_fcfs.c), which
|
||||
* calls plugin_register() (plugin.c, not linked into this white-box TU).
|
||||
* Stub to satisfy the linker -- this file's tests (task 2.3's session/
|
||||
* post-allocation hooks) don't exercise registration; see
|
||||
* test_picker_wiring.c (task 2.4) for coverage against the REAL
|
||||
* plugin_register(). */
|
||||
int plugin_register(void* arg1, void* arg2)
|
||||
{
|
||||
(void)arg1;
|
||||
(void)arg2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include "../src/plugins/builtin_fcfs.c"
|
||||
|
||||
void setUp(void) { return; }
|
||||
|
|
|
|||
491
src/scheduler/testsuite/test_picker_wiring.c
Normal file
491
src/scheduler/testsuite/test_picker_wiring.c
Normal file
|
|
@ -0,0 +1,491 @@
|
|||
/*
|
||||
* test_picker_wiring.c - white-box tests for wiring builtin_fcfs in as the
|
||||
* registered default picker and removing the hardcoded picker string
|
||||
* (scheduler-core spec, task 2.4, Req 1 AC 6, Req 2 AC 5, Req 3 AC 1).
|
||||
*
|
||||
* RED: before task 2.4, register_builtin_fcfs() did not exist (declared in
|
||||
* plugin.h but no body in builtin_fcfs.c) -- this file failed to link.
|
||||
* sched_resolve_job_picker() (schedobj.c, task 2.4) existed but nothing
|
||||
* had ever registered "builtin_fcfs" for it to find, so the "default
|
||||
* resolves to builtin_fcfs" half of this file's coverage could not pass;
|
||||
* sched_job_picker() (scheduler.c) still hardcoded "xce_sched_fcfs".
|
||||
* GREEN: register_builtin_fcfs() registers builtin_fcfs's native
|
||||
* xce_job_picker_iface_t under "builtin_fcfs" via the real plugin_register()
|
||||
* (job_elect_fn stays NULL -- no legacy-wrapper substitution);
|
||||
* sched_set_default_job_pick_plugin() + sched_resolve_job_picker() together
|
||||
* resolve an unset job_pick_plugin to the registered builtin_fcfs plugin; an
|
||||
* unregistered configured name fails resolution with -ENOENT (distinct from
|
||||
* -EINVAL for "registered but not a valid picker" and "unset name") rather
|
||||
* than silently falling back to anything.
|
||||
*
|
||||
* Scope note (this file does NOT drive sched_job_picker() itself): that
|
||||
* function is `static` in scheduler.c, which has a much larger external
|
||||
* dependency surface (xcecli_ctx_*, sel_register_alloc_thread, resource.c's
|
||||
* allocation path, decision.c's decision constructors, ...) than any other
|
||||
* white-box test in this directory pulls in -- no existing test #includes
|
||||
* scheduler.c directly (test.c only pulls in resource.c), and doing so here
|
||||
* would mean stubbing on the order of twenty external symbols this file's
|
||||
* tests would never actually exercise, just to satisfy the linker. Per the
|
||||
* task's own guidance to prefer a tractable direct test over spinning real
|
||||
* threads, this file instead directly tests the exact condition
|
||||
* sched_job_picker() branches on
|
||||
* (`job_picker->job_elect_fn != NULL` selects the legacy delegation path,
|
||||
* `== NULL` selects the generic picker_iface loop): resolving a
|
||||
* legacy-registered plugin (job_elect_fn set, no picker_iface of its own)
|
||||
* comes back with job_elect_fn non-NULL and picker_iface pointing at
|
||||
* plugin.c's legacy_picker_wrapper_iface (visible in this TU -- see below);
|
||||
* resolving builtin_fcfs comes back with job_elect_fn NULL and a fully
|
||||
* populated native picker_iface. That is the entire input the branch reads.
|
||||
*
|
||||
* #include's the real plugin.c, schedobj.c, and plugins/builtin_fcfs.c
|
||||
* (white-box, same convention as test_plugin_picker_registration.c /
|
||||
* test_picker_config_fields.c / test_picker_fcfs.c) so this test exercises
|
||||
* the real plugin_register(), the real sched_set_default_job_pick_plugin()/
|
||||
* sched_resolve_job_picker(), and the real register_builtin_fcfs() -- not a
|
||||
* re-implementation of any of them. No symbol name collisions exist between
|
||||
* the three files (verified: distinct static helper names throughout).
|
||||
*/
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <tlog.h>
|
||||
#include <unity/unity.h>
|
||||
|
||||
#include "kvmgr.h"
|
||||
#include "scheduler.h"
|
||||
#include "xstring.h"
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Stubs for externs referenced by plugin.c/schedobj.c/builtin_fcfs.c that
|
||||
* are defined in other .c files not linked into this whole-file-#include
|
||||
* TU. None of this file's tests exercise these paths -- present only to
|
||||
* satisfy the linker, same stub set (minus plugin_job_update/
|
||||
* plugin_queue_update/plugin_job_rusage_update/plugin_prepare_allocation,
|
||||
* which the REAL plugin.c now provides in this TU) as
|
||||
* test_picker_config_fields.c uses for its own schedobj.c-only include.
|
||||
* ---------------------------------------------------------------------- */
|
||||
xce_sched_user_t* sched_get_user(xce_scheduler_t* s, char* n)
|
||||
{
|
||||
(void)s;
|
||||
(void)n;
|
||||
return NULL;
|
||||
}
|
||||
xce_sched_project_t* sched_get_project(xce_scheduler_t* s, char* n)
|
||||
{
|
||||
(void)s;
|
||||
(void)n;
|
||||
return NULL;
|
||||
}
|
||||
xce_sched_queue_t* sched_get_queue(xce_scheduler_t* s, char* n)
|
||||
{
|
||||
(void)s;
|
||||
(void)n;
|
||||
return NULL;
|
||||
}
|
||||
xce_sched_job_t* sched_get_job(xce_scheduler_t* s, uint64_t jobid)
|
||||
{
|
||||
(void)s;
|
||||
(void)jobid;
|
||||
return NULL;
|
||||
}
|
||||
xce_job_account_t* make_job_account(xce_job_alloc_t* a)
|
||||
{
|
||||
(void)a;
|
||||
return NULL;
|
||||
}
|
||||
void free_job_account(xce_job_account_t* a) { (void)a; }
|
||||
int sched_add_user(xce_scheduler_t* s, xce_sched_user_t* u)
|
||||
{
|
||||
(void)s;
|
||||
(void)u;
|
||||
return 0;
|
||||
}
|
||||
int sched_add_project(xce_scheduler_t* s, xce_sched_project_t* p)
|
||||
{
|
||||
(void)s;
|
||||
(void)p;
|
||||
return 0;
|
||||
}
|
||||
void sched_worker_release_host_ctx(xce_sched_worker_t* w) { (void)w; }
|
||||
|
||||
/* plugin.c's plugin_prepare_allocation() (a category unrelated to picker
|
||||
* resolution) calls this -- stub matching test_plugin_picker_registration.c's
|
||||
* own. */
|
||||
int sched_fill_limit_requests(xce_scheduler_t* sched, xvec_idval_t* req,
|
||||
xlist_t* limits)
|
||||
{
|
||||
(void)sched;
|
||||
(void)req;
|
||||
(void)limits;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* builtin_fcfs.c's next_job() (compiled into this TU even though this
|
||||
* file's tests never call it) calls these -- always-ready stub, same
|
||||
* convention as test_picker_fcfs.c. */
|
||||
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;
|
||||
}
|
||||
|
||||
/* Pull in the real implementations under test. */
|
||||
#include "../src/plugin.c"
|
||||
#include "../src/plugins/builtin_fcfs.c"
|
||||
#include "../src/schedobj.c"
|
||||
|
||||
void setUp(void) { return; }
|
||||
void tearDown(void) { return; }
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Fixture: a real sched->plugins kvmgr (same setup scheduler.c's
|
||||
* sched_plugin_init() uses) plus the xstring/CSE intern tables
|
||||
* sched_set_default_job_pick_plugin()'s makeString() call needs. Nothing is
|
||||
* torn down between tests (suite convention, ASAN_OPTIONS=detect_leaks=0). */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
static void fixture_init(xce_scheduler_t* sched)
|
||||
{
|
||||
kvmgr_params_t params = { .key_type = KVMGR_KEY_STRING,
|
||||
.nslots = 16,
|
||||
.delta = 8,
|
||||
.datakiller = plugin_unregister };
|
||||
|
||||
memset(sched, 0, sizeof(*sched));
|
||||
resreq_init(1);
|
||||
xstring_table_init(1024 * 16);
|
||||
sched->plugins = makeInstance(&xkvmgr_type, ¶ms);
|
||||
TEST_ASSERT_NOT_NULL(sched->plugins);
|
||||
}
|
||||
|
||||
/* Minimal legacy (job_elect_fn-only) plugin, mirroring
|
||||
* test_plugin_picker_registration.c's legacy_plugin_init -- a standalone
|
||||
* stand-in for plugins/fcfs.c's actual shape, not a dependency on fcfs.c
|
||||
* itself. */
|
||||
static void* legacy_job_elect_fn_stub(void* arg)
|
||||
{
|
||||
(void)arg;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static xce_sched_plugin_t* make_legacy_plugin(char* name)
|
||||
{
|
||||
xce_sched_plugin_t* plugin
|
||||
= (xce_sched_plugin_t*)calloc(1, sizeof(*plugin));
|
||||
|
||||
TEST_ASSERT_NOT_NULL(plugin);
|
||||
plugin->name = name;
|
||||
plugin->job_elect_fn = (plugin_job_election_fn_t)legacy_job_elect_fn_stub;
|
||||
|
||||
return plugin;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Req 3 AC 1: register_builtin_fcfs() */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
static void test_register_builtin_fcfs_registers_under_name_builtin_fcfs(void)
|
||||
{
|
||||
xce_scheduler_t sched;
|
||||
xce_sched_plugin_t* found = NULL;
|
||||
int rc;
|
||||
|
||||
fixture_init(&sched);
|
||||
|
||||
rc = register_builtin_fcfs(&sched);
|
||||
TEST_ASSERT_GREATER_OR_EQUAL(0, rc);
|
||||
|
||||
lock(sched.plugins, RTTI_LOCK_READ);
|
||||
rc = key_find(sched.plugins, "builtin_fcfs", (void**)&found);
|
||||
unlock(sched.plugins);
|
||||
|
||||
TEST_ASSERT_GREATER_THAN(0, rc);
|
||||
TEST_ASSERT_NOT_NULL(found);
|
||||
TEST_ASSERT_EQUAL_STRING("builtin_fcfs", found->name);
|
||||
}
|
||||
|
||||
/* builtin_fcfs registers as a NATIVE picker: picker_iface non-NULL,
|
||||
* job_elect_fn NULL -- this is the exact input sched_job_picker()'s branch
|
||||
* condition reads (see this file's own header comment). Also confirms
|
||||
* every MUST hook from tasks 2.1-2.3 is wired, and on_config/on_job_rusage
|
||||
* are NULL per Req 3 AC 2's table. */
|
||||
static void test_register_builtin_fcfs_is_native_not_legacy(void)
|
||||
{
|
||||
xce_scheduler_t sched;
|
||||
xce_sched_plugin_t* found = NULL;
|
||||
|
||||
fixture_init(&sched);
|
||||
TEST_ASSERT_GREATER_OR_EQUAL(0, register_builtin_fcfs(&sched));
|
||||
|
||||
lock(sched.plugins, RTTI_LOCK_READ);
|
||||
key_find(sched.plugins, "builtin_fcfs", (void**)&found);
|
||||
unlock(sched.plugins);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(found);
|
||||
TEST_ASSERT_NULL(found->job_elect_fn);
|
||||
TEST_ASSERT_NOT_NULL(found->picker_iface);
|
||||
TEST_ASSERT_EQUAL_PTR(builtin_fcfs_next_job, found->picker_iface->next_job);
|
||||
TEST_ASSERT_EQUAL_PTR(builtin_fcfs_begin_session,
|
||||
found->picker_iface->begin_session);
|
||||
TEST_ASSERT_EQUAL_PTR(builtin_fcfs_end_session,
|
||||
found->picker_iface->end_session);
|
||||
TEST_ASSERT_EQUAL_PTR(builtin_fcfs_on_dispatch,
|
||||
found->picker_iface->on_dispatch);
|
||||
TEST_ASSERT_EQUAL_PTR(builtin_fcfs_on_pend, found->picker_iface->on_pend);
|
||||
TEST_ASSERT_EQUAL_PTR(builtin_fcfs_on_queue_update,
|
||||
found->picker_iface->on_queue_update);
|
||||
TEST_ASSERT_EQUAL_PTR(builtin_fcfs_on_job_push,
|
||||
found->picker_iface->on_job_push);
|
||||
TEST_ASSERT_EQUAL_PTR(builtin_fcfs_on_job_state_change,
|
||||
found->picker_iface->on_job_state_change);
|
||||
TEST_ASSERT_EQUAL_PTR(builtin_fcfs_on_job_update,
|
||||
found->picker_iface->on_job_update);
|
||||
TEST_ASSERT_NULL(found->picker_iface->on_config);
|
||||
TEST_ASSERT_NULL(found->picker_iface->on_job_rusage);
|
||||
}
|
||||
|
||||
/* register_builtin_fcfs() wires init_fn/fini_fn adapters into the EXISTING
|
||||
* plugin_register()/plugin_unregister() call sites (see builtin_fcfs.c's
|
||||
* own comment on why) -- confirms init actually ran: plugin->data is
|
||||
* non-NULL immediately after registration, not left NULL until some other
|
||||
* call. */
|
||||
static void test_register_builtin_fcfs_runs_init_via_init_fn_adapter(void)
|
||||
{
|
||||
xce_scheduler_t sched;
|
||||
xce_sched_plugin_t* found = NULL;
|
||||
|
||||
fixture_init(&sched);
|
||||
TEST_ASSERT_GREATER_OR_EQUAL(0, register_builtin_fcfs(&sched));
|
||||
|
||||
lock(sched.plugins, RTTI_LOCK_READ);
|
||||
key_find(sched.plugins, "builtin_fcfs", (void**)&found);
|
||||
unlock(sched.plugins);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(found);
|
||||
TEST_ASSERT_NOT_NULL(found->data);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Req 1 AC 6 / Req 2 AC 1 / Req 2 AC 5: default resolution + fail-loud */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
/* tasks.md Observable, half 1: "starting the scheduler with no picker
|
||||
* config uses builtin_fcfs automatically." */
|
||||
static void test_unset_job_pick_plugin_resolves_to_builtin_fcfs_by_default(void)
|
||||
{
|
||||
xce_scheduler_t sched;
|
||||
xce_sched_plugin_t* resolved = NULL;
|
||||
int rc;
|
||||
|
||||
fixture_init(&sched);
|
||||
TEST_ASSERT_GREATER_OR_EQUAL(0, register_builtin_fcfs(&sched));
|
||||
TEST_ASSERT_NULL(sched.job_pick_plugin);
|
||||
|
||||
sched_set_default_job_pick_plugin(&sched);
|
||||
TEST_ASSERT_EQUAL_STRING("builtin_fcfs", sched.job_pick_plugin);
|
||||
|
||||
rc = sched_resolve_job_picker(&sched, &resolved);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
TEST_ASSERT_NOT_NULL(resolved);
|
||||
TEST_ASSERT_EQUAL_STRING("builtin_fcfs", resolved->name);
|
||||
/* This is the exact condition sched_job_picker() (scheduler.c) branches
|
||||
* on: NULL job_elect_fn selects the generic picker_iface loop. */
|
||||
TEST_ASSERT_NULL(resolved->job_elect_fn);
|
||||
TEST_ASSERT_NOT_NULL(resolved->picker_iface);
|
||||
}
|
||||
|
||||
/* tasks.md Observable, half 2: "starting it with an unregistered picker
|
||||
* name fails startup with a clear error instead of silently falling
|
||||
* back." -ENOENT specifically (distinct from -EINVAL below) --
|
||||
* sched_resolve_job_picker's documented contract (scheduler.h). */
|
||||
static void
|
||||
test_unregistered_configured_name_fails_with_enoent_not_silent_fallback(void)
|
||||
{
|
||||
xce_scheduler_t sched;
|
||||
xce_sched_plugin_t* resolved = (xce_sched_plugin_t*)0x1; /* sentinel */
|
||||
int rc;
|
||||
|
||||
fixture_init(&sched);
|
||||
TEST_ASSERT_GREATER_OR_EQUAL(0, register_builtin_fcfs(&sched));
|
||||
|
||||
sched.job_pick_plugin = "totally_unregistered_picker_name";
|
||||
|
||||
rc = sched_resolve_job_picker(&sched, &resolved);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(-ENOENT, rc);
|
||||
/* Not a silent fallback to builtin_fcfs (still registered) or to
|
||||
* whatever the sentinel was -- *out is reset to NULL on any failure
|
||||
* path (schedobj.c's sched_resolve_job_picker, `*out = NULL;` up
|
||||
* front). */
|
||||
TEST_ASSERT_NULL(resolved);
|
||||
}
|
||||
|
||||
/* A DIFFERENT failure mode than "not registered at all": the name IS
|
||||
* registered, but the registered plugin is neither a native picker nor a
|
||||
* legacy job_elect_fn plugin (e.g. today's limit/allocator categories) --
|
||||
* -EINVAL, not -ENOENT. Confirms the two failure modes are genuinely
|
||||
* distinguishable, not the same generic "no" collapsed into one code. */
|
||||
static void test_registered_but_not_a_picker_fails_with_einval_not_enoent(void)
|
||||
{
|
||||
xce_scheduler_t sched;
|
||||
xce_sched_plugin_t* non_picker = NULL;
|
||||
xce_sched_plugin_t* resolved = NULL;
|
||||
int rc;
|
||||
|
||||
fixture_init(&sched);
|
||||
|
||||
non_picker = (xce_sched_plugin_t*)calloc(1, sizeof(*non_picker));
|
||||
TEST_ASSERT_NOT_NULL(non_picker);
|
||||
non_picker->name = "not_a_picker";
|
||||
/* job_elect_fn and picker_iface both left NULL -- today's
|
||||
* allocator/limit plugin shape (register_fcfs's own sibling
|
||||
* categories), which plugin_register() leaves untouched (task 1.2). */
|
||||
TEST_ASSERT_GREATER_OR_EQUAL(0, plugin_register(&sched, non_picker));
|
||||
|
||||
sched.job_pick_plugin = "not_a_picker";
|
||||
|
||||
rc = sched_resolve_job_picker(&sched, &resolved);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(-EINVAL, rc);
|
||||
TEST_ASSERT_NULL(resolved);
|
||||
}
|
||||
|
||||
/* Unset job_pick_plugin, and the caller never called
|
||||
* sched_set_default_job_pick_plugin() either -- -EINVAL (scheduler.h's
|
||||
* documented contract), distinct from both cases above. */
|
||||
static void
|
||||
test_unset_job_pick_plugin_without_defaulting_fails_with_einval(void)
|
||||
{
|
||||
xce_scheduler_t sched;
|
||||
xce_sched_plugin_t* resolved = NULL;
|
||||
int rc;
|
||||
|
||||
fixture_init(&sched);
|
||||
TEST_ASSERT_NULL(sched.job_pick_plugin);
|
||||
|
||||
rc = sched_resolve_job_picker(&sched, &resolved);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(-EINVAL, rc);
|
||||
TEST_ASSERT_NULL(resolved);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* sched_job_picker()'s legacy-vs-native branch condition (scheduler.c) --
|
||||
* see this file's header comment for why this is tested at the resolved-
|
||||
* plugin level rather than by driving the picker thread itself.
|
||||
* ---------------------------------------------------------------------- */
|
||||
|
||||
/* Legacy path input: a job_elect_fn-only plugin, once registered and
|
||||
* resolved, comes back with job_elect_fn set AND picker_iface pointing at
|
||||
* plugin.c's legacy_picker_wrapper_iface (visible in this TU -- same
|
||||
* assertion test_plugin_picker_registration.c makes for plugin_register()
|
||||
* itself). This is exactly the condition
|
||||
* `if (job_picker->job_elect_fn != NULL) return
|
||||
* job_picker->job_elect_fn(job_picker);` in sched_job_picker() reads to
|
||||
* choose the "delegate to fcfs_elect_job-style forever loop" path. */
|
||||
static void
|
||||
test_resolved_legacy_plugin_has_job_elect_fn_set_selecting_legacy_branch(void)
|
||||
{
|
||||
xce_scheduler_t sched;
|
||||
xce_sched_plugin_t* legacy = NULL;
|
||||
xce_sched_plugin_t* resolved = NULL;
|
||||
int rc;
|
||||
|
||||
fixture_init(&sched);
|
||||
legacy = make_legacy_plugin("test_legacy_picker_for_wiring");
|
||||
TEST_ASSERT_GREATER_OR_EQUAL(0, plugin_register(&sched, legacy));
|
||||
|
||||
sched.job_pick_plugin = "test_legacy_picker_for_wiring";
|
||||
|
||||
rc = sched_resolve_job_picker(&sched, &resolved);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
TEST_ASSERT_NOT_NULL(resolved);
|
||||
TEST_ASSERT_NOT_NULL(resolved->job_elect_fn);
|
||||
TEST_ASSERT_EQUAL_PTR(&legacy_picker_wrapper_iface, resolved->picker_iface);
|
||||
}
|
||||
|
||||
/* Native path input: builtin_fcfs, once registered and resolved, comes
|
||||
* back with job_elect_fn NULL -- the condition that selects the generic
|
||||
* picker_iface session loop instead. Distinct test from the "default"
|
||||
* test above: this one asserts on an EXPLICITLY configured name, not the
|
||||
* unset-defaults-to-builtin_fcfs path, isolating the branch condition
|
||||
* itself from the defaulting behavior. */
|
||||
static void
|
||||
test_resolved_builtin_fcfs_has_job_elect_fn_null_selecting_native_branch(void)
|
||||
{
|
||||
xce_scheduler_t sched;
|
||||
xce_sched_plugin_t* resolved = NULL;
|
||||
int rc;
|
||||
|
||||
fixture_init(&sched);
|
||||
TEST_ASSERT_GREATER_OR_EQUAL(0, register_builtin_fcfs(&sched));
|
||||
|
||||
sched.job_pick_plugin = "builtin_fcfs"; /* explicit, not defaulted */
|
||||
|
||||
rc = sched_resolve_job_picker(&sched, &resolved);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
TEST_ASSERT_NOT_NULL(resolved);
|
||||
TEST_ASSERT_NULL(resolved->job_elect_fn);
|
||||
TEST_ASSERT_NOT_NULL(resolved->picker_iface);
|
||||
TEST_ASSERT_EQUAL_PTR(builtin_fcfs_next_job,
|
||||
resolved->picker_iface->next_job);
|
||||
}
|
||||
|
||||
/* Both builtin_fcfs and a legacy plugin can be registered simultaneously
|
||||
* (Req 3 AC 1's "wire builtin_fcfs as the registered default" does not
|
||||
* mean "the only" -- tasks.md's own builtin_fcfs.c header comment: fcfs.c
|
||||
* "stays registered ... reachable only by explicit config"). Confirms
|
||||
* choosing one by name does not disturb the other's own registration or
|
||||
* resolution. */
|
||||
static void
|
||||
test_legacy_and_native_pickers_coexist_and_resolve_independently(void)
|
||||
{
|
||||
xce_scheduler_t sched;
|
||||
xce_sched_plugin_t* legacy = NULL;
|
||||
xce_sched_plugin_t* resolved = NULL;
|
||||
int rc;
|
||||
|
||||
fixture_init(&sched);
|
||||
legacy = make_legacy_plugin("xce_sched_fcfs_stub");
|
||||
TEST_ASSERT_GREATER_OR_EQUAL(0, plugin_register(&sched, legacy));
|
||||
TEST_ASSERT_GREATER_OR_EQUAL(0, register_builtin_fcfs(&sched));
|
||||
|
||||
sched.job_pick_plugin = "xce_sched_fcfs_stub";
|
||||
rc = sched_resolve_job_picker(&sched, &resolved);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
TEST_ASSERT_NOT_NULL(resolved->job_elect_fn);
|
||||
|
||||
sched.job_pick_plugin = "builtin_fcfs";
|
||||
rc = sched_resolve_job_picker(&sched, &resolved);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
TEST_ASSERT_NULL(resolved->job_elect_fn);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(test_register_builtin_fcfs_registers_under_name_builtin_fcfs);
|
||||
RUN_TEST(test_register_builtin_fcfs_is_native_not_legacy);
|
||||
RUN_TEST(test_register_builtin_fcfs_runs_init_via_init_fn_adapter);
|
||||
RUN_TEST(test_unset_job_pick_plugin_resolves_to_builtin_fcfs_by_default);
|
||||
RUN_TEST(
|
||||
test_unregistered_configured_name_fails_with_enoent_not_silent_fallback);
|
||||
RUN_TEST(test_registered_but_not_a_picker_fails_with_einval_not_enoent);
|
||||
RUN_TEST(test_unset_job_pick_plugin_without_defaulting_fails_with_einval);
|
||||
RUN_TEST(
|
||||
test_resolved_legacy_plugin_has_job_elect_fn_set_selecting_legacy_branch);
|
||||
RUN_TEST(
|
||||
test_resolved_builtin_fcfs_has_job_elect_fn_null_selecting_native_branch);
|
||||
RUN_TEST(test_legacy_and_native_pickers_coexist_and_resolve_independently);
|
||||
return UNITY_END();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue