feat(scheduler-allocator-runner): seqjob allocates through the framework runner (task 4.2)
seqjob registers sched_allocator_run with seqjob_alloc_iface (no accepts guard, plus its one-bucket plan iterator); its private my_alloc() is gone. New fixtures pin the intended changes: a reason for every short host, the bug-1893 shared reason, the host-stage OOM debit release, and -EINVAL for missing inputs; seqjob's plan is asserted to be one bucket of one slot. No other test expectation changed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
239bcf95cd
commit
65679285ba
7 changed files with 674 additions and 244 deletions
607
src/scheduler/testsuite/test_alloc_equivalence_seqjob_changes.c
Normal file
607
src/scheduler/testsuite/test_alloc_equivalence_seqjob_changes.c
Normal file
|
|
@ -0,0 +1,607 @@
|
|||
/*
|
||||
* test_alloc_equivalence_seqjob_changes.c - scheduler-allocator-runner task
|
||||
* 4.2: the sequential-job allocator's intentional changes and missing-input
|
||||
* guards, driven through the registered plugin->job_alloc_fn of
|
||||
* seqjob_allocator_new_plugin() (the rest of its characterization is in
|
||||
* test_alloc_equivalence_seqjob.c, whose stubs and fixture this file
|
||||
* mirrors).
|
||||
*
|
||||
* - every selection-matching short host gets its (host, resource)
|
||||
* NO_RESOURCE reasons; main reason, decision and debits unchanged
|
||||
* (Req 3.6, 3.7)
|
||||
* - with two shared resources requested and one short, exactly one
|
||||
* host-less reason, naming the short one (Req 2.2, bug-1893)
|
||||
* - seqjob's plan is one bucket of one slot whatever the task count
|
||||
* (brief.md Constraints item 3)
|
||||
* - a missing core, scheduler back-reference, primary or allocator
|
||||
* contract is -EINVAL with no pend-reason record (Req 5.1)
|
||||
*/
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <unity/unity.h>
|
||||
|
||||
#include "preason.h"
|
||||
#include "resobj.h"
|
||||
#include "resreq.h"
|
||||
#include "sched/alloc_iter.h"
|
||||
#include "scheduler.h"
|
||||
#include "xcealloc.h"
|
||||
#include "xstring.h"
|
||||
|
||||
/* White-box sources under test. */
|
||||
#include "../src/core.c"
|
||||
#include "../src/resource.c"
|
||||
#include "../src/schedobj.c"
|
||||
#include "../src/host_iter.c"
|
||||
#include "../src/alloc_runner.c"
|
||||
#include "../src/plugins/seqjob_allocator.c"
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Link-satisfying stubs (as test_alloc_equivalence_seqjob.c) */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
int plugin_register(void* sched, void* plugin)
|
||||
{
|
||||
(void)sched;
|
||||
(void)plugin;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void plugin_unregister(void* arg) { (void)arg; }
|
||||
|
||||
/* plugin.c's fan-out, seqjob's hook only; resets as plugin.c (bug-1883). */
|
||||
int plugin_prepare_allocation(xce_sched_core_t* sched, xce_sched_ctx_t* ctx)
|
||||
{
|
||||
xce_sched_plugin_t fake_plugin;
|
||||
|
||||
if (ctx->worker == NULL) {
|
||||
xvec_idval_fini(&(ctx->alloc_req.shared));
|
||||
xvec_idval_init(&(ctx->alloc_req.shared), 8);
|
||||
}
|
||||
xvec_idval_fini(&(ctx->alloc_req.host));
|
||||
xvec_idval_init(&(ctx->alloc_req.host), 8);
|
||||
|
||||
memset(&fake_plugin, 0, sizeof(fake_plugin));
|
||||
fake_plugin.sched = sched;
|
||||
fake_plugin.core = sched;
|
||||
|
||||
return seqjob_prepare_allocation(&fake_plugin, ctx);
|
||||
}
|
||||
|
||||
int plugin_job_update(void* s, xce_sched_job_t* j)
|
||||
{
|
||||
return (void)s, (void)j, 0;
|
||||
}
|
||||
|
||||
int plugin_queue_update(void* s, xce_sched_queue_t* q)
|
||||
{
|
||||
return (void)s, (void)q, 0;
|
||||
}
|
||||
|
||||
int plugin_job_rusage_update(void* s, xce_sched_job_t* j, xce_job_rusages_t* r)
|
||||
{
|
||||
return (void)s, (void)j, (void)r, 0;
|
||||
}
|
||||
|
||||
/* scheduler.c's sched_make_allocs(): real make_alloc() records (duration -1
|
||||
* for a NULL duration array, instid 0); NULL for nres <= 0 as the real one.
|
||||
* Counted so a test can assert no result was built. */
|
||||
static int g_make_allocs_calls = 0;
|
||||
|
||||
xce_alloc_t** sched_make_allocs(xce_scheduler_t* sched, char* reserve,
|
||||
int nres, int* idxs, float* vals,
|
||||
int* duration)
|
||||
{
|
||||
xce_alloc_t** allocs;
|
||||
int i;
|
||||
|
||||
g_make_allocs_calls++;
|
||||
if ((sched == NULL) || (nres <= 0) || (idxs == NULL) || (vals == NULL)) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
allocs = (xce_alloc_t**)calloc(nres, sizeof(*allocs));
|
||||
if (allocs == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < nres; i++) {
|
||||
xce_resource_t* r = NULL;
|
||||
|
||||
vget(sched->core.resmgr, idxs[i], (void**)&r);
|
||||
if (r == NULL) {
|
||||
continue;
|
||||
}
|
||||
allocs[i] = make_alloc(r->name, reserve, 0, vals[i],
|
||||
(duration != NULL) ? duration[i] : -1);
|
||||
if (allocs[i] == NULL) {
|
||||
free_ResAlloc_list(nres, allocs);
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return allocs;
|
||||
}
|
||||
|
||||
/* The target links with the same --wrap flags as the seqjob
|
||||
* characterization target; no injection is needed here. */
|
||||
xce_host_alloc_t* __real_make_host_alloc(char*, int, xce_alloc_t**);
|
||||
xce_job_alloc_t* __real_make_job_alloc(uint64_t, int, xce_host_alloc_t**);
|
||||
|
||||
xce_host_alloc_t* __wrap_make_host_alloc(char* host, int nalloc,
|
||||
xce_alloc_t** allocs)
|
||||
{
|
||||
return __real_make_host_alloc(host, nalloc, allocs);
|
||||
}
|
||||
|
||||
xce_job_alloc_t* __wrap_make_job_alloc(uint64_t jobid, int nhost,
|
||||
xce_host_alloc_t** hallocs)
|
||||
{
|
||||
return __real_make_job_alloc(jobid, nhost, hallocs);
|
||||
}
|
||||
|
||||
void setUp(void) { g_make_allocs_calls = 0; }
|
||||
void tearDown(void) { g_make_allocs_calls = 0; }
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Fixture */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
#define HOST_A "eq-host-a"
|
||||
#define HOST_B "eq-host-b"
|
||||
#define HOST_C "eq-host-c"
|
||||
#define HOST_D "eq-host-d"
|
||||
|
||||
typedef struct {
|
||||
xce_scheduler_t sched;
|
||||
xce_resource_t* slots;
|
||||
xce_resource_t* mem;
|
||||
xce_resource_t* rack;
|
||||
xce_resource_t* lic; /* shared, pool set per fixture */
|
||||
xce_resource_t* tok; /* shared, pool 10 */
|
||||
xce_sched_plugin_t* plugin;
|
||||
xce_sched_obj_t obj;
|
||||
xce_primary_schedobj_t primary_obj;
|
||||
xce_sched_ctx_t ctx;
|
||||
int ctx_ready;
|
||||
} eq_fixture_t;
|
||||
|
||||
/* One primary request: `mem` per task, `lic` per job (0 = not requested),
|
||||
* optional `rack == N` select. */
|
||||
typedef struct {
|
||||
xce_primary_resreq_t req;
|
||||
rusage_value_t mem_rusage;
|
||||
rusage_value_t lic_rusage;
|
||||
rusage_value_t tok_rusage;
|
||||
xce_select_t select;
|
||||
xce_select_node_t sel_res, sel_num, sel_eq;
|
||||
} eq_primary_t;
|
||||
|
||||
static xce_resource_t* eq_register_resource(var resmgr, const char* name,
|
||||
int flags)
|
||||
{
|
||||
xce_resource_t res;
|
||||
|
||||
memset(&res, 0, sizeof(res));
|
||||
res.name = (char*)name;
|
||||
res.flags = flags;
|
||||
|
||||
return makeResource(resmgr, &res);
|
||||
}
|
||||
|
||||
/* Shared pools "licenses" (`lic_total`) and "tokens" (10), none in use. */
|
||||
static void eq_fixture_init(eq_fixture_t* f, float lic_total)
|
||||
{
|
||||
int idx[2];
|
||||
float val[2];
|
||||
int i;
|
||||
|
||||
memset(f, 0, sizeof(*f));
|
||||
TEST_ASSERT_EQUAL_INT(0, xce_sched_core_init(&(f->sched.core)));
|
||||
/* xce_sched_core_fini() finalizes these; both drivers calloc them */
|
||||
for (i = 0; i < 2; i++) {
|
||||
f->sched.core.pend_updates[i] = (dataq_t*)calloc(1, sizeof(dataq_t));
|
||||
TEST_ASSERT_NOT_NULL(f->sched.core.pend_updates[i]);
|
||||
dataq_init(f->sched.core.pend_updates[i], NULL);
|
||||
}
|
||||
|
||||
f->slots = eq_register_resource(f->sched.core.resmgr, "slots", 0);
|
||||
f->mem = eq_register_resource(f->sched.core.resmgr, "mem", 0);
|
||||
f->rack = eq_register_resource(f->sched.core.resmgr, "rack", 0);
|
||||
f->lic = eq_register_resource(f->sched.core.resmgr, "licenses",
|
||||
XCE_RESF_GLOBAL);
|
||||
f->tok = eq_register_resource(f->sched.core.resmgr, "tokens",
|
||||
XCE_RESF_GLOBAL);
|
||||
TEST_ASSERT_NOT_NULL(f->slots);
|
||||
TEST_ASSERT_NOT_NULL(f->mem);
|
||||
TEST_ASSERT_NOT_NULL(f->rack);
|
||||
TEST_ASSERT_NOT_NULL(f->lic);
|
||||
TEST_ASSERT_NOT_NULL(f->tok);
|
||||
|
||||
idx[0] = f->lic->idx;
|
||||
val[0] = lic_total;
|
||||
idx[1] = f->tok->idx;
|
||||
val[1] = 10.0f;
|
||||
put_resvals(&(f->sched.core.sharedInst), 2, idx, val);
|
||||
|
||||
f->plugin = seqjob_allocator_new_plugin();
|
||||
TEST_ASSERT_NOT_NULL(f->plugin);
|
||||
TEST_ASSERT_NOT_NULL(f->plugin->job_alloc_fn);
|
||||
f->plugin->sched = &(f->sched);
|
||||
f->plugin->core = &(f->sched.core);
|
||||
}
|
||||
|
||||
static xce_sched_worker_t* eq_add_host(eq_fixture_t* f, const char* name,
|
||||
float slots, float mem, float rack)
|
||||
{
|
||||
xce_sched_worker_t* w;
|
||||
int idx[3] = { f->slots->idx, f->mem->idx, f->rack->idx };
|
||||
float total[3] = { slots, mem, rack };
|
||||
float inuse[2] = { 0.0f, 0.0f };
|
||||
|
||||
w = (xce_sched_worker_t*)calloc(1, sizeof(*w));
|
||||
TEST_ASSERT_NOT_NULL(w);
|
||||
w->name = makeString((char*)name);
|
||||
TEST_ASSERT_NOT_NULL(w->name);
|
||||
zarray_init(&(w->resvals), 8, (zarray_item_killer_t)kill_sched_resval);
|
||||
zarray_init(&(w->resInUse), 8, (zarray_item_killer_t)kill_sched_resval);
|
||||
put_resvals(&(w->resvals), 3, idx, total);
|
||||
put_resvals(&(w->resInUse), 2, idx, inuse);
|
||||
|
||||
TEST_ASSERT_TRUE(sched_add_worker(&(f->sched.core), w) >= 0);
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
static void eq_primary_init(eq_primary_t* p, int max_tasks, float mem,
|
||||
float lic, const eq_fixture_t* f, int rack)
|
||||
{
|
||||
memset(p, 0, sizeof(*p));
|
||||
p->req.minTasks = (max_tasks > 0) ? 1 : 0;
|
||||
p->req.maxTasks = max_tasks;
|
||||
xlist_init(&(p->req.rusages), 4);
|
||||
|
||||
if (mem > 0.0f) {
|
||||
p->mem_rusage.res = (char*)"mem";
|
||||
p->mem_rusage.value = mem;
|
||||
p->mem_rusage.per_flag = PER_TASK;
|
||||
xlist_store(&(p->req.rusages), &(p->mem_rusage));
|
||||
}
|
||||
if (lic > 0.0f) {
|
||||
p->lic_rusage.res = (char*)"licenses";
|
||||
p->lic_rusage.value = lic;
|
||||
p->lic_rusage.per_flag = PER_JOB;
|
||||
xlist_store(&(p->req.rusages), &(p->lic_rusage));
|
||||
}
|
||||
if (rack > 0) {
|
||||
p->sel_res.type = SEL_RESNAME;
|
||||
p->sel_res.u.resname.res_idx = f->rack->idx;
|
||||
p->sel_res.u.resname.res_type = RES_TYPE_NUMERIC;
|
||||
p->sel_num.type = SEL_NUM;
|
||||
p->sel_num.u.numval = (double)rack;
|
||||
p->sel_eq.type = SEL_EQ;
|
||||
p->sel_eq.u.binop.left = &(p->sel_res);
|
||||
p->sel_eq.u.binop.right = &(p->sel_num);
|
||||
p->select.root = &(p->sel_eq);
|
||||
p->req.select = &(p->select);
|
||||
}
|
||||
}
|
||||
|
||||
/* ctx over a PRIMARY obj for `p`, job-level prepare pass included, exactly
|
||||
* as sched_make_primary_allocation() drives it. */
|
||||
static void eq_ctx_begin_primary(eq_fixture_t* f, eq_primary_t* p)
|
||||
{
|
||||
f->primary_obj.resreq = &(p->req);
|
||||
f->obj.tag = XCE_RESREQ_PRIMARY;
|
||||
f->obj.u.primary = &(f->primary_obj);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, sched_ctx_init(&(f->ctx), &(f->sched.core),
|
||||
&(f->obj)));
|
||||
f->ctx_ready = 1;
|
||||
sched_ctx_set_primary(&(f->ctx), &(p->req));
|
||||
}
|
||||
|
||||
static int eq_alloc(eq_fixture_t* f)
|
||||
{
|
||||
return f->plugin->job_alloc_fn(f->plugin, &(f->ctx));
|
||||
}
|
||||
|
||||
static void eq_finish(eq_fixture_t* f, eq_primary_t* p)
|
||||
{
|
||||
if (f->ctx_ready) {
|
||||
free_JobAlloc_ptr(&(f->ctx.alloc));
|
||||
free_PendReason_ptr(&(f->ctx.reason));
|
||||
sched_ctx_fini(&(f->ctx));
|
||||
}
|
||||
f->plugin->fini_fn(f->plugin);
|
||||
xce_sched_core_fini(&(f->sched.core));
|
||||
/* xce_sched_core_init() creates the process-wide string table on every
|
||||
* call and xce_sched_core_fini() leaves it; release it per fixture */
|
||||
xstring_table_fini();
|
||||
xlist_fini(&(p->req.rusages));
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Assertion helpers */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
static float eq_host_uncomit(eq_fixture_t* f, xce_sched_worker_t* w,
|
||||
xce_resource_t* r)
|
||||
{
|
||||
xvec_t* v = (xvec_t*)zarray_get(&(f->ctx.uncomitted.hosts), w->idx);
|
||||
|
||||
return (v == NULL) ? 0.0f : ((float*)v->data)[r->idx];
|
||||
}
|
||||
|
||||
static float eq_shared_uncomit(eq_fixture_t* f, xce_resource_t* r)
|
||||
{
|
||||
return ((float*)f->ctx.uncomitted.shared->data)[r->idx];
|
||||
}
|
||||
|
||||
static void eq_assert_reason_counts(eq_fixture_t* f, int main_reason,
|
||||
int nhost, int nres)
|
||||
{
|
||||
TEST_ASSERT_NOT_NULL(f->ctx.reason);
|
||||
TEST_ASSERT_NOT_NULL(f->ctx.reason->reasons);
|
||||
TEST_ASSERT_EQUAL_INT(main_reason, f->ctx.reason->reasons->main_reason);
|
||||
TEST_ASSERT_EQUAL_INT(nhost, f->ctx.reason->reasons->nhost_reasons);
|
||||
TEST_ASSERT_EQUAL_INT(nres, f->ctx.reason->reasons->nres_reasons);
|
||||
}
|
||||
|
||||
static void eq_assert_res_reason(eq_fixture_t* f, int i, const char* host,
|
||||
const char* res)
|
||||
{
|
||||
xce_res_reason_t* rr = f->ctx.reason->reasons->res_based_reasons[i];
|
||||
|
||||
TEST_ASSERT_NOT_NULL(rr);
|
||||
TEST_ASSERT_EQUAL_STRING(host, rr->host);
|
||||
TEST_ASSERT_EQUAL_STRING(res, rr->resource);
|
||||
TEST_ASSERT_EQUAL_INT(PREASON_NO_RESOURCE, rr->reason);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Req 3.6 / 3.7: a reason for every selection-matching short host */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
/* Hosts in iteration order: A (no free slot), B (short of mem), D (rack 2,
|
||||
* outside the job's rack-1 select), C (short of mem). Before the switch,
|
||||
* A was pre-filtered against the job-level request (slots 1) and C against
|
||||
* the request B left behind (mem 4, slots 1), both silently: one reason,
|
||||
* (B, mem). Now the per-host request is empty before every candidate, so
|
||||
* A, B and C each reach the per-host check; D is still skipped by the
|
||||
* select filter alone and gets no reason. Decision, main reason and debits
|
||||
* are unchanged. */
|
||||
static void test_seqjob_every_short_host_gets_reasons(void)
|
||||
{
|
||||
eq_fixture_t f;
|
||||
eq_primary_t p;
|
||||
xce_sched_worker_t* w[4];
|
||||
int i;
|
||||
|
||||
eq_fixture_init(&f, 10.0f);
|
||||
w[0] = eq_add_host(&f, HOST_A, 0.0f, 8.0f, 1.0f);
|
||||
w[1] = eq_add_host(&f, HOST_B, 4.0f, 1.0f, 1.0f);
|
||||
w[2] = eq_add_host(&f, HOST_D, 4.0f, 1.0f, 2.0f);
|
||||
w[3] = eq_add_host(&f, HOST_C, 4.0f, 1.0f, 1.0f);
|
||||
eq_primary_init(&p, 1, 4.0f, 3.0f, &f, 1);
|
||||
eq_ctx_begin_primary(&f, &p);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, eq_alloc(&f));
|
||||
TEST_ASSERT_NULL(f.ctx.alloc);
|
||||
|
||||
eq_assert_reason_counts(&f, 0, 0, 3);
|
||||
eq_assert_res_reason(&f, 0, HOST_A, "slots");
|
||||
eq_assert_res_reason(&f, 1, HOST_B, "mem");
|
||||
eq_assert_res_reason(&f, 2, HOST_C, "mem");
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
TEST_ASSERT_EQUAL_FLOAT(0.0f, eq_host_uncomit(&f, w[i], f.mem));
|
||||
TEST_ASSERT_EQUAL_FLOAT(0.0f, eq_host_uncomit(&f, w[i], f.slots));
|
||||
}
|
||||
TEST_ASSERT_EQUAL_FLOAT(3.0f, eq_shared_uncomit(&f, f.lic));
|
||||
TEST_ASSERT_EQUAL_INT(0, g_make_allocs_calls);
|
||||
|
||||
eq_finish(&f, &p);
|
||||
}
|
||||
|
||||
/* Two short hosts, then one that fits: A (no free slot) and B (short of
|
||||
* mem) each get their reasons, C is claimed; the decision is unchanged.
|
||||
* Before the switch A was pre-filtered silently: one reason, (B, mem). */
|
||||
static void test_seqjob_short_hosts_before_claim_get_reasons(void)
|
||||
{
|
||||
eq_fixture_t f;
|
||||
eq_primary_t p;
|
||||
xce_sched_worker_t* w[3];
|
||||
|
||||
eq_fixture_init(&f, 10.0f);
|
||||
w[0] = eq_add_host(&f, HOST_A, 0.0f, 8.0f, 1.0f);
|
||||
w[1] = eq_add_host(&f, HOST_B, 4.0f, 1.0f, 1.0f);
|
||||
w[2] = eq_add_host(&f, HOST_C, 4.0f, 8.0f, 1.0f);
|
||||
eq_primary_init(&p, 1, 4.0f, 0.0f, &f, 0);
|
||||
eq_ctx_begin_primary(&f, &p);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(1, eq_alloc(&f));
|
||||
TEST_ASSERT_NOT_NULL(f.ctx.alloc);
|
||||
TEST_ASSERT_EQUAL_INT(1, f.ctx.alloc->nhosts);
|
||||
TEST_ASSERT_EQUAL_STRING(HOST_C, f.ctx.alloc->allocs[0]->host);
|
||||
|
||||
eq_assert_reason_counts(&f, 0, 0, 2);
|
||||
eq_assert_res_reason(&f, 0, HOST_A, "slots");
|
||||
eq_assert_res_reason(&f, 1, HOST_B, "mem");
|
||||
|
||||
TEST_ASSERT_EQUAL_FLOAT(0.0f, eq_host_uncomit(&f, w[0], f.slots));
|
||||
TEST_ASSERT_EQUAL_FLOAT(0.0f, eq_host_uncomit(&f, w[1], f.mem));
|
||||
TEST_ASSERT_EQUAL_FLOAT(4.0f, eq_host_uncomit(&f, w[2], f.mem));
|
||||
TEST_ASSERT_EQUAL_FLOAT(1.0f, eq_host_uncomit(&f, w[2], f.slots));
|
||||
|
||||
eq_finish(&f, &p);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Req 2.2 / bug-1893: only the short shared resource is named */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
/* "licenses" (3 wanted, pool 2) is short, "tokens" (1 wanted, pool 10) is
|
||||
* met: exactly one host-less reason (host stored as ""), naming licenses.
|
||||
* No host is tried and nothing is counted. */
|
||||
static void test_seqjob_pending_on_short_shared_names_short_one(void)
|
||||
{
|
||||
eq_fixture_t f;
|
||||
eq_primary_t p;
|
||||
xce_sched_worker_t* a;
|
||||
|
||||
eq_fixture_init(&f, 2.0f);
|
||||
a = eq_add_host(&f, HOST_A, 4.0f, 8.0f, 1.0f);
|
||||
eq_primary_init(&p, 1, 2.0f, 3.0f, &f, 0);
|
||||
p.tok_rusage.res = (char*)"tokens";
|
||||
p.tok_rusage.value = 1.0f;
|
||||
p.tok_rusage.per_flag = PER_JOB;
|
||||
xlist_store(&(p.req.rusages), &(p.tok_rusage));
|
||||
eq_ctx_begin_primary(&f, &p);
|
||||
TEST_ASSERT_EQUAL_INT(2, f.ctx.alloc_req.shared.count);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, eq_alloc(&f));
|
||||
TEST_ASSERT_NULL(f.ctx.alloc);
|
||||
|
||||
eq_assert_reason_counts(&f, 0, 0, 1);
|
||||
eq_assert_res_reason(&f, 0, "", "licenses");
|
||||
|
||||
TEST_ASSERT_NULL(zarray_get(&(f.ctx.uncomitted.hosts), a->idx));
|
||||
TEST_ASSERT_EQUAL_FLOAT(0.0f, eq_shared_uncomit(&f, f.lic));
|
||||
TEST_ASSERT_EQUAL_FLOAT(0.0f, eq_shared_uncomit(&f, f.tok));
|
||||
TEST_ASSERT_EQUAL_INT(0, g_make_allocs_calls);
|
||||
|
||||
eq_finish(&f, &p);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* brief.md Constraints item 3: seqjob's plan shape */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
/* The runner sets nslots to bucket_slots[b] and nhosts to 1 per bucket;
|
||||
* seqjob relies on its one plan being one bucket of one slot, for every
|
||||
* task count that can reach it. */
|
||||
static void eq_check_plan_shape(int max_tasks)
|
||||
{
|
||||
xce_alloc_plan_iter_t it;
|
||||
xce_alloc_plan_t* plan;
|
||||
xce_primary_resreq_t req;
|
||||
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.maxTasks = max_tasks;
|
||||
req.minTasks = (max_tasks > 0) ? 1 : 0;
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, seqjob_plan_iter_init(&it, &req, NULL));
|
||||
|
||||
plan = seqjob_plan_iter_next(&it);
|
||||
TEST_ASSERT_NOT_NULL(plan);
|
||||
TEST_ASSERT_EQUAL_INT(1, plan->nbuckets);
|
||||
TEST_ASSERT_NOT_NULL(plan->bucket_slots);
|
||||
TEST_ASSERT_EQUAL_INT(1, plan->bucket_slots[0]);
|
||||
|
||||
TEST_ASSERT_NULL(seqjob_plan_iter_next(&it));
|
||||
TEST_ASSERT_NULL(seqjob_plan_iter_next(&it));
|
||||
|
||||
seqjob_plan_iter_fini(&it);
|
||||
TEST_ASSERT_NULL(it.private);
|
||||
}
|
||||
|
||||
static void test_seqjob_plan_is_one_bucket_of_one_slot(void)
|
||||
{
|
||||
eq_check_plan_shape(1);
|
||||
eq_check_plan_shape(0);
|
||||
eq_check_plan_shape(2);
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* Req 5.1: missing inputs */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
enum { EQ_NO_CORE, EQ_NO_SCHED, EQ_NO_PRIMARY, EQ_NO_IFACE };
|
||||
|
||||
/* The standard satisfiable job with one input removed just before the
|
||||
* call: -EINVAL, no pend-reason record, nothing built or debited. The
|
||||
* input is put back before teardown. */
|
||||
static void eq_check_missing_input(int which)
|
||||
{
|
||||
eq_fixture_t f;
|
||||
eq_primary_t p;
|
||||
xce_sched_worker_t* a;
|
||||
xce_primary_resreq_t* primary;
|
||||
int rc;
|
||||
|
||||
eq_fixture_init(&f, 10.0f);
|
||||
a = eq_add_host(&f, HOST_A, 4.0f, 8.0f, 1.0f);
|
||||
eq_primary_init(&p, 1, 2.0f, 3.0f, &f, 0);
|
||||
eq_ctx_begin_primary(&f, &p);
|
||||
primary = f.ctx.primary;
|
||||
|
||||
switch (which) {
|
||||
case EQ_NO_CORE:
|
||||
f.plugin->core = NULL;
|
||||
break;
|
||||
case EQ_NO_SCHED:
|
||||
f.plugin->sched = NULL;
|
||||
break;
|
||||
case EQ_NO_PRIMARY:
|
||||
f.ctx.primary = NULL;
|
||||
break;
|
||||
default:
|
||||
f.plugin->allocator_iface = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
rc = eq_alloc(&f);
|
||||
|
||||
f.plugin->core = &(f.sched.core);
|
||||
f.plugin->sched = &(f.sched);
|
||||
f.plugin->allocator_iface = &seqjob_alloc_iface;
|
||||
f.ctx.primary = primary;
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(-EINVAL, rc);
|
||||
TEST_ASSERT_NULL(f.ctx.reason);
|
||||
TEST_ASSERT_NULL(f.ctx.alloc);
|
||||
TEST_ASSERT_NULL(zarray_get(&(f.ctx.uncomitted.hosts), a->idx));
|
||||
TEST_ASSERT_EQUAL_FLOAT(0.0f, eq_shared_uncomit(&f, f.lic));
|
||||
TEST_ASSERT_EQUAL_INT(0, g_make_allocs_calls);
|
||||
|
||||
eq_finish(&f, &p);
|
||||
}
|
||||
|
||||
static void test_seqjob_missing_core_is_einval(void)
|
||||
{
|
||||
eq_check_missing_input(EQ_NO_CORE);
|
||||
}
|
||||
|
||||
static void test_seqjob_missing_sched_is_einval(void)
|
||||
{
|
||||
eq_check_missing_input(EQ_NO_SCHED);
|
||||
}
|
||||
|
||||
static void test_seqjob_missing_primary_is_einval(void)
|
||||
{
|
||||
eq_check_missing_input(EQ_NO_PRIMARY);
|
||||
}
|
||||
|
||||
static void test_seqjob_missing_iface_is_einval(void)
|
||||
{
|
||||
eq_check_missing_input(EQ_NO_IFACE);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(test_seqjob_every_short_host_gets_reasons);
|
||||
RUN_TEST(test_seqjob_short_hosts_before_claim_get_reasons);
|
||||
RUN_TEST(test_seqjob_pending_on_short_shared_names_short_one);
|
||||
RUN_TEST(test_seqjob_plan_is_one_bucket_of_one_slot);
|
||||
RUN_TEST(test_seqjob_missing_core_is_einval);
|
||||
RUN_TEST(test_seqjob_missing_sched_is_einval);
|
||||
RUN_TEST(test_seqjob_missing_primary_is_einval);
|
||||
RUN_TEST(test_seqjob_missing_iface_is_einval);
|
||||
return UNITY_END();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue