feat(scheduler-sim-decision-trace): hook the host pre-filter, host scan and satisfaction check
Task 3.3: the pre-filter records selection mismatches and internal errors as host verdicts, a scan stopped by an error marks the position as a scan error, and unsatisfy_() hands every checked row to the current record. Iterator and check results are unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
542c6e602a
commit
d4bee4b5d0
6 changed files with 834 additions and 3 deletions
|
|
@ -154,7 +154,7 @@
|
|||
- _Boundary: TraceHooks (resource-requirement branch)_
|
||||
- _Depends: 2.1_
|
||||
|
||||
- [ ] 3.3 Hook the host pre-filter, the host scan and the satisfaction check (P)
|
||||
- [x] 3.3 Hook the host pre-filter, the host scan and the satisfaction check (P)
|
||||
- The pre-filter records a selection mismatch and a pre-filter internal error as host verdicts.
|
||||
- A host scan that stops on an error marks the current position as a scan error.
|
||||
- The satisfaction check hands every row to the current record.
|
||||
|
|
@ -362,3 +362,4 @@
|
|||
- 2.4: the trace sources are now sched_trace.c (record, arena, entries, budget, requirement/plan/check), sched_trace_scan.c (positions, host verdicts/rows, tallies, rollback, commit, search end), sched_trace_capture.c, sched_trace_outcome.c (dispatch, pend, reconstructed) and sched_trace_internal.h (12 shared `sti_` helpers). Every target that compiles one trace .c file needs all four. Later tasks that extend the trace must keep each file under 800 lines and add new files to the core isolation scan.
|
||||
- 3.1: allocator-loop hooks live in `sched_allocator()` (sched_election.c), with helpers `sched_trace_push_no_decision()` and `sched_trace_attach()`. Selection is captured at job lookup, before election, so NO_SCHEDOBJ still has its selection; cluster-wide limits are captured after election. The position counter (`ctl->position`) is not SCHED_TRACE-gated (it creates the record) and counts only real pops. `sched_sim_isolation_lib` stays without the trace sources (unresolved references in a static archive are harmless); 5.1 decides whether to add them. `core->decisions` holds 5 items, so trace-only decisions take FIFO slots and cannot reorder real ones.
|
||||
- 3.2: requirement-branch hooks are in scheduler.c (`sched_make_allocation`, `sched_make_alter_allocation`, `sched_make_compound_alloc_`). The alternatives after the chosen one are not stored as entries; 4.1 renders "alternatives i+1..n not tried" from the alt_result (i,n). A merge failure stores an ATTEMPT_REASONS entry with ncodes==0. A failing single requirement records no attempt reasons; its pend outcome carries them. A single job with a NULL resreq stores its begin as a container with 0 members before returning -EINVAL, and 4.1 must tolerate that.
|
||||
- 3.3: the pre-filter (`sched_host_iter_next`) opens a host scope (`sched_trace_host_begin`) for every candidate and closes it with a verdict on each rejection: SELECT (pre-filter), INTERNAL(rc) for an uncommitted-fetch or check error, from-rows for a resource rejection (unreachable from the runner). A host the iterator returns is left in an open scope with no entry, so 3.4 must end or replace it for the accepted, already-claimed, snapshot-failure and per-host-error paths, or later rows attach to a stale host. A scan stopped with rc<0 calls `pos_scan_error`; exhaustion does not. `unsatisfy_` hands every row to `sched_trace_check_rows`. The sim thread's reconciliation reaches that hook but sees trace_rec NULL.
|
||||
|
|
|
|||
|
|
@ -94,8 +94,10 @@ typedef struct xce_sched_trace_rec_s xce_sched_trace_rec_t;
|
|||
* effects and argument evaluation included) runs only while `core` (an
|
||||
* xce_sched_core_t*) has a record under construction in trace_rec, so a
|
||||
* hook costs one pointer test when nothing is traced, and nothing else.
|
||||
* Only the allocator thread sets trace_rec, so hooks are placed only on
|
||||
* that thread. One statement: safe in an unbraced if/else.
|
||||
* Only the allocator thread sets trace_rec. A hooked function called from
|
||||
* another thread (the sim thread's reconciliation reaches unsatisfy_)
|
||||
* sees NULL there, because the allocator clears trace_rec before it hands
|
||||
* each decision back. One statement: safe in an unbraced if/else.
|
||||
*/
|
||||
#define SCHED_TRACE(core, call) \
|
||||
do { \
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include "sched/alloc_iter.h"
|
||||
#include "sched/trace.h"
|
||||
#include "sched/types.h"
|
||||
#include "scheduler.h"
|
||||
|
||||
|
|
@ -124,6 +125,12 @@ xce_sched_worker_t* sched_host_iter_next(xce_host_iter_t* iter)
|
|||
|
||||
rc = iter_next(&(priv->it), (void**)&w);
|
||||
if ((rc < 0) || (rc == RTTI_ITER_NO_DATA)) {
|
||||
if (rc < 0) {
|
||||
/* Decision trace (4.4): the scan stopped on an error, not
|
||||
* on exhaustion; the position's unfilled end shows it */
|
||||
SCHED_TRACE(core,
|
||||
sched_trace_pos_scan_error(core->trace_rec, rc));
|
||||
}
|
||||
priv->exhausted = 1;
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -132,10 +139,20 @@ xce_sched_worker_t* sched_host_iter_next(xce_host_iter_t* iter)
|
|||
continue;
|
||||
}
|
||||
|
||||
/* Decision trace: the pre-filter's own host scope. A rejection
|
||||
* below ends it with the host's verdict; a host returned to the
|
||||
* caller leaves it open for the caller's own host_begin, which
|
||||
* replaces it (alloc_runner.c, task 3.4). */
|
||||
SCHED_TRACE(core, sched_trace_host_begin(core->trace_rec,
|
||||
ctx->schedobj, w));
|
||||
|
||||
/* 1. Select-expression filter -- existing evaluator, unchanged. */
|
||||
if (!sched_meet_host_selection(
|
||||
core, w,
|
||||
(ctx->primary != NULL) ? ctx->primary->select : NULL)) {
|
||||
/* Decision trace (5.3): selection mismatch, pre-filter phase */
|
||||
SCHED_TRACE(core, sched_trace_host_verdict(
|
||||
core->trace_rec, XCE_TRACE_V_SELECT, 0));
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -152,6 +169,10 @@ xce_sched_worker_t* sched_host_iter_next(xce_host_iter_t* iter)
|
|||
* growing its per-host uncommitted-debit vector) -- not a
|
||||
* resource-availability determination. Skip this host and
|
||||
* keep scanning rather than aborting the whole search. */
|
||||
/* Decision trace (5.7): not evaluated, internal error */
|
||||
SCHED_TRACE(core, sched_trace_host_verdict(
|
||||
core->trace_rec, XCE_TRACE_V_INTERNAL,
|
||||
rc));
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -161,6 +182,16 @@ xce_sched_worker_t* sched_host_iter_next(xce_host_iter_t* iter)
|
|||
&(ctx->alloc_req.host),
|
||||
&(ctx->unmeet));
|
||||
if (rc <= 0) {
|
||||
/* Decision trace: an error is an internal-error verdict
|
||||
* (5.7); a rejection takes its verdict from the unmet rows
|
||||
* the check just recorded in this host scope (5.4, 5.5).
|
||||
* That verdict carries the per-host phase; the runner never
|
||||
* reaches it, since it empties alloc_req.host before every
|
||||
* next() and an empty want is satisfied at once. */
|
||||
SCHED_TRACE(core, sched_trace_host_verdict(
|
||||
core->trace_rec,
|
||||
(rc < 0) ? XCE_TRACE_V_INTERNAL : 0u,
|
||||
rc));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include "plugin.h"
|
||||
#include "preason.h"
|
||||
#include "resreq.h"
|
||||
#include "sched/trace.h"
|
||||
#include "sched/types.h"
|
||||
#include "scheduler.h"
|
||||
#include "utils.h"
|
||||
|
|
@ -151,6 +152,11 @@ static int unsatisfy_(xce_sched_core_t* core, int nres, int* idxs, float* total,
|
|||
int i, j;
|
||||
int count;
|
||||
|
||||
/* Decision trace (4.1, 4.2, 5.4, 5.5): every row to the record; the
|
||||
* capture keeps them by the open scope (cluster-wide check or host) */
|
||||
SCHED_TRACE(core, sched_trace_check_rows(core->trace_rec, core, nres, idxs,
|
||||
total, inuse, uncomitted, wanted));
|
||||
|
||||
count = 0;
|
||||
for (i = 0; i < nres; i++) {
|
||||
float uncomit = uncomit_at_(uncomitted, idxs[i]);
|
||||
|
|
|
|||
|
|
@ -2680,3 +2680,19 @@ sched_trace_hooks_resreq_test = executable(
|
|||
test('scheduler trace hooks resreq test',
|
||||
sched_trace_hooks_resreq_test, suite: 'scheduler',
|
||||
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
||||
|
||||
# scheduler-sim-decision-trace task 3.3: the host pre-filter, host scan and
|
||||
# satisfaction-check trace hooks (design.md "TraceHooks": Pre-filter, Scan
|
||||
# stop, Shortfall rows; Requirements 4.1, 4.2, 4.4, 5.2-5.5, 5.7) on the
|
||||
# real host_iter.c and resource.c. Same white-box #include set as
|
||||
# sched_host_iter_test, with host_iter.c's fault seams renamed by macros.
|
||||
sched_trace_host_hooks_test = executable(
|
||||
'sched_trace_host_hooks_test',
|
||||
['test_trace_host_hooks.c'],
|
||||
include_directories:[incdir],
|
||||
dependencies: [scheduler_test_deps],
|
||||
install: false)
|
||||
|
||||
test('scheduler trace host hooks test',
|
||||
sched_trace_host_hooks_test, suite: 'scheduler',
|
||||
env: ['ASAN_OPTIONS=detect_leaks=0'])
|
||||
|
|
|
|||
775
src/scheduler/testsuite/test_trace_host_hooks.c
Normal file
775
src/scheduler/testsuite/test_trace_host_hooks.c
Normal file
|
|
@ -0,0 +1,775 @@
|
|||
/*
|
||||
* test_trace_host_hooks.c - decision-trace hooks in the host pre-filter,
|
||||
* the host scan and the satisfaction check (scheduler-sim-decision-trace
|
||||
* task 3.3; design.md "TraceHooks": Pre-filter, Scan stop, Shortfall rows;
|
||||
* Requirements 4.1, 4.2, 4.4, 5.2-5.5, 5.7).
|
||||
*
|
||||
* Drives the real host_iter.c and resource.c over the same white-box
|
||||
* fixture as test_host_iter.c (core.c + resource.c + schedobj.c +
|
||||
* host_iter.c, all #included), with a trace record set on the core:
|
||||
* - the pre-filter's selection mismatch is a SELECT verdict in the
|
||||
* pre-filter phase, and its internal errors (the per-host uncommitted
|
||||
* fetch, the availability check) are INTERNAL verdicts with the error;
|
||||
* - a pre-filter resource rejection carries the unmet rows it found;
|
||||
* - a scan that stops on an error marks the position, so its unfilled
|
||||
* end is a scan error; plain exhaustion does not;
|
||||
* - the satisfaction check hands its rows to the record in both scopes:
|
||||
* the cluster-wide check keeps every row, a host scope its unmet rows;
|
||||
* outside any scope nothing is kept.
|
||||
* Every scenario is also run untraced, and the iterator's hosts and the
|
||||
* check's return value and unmet vector must be identical (Requirement 10).
|
||||
*
|
||||
* Faults are injected by renaming three calls inside host_iter.c only
|
||||
* (macros around its #include): the per-host uncommitted fetch, the
|
||||
* availability check and the RTTI iterator step.
|
||||
*/
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <unity/unity.h>
|
||||
|
||||
#include "resreq.h"
|
||||
#include "sched/alloc_iter.h"
|
||||
#include "sched/trace.h"
|
||||
#include "scheduler.h"
|
||||
|
||||
#include "../src/core.c"
|
||||
#include "../src/resource.c"
|
||||
#include "../src/schedobj.c"
|
||||
|
||||
/* ---- fault injection inside host_iter.c ------------------------------- */
|
||||
|
||||
static int g_uncomit_calls;
|
||||
static int g_uncomit_fail_at; /* 1-based call to fail; 0: never */
|
||||
static int g_check_calls;
|
||||
static int g_check_fail_at;
|
||||
static int g_iter_calls;
|
||||
static int g_iter_fail_at;
|
||||
|
||||
static int test_get_uncomit_host(xvec_t** host, xce_sched_ctx_t* ctx, int idx)
|
||||
{
|
||||
if (++g_uncomit_calls == g_uncomit_fail_at) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
return sched_ctx_get_uncomit_host(host, ctx, idx);
|
||||
}
|
||||
|
||||
static int test_check_host_satisfy(xce_sched_core_t* core,
|
||||
xce_sched_worker_t* w, xvec_t* uncomitted,
|
||||
xvec_idval_t* wanted, xvec_idval_t* unmeet)
|
||||
{
|
||||
if (++g_check_calls == g_check_fail_at) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
return sched_check_host_satisfy(core, w, uncomitted, wanted, unmeet);
|
||||
}
|
||||
|
||||
static int test_iter_next(iterator_t* it, void** item)
|
||||
{
|
||||
if (++g_iter_calls == g_iter_fail_at) {
|
||||
return -EIO;
|
||||
}
|
||||
return iter_next(it, item);
|
||||
}
|
||||
|
||||
#define sched_ctx_get_uncomit_host test_get_uncomit_host
|
||||
#define sched_check_host_satisfy test_check_host_satisfy
|
||||
#define iter_next test_iter_next
|
||||
#include "../src/host_iter.c"
|
||||
#undef sched_ctx_get_uncomit_host
|
||||
#undef sched_check_host_satisfy
|
||||
#undef iter_next
|
||||
|
||||
#include "../src/sched_trace.c"
|
||||
#include "../src/sched_trace_capture.c"
|
||||
#include "../src/sched_trace_outcome.c"
|
||||
#include "../src/sched_trace_scan.c"
|
||||
|
||||
/* Link-satisfying stubs, as in test_host_iter.c: none is reached here. */
|
||||
void plugin_unregister(void* arg) { (void)arg; }
|
||||
|
||||
int plugin_prepare_allocation(xce_sched_core_t* s, xce_sched_ctx_t* c)
|
||||
{
|
||||
(void)s;
|
||||
(void)c;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static void reset_faults(void)
|
||||
{
|
||||
g_uncomit_calls = 0;
|
||||
g_uncomit_fail_at = 0;
|
||||
g_check_calls = 0;
|
||||
g_check_fail_at = 0;
|
||||
g_iter_calls = 0;
|
||||
g_iter_fail_at = 0;
|
||||
}
|
||||
|
||||
void setUp(void) { reset_faults(); }
|
||||
void tearDown(void) { return; }
|
||||
|
||||
/* ---- fixture ------------------------------------------------------------ */
|
||||
|
||||
#define SLOTS_RESIDX 0
|
||||
#define RACK_RESIDX 1
|
||||
#define MAX_COLLECT 16
|
||||
|
||||
typedef struct {
|
||||
xce_scheduler_t sched;
|
||||
xce_sched_ctx_t ctx;
|
||||
xce_sched_obj_t obj;
|
||||
xce_primary_resreq_t primary;
|
||||
xce_select_node_t resname, num, eq;
|
||||
xce_select_t select;
|
||||
int bucket_slots[1];
|
||||
xce_alloc_plan_t plan;
|
||||
xce_sched_trace_ctl_t ctl;
|
||||
xce_sched_trace_rec_t* rec; /* NULL: untraced */
|
||||
} fx_t;
|
||||
|
||||
static void add_worker(xce_sched_core_t* core, const char* name,
|
||||
float slots_total, float slots_inuse, float rack)
|
||||
{
|
||||
xce_sched_worker_t* w;
|
||||
int idx[1];
|
||||
float val[1];
|
||||
|
||||
w = (xce_sched_worker_t*)calloc(1, sizeof(*w));
|
||||
TEST_ASSERT_NOT_NULL(w);
|
||||
w->name = strdup(name);
|
||||
TEST_ASSERT_NOT_NULL(w->name);
|
||||
TEST_ASSERT_EQUAL_INT(0, zarray_init(&(w->resvals), 8, NULL));
|
||||
TEST_ASSERT_EQUAL_INT(0, zarray_init(&(w->resInUse), 8, NULL));
|
||||
|
||||
idx[0] = SLOTS_RESIDX;
|
||||
val[0] = slots_total;
|
||||
put_resvals(&(w->resvals), 1, idx, val);
|
||||
val[0] = slots_inuse;
|
||||
put_resvals(&(w->resInUse), 1, idx, val);
|
||||
|
||||
idx[0] = RACK_RESIDX;
|
||||
val[0] = rack;
|
||||
put_resvals(&(w->resvals), 1, idx, val);
|
||||
|
||||
TEST_ASSERT_TRUE(sched_add_worker(core, w) >= 0);
|
||||
}
|
||||
|
||||
/* A fixture with no workers, no select and no staged want; traced when
|
||||
* `traced`, with a full-level record set on the core. */
|
||||
static fx_t* fx_new(bool traced)
|
||||
{
|
||||
fx_t* fx = (fx_t*)calloc(1, sizeof(*fx));
|
||||
|
||||
TEST_ASSERT_NOT_NULL(fx);
|
||||
TEST_ASSERT_EQUAL_INT(0, xce_sched_core_init(&(fx->sched.core)));
|
||||
|
||||
fx->primary.minTasks = 1;
|
||||
fx->primary.maxTasks = 1;
|
||||
fx->obj.tag = XCE_RESREQ_PRIMARY;
|
||||
TEST_ASSERT_EQUAL_INT(
|
||||
0, sched_ctx_init(&(fx->ctx), &(fx->sched.core), &(fx->obj)));
|
||||
fx->ctx.primary = &(fx->primary);
|
||||
|
||||
fx->bucket_slots[0] = 1;
|
||||
fx->plan.nbuckets = 1;
|
||||
fx->plan.bucket_slots = fx->bucket_slots;
|
||||
fx->plan.ctx = &(fx->ctx);
|
||||
|
||||
fx->ctl.all = true;
|
||||
fx->ctl.level = XCE_TRACE_LEVEL_FULL;
|
||||
fx->ctl.line_bound = XCE_TRACE_LINE_BOUND_DEFAULT;
|
||||
fx->ctl.position = 1;
|
||||
if (traced) {
|
||||
fx->rec = sched_trace_rec_new(&(fx->ctl), 7, XCE_TRACE_REC_DECISION);
|
||||
TEST_ASSERT_NOT_NULL(fx->rec);
|
||||
fx->sched.core.trace_rec = fx->rec;
|
||||
}
|
||||
return fx;
|
||||
}
|
||||
|
||||
static void fx_free(fx_t* fx)
|
||||
{
|
||||
fx->sched.core.trace_rec = NULL;
|
||||
sched_trace_rec_free(fx->rec);
|
||||
free(fx);
|
||||
}
|
||||
|
||||
/* select `rack == literal` (as test_host_iter.c) */
|
||||
static void fx_select_rack(fx_t* fx, double literal)
|
||||
{
|
||||
fx->resname.type = SEL_RESNAME;
|
||||
fx->resname.u.resname.res_idx = RACK_RESIDX;
|
||||
fx->resname.u.resname.res_type = RES_TYPE_NUMERIC;
|
||||
fx->num.type = SEL_NUM;
|
||||
fx->num.u.numval = literal;
|
||||
fx->eq.type = SEL_EQ;
|
||||
fx->eq.u.binop.left = &(fx->resname);
|
||||
fx->eq.u.binop.right = &(fx->num);
|
||||
fx->select.root = &(fx->eq);
|
||||
fx->primary.select = &(fx->select);
|
||||
}
|
||||
|
||||
static void fx_want(fx_t* fx, int idx, float val)
|
||||
{
|
||||
TEST_ASSERT_TRUE(xvec_idval_store(&(fx->ctx.alloc_req.host), idx, val)
|
||||
>= 0);
|
||||
}
|
||||
|
||||
/* One whole scan of a fresh iterator; the hosts returned, in order. */
|
||||
static int fx_scan(fx_t* fx, char names[][64])
|
||||
{
|
||||
xce_host_iter_t it;
|
||||
xce_sched_worker_t* w;
|
||||
int n = 0;
|
||||
|
||||
memset(&it, 0, sizeof(it));
|
||||
it.init = sched_host_iter_init;
|
||||
it.next = sched_host_iter_next;
|
||||
it.fini = sched_host_iter_fini;
|
||||
TEST_ASSERT_EQUAL_INT(0, it.init(&it, &(fx->plan), fx->sched.core.workers));
|
||||
while ((n < MAX_COLLECT) && ((w = it.next(&it)) != NULL)) {
|
||||
strncpy(names[n], w->name, 63);
|
||||
names[n][63] = '\0';
|
||||
n++;
|
||||
}
|
||||
it.fini(&it);
|
||||
return n;
|
||||
}
|
||||
|
||||
static void assert_same_hosts(char a[][64], int na, char b[][64], int nb)
|
||||
{
|
||||
int i;
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(na, nb);
|
||||
for (i = 0; i < na; i++) {
|
||||
TEST_ASSERT_EQUAL_STRING(a[i], b[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static int count_entries(const xce_sched_trace_rec_t* rec, int type)
|
||||
{
|
||||
const sched_trace_entry_t* e;
|
||||
int n = 0;
|
||||
|
||||
for (e = rec->entries; e != NULL; e = e->next) {
|
||||
if ((type < 0) || ((int)e->type == type)) {
|
||||
n++;
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/* The HOST entry naming `host`, or NULL. */
|
||||
static sched_trace_entry_t* host_entry(xce_sched_trace_rec_t* rec,
|
||||
const char* host)
|
||||
{
|
||||
sched_trace_entry_t* e;
|
||||
|
||||
for (e = rec->entries; e != NULL; e = e->next) {
|
||||
if ((e->type == SCHED_TRACE_E_HOST) && (e->u.host.host != NULL)
|
||||
&& (strcmp(e->u.host.host, host) == 0)) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static sched_trace_entry_t* first_entry(xce_sched_trace_rec_t* rec, int type)
|
||||
{
|
||||
sched_trace_entry_t* e;
|
||||
|
||||
for (e = rec->entries; e != NULL; e = e->next) {
|
||||
if ((int)e->type == type) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void assert_row(const sched_trace_entry_t* e, int type, int idx,
|
||||
float total, float inuse, float reserved,
|
||||
float requested, bool met)
|
||||
{
|
||||
TEST_ASSERT_NOT_NULL(e);
|
||||
TEST_ASSERT_EQUAL_INT(type, e->type);
|
||||
TEST_ASSERT_EQUAL_INT(idx, e->u.row.idx);
|
||||
TEST_ASSERT_EQUAL_FLOAT(total, e->u.row.total);
|
||||
TEST_ASSERT_EQUAL_FLOAT(inuse, e->u.row.inuse);
|
||||
TEST_ASSERT_EQUAL_FLOAT(reserved, e->u.row.reserved);
|
||||
TEST_ASSERT_EQUAL_FLOAT(requested, e->u.row.requested);
|
||||
TEST_ASSERT_EQUAL(met, e->u.row.met);
|
||||
}
|
||||
|
||||
/* verdict bit -> host_evals index */
|
||||
static int vk(unsigned bit)
|
||||
{
|
||||
int k = 0;
|
||||
|
||||
while ((bit >> k) != 1u) {
|
||||
k++;
|
||||
}
|
||||
return k;
|
||||
}
|
||||
|
||||
/* ---- pre-filter verdicts ------------------------------------------------ */
|
||||
|
||||
/* 5.2, 5.3: a host failing the select expression gets a selection-mismatch
|
||||
* verdict from the pre-filter; the host it returns gets none here (the
|
||||
* caller's own per-host check gives it one, task 3.4). */
|
||||
static void test_prefilter_selection_mismatch_is_a_select_verdict(void)
|
||||
{
|
||||
char plain[MAX_COLLECT][64], traced[MAX_COLLECT][64];
|
||||
int np, nt;
|
||||
fx_t* fx;
|
||||
sched_trace_entry_t* e;
|
||||
|
||||
fx = fx_new(false);
|
||||
add_worker(&(fx->sched.core), "rack-match", 4.0f, 0.0f, 1.0f);
|
||||
add_worker(&(fx->sched.core), "rack-nomatch", 4.0f, 0.0f, 2.0f);
|
||||
fx_select_rack(fx, 1.0);
|
||||
np = fx_scan(fx, plain);
|
||||
fx_free(fx);
|
||||
|
||||
fx = fx_new(true);
|
||||
add_worker(&(fx->sched.core), "rack-match", 4.0f, 0.0f, 1.0f);
|
||||
add_worker(&(fx->sched.core), "rack-nomatch", 4.0f, 0.0f, 2.0f);
|
||||
fx_select_rack(fx, 1.0);
|
||||
nt = fx_scan(fx, traced);
|
||||
|
||||
assert_same_hosts(plain, np, traced, nt);
|
||||
TEST_ASSERT_EQUAL_INT(1, nt);
|
||||
TEST_ASSERT_EQUAL_STRING("rack-match", traced[0]);
|
||||
|
||||
e = host_entry(fx->rec, "rack-nomatch");
|
||||
TEST_ASSERT_NOT_NULL(e);
|
||||
TEST_ASSERT_EQUAL_UINT(XCE_TRACE_V_SELECT, e->u.host.verdicts);
|
||||
TEST_ASSERT_EQUAL_INT(SCHED_TRACE_PHASE_PREFILTER, e->u.host.phase);
|
||||
TEST_ASSERT_EQUAL_INT(0, e->u.host.err);
|
||||
TEST_ASSERT_NULL(host_entry(fx->rec, "rack-match"));
|
||||
TEST_ASSERT_EQUAL_INT(1, count_entries(fx->rec, -1));
|
||||
TEST_ASSERT_EQUAL_INT(1,
|
||||
fx->rec->counters.host_evals[vk(XCE_TRACE_V_SELECT)]);
|
||||
fx_free(fx);
|
||||
}
|
||||
|
||||
/* 5.7: the per-host uncommitted fetch failing in the pre-filter makes the
|
||||
* host "not evaluated", with the error; the scan goes on. */
|
||||
static void test_prefilter_uncomit_error_is_an_internal_verdict(void)
|
||||
{
|
||||
char plain[MAX_COLLECT][64], traced[MAX_COLLECT][64];
|
||||
int np, nt;
|
||||
fx_t* fx;
|
||||
sched_trace_entry_t* e;
|
||||
const char* skipped;
|
||||
|
||||
fx = fx_new(false);
|
||||
add_worker(&(fx->sched.core), "a", 4.0f, 0.0f, 0.0f);
|
||||
add_worker(&(fx->sched.core), "b", 4.0f, 0.0f, 0.0f);
|
||||
g_uncomit_fail_at = 1;
|
||||
np = fx_scan(fx, plain);
|
||||
fx_free(fx);
|
||||
|
||||
reset_faults();
|
||||
fx = fx_new(true);
|
||||
add_worker(&(fx->sched.core), "a", 4.0f, 0.0f, 0.0f);
|
||||
add_worker(&(fx->sched.core), "b", 4.0f, 0.0f, 0.0f);
|
||||
g_uncomit_fail_at = 1;
|
||||
nt = fx_scan(fx, traced);
|
||||
|
||||
assert_same_hosts(plain, np, traced, nt);
|
||||
TEST_ASSERT_EQUAL_INT(1, nt);
|
||||
skipped = (strcmp(traced[0], "a") == 0) ? "b" : "a";
|
||||
|
||||
e = host_entry(fx->rec, skipped);
|
||||
TEST_ASSERT_NOT_NULL(e);
|
||||
TEST_ASSERT_EQUAL_UINT(XCE_TRACE_V_INTERNAL, e->u.host.verdicts);
|
||||
TEST_ASSERT_EQUAL_INT(SCHED_TRACE_PHASE_NONE, e->u.host.phase);
|
||||
TEST_ASSERT_EQUAL_INT(-ENOMEM, e->u.host.err);
|
||||
TEST_ASSERT_NULL(host_entry(fx->rec, traced[0]));
|
||||
TEST_ASSERT_EQUAL_INT(
|
||||
1, fx->rec->counters.host_evals[vk(XCE_TRACE_V_INTERNAL)]);
|
||||
fx_free(fx);
|
||||
}
|
||||
|
||||
/* 5.7: the pre-filter's availability check failing is likewise an
|
||||
* internal-error verdict, not a shortfall. */
|
||||
static void test_prefilter_check_error_is_an_internal_verdict(void)
|
||||
{
|
||||
char plain[MAX_COLLECT][64], traced[MAX_COLLECT][64];
|
||||
int np, nt;
|
||||
fx_t* fx;
|
||||
sched_trace_entry_t* e;
|
||||
const char* skipped;
|
||||
|
||||
fx = fx_new(false);
|
||||
add_worker(&(fx->sched.core), "a", 4.0f, 0.0f, 0.0f);
|
||||
add_worker(&(fx->sched.core), "b", 4.0f, 0.0f, 0.0f);
|
||||
fx_want(fx, SLOTS_RESIDX, 1.0f);
|
||||
g_check_fail_at = 1;
|
||||
np = fx_scan(fx, plain);
|
||||
fx_free(fx);
|
||||
|
||||
reset_faults();
|
||||
fx = fx_new(true);
|
||||
add_worker(&(fx->sched.core), "a", 4.0f, 0.0f, 0.0f);
|
||||
add_worker(&(fx->sched.core), "b", 4.0f, 0.0f, 0.0f);
|
||||
fx_want(fx, SLOTS_RESIDX, 1.0f);
|
||||
g_check_fail_at = 1;
|
||||
nt = fx_scan(fx, traced);
|
||||
|
||||
assert_same_hosts(plain, np, traced, nt);
|
||||
TEST_ASSERT_EQUAL_INT(1, nt);
|
||||
skipped = (strcmp(traced[0], "a") == 0) ? "b" : "a";
|
||||
|
||||
e = host_entry(fx->rec, skipped);
|
||||
TEST_ASSERT_NOT_NULL(e);
|
||||
TEST_ASSERT_EQUAL_UINT(XCE_TRACE_V_INTERNAL, e->u.host.verdicts);
|
||||
TEST_ASSERT_EQUAL_INT(-ENOMEM, e->u.host.err);
|
||||
TEST_ASSERT_EQUAL_INT(0, count_entries(fx->rec, SCHED_TRACE_E_HOST_ROW));
|
||||
fx_free(fx);
|
||||
}
|
||||
|
||||
/* 5.4: a host the pre-filter's availability check rejects is recorded with
|
||||
* the unmet rows that check found, under its verdict line. */
|
||||
static void test_prefilter_resource_rejection_carries_its_rows(void)
|
||||
{
|
||||
char plain[MAX_COLLECT][64], traced[MAX_COLLECT][64];
|
||||
int np, nt;
|
||||
fx_t* fx;
|
||||
sched_trace_entry_t* e;
|
||||
|
||||
fx = fx_new(false);
|
||||
add_worker(&(fx->sched.core), "big", 4.0f, 0.0f, 0.0f);
|
||||
add_worker(&(fx->sched.core), "small", 4.0f, 3.0f, 0.0f);
|
||||
fx_want(fx, SLOTS_RESIDX, 2.0f);
|
||||
np = fx_scan(fx, plain);
|
||||
fx_free(fx);
|
||||
|
||||
fx = fx_new(true);
|
||||
add_worker(&(fx->sched.core), "big", 4.0f, 0.0f, 0.0f);
|
||||
add_worker(&(fx->sched.core), "small", 4.0f, 3.0f, 0.0f);
|
||||
fx_want(fx, SLOTS_RESIDX, 2.0f);
|
||||
nt = fx_scan(fx, traced);
|
||||
|
||||
assert_same_hosts(plain, np, traced, nt);
|
||||
TEST_ASSERT_EQUAL_INT(1, nt);
|
||||
TEST_ASSERT_EQUAL_STRING("big", traced[0]);
|
||||
|
||||
e = host_entry(fx->rec, "small");
|
||||
TEST_ASSERT_NOT_NULL(e);
|
||||
TEST_ASSERT_EQUAL_UINT(XCE_TRACE_V_SHORTFALL, e->u.host.verdicts);
|
||||
assert_row(e->next, SCHED_TRACE_E_HOST_ROW, SLOTS_RESIDX, 4.0f, 3.0f, 0.0f,
|
||||
2.0f, false);
|
||||
TEST_ASSERT_EQUAL_INT(e->depth + 1, e->next->depth);
|
||||
TEST_ASSERT_EQUAL_INT(1, count_entries(fx->rec, SCHED_TRACE_E_HOST_ROW));
|
||||
TEST_ASSERT_NULL(host_entry(fx->rec, "big"));
|
||||
fx_free(fx);
|
||||
}
|
||||
|
||||
/* ---- scan stop ---------------------------------------------------------- */
|
||||
|
||||
/* 4.4: a scan that stops on an iterator error marks the position, so the
|
||||
* position's unfilled end becomes a scan error with that error. */
|
||||
static void test_scan_error_marks_the_position(void)
|
||||
{
|
||||
char plain[MAX_COLLECT][64], traced[MAX_COLLECT][64];
|
||||
int np, nt;
|
||||
fx_t* fx;
|
||||
sched_trace_entry_t* e;
|
||||
|
||||
fx = fx_new(false);
|
||||
add_worker(&(fx->sched.core), "a", 4.0f, 0.0f, 0.0f);
|
||||
add_worker(&(fx->sched.core), "b", 4.0f, 0.0f, 0.0f);
|
||||
g_iter_fail_at = 2;
|
||||
np = fx_scan(fx, plain);
|
||||
fx_free(fx);
|
||||
|
||||
reset_faults();
|
||||
fx = fx_new(true);
|
||||
add_worker(&(fx->sched.core), "a", 4.0f, 0.0f, 0.0f);
|
||||
add_worker(&(fx->sched.core), "b", 4.0f, 0.0f, 0.0f);
|
||||
g_iter_fail_at = 2;
|
||||
sched_trace_pos_begin(fx->rec, 1, 1);
|
||||
nt = fx_scan(fx, traced);
|
||||
|
||||
assert_same_hosts(plain, np, traced, nt);
|
||||
TEST_ASSERT_EQUAL_INT(1, nt);
|
||||
TEST_ASSERT_TRUE(fx->rec->scan_err_set);
|
||||
TEST_ASSERT_EQUAL_INT(-EIO, fx->rec->scan_err);
|
||||
|
||||
sched_trace_pos_end(fx->rec, XCE_TRACE_POS_UNFILLED, NULL);
|
||||
e = first_entry(fx->rec, SCHED_TRACE_E_POS_END);
|
||||
TEST_ASSERT_NOT_NULL(e);
|
||||
TEST_ASSERT_EQUAL_INT(XCE_TRACE_POS_SCAN_ERROR, e->u.pos_end.how);
|
||||
TEST_ASSERT_EQUAL_INT(-EIO, e->u.pos_end.err);
|
||||
fx_free(fx);
|
||||
}
|
||||
|
||||
/* 4.4: an error on the very first step leaves no host and still marks the
|
||||
* position. */
|
||||
static void test_scan_error_before_any_host(void)
|
||||
{
|
||||
char plain[MAX_COLLECT][64], traced[MAX_COLLECT][64];
|
||||
int np, nt;
|
||||
fx_t* fx;
|
||||
|
||||
fx = fx_new(false);
|
||||
add_worker(&(fx->sched.core), "a", 4.0f, 0.0f, 0.0f);
|
||||
g_iter_fail_at = 1;
|
||||
np = fx_scan(fx, plain);
|
||||
fx_free(fx);
|
||||
|
||||
reset_faults();
|
||||
fx = fx_new(true);
|
||||
add_worker(&(fx->sched.core), "a", 4.0f, 0.0f, 0.0f);
|
||||
g_iter_fail_at = 1;
|
||||
nt = fx_scan(fx, traced);
|
||||
|
||||
assert_same_hosts(plain, np, traced, nt);
|
||||
TEST_ASSERT_EQUAL_INT(0, nt);
|
||||
TEST_ASSERT_TRUE(fx->rec->scan_err_set);
|
||||
TEST_ASSERT_EQUAL_INT(-EIO, fx->rec->scan_err);
|
||||
TEST_ASSERT_EQUAL_INT(0, count_entries(fx->rec, -1));
|
||||
fx_free(fx);
|
||||
}
|
||||
|
||||
/* A scan that simply runs out of hosts is not a scan error. */
|
||||
static void test_exhausted_scan_is_not_a_scan_error(void)
|
||||
{
|
||||
char traced[MAX_COLLECT][64];
|
||||
fx_t* fx;
|
||||
sched_trace_entry_t* e;
|
||||
|
||||
fx = fx_new(true);
|
||||
add_worker(&(fx->sched.core), "a", 4.0f, 0.0f, 0.0f);
|
||||
sched_trace_pos_begin(fx->rec, 1, 1);
|
||||
TEST_ASSERT_EQUAL_INT(1, fx_scan(fx, traced));
|
||||
TEST_ASSERT_FALSE(fx->rec->scan_err_set);
|
||||
|
||||
sched_trace_pos_end(fx->rec, XCE_TRACE_POS_UNFILLED, NULL);
|
||||
e = first_entry(fx->rec, SCHED_TRACE_E_POS_END);
|
||||
TEST_ASSERT_NOT_NULL(e);
|
||||
TEST_ASSERT_EQUAL_INT(XCE_TRACE_POS_UNFILLED, e->u.pos_end.how);
|
||||
fx_free(fx);
|
||||
}
|
||||
|
||||
/* ---- satisfaction check rows -------------------------------------------- */
|
||||
|
||||
static void make_uncomit(xvec_t* v, float* buf, int n, int idx, float val)
|
||||
{
|
||||
memset(buf, 0, sizeof(float) * (size_t)n);
|
||||
buf[idx] = val;
|
||||
v->count = n;
|
||||
v->elsize = sizeof(float);
|
||||
v->data = buf;
|
||||
}
|
||||
|
||||
static void assert_same_unmeet(int rc_a, const xvec_idval_t* a, int rc_b,
|
||||
const xvec_idval_t* b)
|
||||
{
|
||||
int i;
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(rc_a, rc_b);
|
||||
TEST_ASSERT_EQUAL_INT(a->count, b->count);
|
||||
for (i = 0; i < a->count; i++) {
|
||||
TEST_ASSERT_EQUAL_INT(((int*)a->idxs.data)[i], ((int*)b->idxs.data)[i]);
|
||||
TEST_ASSERT_EQUAL_FLOAT(((float*)a->vals.data)[i],
|
||||
((float*)b->vals.data)[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Shared state: resource 0 has 10, 4 in use; resource 1 has 2, 2 in use. */
|
||||
static void shared_state(xce_sched_core_t* core)
|
||||
{
|
||||
int idx[2] = { 0, 1 };
|
||||
float tot[2] = { 10.0f, 2.0f };
|
||||
float use[2] = { 4.0f, 2.0f };
|
||||
|
||||
put_resvals(&(core->sharedInst), 2, idx, tot);
|
||||
put_resvals(&(core->sharedInUse), 2, idx, use);
|
||||
}
|
||||
|
||||
/* Runs the cluster-wide check for {0: 3, 1: 1} with 1 of resource 0
|
||||
* reserved; inside an open check scope when `scoped`. */
|
||||
static int shared_check(fx_t* fx, bool scoped, xvec_idval_t* unmeet)
|
||||
{
|
||||
xvec_idval_t want;
|
||||
xvec_t uncomit;
|
||||
float buf[4];
|
||||
int rc;
|
||||
|
||||
shared_state(&(fx->sched.core));
|
||||
make_uncomit(&uncomit, buf, 4, 0, 1.0f);
|
||||
TEST_ASSERT_EQUAL_INT(0, xvec_idval_init(&want, 2));
|
||||
TEST_ASSERT_TRUE(xvec_idval_store(&want, 0, 3.0f) >= 0);
|
||||
TEST_ASSERT_TRUE(xvec_idval_store(&want, 1, 1.0f) >= 0);
|
||||
memset(unmeet, 0, sizeof(*unmeet));
|
||||
|
||||
if (scoped && (fx->rec != NULL)) {
|
||||
sched_trace_check_begin(fx->rec, &(fx->obj));
|
||||
}
|
||||
rc = sched_check_shared_satisfy(&(fx->sched.core), &uncomit, &want, unmeet);
|
||||
if (scoped && (fx->rec != NULL)) {
|
||||
sched_trace_check_end(fx->rec, rc, rc > 0);
|
||||
}
|
||||
xvec_idval_fini(&want);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* 4.1: in the cluster-wide check every row is kept, met or not, with its
|
||||
* total, usage, reserved and requested amounts, before the check's end. */
|
||||
static void test_check_rows_cluster_wide_scope(void)
|
||||
{
|
||||
xvec_idval_t um_plain, um_traced;
|
||||
int rc_plain, rc_traced;
|
||||
fx_t* fx;
|
||||
sched_trace_entry_t* e;
|
||||
|
||||
fx = fx_new(false);
|
||||
rc_plain = shared_check(fx, true, &um_plain);
|
||||
fx_free(fx);
|
||||
|
||||
fx = fx_new(true);
|
||||
rc_traced = shared_check(fx, true, &um_traced);
|
||||
assert_same_unmeet(rc_plain, &um_plain, rc_traced, &um_traced);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc_traced);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(3, count_entries(fx->rec, -1));
|
||||
e = fx->rec->entries;
|
||||
assert_row(e, SCHED_TRACE_E_CHECK_ROW, 0, 10.0f, 4.0f, 1.0f, 3.0f, true);
|
||||
assert_row(e->next, SCHED_TRACE_E_CHECK_ROW, 1, 2.0f, 2.0f, 0.0f, 1.0f,
|
||||
false);
|
||||
TEST_ASSERT_EQUAL_INT(SCHED_TRACE_E_CHECK_END, e->next->next->type);
|
||||
|
||||
xvec_idval_fini(&um_plain);
|
||||
xvec_idval_fini(&um_traced);
|
||||
fx_free(fx);
|
||||
}
|
||||
|
||||
/* Rows of a check run outside any scope belong to nothing and are not
|
||||
* kept; the check's result is unchanged. */
|
||||
static void test_check_rows_outside_a_scope_are_dropped(void)
|
||||
{
|
||||
xvec_idval_t um_plain, um_traced;
|
||||
int rc_plain, rc_traced;
|
||||
fx_t* fx;
|
||||
|
||||
fx = fx_new(false);
|
||||
rc_plain = shared_check(fx, false, &um_plain);
|
||||
fx_free(fx);
|
||||
|
||||
fx = fx_new(true);
|
||||
rc_traced = shared_check(fx, false, &um_traced);
|
||||
assert_same_unmeet(rc_plain, &um_plain, rc_traced, &um_traced);
|
||||
TEST_ASSERT_EQUAL_INT(0, count_entries(fx->rec, -1));
|
||||
|
||||
xvec_idval_fini(&um_plain);
|
||||
xvec_idval_fini(&um_traced);
|
||||
fx_free(fx);
|
||||
}
|
||||
|
||||
/* Runs the per-host check of worker "h" (4 slots, 1 in use, rack 0) for
|
||||
* {slots: 2, rack: 1} inside a host scope ended by a from-rows verdict,
|
||||
* as the runner's per-host check is (task 3.4). */
|
||||
static int host_check(fx_t* fx, xvec_idval_t* unmeet)
|
||||
{
|
||||
xce_sched_worker_t* w;
|
||||
xvec_idval_t want;
|
||||
xvec_t uncomit;
|
||||
float buf[4];
|
||||
int rc;
|
||||
|
||||
add_worker(&(fx->sched.core), "h", 4.0f, 1.0f, 0.0f);
|
||||
w = sched_get_worker(&(fx->sched.core), "h");
|
||||
TEST_ASSERT_NOT_NULL(w);
|
||||
make_uncomit(&uncomit, buf, 4, SLOTS_RESIDX, 1.0f);
|
||||
TEST_ASSERT_EQUAL_INT(0, xvec_idval_init(&want, 2));
|
||||
TEST_ASSERT_TRUE(xvec_idval_store(&want, SLOTS_RESIDX, 2.0f) >= 0);
|
||||
TEST_ASSERT_TRUE(xvec_idval_store(&want, RACK_RESIDX, 1.0f) >= 0);
|
||||
memset(unmeet, 0, sizeof(*unmeet));
|
||||
|
||||
if (fx->rec != NULL) {
|
||||
sched_trace_host_begin(fx->rec, &(fx->obj), w);
|
||||
}
|
||||
rc = sched_check_host_satisfy(&(fx->sched.core), w, &uncomit, &want,
|
||||
unmeet);
|
||||
if (fx->rec != NULL) {
|
||||
sched_trace_host_verdict(fx->rec, (rc < 0) ? XCE_TRACE_V_INTERNAL : 0u,
|
||||
rc);
|
||||
}
|
||||
xvec_idval_fini(&want);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* 5.4: in a host scope only the unmet rows are kept, under the host's
|
||||
* verdict line, and they make the verdict a resource shortfall. */
|
||||
static void test_check_rows_host_scope(void)
|
||||
{
|
||||
xvec_idval_t um_plain, um_traced;
|
||||
int rc_plain, rc_traced;
|
||||
fx_t* fx;
|
||||
sched_trace_entry_t* e;
|
||||
|
||||
fx = fx_new(false);
|
||||
rc_plain = host_check(fx, &um_plain);
|
||||
fx_free(fx);
|
||||
|
||||
fx = fx_new(true);
|
||||
rc_traced = host_check(fx, &um_traced);
|
||||
assert_same_unmeet(rc_plain, &um_plain, rc_traced, &um_traced);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc_traced);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(2, count_entries(fx->rec, -1));
|
||||
e = host_entry(fx->rec, "h");
|
||||
TEST_ASSERT_NOT_NULL(e);
|
||||
TEST_ASSERT_EQUAL_UINT(XCE_TRACE_V_SHORTFALL, e->u.host.verdicts);
|
||||
TEST_ASSERT_EQUAL_INT(SCHED_TRACE_PHASE_PERHOST, e->u.host.phase);
|
||||
/* slots: 1 in use + 1 reserved + 2 wanted <= 4, met and not kept */
|
||||
assert_row(e->next, SCHED_TRACE_E_HOST_ROW, RACK_RESIDX, 0.0f, 0.0f, 0.0f,
|
||||
1.0f, false);
|
||||
TEST_ASSERT_EQUAL_INT(e->depth + 1, e->next->depth);
|
||||
|
||||
xvec_idval_fini(&um_plain);
|
||||
xvec_idval_fini(&um_traced);
|
||||
fx_free(fx);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(test_prefilter_selection_mismatch_is_a_select_verdict);
|
||||
RUN_TEST(test_prefilter_uncomit_error_is_an_internal_verdict);
|
||||
RUN_TEST(test_prefilter_check_error_is_an_internal_verdict);
|
||||
RUN_TEST(test_prefilter_resource_rejection_carries_its_rows);
|
||||
RUN_TEST(test_scan_error_marks_the_position);
|
||||
RUN_TEST(test_scan_error_before_any_host);
|
||||
RUN_TEST(test_exhausted_scan_is_not_a_scan_error);
|
||||
RUN_TEST(test_check_rows_cluster_wide_scope);
|
||||
RUN_TEST(test_check_rows_outside_a_scope_are_dropped);
|
||||
RUN_TEST(test_check_rows_host_scope);
|
||||
return UNITY_END();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue