forked from lei.chen/myxce
test(job-array-single-wal-record): close remaining array expansion gaps
Adds 2 targeted tests after confirming tasks 3.1/3.2/3.3 already
cover most of this task's observable criteria:
- N-way expansion swept across sizes {2,3,100,1000} with exact-
endpoint non-leakage checks (existing coverage only tested size 5)
- Requirement 5.2 (individually addressable exactly as a per-element
job) exercised through the real production command dispatch
(job_ctrl(), not the raw key_find() every existing test used) --
confirms an array-expanded element can be cancelled by identity,
siblings stay untouched, and an out-of-range jobid is rejected
Idempotent no-op, forced-failure-then-retry, and partial-reapply
were already fully covered by 3.1/3.2 -- no new tests needed.
Requirement 9.4's node-fencing behavior is confirmed out of this
task's boundary (Live Apply Resource Fence, task 4.x); restore_job_
array()'s own atomicity contract that the fence depends on is
already proven. No production code changed -- test-only.
Task 3.4 of job-array-single-wal-record.
This commit is contained in:
parent
b0c7cfe548
commit
107e3839d9
2 changed files with 158 additions and 1 deletions
|
|
@ -47,6 +47,7 @@
|
|||
|
||||
#include <capnp_c.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
|
@ -235,6 +236,79 @@ static void test_rja_expands_contiguous_set_and_tags_lsn(void)
|
|||
printf("test_rja_expands_contiguous_set_and_tags_lsn: PASS\n");
|
||||
}
|
||||
|
||||
/* ── N-way expansion across SEVERAL array sizes (task 3.4 gap-closer) ───
|
||||
* The test above only proves correctness at ONE size (5). Task 3.4's own
|
||||
* observable explicitly asks for "several array sizes" -- mirroring the
|
||||
* WAL-append/commit-continuation size sweep task 2.3 already added
|
||||
* (test_job_array_submit_one_entry_one_continuation_across_sizes,
|
||||
* test_commit_barrier_rollout.c) for the encode/commit side. This is the
|
||||
* expansion-side analog: same size set {2, 3, 100, 1000}, proving the
|
||||
* CONTIGUOUS jobid set (and its exact endpoints -- no off-by-one leakage
|
||||
* into an adjacent, never-submitted jobid) holds at small, medium, and
|
||||
* large array sizes, not just one. */
|
||||
static void test_rja_expands_contiguous_set_across_sizes(void)
|
||||
{
|
||||
static const uint64_t sizes[] = { 2, 3, 100, 1000 };
|
||||
size_t idx;
|
||||
|
||||
taj_setup();
|
||||
|
||||
for (idx = 0; idx < sizeof(sizes) / sizeof(sizes[0]); idx++) {
|
||||
const uint64_t array_size = sizes[idx];
|
||||
const uint64_t base_jobid = 90000 + (uint64_t)idx * 2000000;
|
||||
const uint64_t entry_lsn = 900 + (uint64_t)idx;
|
||||
job_submit_data_t spec = taj_make_spec();
|
||||
uint8_t* payload = NULL;
|
||||
size_t len = 0;
|
||||
char msg[160];
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0,
|
||||
taj_encode_job_submit(base_jobid, array_size, &spec, &payload,
|
||||
&len));
|
||||
|
||||
xlist_t out;
|
||||
memset(&out, 0, sizeof(out));
|
||||
int rc = restore_job_array(&g_taj_c, payload, len, entry_lsn, &out);
|
||||
|
||||
snprintf(msg, sizeof(msg),
|
||||
"array_size=%llu: must report array_size newly created elements",
|
||||
(unsigned long long)array_size);
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE((int)array_size, rc, msg);
|
||||
|
||||
snprintf(msg, sizeof(msg),
|
||||
"array_size=%llu: out_new_jobs count must equal array_size",
|
||||
(unsigned long long)array_size);
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE((int)array_size, out.count, msg);
|
||||
|
||||
for (uint64_t i = 0; i < array_size; i++) {
|
||||
job_t* j = taj_find_job(base_jobid + i);
|
||||
snprintf(msg, sizeof(msg),
|
||||
"array_size=%llu index=%llu: element must be visible",
|
||||
(unsigned long long)array_size, (unsigned long long)i);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(j, msg);
|
||||
TEST_ASSERT_EQUAL_UINT64(base_jobid + i, j->job.jobid);
|
||||
TEST_ASSERT_EQUAL_UINT64(entry_lsn, j->submit_lsn);
|
||||
}
|
||||
|
||||
/* Exact endpoints -- no off-by-one leakage into an adjacent,
|
||||
* never-submitted jobid on either side of the range. */
|
||||
snprintf(msg, sizeof(msg),
|
||||
"array_size=%llu: one-below-base must not exist",
|
||||
(unsigned long long)array_size);
|
||||
TEST_ASSERT_NULL_MESSAGE(taj_find_job(base_jobid - 1), msg);
|
||||
snprintf(msg, sizeof(msg),
|
||||
"array_size=%llu: one-past-the-end must not exist",
|
||||
(unsigned long long)array_size);
|
||||
TEST_ASSERT_NULL_MESSAGE(taj_find_job(base_jobid + array_size), msg);
|
||||
|
||||
xlist_fini(&out);
|
||||
free(payload);
|
||||
}
|
||||
|
||||
taj_teardown();
|
||||
printf("test_rja_expands_contiguous_set_across_sizes: PASS\n");
|
||||
}
|
||||
|
||||
/* ── Idempotent whole-record re-apply is a no-op (Req 3.3) ─────────────── */
|
||||
|
||||
static void test_rja_idempotent_whole_record_replay_is_noop(void)
|
||||
|
|
@ -564,6 +638,87 @@ static job_t* tajd_find_job(cluster_t* c, uint64_t jobid)
|
|||
return (found > 0) ? j : NULL;
|
||||
}
|
||||
|
||||
/* ── job_ctrl() addresses an array-expanded element by its per-element
|
||||
* identity, exactly as it does for a single-submitted job (Req 5.2, task
|
||||
* 3.4 gap-closer) ───────────────────────────────────────────────────────
|
||||
* Every test above only proves an array element is FOUND via the raw
|
||||
* key_find(c->jobmgr, jobid) primitive -- the SAME primitive job_ctrl()
|
||||
* itself uses internally (job.c:880) to resolve a real client cancel/
|
||||
* signal request. This test drives job_ctrl() itself (the real production
|
||||
* command entry point, not a bypass) against ONE array-expanded jobid, to
|
||||
* prove Requirement 5.2's "querying, cancelling ... exactly as it does for
|
||||
* job elements created via the current per-element path" holds for the
|
||||
* actual command path -- not only for the lookup primitive it happens to
|
||||
* share with every other test's assertions. */
|
||||
static void test_tajd_job_ctrl_cancels_array_expanded_element_by_identity(void)
|
||||
{
|
||||
tajd_setup();
|
||||
|
||||
const uint64_t base_jobid = 95000;
|
||||
const uint64_t array_size = 4;
|
||||
job_submit_data_t spec = taj_make_spec();
|
||||
uint8_t* payload = NULL;
|
||||
size_t len = 0;
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0,
|
||||
taj_encode_job_submit(base_jobid, array_size, &spec, &payload, &len));
|
||||
|
||||
xlist_t out;
|
||||
memset(&out, 0, sizeof(out));
|
||||
int rc = restore_job_array(&g_tajd_c, payload, len, 500, &out);
|
||||
TEST_ASSERT_EQUAL_INT((int)array_size, rc);
|
||||
xlist_fini(&out);
|
||||
free(payload);
|
||||
|
||||
/* Cancel exactly ONE element (index 2 of 4) via the real command entry
|
||||
* point job_ctrl() -- the same function a client cancel/signal request
|
||||
* dispatches through for a single-submitted job. */
|
||||
xce_signal_t sig = { .signo = SIGKILL, .force = 0 };
|
||||
xce_jobctrl_t ctrinfo = {
|
||||
.action = JobControl_u_siginfo,
|
||||
.jobid = base_jobid + 2,
|
||||
.u = { .siginfo = &sig },
|
||||
};
|
||||
|
||||
int ctrl_rc = job_ctrl(&g_tajd_c, &ctrinfo);
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(0, ctrl_rc,
|
||||
"job_ctrl must resolve an array-expanded jobid exactly as it does"
|
||||
" for a single-submitted job");
|
||||
|
||||
job_t* cancelled = tajd_find_job(&g_tajd_c, base_jobid + 2);
|
||||
TEST_ASSERT_NOT_NULL(cancelled);
|
||||
TEST_ASSERT_EQUAL_UINT32_MESSAGE(JOB_STAT_EXIT, cancelled->job.state,
|
||||
"the targeted array-expanded element must transition to EXIT via"
|
||||
" job_ctrl's real not-yet-started-job SIGKILL path");
|
||||
|
||||
/* Every OTHER array-expanded element must be untouched -- proving
|
||||
* job_ctrl addresses exactly the requested per-element identity, not
|
||||
* the whole array. */
|
||||
for (uint64_t i = 0; i < array_size; i++) {
|
||||
if (i == 2)
|
||||
continue;
|
||||
job_t* j = tajd_find_job(&g_tajd_c, base_jobid + i);
|
||||
TEST_ASSERT_NOT_NULL(j);
|
||||
TEST_ASSERT_EQUAL_UINT32_MESSAGE(JOB_STAT_PEND, j->job.state,
|
||||
"an untargeted sibling array element must be unaffected by a"
|
||||
" job_ctrl call against a different index");
|
||||
}
|
||||
|
||||
/* A jobid one past the array's committed range must not resolve. */
|
||||
xce_jobctrl_t missing_ctrinfo = {
|
||||
.action = JobControl_u_siginfo,
|
||||
.jobid = base_jobid + array_size,
|
||||
.u = { .siginfo = &sig },
|
||||
};
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(-ENOENT,
|
||||
job_ctrl(&g_tajd_c, &missing_ctrinfo),
|
||||
"a jobid one past the array's committed range must not resolve");
|
||||
|
||||
tajd_teardown();
|
||||
printf("test_tajd_job_ctrl_cancels_array_expanded_element_by_identity:"
|
||||
" PASS\n");
|
||||
}
|
||||
|
||||
/* apply_wal_entry()'s WAL_JOB_ARRAY_SUBMIT case constructs every element
|
||||
* through restore_job_array() and fires NO notification -- mirroring the
|
||||
* existing WAL_JOB_SUBMIT case's hook-free behavior at this same dispatch
|
||||
|
|
@ -707,6 +862,7 @@ void test_job_array_apply(void)
|
|||
{
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(test_rja_expands_contiguous_set_and_tags_lsn);
|
||||
RUN_TEST(test_rja_expands_contiguous_set_across_sizes);
|
||||
RUN_TEST(test_rja_idempotent_whole_record_replay_is_noop);
|
||||
RUN_TEST(test_rja_partial_reapply_skips_existing_indices);
|
||||
#ifdef XCE_TESTING
|
||||
|
|
@ -715,6 +871,7 @@ void test_job_array_apply(void)
|
|||
#endif
|
||||
RUN_TEST(test_rja_null_or_empty_payload_returns_error);
|
||||
RUN_TEST(test_rja_out_new_jobs_is_optional);
|
||||
RUN_TEST(test_tajd_job_ctrl_cancels_array_expanded_element_by_identity);
|
||||
RUN_TEST(test_tajd_apply_wal_entry_constructs_all_and_fires_no_hook);
|
||||
RUN_TEST(test_tajd_apply_committed_entry_vs_apply_wal_entry_byte_identical);
|
||||
UNITY_END();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue