skip to content
==> tests/merge.c <==
1/* The merge: random answers (committed tables at random commit points, entries of random
2 * leaderships) merged here and in the Rust to the same table; an answer built from a replica. */
3#include "mesh/jobs/merge.h"
4#include "mesh/check.h"
5#include "gen.h"
6#include "gold_merge.h"
7
8static uint64_t fnv(const void *p, size_t n) {
9    const uint8_t *b = p;
10    uint64_t h = 0xcbf29ce484222325u;
11    for (size_t i = 0; i < n; i++) {
12        h ^= b[i];
13        h *= 0x100000001b3u;
14    }
15    return h;
16}
17
18int main(void) {
19    size_t g = 0, ng = sizeof GOLD / sizeof *GOLD;
20    char line[128];
21    for (uint64_t seeds[] = {2, 4, 8, 16, 32}, *sd = seeds; sd < seeds + 5; sd++) {
22        rs = *sd * 0x9e3779b97f4a7c15u | 1;
23        for (int round = 0; round < 20; round++) {
24            mj_answer slot[6];
25            bool have[6] = {0};
26            uint64_t k = 1 + rnd(4);
27            for (uint64_t i = 0; i < k; i++) {
28                uint32_t m = (uint32_t)rnd(6);
29                mj_answer a;
30                mj_answer_init(&a);
31                uint64_t cl = rnd(3) << 40;
32                a.commit_lid = cl;
33                a.commit_n = rnd(4);
34                uint64_t nops = rnd(30);
35                for (uint64_t j = 0; j < nops; j++) {
36                    mj_op o = gen();
37                    mj_table_apply(&a.committed, &o);
38                    mj_op_free(&o);
39                }
40                uint64_t ne = rnd(4);
41                for (uint64_t j = 0; j < ne; j++) {
42                    uint64_t lid = (1 + rnd(3)) << 40;
43                    uint64_t n = rnd(5), c = rnd(6);
44                    mj_ops ops = {0};
45                    for (uint64_t q = 0; q < c; q++) mj_ops_push(&ops, gen());
46                    mj_ops *e = mj_answer_entry(&a, lid, n);
47                    mj_ops_free(e);
48                    *e = ops;
49                }
50                if (have[m]) mj_answer_free(&slot[m]);
51                slot[m] = a;
52                have[m] = true;
53            }
54            mj_answer *in[6];
55            size_t n = 0;
56            for (int m = 0; m < 6; m++)
57                if (have[m]) in[n++] = &slot[m];
58            mj_table t = mj_merge(in, n);
59            mj_ops to = {0};
60            mj_table_ops(&t, &to);
61            msb enc = {0};
62            mj_encode_ops(to.v, to.n, &enc);
63            snprintf(line, sizeof line, "%llu %d %zu %016llx %zu", (unsigned long long)*sd, round, n, (unsigned long long)fnv(enc.p, enc.n), enc.n);
64            if (CHECK(g < ng)) CHECK_S(line, GOLD[g]);
65            g++;
66            msb_free(&enc);
67            mj_ops_free(&to);
68            mj_table_free(&t);
69            for (int m = 0; m < 6; m++)
70                if (have[m]) mj_answer_free(&slot[m]);
71        }
72    }
73    CHECK_U(g, ng);
74    /* A replica's own answer: its committed table, and each pending entry under (lid, n). */
75    mj_replica r;
76    mj_replica_init(&r);
77    mj_spec s = gspec(0);
78    mj_ops one = {0};
79    mj_ops_push(&one, mj_op_submit(&s));
80    mj_log e1 = mj_log_entry((uint64_t)2 << 40, 1, true, one);
81    mj_replica_apply(&r, &e1);
82    mj_answer a = mj_answer_of(&r);
83    uint64_t key[2] = {(uint64_t)2 << 40, 1};
84    mj_ops *got = momap_get(a.entries, key);
85    CHECK(got && got->n == 1 && got->v[0].kind == MJ_OP_SUBMIT);
86    mj_answer *in[1] = {&a};
87    mj_table t = mj_merge(in, 1);
88    CHECK(mj_table_get(&t, s.id) != NULL);
89    mj_table_free(&t);
90    mj_answer_free(&a);
91    mj_log_free(&e1);
92    mj_spec_free(&s);
93    mj_replica_free(&r);
94    return mesh_check_done();
95}