feat(raft-sim-conformance): seeded PRNG + in-memory adapter callbacks + conformance init (task 1.2)
This commit is contained in:
parent
35c012ddc2
commit
631854303c
4 changed files with 307 additions and 1 deletions
|
|
@ -4466,6 +4466,82 @@ static void test_sim_7_4_on_commit_advanced_trigger_semantics(void)
|
|||
TEST_ASSERT_EQUAL_INT(2, L->commit_advanced_count); /* no fire off-leader */
|
||||
}
|
||||
|
||||
/*
|
||||
* test_sim_conformance_init_append_readback (raft-sim-conformance task 1.2; Req 1.1, 1.2, 2.1)
|
||||
*
|
||||
* Observable completion for the conformance init + in-memory adapter callbacks:
|
||||
* - raft_sim_conformance_init(&c, n, timeout, seed) wires the six conformance
|
||||
* adapter callbacks (log persist/get-term/truncate + conformance commit +
|
||||
* send-to-bag) and seeds the deterministic PRNG from `seed`.
|
||||
* - Drive node 0 to LEADER through the published election entry points
|
||||
* (start_election on node 0, deliver the pending vote requests so peers grant).
|
||||
* - raft_log_append a known value on the leader; the entry must be readable back
|
||||
* two ways: directly via node0.log[0] (term + decoded value) AND through the
|
||||
* wired on_get_entry_term callback (returns the entry's term, not 0).
|
||||
* - Determinism smoke: two conformance inits with the SAME seed leave c->prng in
|
||||
* the same state (R1.2: every nondeterministic choice flows from the seed).
|
||||
*/
|
||||
static void test_sim_conformance_init_append_readback(void)
|
||||
{
|
||||
raft_sim_cluster_t c;
|
||||
memset(&c, 0, sizeof(c));
|
||||
|
||||
/* Single-node conformance cluster: raft_start_election short-circuits node 0
|
||||
* straight to LEADER (no peers to canvass), so the leader is forced without
|
||||
* depending on bag delivery (task 2.1). The PRNG seed is still asserted. */
|
||||
TEST_ASSERT_EQUAL_INT(0, raft_sim_conformance_init(&c, 1, 2000, 0xC0FFEEULL));
|
||||
|
||||
/* PRNG seeded from the run key. */
|
||||
TEST_ASSERT_EQUAL_UINT64(0xC0FFEEULL, c.seed);
|
||||
|
||||
/* Node 0 starts as a follower, no leader yet. */
|
||||
TEST_ASSERT_EQUAL_INT(RAFT_FOLLOWER, c.nodes[0].state.role);
|
||||
TEST_ASSERT_EQUAL_INT(0, c.leader_count);
|
||||
|
||||
/* Force node 0 to LEADER via the published election entry point. */
|
||||
raft_start_election(&c.nodes[0].state);
|
||||
TEST_ASSERT_EQUAL_INT(RAFT_LEADER, c.nodes[0].state.role);
|
||||
TEST_ASSERT_EQUAL_INT(1, c.leader_count);
|
||||
|
||||
/* Append a known client value on the leader. The first 8 bytes decode to the
|
||||
* applied value; the conformance adapter stores it in node0.log[0]. */
|
||||
uint64_t value = 0x1122334455667788ULL;
|
||||
uint64_t out_index = 0;
|
||||
int rc = raft_log_append(&c.nodes[0].state, &value, sizeof(value), &out_index);
|
||||
TEST_ASSERT_EQUAL_INT(RAFT_OK, rc);
|
||||
TEST_ASSERT_EQUAL_UINT64(1, out_index);
|
||||
|
||||
/* Read back directly via the in-memory log: index 1 -> log[0]. */
|
||||
raft_sim_node_t *L = &c.nodes[0];
|
||||
TEST_ASSERT_EQUAL_INT(1, L->log_count);
|
||||
TEST_ASSERT_EQUAL_UINT64(L->state.term, L->log[0].term);
|
||||
uint64_t decoded = 0;
|
||||
memcpy(&decoded, L->log[0].bytes, sizeof(decoded));
|
||||
TEST_ASSERT_EQUAL_UINT64(value, decoded);
|
||||
|
||||
/* Read back the entry's term through the wired on_get_entry_term callback. */
|
||||
uint64_t got_term = L->cbs.on_get_entry_term(L, 1);
|
||||
TEST_ASSERT_EQUAL_UINT64(L->state.term, got_term);
|
||||
TEST_ASSERT_NOT_EQUAL(0, got_term);
|
||||
|
||||
/* Determinism smoke: same seed -> identical PRNG state after init. */
|
||||
raft_sim_cluster_t c2;
|
||||
memset(&c2, 0, sizeof(c2));
|
||||
TEST_ASSERT_EQUAL_INT(0, raft_sim_conformance_init(&c2, 1, 2000, 0xC0FFEEULL));
|
||||
TEST_ASSERT_EQUAL_UINT64(c.prng, c2.prng); /* same seed -> same start state */
|
||||
|
||||
/* The seeded generator advances identically for the same seed: the first few
|
||||
* draws match, and the post-draw state stays in lockstep (R1.2 determinism). */
|
||||
raft_sim_cluster_t g1, g2;
|
||||
memset(&g1, 0, sizeof(g1));
|
||||
memset(&g2, 0, sizeof(g2));
|
||||
TEST_ASSERT_EQUAL_INT(0, raft_sim_conformance_init(&g1, 1, 2000, 0x5EEDULL));
|
||||
TEST_ASSERT_EQUAL_INT(0, raft_sim_conformance_init(&g2, 1, 2000, 0x5EEDULL));
|
||||
for (int k = 0; k < 4; k++)
|
||||
TEST_ASSERT_EQUAL_UINT64(raft_sim_prng_next(&g1), raft_sim_prng_next(&g2));
|
||||
TEST_ASSERT_EQUAL_UINT64(g1.prng, g2.prng);
|
||||
}
|
||||
|
||||
/* ---------- Group A: Basic Election Correctness (task 5.1) ---------- */
|
||||
|
||||
/*
|
||||
|
|
@ -7029,6 +7105,9 @@ void test_raft(void)
|
|||
RUN_TEST(test_sim_7_3_freshness_gate_lexicographic);
|
||||
RUN_TEST(test_sim_7_4_on_commit_advanced_trigger_semantics);
|
||||
|
||||
/* conformance init + in-memory adapter callbacks (task 1.2) */
|
||||
RUN_TEST(test_sim_conformance_init_append_readback);
|
||||
|
||||
/* Group A: basic election correctness (task 5.1) */
|
||||
RUN_TEST(test_proto_A1_single_node_self_elects);
|
||||
RUN_TEST(test_proto_A2_3node_one_leader);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue