/* * test_picker_iface.c - standalone compile-check test for the job picker * plugin contract, xce_job_picker_iface_t (scheduler-core spec, task 1.1). * * This task only adds a header (plugin.h) -- there is no picker * implementation yet (that lands in task 2.x, builtin_fcfs). So the * red/green evidence for this task is a *compile-time* contract check, not * a runtime behavior check: * * RED: before xce_job_picker_iface_t existed in plugin.h, this file * failed to compile (unknown type xce_job_picker_iface_t). * GREEN: with the header in place, every hook category from Req 1 AC 2 * (lifecycle, state-change, session, post-allocation, optional) * is present with the exact signature design.md specifies, and a * correctly-typed function pointer can be assigned to every * field without an incompatible-pointer-type diagnostic. This * test target builds with -Werror=incompatible-pointer-types * (see testsuite/meson.build) specifically so a signature drift * in plugin.h fails the build, not just this assertion file. * * Each stub below is intentionally typed via an explicit named function * pointer variable of the exact field type (copied from plugin.h) before * being stored into the struct literal -- that forces the compiler to * check the stub's signature against the interface's declared signature * at the point of assignment, which is the property this test exists to * verify. */ #include #include #include #include "plugin.h" /* ---- lifecycle hooks ---- */ static int stub_init(void* picker, xce_sched_core_t* core) { (void)picker; (void)core; return 0; } static void stub_fini(void* picker) { (void)picker; } /* ---- session hooks ---- */ static int stub_begin_session(void* picker) { (void)picker; return 0; } static xce_sched_job_t* stub_next_job(void* picker) { (void)picker; return NULL; } static int stub_end_session(void* picker) { (void)picker; return 0; } /* ---- post-allocation hooks ---- */ static int stub_on_dispatch(void* picker, uint64_t jobid) { (void)picker; (void)jobid; return 0; } static int stub_on_pend(void* picker, uint64_t jobid) { (void)picker; (void)jobid; return 0; } /* ---- state-change hooks ---- */ static int stub_on_queue_update(void* picker, xce_sched_queue_t* queue) { (void)picker; (void)queue; return 0; } static int stub_on_job_push(void* picker, xce_sched_job_t* job) { (void)picker; (void)job; return 0; } static int stub_on_job_state_change(void* picker, xce_sched_job_t* job, uint32_t state) { (void)picker; (void)job; (void)state; return 0; } static int stub_on_job_update(void* picker, xce_sched_job_t* job) { (void)picker; (void)job; return 0; } /* ---- optional hooks (MAY be NULL) ---- */ static int stub_on_config(void* picker, xce_conf4sched_t* config) { (void)picker; (void)config; return 0; } static int stub_on_job_rusage(void* picker, xce_sched_job_t* job, xce_job_rusages_t* rusages) { (void)picker; (void)job; (void)rusages; return 0; } void setUp(void) { return; } void tearDown(void) { return; } /* Assigns every hook through an explicitly-typed intermediate function * pointer -- see file header comment. If any field's declared signature in * plugin.h drifted from the stub below, this would fail to compile * (incompatible-pointer-types, -Werror'd for this target). */ static void test_picker_iface_has_every_hook_with_correct_signature(void) { int (*init_fn)(void*, xce_sched_core_t*) = stub_init; void (*fini_fn)(void*) = stub_fini; int (*begin_session_fn)(void*) = stub_begin_session; xce_sched_job_t* (*next_job_fn)(void*) = stub_next_job; int (*end_session_fn)(void*) = stub_end_session; int (*on_dispatch_fn)(void*, uint64_t) = stub_on_dispatch; int (*on_pend_fn)(void*, uint64_t) = stub_on_pend; int (*on_queue_update_fn)(void*, xce_sched_queue_t*) = stub_on_queue_update; int (*on_job_push_fn)(void*, xce_sched_job_t*) = stub_on_job_push; int (*on_job_state_change_fn)(void*, xce_sched_job_t*, uint32_t) = stub_on_job_state_change; int (*on_job_update_fn)(void*, xce_sched_job_t*) = stub_on_job_update; int (*on_config_fn)(void*, xce_conf4sched_t*) = stub_on_config; int (*on_job_rusage_fn)(void*, xce_sched_job_t*, xce_job_rusages_t*) = stub_on_job_rusage; xce_job_picker_iface_t iface = { .init = init_fn, .fini = fini_fn, .begin_session = begin_session_fn, .next_job = next_job_fn, .end_session = end_session_fn, .on_dispatch = on_dispatch_fn, .on_pend = on_pend_fn, .on_queue_update = on_queue_update_fn, .on_job_push = on_job_push_fn, .on_job_state_change = on_job_state_change_fn, .on_job_update = on_job_update_fn, .on_config = on_config_fn, .on_job_rusage = on_job_rusage_fn, }; /* MUST hooks are all non-NULL when populated. */ TEST_ASSERT_NOT_NULL(iface.init); TEST_ASSERT_NOT_NULL(iface.fini); TEST_ASSERT_NOT_NULL(iface.begin_session); TEST_ASSERT_NOT_NULL(iface.next_job); TEST_ASSERT_NOT_NULL(iface.end_session); TEST_ASSERT_NOT_NULL(iface.on_dispatch); TEST_ASSERT_NOT_NULL(iface.on_pend); TEST_ASSERT_NOT_NULL(iface.on_queue_update); TEST_ASSERT_NOT_NULL(iface.on_job_push); TEST_ASSERT_NOT_NULL(iface.on_job_state_change); TEST_ASSERT_NOT_NULL(iface.on_job_update); /* Optional hooks are populated here (proving the signature compiles), * but the contract (Req 1 AC 2) is that they MAY be NULL -- exercised * by the next test. */ TEST_ASSERT_NOT_NULL(iface.on_config); TEST_ASSERT_NOT_NULL(iface.on_job_rusage); /* Confirm the assignment actually stored the intended function, not * merely "a" function -- pins the test to the real symbols above. */ TEST_ASSERT_EQUAL_PTR(stub_init, iface.init); TEST_ASSERT_EQUAL_PTR(stub_next_job, iface.next_job); TEST_ASSERT_EQUAL_PTR(stub_on_job_state_change, iface.on_job_state_change); } /* Req 1 AC 2: on_config and on_job_rusage are the two hooks explicitly * documented "MAY be NULL". A picker that doesn't need them (e.g. FCFS) * must be able to leave them unset. */ static void test_picker_iface_optional_hooks_may_be_null(void) { xce_job_picker_iface_t iface = { .init = stub_init, .fini = stub_fini, .begin_session = stub_begin_session, .next_job = stub_next_job, .end_session = stub_end_session, .on_dispatch = stub_on_dispatch, .on_pend = stub_on_pend, .on_queue_update = stub_on_queue_update, .on_job_push = stub_on_job_push, .on_job_state_change = stub_on_job_state_change, .on_job_update = stub_on_job_update, .on_config = NULL, .on_job_rusage = NULL, }; TEST_ASSERT_NULL(iface.on_config); TEST_ASSERT_NULL(iface.on_job_rusage); /* MUST hooks stay required even when the optional ones are absent. */ TEST_ASSERT_NOT_NULL(iface.next_job); } int main(void) { UNITY_BEGIN(); RUN_TEST(test_picker_iface_has_every_hook_with_correct_signature); RUN_TEST(test_picker_iface_optional_hooks_may_be_null); return UNITY_END(); }