test(raft-sim-conformance): negative test proving oracle teeth, both directions (task 7.2)
This commit is contained in:
parent
62523892d9
commit
90197f8595
2 changed files with 97 additions and 1 deletions
|
|
@ -8640,6 +8640,101 @@ static void test_sim_7_1_fault_switch_mechanism(void)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* test_sim_7_2_negative_both_directions (raft-sim-conformance task 7.2; Req 7.1, 7.2, 7.4):
|
||||
* the oracle's-teeth negative test — ONE test that proves checker sensitivity in BOTH
|
||||
* directions within a SINGLE binary:
|
||||
*
|
||||
* (1) BROKEN: with RAFT_SIM_BREAK_TERM_STAMP set (on_persist_entry stamps every entry
|
||||
* term 0 — emulating bug-851 G1), the randomized action scheduler driving the same
|
||||
* (n, seed) reaches a state where two nodes share a (index, term) yet hold differing
|
||||
* prefixes (term-0 collapse makes a term-A and a term-B entry at the same index look
|
||||
* identical to the matching test). raft_sim_check_log_matching FIRES — a GENUINE Log
|
||||
* Matching SAFETY violation, surfaced as rc == -(int)RAFT_SIM_INV_LOG_MATCHING with
|
||||
* the replayable trace dumped to stderr (expected for a deliberately-failing run).
|
||||
* (2) CORRECT: a FRESH cluster with the SAME (n, seed) and the fault CLEARED runs the
|
||||
* SAME scheduler to completion with ZERO violations (rc == 0).
|
||||
*
|
||||
* Both asserted here, so the checker's ability to BOTH catch the broken build AND pass the
|
||||
* correct one is itself verified (R7.4). The (fault, driver, n, seed) is fixed and named so
|
||||
* the trip is reproducible: RAFT_SIM_BREAK_TERM_STAMP + raft_sim_run(&c, 400) at n=3,
|
||||
* seed=1 (empirically the first index that exercises replication far enough to expose the
|
||||
* term-0 collapse). raft_sim_run is preferred over the targeted scenarios because the
|
||||
* scenarios self-init their cluster INTERNALLY and so cannot have the fault set BEFORE init;
|
||||
* the action scheduler, by contrast, is driven over a caller-owned, pre-faulted cluster.
|
||||
*/
|
||||
#define RAFT_SIM_NEG_N 3 /* cluster size for the negative-test trip (named, reproducible) */
|
||||
#define RAFT_SIM_NEG_SEED 1ULL /* seed that exercises replication far enough to trip log-matching */
|
||||
#define RAFT_SIM_NEG_STEPS 400 /* bounded scheduler budget (matches the conformance sweep cap) */
|
||||
|
||||
static void test_sim_7_2_negative_both_directions(void)
|
||||
{
|
||||
/* ---- (1) BROKEN direction: fault ON must trip a GENUINE safety invariant ---- */
|
||||
int broken_rc;
|
||||
{
|
||||
raft_sim_cluster_t c;
|
||||
memset(&c, 0, sizeof(c));
|
||||
TEST_ASSERT_EQUAL_INT(0,
|
||||
raft_sim_conformance_init(&c, RAFT_SIM_NEG_N, 2000, RAFT_SIM_NEG_SEED));
|
||||
|
||||
/* Corrupt the persist adapter: every entry is stamped term 0 (bug-851 G1). */
|
||||
raft_sim_set_fault_inject(&c, RAFT_SIM_BREAK_TERM_STAMP);
|
||||
TEST_ASSERT_EQUAL_UINT(RAFT_SIM_BREAK_TERM_STAMP, c.fault_inject);
|
||||
|
||||
/* Drive the randomized scheduler. It halts and returns < 0 (dumping the
|
||||
* replayable trace to stderr — EXPECTED for this deliberately-broken run) on
|
||||
* the FIRST invariant violation raft_sim_check_all detects. */
|
||||
broken_rc = raft_sim_run(&c, RAFT_SIM_NEG_STEPS);
|
||||
|
||||
/* The broken build is CAUGHT: a real violation fired (rc < 0). */
|
||||
TEST_ASSERT_TRUE_MESSAGE(broken_rc < 0,
|
||||
"BROKEN config (term-0 stamp) did NOT trip any invariant — the oracle lacks teeth");
|
||||
|
||||
/* And the failing invariant is a genuine SAFETY property, not merely a different
|
||||
* return code: decode the negated return and require Log Matching / Leader
|
||||
* Completeness / State Machine Safety (the three core safety invariants). */
|
||||
raft_sim_invariant_t inv = (raft_sim_invariant_t)(-broken_rc);
|
||||
int is_safety = (inv == RAFT_SIM_INV_LOG_MATCHING) ||
|
||||
(inv == RAFT_SIM_INV_LEADER_COMPLETENESS) ||
|
||||
(inv == RAFT_SIM_INV_STATE_MACHINE_SAFETY);
|
||||
char msg[160];
|
||||
snprintf(msg, sizeof(msg),
|
||||
"BROKEN config tripped '%s' (rc=%d) — expected a Log Matching / Leader "
|
||||
"Completeness / State Machine Safety violation",
|
||||
raft_sim_invariant_name(inv), broken_rc);
|
||||
TEST_ASSERT_TRUE_MESSAGE(is_safety, msg);
|
||||
|
||||
/* For this fixed key the deterministic first trip is Log Matching (term-0 collapse
|
||||
* makes two distinct-term entries at one index look identical to the prefix check). */
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(RAFT_SIM_INV_LOG_MATCHING, inv,
|
||||
"BROKEN config at the fixed key must deterministically trip Log Matching");
|
||||
}
|
||||
|
||||
/* ---- (2) CORRECT direction: SAME (n, seed), fault OFF, ZERO violations ---- */
|
||||
{
|
||||
raft_sim_cluster_t c;
|
||||
memset(&c, 0, sizeof(c));
|
||||
TEST_ASSERT_EQUAL_INT(0,
|
||||
raft_sim_conformance_init(&c, RAFT_SIM_NEG_N, 2000, RAFT_SIM_NEG_SEED));
|
||||
|
||||
/* Explicitly clear the fault (also the zero-init default) — the correct build. */
|
||||
raft_sim_set_fault_inject(&c, 0);
|
||||
TEST_ASSERT_EQUAL_UINT(0u, c.fault_inject);
|
||||
|
||||
int correct_rc = raft_sim_run(&c, RAFT_SIM_NEG_STEPS);
|
||||
|
||||
/* The correct build PASSES the SAME scenario/seed with no invariant violation. */
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(0, correct_rc,
|
||||
"CORRECT config (fault off) surfaced a violation on the SAME (n, seed) — "
|
||||
"the library is correct, so this must be clean");
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(RAFT_SIM_INV_NONE, raft_sim_check_all(&c),
|
||||
"CORRECT config final state violates an invariant on the SAME (n, seed)");
|
||||
}
|
||||
|
||||
/* R7.4: BOTH directions asserted in this ONE test — the broken build is caught
|
||||
* (broken_rc < 0, Log Matching) AND the correct build passes (correct_rc == 0). */
|
||||
}
|
||||
|
||||
#define RAFT_SIM_SWEEP_M 32 /* configured minimum number of randomized seeds (R6.2) */
|
||||
#define RAFT_SIM_SWEEP_STEPS 400 /* per-run step cap — bounded for the suite budget (R6.4) */
|
||||
|
||||
|
|
@ -8935,6 +9030,7 @@ void test_raft(void)
|
|||
RUN_TEST(test_sim_5_4_figure8);
|
||||
RUN_TEST(test_sim_5_5_stale_rejoin);
|
||||
RUN_TEST(test_sim_7_1_fault_switch_mechanism);
|
||||
RUN_TEST(test_sim_7_2_negative_both_directions);
|
||||
RUN_TEST(test_sim_6_1_seed_sweep);
|
||||
|
||||
/* Group A: basic election correctness (task 5.1) */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue