/* * test_picker_config_fields.c - white-box tests for the picker * configuration fields added to xce_scheduler_t, xce_sched_queue_t, and * xce_sched_project_t (scheduler-core spec, task 1.3, Req 2 AC 1-4). * * RED: before task 1.3, xce_scheduler_t had no job_pick_plugin field, * xce_sched_queue_t had no project_name/job_pick_plugin fields, * xce_sched_project_t had no priority field, and * sched_set_default_job_pick_plugin() did not exist -- this file failed to * compile. * GREEN: the fields exist with the documented types/defaults, and * make_sched_queue/make_sched_project (schedobj.c) leave the new fields at * their documented default values. * * This task is scaffolding only -- no picker-name lookup/resolution * (plugin_register/key_find against the plugin registry) exists yet, that * is task 2.4's job. The tasks.md "Observable" for this task is: * "a queue config with no override inherits the global picker name; * a queue config with an override reports that override, not the * global default" * Read literally that would require the lookup logic this task explicitly * excludes, so it is exercised here at the structural level instead: for * a NULL-override queue, the field values a resolver would read are * exactly sched->job_pick_plugin; for an overridden queue, the queue's own * field is a different, non-NULL string, distinct from the scheduler's * global default -- proving the two fields are independently readable and * correctly defaulted, without this test (or production code) performing * the resolution itself. * * Strategy: pull the REAL make_sched_queue/make_sched_project/ * sched_set_default_job_pick_plugin into this TU by including schedobj.c * (same white-box convention as test_job_update.c / * test_job_array_expand.c / test_plugin_picker_registration.c). The * scheduler-internal externs schedobj.c references but which are defined * in other .c files not linked here are stubbed below -- identical stub * set to test_job_update.c, since this task added no new external calls * to schedobj.c (only field assignments plus one new self-contained * function). */ #include #include #include #include #include #include "kvmgr.h" #include "scheduler.h" #include "xstring.h" /* Pull the real implementations under test into this TU. sched_get_user/ * sched_get_project/sched_get_queue/sched_get_job/sched_add_user/ * sched_add_project/make_sched_user/make_sched_project relocated to core.c * (scheduler-core spec, task 4.1) -- pulling core.c in alongside schedobj.c * (rather than re-stubbing them) is what lets this file keep exercising the * REAL make_sched_project, per this file's own header comment. */ #include "../src/core.c" #include "../src/schedobj.c" /* Stubs for scheduler-internal externs schedobj.c/core.c reference but * which are defined in other .c files not linked into this test. Not * exercised by the tests below -- present only to satisfy the linker for * this whole-file #include. plugin_unregister is core.c's kvmgr datakiller * for the (never-called-in-this-test) plugins kvmgr inside * xce_sched_core_init() -- still needs a resolvable address even though * that code path never runs here. */ int plugin_job_update(void* s, xce_sched_job_t* j) { (void)s; (void)j; return 0; } int plugin_queue_update(void* s, xce_sched_queue_t* q) { (void)s; (void)q; return 0; } int plugin_job_rusage_update(void* s, xce_sched_job_t* j, xce_job_rusages_t* r) { (void)s; (void)j; (void)r; return 0; } 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; } void sched_worker_release_host_ctx(xce_sched_worker_t* w) { (void)w; } int plugin_prepare_allocation(void* s, xce_sched_ctx_t* c) { (void)s; (void)c; return 0; } void plugin_unregister(void* arg) { (void)arg; } void setUp(void) { return; } void tearDown(void) { return; } /* Per-test fixture: a zeroed scheduler, with the xstring intern table * (needed by makeString(), which every constructor under test calls) and * the CSE intern table live. Mirrors test_job_update.c's fixture_init -- * called once per test so each test is self-contained. */ static void fixture_init(xce_scheduler_t* sched) { memset(sched, 0, sizeof(*sched)); resreq_init(1); xstring_table_init(1024 * 16); } static xce_queue_t make_test_raw_queue(const char* name) { xce_queue_t q; memset(&q, 0, sizeof(q)); q.queue = (char*)name; return q; } /* ---------------------------------------------------------------------- */ /* Req 2 AC 1: xce_scheduler_t.job_pick_plugin */ /* ---------------------------------------------------------------------- */ /* Field exists with the right type (char*) and is NULL ("unset") on a * freshly zeroed scheduler, before any default is applied. */ static void test_scheduler_job_pick_plugin_field_is_char_star_and_unset_by_default(void) { xce_scheduler_t sched; fixture_init(&sched); TEST_ASSERT_NULL(sched.core.job_pick_plugin); /* Type check: assigning a char* literal must compile without a cast -- * this is a compile-time proof of the declared field type. */ sched.core.job_pick_plugin = "some_name"; TEST_ASSERT_EQUAL_STRING("some_name", sched.core.job_pick_plugin); } /* Req 2 AC 1's literal wording: "defaulting to builtin_fcfs when unset". */ static void test_sched_set_default_job_pick_plugin_applies_builtin_fcfs_when_unset(void) { xce_scheduler_t sched; fixture_init(&sched); TEST_ASSERT_NULL(sched.core.job_pick_plugin); sched_set_default_job_pick_plugin(&sched); TEST_ASSERT_NOT_NULL(sched.core.job_pick_plugin); TEST_ASSERT_EQUAL_STRING("builtin_fcfs", sched.core.job_pick_plugin); } /* "...when unset" implies idempotence: an already-configured name (however * it got there) must not be clobbered by the default. */ static void test_sched_set_default_job_pick_plugin_does_not_clobber_configured_value(void) { xce_scheduler_t sched; fixture_init(&sched); sched.core.job_pick_plugin = makeString("priority_picker"); sched_set_default_job_pick_plugin(&sched); TEST_ASSERT_EQUAL_STRING("priority_picker", sched.core.job_pick_plugin); } static void test_sched_set_default_job_pick_plugin_is_null_safe(void) { sched_set_default_job_pick_plugin(NULL); /* must not crash */ TEST_PASS(); } /* ---------------------------------------------------------------------- */ /* Req 2 AC 2/3: xce_sched_queue_t.job_pick_plugin / .project_name */ /* ---------------------------------------------------------------------- */ /* A queue built from real config (make_sched_queue) with no picker/project * info in the raw config struct leaves both new fields NULL -- the * documented "unset"/"inherit" sentinel, not garbage or an empty string. */ static void test_make_sched_queue_defaults_project_name_and_override_to_null(void) { xce_scheduler_t sched; xce_queue_t q = make_test_raw_queue("normal"); xce_sched_queue_t* queue; fixture_init(&sched); queue = make_sched_queue(&sched, &q); TEST_ASSERT_NOT_NULL(queue); TEST_ASSERT_EQUAL_STRING("normal", queue->name); TEST_ASSERT_NULL(queue->job_pick_plugin); TEST_ASSERT_NULL(queue->project_name); } /* tasks.md Observable, half 1: "a queue config with no override inherits * the global picker name." Structural proof: the queue's own override * field is NULL, and the value any resolver would fall back to * (sched->job_pick_plugin) is the defaulted global name. */ static void test_queue_with_no_override_inherits_global_picker_name(void) { xce_scheduler_t sched; xce_queue_t q = make_test_raw_queue("batch"); xce_sched_queue_t* queue; fixture_init(&sched); sched_set_default_job_pick_plugin(&sched); /* global = "builtin_fcfs" */ queue = make_sched_queue(&sched, &q); TEST_ASSERT_NOT_NULL(queue); TEST_ASSERT_NULL(queue->job_pick_plugin); TEST_ASSERT_EQUAL_STRING("builtin_fcfs", sched.core.job_pick_plugin); } /* tasks.md Observable, half 2: "a queue config with an override reports * that override, not the global default." Config-driven wiring of this * field lands in a later task (xce_queue_t has no job_pick_plugin source * yet), so the override is applied via direct struct assignment here -- * exactly the "structurally representable" test style this task calls * for -- and then read back. */ static void test_queue_with_override_reports_its_own_override_not_global_default(void) { xce_scheduler_t sched; xce_queue_t q = make_test_raw_queue("gpu"); xce_sched_queue_t* queue; fixture_init(&sched); sched_set_default_job_pick_plugin(&sched); /* global = "builtin_fcfs" */ queue = make_sched_queue(&sched, &q); TEST_ASSERT_NOT_NULL(queue); queue->job_pick_plugin = makeString("priority_picker"); TEST_ASSERT_NOT_NULL(queue->job_pick_plugin); TEST_ASSERT_EQUAL_STRING("priority_picker", queue->job_pick_plugin); /* The override is reported, NOT the global default. */ TEST_ASSERT_TRUE(strcmp(queue->job_pick_plugin, sched.core.job_pick_plugin) != 0); TEST_ASSERT_EQUAL_STRING("builtin_fcfs", sched.core.job_pick_plugin); } /* project_name is independently settable/readable (structural field check * -- config-source wiring is a later task, same rationale as above). */ static void test_queue_project_name_is_independently_settable(void) { xce_scheduler_t sched; xce_queue_t q = make_test_raw_queue("gpu"); xce_sched_queue_t* queue; fixture_init(&sched); queue = make_sched_queue(&sched, &q); TEST_ASSERT_NOT_NULL(queue); queue->project_name = makeString("acct-42"); TEST_ASSERT_EQUAL_STRING("acct-42", queue->project_name); /* Setting project_name must not disturb the independent picker-override * field (still inheriting -- NULL). */ TEST_ASSERT_NULL(queue->job_pick_plugin); } /* ---------------------------------------------------------------------- */ /* Req 2 AC: xce_sched_project_t.priority (scaffolding) */ /* ---------------------------------------------------------------------- */ static void test_make_sched_project_priority_field_defaults_to_zero(void) { xce_scheduler_t sched; xce_sched_project_t* project; fixture_init(&sched); project = make_sched_project(&sched.core, "acct-42"); TEST_ASSERT_NOT_NULL(project); TEST_ASSERT_EQUAL_STRING("acct-42", project->name); /* Scaffolding only -- no quota/hierarchy logic exists yet * (project-resource-ownership spec). */ TEST_ASSERT_EQUAL_INT32(0, project->priority); } /* Type check: field accepts a plain int32_t assignment/comparison -- * compile-time proof of the declared type. */ static void test_project_priority_field_is_int32_and_independently_settable(void) { xce_scheduler_t sched; xce_sched_project_t* project; fixture_init(&sched); project = make_sched_project(&sched.core, "acct-7"); project->priority = 7; TEST_ASSERT_EQUAL_INT32(7, project->priority); /* Setting priority must not disturb the project's name. */ TEST_ASSERT_EQUAL_STRING("acct-7", project->name); } int main(void) { UNITY_BEGIN(); RUN_TEST(test_scheduler_job_pick_plugin_field_is_char_star_and_unset_by_default); RUN_TEST(test_sched_set_default_job_pick_plugin_applies_builtin_fcfs_when_unset); RUN_TEST(test_sched_set_default_job_pick_plugin_does_not_clobber_configured_value); RUN_TEST(test_sched_set_default_job_pick_plugin_is_null_safe); RUN_TEST(test_make_sched_queue_defaults_project_name_and_override_to_null); RUN_TEST(test_queue_with_no_override_inherits_global_picker_name); RUN_TEST(test_queue_with_override_reports_its_own_override_not_global_default); RUN_TEST(test_queue_project_name_is_independently_settable); RUN_TEST(test_make_sched_project_priority_field_defaults_to_zero); RUN_TEST(test_project_priority_field_is_int32_and_independently_settable); return UNITY_END(); }