- bug-1872 (worker): "Failed to find any disk statistics in /proc/diskstats" ERROR every 15 s on an NVMe-only host. The whole-disk filter guessed from the name and skipped anything ending in a digit or containing 'p' -- every nvmeXnY, mmcblkN, even sdp -- so io was never reported. diskstats.h now counts a device only when /sys/block/<name>/device exists (real disks; not partitions, loop, dm, md, zram), and a host with no disk warns once. - bug-1873 (worker): "Failed to get worker pid" WARN on job start. MainPID reads 0 until systemd has forked the new transient unit; xcecd.c already retries, so the miss is DEBUG and the final failure names the unit. - bug-1874 (scheduler): deprecation WARN for the legacy xce_sched_fcfs on every start, though builtin_fcfs is the picker. Registration still wraps a job_elect_fn plugin but no longer warns; selecting one warns once. - bug-1875 (apiserver): "Not running as root -- PAM authentication will not work" with the default provider config_file; now only for provider pam. - bug-1876 (controller): standbys logged "cluster_init_phase2 ... failed (rc=-22)" WARN every 3 s while waiting for the primary's config push, a state the function itself calls normal. It returns -EAGAIN for that wait; callers log it at INFO, other failures still WARN. - bug-1877 (controller): "WAL/snapshot stored unencrypted" warned without looking. persist_path_encrypted() checks fscrypt (statx) and dm-crypt (sysfs dm/uuid CRYPT-, through LVM slaves); the warning stays when the storage is not shown to be encrypted (e.g. a plain /tmp). Tests: worker diskstats_test (2 of 4 RED on the old filter with pc13v's /proc/diskstats), controller test_phase2_cold_boot and test_persist_encryption, scheduler picker tests, apiserver TestRootWarning. Full ASAN build: all 113 suites pass (controller 319/0, harness 141/0). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
537 lines
21 KiB
C
537 lines
21 KiB
C
/*
|
|
* 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.
|
|
* ---------------------------------------------------------------------- */
|
|
/* sched_get_user/sched_get_project/sched_get_queue/sched_get_job/
|
|
* sched_add_user/sched_add_project relocated to core.h (scheduler-core
|
|
* spec, task 4.1) -- now operate on xce_sched_core_t*, not
|
|
* xce_scheduler_t*. Kept as stubs here (not pulling in core.c's real
|
|
* implementations): this file never exercises make_sched_job/queue
|
|
* lookup, only picker registration/resolution. */
|
|
xce_sched_user_t* sched_get_user(xce_sched_core_t* s, char* n)
|
|
{
|
|
(void)s;
|
|
(void)n;
|
|
return NULL;
|
|
}
|
|
xce_sched_project_t* sched_get_project(xce_sched_core_t* s, char* n)
|
|
{
|
|
(void)s;
|
|
(void)n;
|
|
return NULL;
|
|
}
|
|
xce_sched_queue_t* sched_get_queue(xce_sched_core_t* s, char* n)
|
|
{
|
|
(void)s;
|
|
(void)n;
|
|
return NULL;
|
|
}
|
|
xce_sched_job_t* sched_get_job(xce_sched_core_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; }
|
|
/* resource-accounting.md's reconciliation (resource.c, not linked here) --
|
|
* link-satisfying stub only, same rationale as every other stub here. */
|
|
int sched_reconcile_job_resource_usage(xce_sched_core_t* c,
|
|
xce_job_account_t* a,
|
|
xce_job_rusages_t* r)
|
|
{
|
|
(void)c; (void)a; (void)r; return 0;
|
|
}
|
|
int sched_add_user(xce_sched_core_t* s, xce_sched_user_t* u)
|
|
{
|
|
(void)s;
|
|
(void)u;
|
|
return 0;
|
|
}
|
|
int sched_add_project(xce_sched_core_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_sched_alloc_req_t* alloc_req,
|
|
xlist_t* limits, xlist_t* rusages, int nhosts,
|
|
int nslots)
|
|
{
|
|
(void)alloc_req;
|
|
(void)limits;
|
|
(void)rusages;
|
|
(void)nhosts;
|
|
(void)nslots;
|
|
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->core.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->core.plugins = makeInstance(&xkvmgr_type, ¶ms);
|
|
TEST_ASSERT_NOT_NULL(sched->core.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.core.plugins, RTTI_LOCK_READ);
|
|
rc = key_find(sched.core.plugins, "builtin_fcfs", (void**)&found);
|
|
unlock(sched.core.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.core.plugins, RTTI_LOCK_READ);
|
|
key_find(sched.core.plugins, "builtin_fcfs", (void**)&found);
|
|
unlock(sched.core.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.core.plugins, RTTI_LOCK_READ);
|
|
key_find(sched.core.plugins, "builtin_fcfs", (void**)&found);
|
|
unlock(sched.core.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.core.job_pick_plugin);
|
|
|
|
sched_set_default_job_pick_plugin(&sched);
|
|
TEST_ASSERT_EQUAL_STRING("builtin_fcfs", sched.core.job_pick_plugin);
|
|
|
|
rc = sched_resolve_job_picker(&sched.core, &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 (core.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.core.job_pick_plugin = "totally_unregistered_picker_name";
|
|
|
|
rc = sched_resolve_job_picker(&sched.core, &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.core.job_pick_plugin = "not_a_picker";
|
|
|
|
rc = sched_resolve_job_picker(&sched.core, &resolved);
|
|
|
|
TEST_ASSERT_EQUAL_INT(-EINVAL, rc);
|
|
TEST_ASSERT_NULL(resolved);
|
|
}
|
|
|
|
/* bug-1874: plugin_register() warned about the deprecated job_elect_fn hook
|
|
* for every legacy plugin it wrapped -- so the legacy "xce_sched_fcfs",
|
|
* registered on every scheduler start but never the normal-mode picker
|
|
* (builtin_fcfs is), logged a deprecation WARN on every start. The warning
|
|
* belongs to selecting such a plugin as the picker, and only once. */
|
|
static void
|
|
test_legacy_picker_deprecation_flagged_on_selection_not_registration(void)
|
|
{
|
|
xce_scheduler_t sched;
|
|
xce_sched_plugin_t* legacy = NULL;
|
|
xce_sched_plugin_t* resolved = NULL;
|
|
|
|
fixture_init(&sched);
|
|
legacy = make_legacy_plugin("legacy_picker_1874");
|
|
TEST_ASSERT_GREATER_OR_EQUAL(0, plugin_register(&sched, legacy));
|
|
TEST_ASSERT_NOT_NULL_MESSAGE(legacy->picker_iface,
|
|
"still adapted to the picker interface");
|
|
TEST_ASSERT_FALSE_MESSAGE(legacy->legacy_picker_warned,
|
|
"registration alone must not warn");
|
|
|
|
sched.core.job_pick_plugin = "legacy_picker_1874";
|
|
TEST_ASSERT_EQUAL_INT(0, sched_resolve_job_picker(&sched.core, &resolved));
|
|
TEST_ASSERT_EQUAL_PTR(legacy, resolved);
|
|
TEST_ASSERT_TRUE_MESSAGE(legacy->legacy_picker_warned,
|
|
"selecting it as the picker warns");
|
|
}
|
|
|
|
/* 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.core.job_pick_plugin);
|
|
|
|
rc = sched_resolve_job_picker(&sched.core, &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.core.job_pick_plugin = "test_legacy_picker_for_wiring";
|
|
|
|
rc = sched_resolve_job_picker(&sched.core, &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.core.job_pick_plugin = "builtin_fcfs"; /* explicit, not defaulted */
|
|
|
|
rc = sched_resolve_job_picker(&sched.core, &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.core.job_pick_plugin = "xce_sched_fcfs_stub";
|
|
rc = sched_resolve_job_picker(&sched.core, &resolved);
|
|
TEST_ASSERT_EQUAL_INT(0, rc);
|
|
TEST_ASSERT_NOT_NULL(resolved->job_elect_fn);
|
|
|
|
sched.core.job_pick_plugin = "builtin_fcfs";
|
|
rc = sched_resolve_job_picker(&sched.core, &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);
|
|
RUN_TEST(
|
|
test_legacy_picker_deprecation_flagged_on_selection_not_registration);
|
|
return UNITY_END();
|
|
}
|