feat(scheduler-core): register picker plugin category + job_elect_fn compat wrapper (task 1.2)
plugin_register() now adapts an old-style job_elect_fn-only plugin to xce_job_picker_iface_t via legacy_picker_wrapper_iface (next_job() forwards to job_elect_fn; other hooks are safe no-ops covered elsewhere in the existing lifecycle), logging a one-time deprecation warning. A plugin that already sets picker_iface directly skips the wrapper and the warning. Reuses the existing plugin_register()/key_find() mechanism -- no parallel registration system. Requirements: 1.6, 2.5
This commit is contained in:
parent
065025a3a6
commit
4d8d72142e
5 changed files with 409 additions and 2 deletions
|
|
@ -68,6 +68,19 @@ sched_picker_iface_test = executable('sched_picker_iface_test',
|
|||
c_args: ['-Werror=incompatible-pointer-types'],
|
||||
install: false)
|
||||
|
||||
# White-box test for the picker plugin registration category and the
|
||||
# job_elect_fn compatibility wrapper (scheduler-core spec, task 1.2). It
|
||||
# #includes ../src/plugin.c directly (same white-box pattern as
|
||||
# sched_handlers_test/sched_job_array_test) so it exercises the real
|
||||
# plugin_register() and the real legacy_picker_wrapper_iface, and needs a
|
||||
# REAL sched->plugins kvmgr (same setup scheduler.c's sched_plugin_init()
|
||||
# uses) to register plugins through it for real.
|
||||
sched_picker_registration_test = executable('sched_picker_registration_test',
|
||||
['test_plugin_picker_registration.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',
|
||||
|
|
@ -82,3 +95,5 @@ test('scheduler job-array wire e2e test', sched_job_array_wire_e2e_test, suite:
|
|||
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
||||
test('scheduler picker iface test', sched_picker_iface_test, suite: 'scheduler',
|
||||
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
||||
test('scheduler picker registration test', sched_picker_registration_test, suite: 'scheduler',
|
||||
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
||||
|
|
|
|||
249
src/scheduler/testsuite/test_plugin_picker_registration.c
Normal file
249
src/scheduler/testsuite/test_plugin_picker_registration.c
Normal file
|
|
@ -0,0 +1,249 @@
|
|||
/*
|
||||
* test_plugin_picker_registration.c - white-box tests for the picker
|
||||
* plugin registration category and job_elect_fn compatibility wrapper
|
||||
* added to plugin.c (scheduler-core spec, task 1.2).
|
||||
*
|
||||
* RED: before task 1.2's plugin.h/plugin.c changes, xce_sched_plugin_t had
|
||||
* no picker_iface field and legacy_picker_wrapper_iface did not exist --
|
||||
* this file failed to compile.
|
||||
* GREEN: plugin_register() adapts an old-style, job_elect_fn-only plugin
|
||||
* to xce_job_picker_iface_t via a compatibility wrapper and logs a
|
||||
* deprecation warning exactly once, at registration time (Req 5
|
||||
* "Migration from old job_elect_fn"); a plugin that already provides
|
||||
* picker_iface registers as-is, with no warning; a plugin that provides
|
||||
* neither (today's allocator/limit categories) is unaffected.
|
||||
*
|
||||
* #include's the real plugin.c (white-box, same convention as
|
||||
* test_handlers.c / test_job_array_expand.c / test_job_array_wire_e2e.c)
|
||||
* so this test exercises the real plugin_register() and the real
|
||||
* legacy_picker_wrapper_iface -- not a re-implementation of either.
|
||||
*/
|
||||
#include <errno.h>
|
||||
#include <stdarg.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"
|
||||
|
||||
/* plugin.c's plugin_prepare_allocation() (unrelated to the picker
|
||||
* registration category under test here) calls sched_fill_limit_requests,
|
||||
* defined in scheduler.c -- not pulled into this white-box TU. Stubbed the
|
||||
* same way test_handlers.c stubs sched_init_config/sched_get_job: this
|
||||
* test never exercises plugin_prepare_allocation, so a no-op body is
|
||||
* sufficient to satisfy the linker. */
|
||||
int sched_fill_limit_requests(xce_scheduler_t* sched, xvec_idval_t* req,
|
||||
xlist_t* limits)
|
||||
{
|
||||
(void)sched;
|
||||
(void)req;
|
||||
(void)limits;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include "../src/plugin.c"
|
||||
|
||||
void setUp(void) { return; }
|
||||
void tearDown(void) { return; }
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Early-print log capture: same pattern as test_job_array_wire_e2e.c's
|
||||
* wire_e2e_early_print_cb. No test binary in this suite calls tlog_init(),
|
||||
* so WARN() (tlog_warn -> tlog(TLOG_WARN, ...)) routes through tlog's
|
||||
* pre-init "early print" fallback, which tlog_reg_early_printf_callback()
|
||||
* lets a test intercept synchronously on the calling thread -- no
|
||||
* tlog_init()/tlog_exit() lifecycle, so it cannot disturb any other test
|
||||
* sharing this process.
|
||||
* ---------------------------------------------------------------------- */
|
||||
|
||||
static int g_warn_count = 0;
|
||||
|
||||
static void picker_reg_early_print_cb(struct tlog_loginfo* loginfo,
|
||||
const char* format, va_list ap)
|
||||
{
|
||||
char buf[512];
|
||||
va_list ap_copy;
|
||||
|
||||
if (loginfo == NULL || loginfo->level != TLOG_WARN) {
|
||||
return;
|
||||
}
|
||||
|
||||
va_copy(ap_copy, ap);
|
||||
vsnprintf(buf, sizeof(buf), format, ap_copy);
|
||||
va_end(ap_copy);
|
||||
|
||||
if (strstr(buf, "job_elect_fn") != NULL) {
|
||||
g_warn_count++;
|
||||
}
|
||||
}
|
||||
|
||||
static void start_log_capture(void)
|
||||
{
|
||||
g_warn_count = 0;
|
||||
tlog_set_early_printf(1, 1, 0);
|
||||
tlog_reg_early_printf_callback(picker_reg_early_print_cb);
|
||||
}
|
||||
|
||||
static void stop_log_capture(void) { tlog_reg_early_printf_callback(NULL); }
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Fixture: a real sched->plugins kvmgr -- same setup as scheduler.c's
|
||||
* (static, so not reusable) sched_plugin_init(). Each test gets its own
|
||||
* fresh instance; nothing is torn down (suite convention, see meson.build:
|
||||
* 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));
|
||||
sched->plugins = makeInstance(&xkvmgr_type, ¶ms);
|
||||
TEST_ASSERT_NOT_NULL(sched->plugins);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Old-style plugin fixture: only job_elect_fn is set, no native
|
||||
* picker_iface -- the same shape as today's plugins/fcfs.c (out of scope
|
||||
* for this task; this is a minimal standalone stand-in so the test does
|
||||
* not depend on fcfs.c's real threading behavior). */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
static xce_sched_job_t g_legacy_elected_job;
|
||||
static int g_legacy_elect_calls = 0;
|
||||
|
||||
static void* legacy_job_elect_fn(void* arg)
|
||||
{
|
||||
TEST_ASSERT_NOT_NULL(arg);
|
||||
g_legacy_elect_calls++;
|
||||
|
||||
return (void*)&g_legacy_elected_job;
|
||||
}
|
||||
|
||||
static void legacy_plugin_init(xce_sched_plugin_t* plugin, char* name)
|
||||
{
|
||||
memset(plugin, 0, sizeof(*plugin));
|
||||
plugin->name = name;
|
||||
plugin->job_elect_fn = (plugin_job_election_fn_t)legacy_job_elect_fn;
|
||||
}
|
||||
|
||||
/* Req 5 "Migration from old job_elect_fn": an old-style, job_elect_fn-only
|
||||
* plugin registers successfully via the compatibility wrapper and logs a
|
||||
* deprecation warning exactly once, at registration time. */
|
||||
static void
|
||||
test_register_legacy_job_elect_fn_only_plugin_uses_wrapper_and_warns(void)
|
||||
{
|
||||
xce_scheduler_t sched;
|
||||
xce_sched_plugin_t plugin;
|
||||
int rc;
|
||||
|
||||
fixture_init(&sched);
|
||||
legacy_plugin_init(&plugin, "test_legacy_picker");
|
||||
|
||||
start_log_capture();
|
||||
rc = plugin_register(&sched, &plugin);
|
||||
stop_log_capture();
|
||||
|
||||
TEST_ASSERT_GREATER_OR_EQUAL(0, rc);
|
||||
TEST_ASSERT_EQUAL_INT(1, g_warn_count);
|
||||
TEST_ASSERT_NOT_NULL(plugin.picker_iface);
|
||||
TEST_ASSERT_EQUAL_PTR(&legacy_picker_wrapper_iface, plugin.picker_iface);
|
||||
}
|
||||
|
||||
/* The wrapper must actually work: next_job() genuinely forwards to the old
|
||||
* job_elect_fn, it does not merely register without crashing. */
|
||||
static void test_legacy_wrapper_next_job_calls_job_elect_fn(void)
|
||||
{
|
||||
xce_scheduler_t sched;
|
||||
xce_sched_plugin_t plugin;
|
||||
xce_sched_job_t* got;
|
||||
|
||||
fixture_init(&sched);
|
||||
legacy_plugin_init(&plugin, "test_legacy_picker_2");
|
||||
g_legacy_elect_calls = 0;
|
||||
|
||||
TEST_ASSERT_TRUE(plugin_register(&sched, &plugin) >= 0);
|
||||
TEST_ASSERT_NOT_NULL(plugin.picker_iface);
|
||||
TEST_ASSERT_NOT_NULL(plugin.picker_iface->next_job);
|
||||
|
||||
got = plugin.picker_iface->next_job(&plugin);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(1, g_legacy_elect_calls);
|
||||
TEST_ASSERT_EQUAL_PTR(&g_legacy_elected_job, got);
|
||||
}
|
||||
|
||||
/* Req 1 AC 2 / Req 5: a plugin that already implements
|
||||
* xce_job_picker_iface_t natively registers as-is -- no wrapper
|
||||
* substitution, no deprecation warning. */
|
||||
static xce_sched_job_t* new_style_next_job_stub(void* picker)
|
||||
{
|
||||
(void)picker;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static xce_job_picker_iface_t g_new_style_iface = {
|
||||
.next_job = new_style_next_job_stub,
|
||||
};
|
||||
|
||||
static void
|
||||
test_register_new_style_picker_plugin_skips_wrapper_and_does_not_warn(void)
|
||||
{
|
||||
xce_scheduler_t sched;
|
||||
xce_sched_plugin_t plugin;
|
||||
int rc;
|
||||
|
||||
fixture_init(&sched);
|
||||
memset(&plugin, 0, sizeof(plugin));
|
||||
plugin.name = "test_new_style_picker";
|
||||
plugin.picker_iface = &g_new_style_iface;
|
||||
|
||||
start_log_capture();
|
||||
rc = plugin_register(&sched, &plugin);
|
||||
stop_log_capture();
|
||||
|
||||
TEST_ASSERT_GREATER_OR_EQUAL(0, rc);
|
||||
TEST_ASSERT_EQUAL_INT(0, g_warn_count);
|
||||
TEST_ASSERT_EQUAL_PTR(&g_new_style_iface, plugin.picker_iface);
|
||||
}
|
||||
|
||||
/* Regression guard: a plugin that is neither category (no job_elect_fn, no
|
||||
* picker_iface -- today's allocator/limit plugins) registers unaffected --
|
||||
* no wrapper substitution, no warning, picker_iface stays NULL. */
|
||||
static void test_register_non_picker_plugin_is_unaffected(void)
|
||||
{
|
||||
xce_scheduler_t sched;
|
||||
xce_sched_plugin_t plugin;
|
||||
int rc;
|
||||
|
||||
fixture_init(&sched);
|
||||
memset(&plugin, 0, sizeof(plugin));
|
||||
plugin.name = "test_non_picker_plugin";
|
||||
|
||||
start_log_capture();
|
||||
rc = plugin_register(&sched, &plugin);
|
||||
stop_log_capture();
|
||||
|
||||
TEST_ASSERT_GREATER_OR_EQUAL(0, rc);
|
||||
TEST_ASSERT_EQUAL_INT(0, g_warn_count);
|
||||
TEST_ASSERT_NULL(plugin.picker_iface);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(
|
||||
test_register_legacy_job_elect_fn_only_plugin_uses_wrapper_and_warns);
|
||||
RUN_TEST(test_legacy_wrapper_next_job_calls_job_elect_fn);
|
||||
RUN_TEST(
|
||||
test_register_new_style_picker_plugin_skips_wrapper_and_does_not_warn);
|
||||
RUN_TEST(test_register_non_picker_plugin_is_unaffected);
|
||||
return UNITY_END();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue