==> tests/op.c <==1/* Ops: one of each kind encoded exactly as the Rust encodes them, decoded back, and decode's answers
2 * to bad bytes; parts of at most PART_BYTES. */
3#include "mesh/jobs/op.h"
4#include "mesh/check.h"
5#include "gold_op.h"
6
7#include <stdlib.h>
8#include <string.h>
9
10static size_t unhexs(const char *h, uint8_t *out) {
11 size_t n = strlen(h) / 2;
12 for (size_t i = 0; i < n; i++) {
13 unsigned v;
14 sscanf(h + 2 * i, "%2x", &v);
15 out[i] = (uint8_t)v;
16 }
17 return n;
18}
19
20static mj_res res(uint64_t lease) {
21 mj_res r = {.lease = lease, .member = 2, .result = 9, .out = mxalloc(3), .out_len = 3};
22 memcpy(r.out, (uint8_t[]){1, 2, 3}, 3);
23 if (lease % 2 == 1) {
24 r.has_blob = true;
25 memset(r.blob.hash.b, (int)lease, 32);
26 r.blob.len = 9216;
27 r.blob.holders = 0xb;
28 }
29 return r;
30}
31
32int main(void) {
33 mj_setup su = {6, 0};
34 mj_spec s = {.id = (uint64_t)1 << 32 | 1, .program = mstrdup("p"), .op = 254, .items = mj_ranges_of(0, 10), .attempts = 3,
35 .mode = MJ_MODE_COLLECT, .setup = mj_setup_clone(&su, 1), .nsetup = 1, .submitted_ms = 5, .members = 5};
36 mj_res r7 = res(7);
37 mj_tally tl[] = {{1, 4}, {2, 5}};
38 mj_ops ops = {0};
39 mj_ops_push(&ops, mj_op_submit(&s));
40 mj_ops_push(&ops, mj_op_done(s.id, 3, &r7));
41 mj_ops_push(&ops, mj_op_done_run(s.id, 4, 6, 8, 1));
42 mj_ops_push(&ops, mj_op_fail(s.id, 9, 3, "boom"));
43 mj_ops_push(&ops, mj_op_fails(s.id, 2, 1));
44 mj_ops_push(&ops, mj_op_dupes(s.id, 4));
45 mj_ops_push(&ops, mj_op_finish(s.id, 77, 1));
46 mj_ops_push(&ops, mj_op_cancel(s.id, "x"));
47 mj_ops_push(&ops, mj_op_tone(s.id, 2, 1));
48 mj_ops_push(&ops, mj_op_fold(s.id, 9, tl, 2));
49 mj_ops_push(&ops, mj_op_forget(s.id));
50 msb enc = {0};
51 mj_encode_ops(ops.v, ops.n, &enc);
52 static uint8_t want[4096];
53 size_t wn = unhexs(GOLD_OPS, want);
54 CHECK_U(enc.n, wn);
55 CHECK(enc.n == wn && memcmp(enc.p, want, wn) == 0);
56 mj_ops back = {0};
57 char *err = NULL;
58 CHECK(mj_decode_ops(enc.p, enc.n, &back, &err) && mj_ops_eq(&back, &ops));
59 /* Clones are deep and equal. */
60 mj_ops c = mj_ops_clone(&ops);
61 CHECK(mj_ops_eq(&c, &ops));
62 mj_ops_free(&c);
63 /* Bad bytes: the Rust's messages. */
64 for (size_t i = 0; i < sizeof GOLD_BAD / sizeof *GOLD_BAD; i++) {
65 uint8_t b[256];
66 size_t n = unhexs(GOLD_BAD[i][1], b);
67 mj_ops v = {0};
68 char *e = NULL;
69 if (mj_decode_ops(b, n, &v, &e)) {
70 char got[32];
71 snprintf(got, sizeof got, "ok %zu", v.n);
72 CHECK_S(got, GOLD_BAD[i][2]);
73 } else {
74 CHECK_S(e, GOLD_BAD[i][2]);
75 free(e);
76 }
77 mj_ops_free(&v);
78 }
79 /* Parts: under PART_BYTES each until the op that crosses it; never an op split; at least one. */
80 mj_parts p = mj_parts_of(NULL, 0);
81 CHECK(p.n == 1 && p.v[0].n == 0);
82 mj_parts_free(&p);
83 mj_ops many = {0};
84 mj_res big = {.lease = 1, .out = mxalloc(1000), .out_len = 1000};
85 for (int i = 0; i < 3000; i++) mj_ops_push(&many, mj_op_done(1, (uint64_t)i, &big));
86 p = mj_parts_of(many.v, many.n);
87 CHECK_U(p.n, 3); /* 3000 x 1038 bytes = 3.0 MiB: two full parts and the rest */
88 size_t total = 0, nops = 0;
89 for (size_t i = 0; i < p.n; i++) {
90 mj_ops part = {0};
91 CHECK(mj_decode_ops(p.v[i].p, p.v[i].n, &part, NULL));
92 if (i + 1 < p.n) CHECK(p.v[i].n >= MESH_JOBS_PART_BYTES && p.v[i].n < MESH_JOBS_PART_BYTES + 1038);
93 total += p.v[i].n;
94 nops += part.n;
95 mj_ops_free(&part);
96 }
97 CHECK_U(nops, 3000);
98 CHECK_U(total, 3000u * 1038u);
99 mj_parts_free(&p);
100 mj_ops_free(&many);
101 mj_res_free(&big);
102 mj_ops_free(&back);
103 mj_ops_free(&ops);
104 mj_res_free(&r7);
105 mj_spec_free(&s);
106 msb_free(&enc);
107 return mesh_check_done();
108}