Files
LithosAnanake/v4/tests/test_wire.c
T
rajamesandClaude Opus 5.5 d5cd64605d feat(v4.0.0): the wire in the engine -- whole messages, put and taken in one step
Queues of whole messages on the wires between nodes (v4/wire.h), the six
addresses and ten operations a node asks of the fabric (MESH.md 7d.3), and the
fabric's pool of queues. Engine only: the nucleus does not use any of it yet.
The ports in the host map move to BUF0_W - 140 so that the new addresses are
not among the nucleus variables, with a compile-time check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 13:50:15 -04:00

295 lines
16 KiB
C

/* test_wire.c -- a queue of whole messages, one way along a wire, with no
* node and no fabric: docs/v4.0.0/MESH.md section 7d.2.
*
* The first attempt at messages that wait (7c.6) moved a message a word at a
* time and left both ends out of step when a fault came in the middle. So
* what is tested here, over and over, is that a queue which answers anything
* but "done" is exactly as it was.
*/
#include "v4/wire.h"
#include <stdio.h>
#include <string.h>
static int failures = 0, checks = 0;
#define CHECK(c,...) do{checks++; if(!(c)){failures++; printf("FAIL %s:%d: ",__FILE__,__LINE__); printf(__VA_ARGS__); printf("\n");}}while(0)
static v4_wire_queue q, other, before, before_other;
/* A message of `len` characters: seven words, then its text. */
static void make(v4_cell *h, v4_cell *t, v4_cell len, v4_cell seed)
{
unsigned i, n = len > 0 ? (unsigned)(len + 3) / 4u : 0;
for (i = 0; i < 7; i++) h[i] = seed * 100 + (v4_cell)i;
h[6] = len;
for (i = 0; i < n; i++) t[i] = seed * 1000 + 17 * (v4_cell)i + 5;
}
/* 1 if the message taken back is the one made from `len` and `seed` */
static int same(const v4_cell *h, const v4_cell *t, v4_cell len, v4_cell seed)
{
v4_cell wh[7], wt[V4_WIRE_CELLS];
unsigned i, n = (unsigned)(len + 3) / 4u;
make(wh, wt, len, seed);
for (i = 0; i < 7; i++) if (h[i] != wh[i]) return 0;
for (i = 0; i < n; i++) if (t[i] != wt[i]) return 0;
return 1;
}
static void put_ok(v4_wire_queue *w, v4_cell len, v4_cell seed, int *ok)
{
v4_cell h[7], t[V4_WIRE_CELLS];
make(h, t, len, seed);
if (v4_wire_put(w, h, t) != V4_WIRE_DONE) *ok = 0;
}
int main(void)
{
static const v4_cell lens[] = { 0, 1, 4, 5, 1024 };
v4_cell h[7], t[V4_WIRE_CELLS], h2[7], t2[V4_WIRE_CELLS];
unsigned i, k, round, count, wrapped, cap_cells;
int ok;
printf("v4 wire tests: V4_CELL_BITS=%d, %d cells to a queue\n", V4_CELL_BITS, V4_WIRE_CELLS);
/* ---- an empty queue ---- */
v4_wire_reset(&q);
CHECK(v4_wire_room(&q, 1024) == V4_WIRE_DONE, "an empty queue has room for 1,024");
CHECK(v4_wire_room(&q, 1025) == V4_WIRE_NO_ROOM, "and not for 1,025");
memcpy(&before, &q, sizeof q);
CHECK(v4_wire_look(&q, h) == V4_WIRE_NO_MESSAGE, "look at nothing answers 3");
CHECK(v4_wire_take(&q, h, t, V4_WIRE_CELLS) == V4_WIRE_NO_MESSAGE, "take answers 3");
CHECK(v4_wire_drop(&q) == V4_WIRE_NO_MESSAGE, "drop answers 3");
CHECK(v4_wire_next(&q) == V4_WIRE_NO_MESSAGE, "next answers 3");
CHECK(memcmp(&before, &q, sizeof q) == 0, "and none of them changed it");
CHECK(q.mark == q.used && q.used == 0 && q.arrived == 0, "its mark is at no message, and nothing has arrived");
/* ---- the cells a message takes ---- */
CHECK(v4_wire_cells(0) == 7 && v4_wire_cells(1) == 8 && v4_wire_cells(4) == 8 && v4_wire_cells(5) == 9 && v4_wire_cells(1024) == 263,
"a message takes 7 cells and one for each four characters: 7 8 8 9 and 263");
CHECK(v4_wire_cells(-1) == 0 && v4_wire_cells(1025) == 0, "a length of -1 or 1,025 is not a message");
/* ---- lengths of 0, 1, 4, 5 and 1,024, each put and taken back ---- */
for (k = 0; k < sizeof lens / sizeof lens[0]; k++) {
v4_wire_reset(&q);
make(h, t, lens[k], (v4_cell)k + 1);
CHECK(v4_wire_put(&q, h, t) == V4_WIRE_DONE, "a message of %ld characters is put", (long)lens[k]);
CHECK(q.used == v4_wire_cells(lens[k]), "it takes %u cells: %u", v4_wire_cells(lens[k]), q.used);
memset(h2, 0, sizeof h2); memset(t2, 0, sizeof t2);
CHECK(v4_wire_look(&q, h2) == V4_WIRE_DONE && memcmp(h, h2, sizeof h2) == 0, "look gives its seven words");
CHECK(q.used == v4_wire_cells(lens[k]), "and takes nothing");
CHECK(v4_wire_take(&q, h2, t2, V4_WIRE_CELLS) == V4_WIRE_DONE && same(h2, t2, lens[k], (v4_cell)k + 1),
"taken, its seven words and every word of its text are what was put");
CHECK(q.used == 0 && q.mark == 0, "and the queue is empty again, its mark at no message");
}
/* ---- round the end of the array: fill and empty it three times, with lengths that do not divide 1,024 ---- */
v4_wire_reset(&q);
wrapped = 0;
for (round = 0; round < 3; round++) {
static const v4_cell mix[] = { 100, 37, 333, 1, 250, 58, 0, 411 };
v4_cell seed[200], len[200];
count = 0;
for (;;) {
v4_cell l = mix[(count + round * 3) % 8];
make(h, t, l, (v4_cell)(round * 50 + count + 1));
if (q.head + v4_wire_cells(l) > V4_WIRE_CELLS && q.used + v4_wire_cells(l) <= V4_WIRE_CELLS) wrapped++;
if (v4_wire_put(&q, h, t) != V4_WIRE_DONE) break;
seed[count] = (v4_cell)(round * 50 + count + 1);
len[count++] = l;
}
CHECK(count > 5 && count < 200 && q.used <= V4_WIRE_CELLS, "round %u: %u messages fill it, %u cells", round, count, q.used);
ok = 1;
for (i = 0; i < count; i++) {
if (v4_wire_take(&q, h2, t2, V4_WIRE_CELLS) != V4_WIRE_DONE || !same(h2, t2, len[i], seed[i])) ok = 0;
}
CHECK(ok && q.used == 0, "round %u: they come out whole and in the order they went in", round);
}
CHECK(wrapped > 0, "and some of them went across the end of the array: %u", wrapped);
/* ---- a put that does not fit changes nothing ---- */
v4_wire_reset(&q);
ok = 1;
put_ok(&q, 1024, 1, &ok); /* 263 */
put_ok(&q, 1024, 2, &ok); /* 526 */
put_ok(&q, 1024, 3, &ok); /* 789 */
CHECK(ok && q.used == 789, "three long messages take 789 cells");
CHECK(v4_wire_room(&q, 235) == V4_WIRE_DONE && v4_wire_room(&q, 236) == V4_WIRE_NO_ROOM, "which leaves room for 235 and not 236");
make(h, t, 4 * (235 - 7) + 1, 9); /* 236 cells */
memcpy(&before, &q, sizeof q);
CHECK(v4_wire_put(&q, h, t) == V4_WIRE_NO_ROOM, "a message of 236 cells is refused, 1");
CHECK(memcmp(&before, &q, sizeof q) == 0, "and the queue is exactly as it was: head, used, mark and every cell");
make(h, t, 4 * (235 - 7), 9); /* 235 cells: the last that fits */
CHECK(v4_wire_put(&q, h, t) == V4_WIRE_DONE && q.used == V4_WIRE_CELLS, "one of 235 cells fits, and fills it");
CHECK(v4_wire_room(&q, 0) == V4_WIRE_DONE && v4_wire_room(&q, 1) == V4_WIRE_NO_ROOM, "a full queue has room for none");
/* with 200 cells free: a short message goes, a long one is refused */
v4_wire_reset(&q);
ok = 1;
put_ok(&q, 1024, 1, &ok); put_ok(&q, 1024, 2, &ok); put_ok(&q, 1024, 3, &ok);
put_ok(&q, 4 * (35 - 7), 4, &ok);
CHECK(ok && V4_WIRE_CELLS - q.used == 200, "a queue with 200 cells free: %u", V4_WIRE_CELLS - q.used);
make(h, t, 4 * (201 - 7), 5);
memcpy(&before, &q, sizeof q);
CHECK(v4_wire_put(&q, h, t) == V4_WIRE_NO_ROOM && memcmp(&before, &q, sizeof q) == 0, "refuses a message of 201 cells and is as it was");
make(h, t, 4 * (200 - 7), 5);
CHECK(v4_wire_put(&q, h, t) == V4_WIRE_DONE, "and takes one of 200");
v4_wire_reset(&q);
put_ok(&q, 1024, 1, &ok); put_ok(&q, 1024, 2, &ok); put_ok(&q, 1024, 3, &ok); put_ok(&q, 4 * (35 - 7), 4, &ok);
make(h, t, 40, 6);
CHECK(v4_wire_put(&q, h, t) == V4_WIRE_DONE && q.used == 824 + v4_wire_cells(40), "and a short one, of 40 characters");
/* ---- not a message ---- */
v4_wire_reset(&q);
ok = 1;
put_ok(&q, 100, 1, &ok); put_ok(&q, 7, 2, &ok);
memcpy(&before, &q, sizeof q);
make(h, t, 10, 3);
h[6] = -1;
CHECK(v4_wire_put(&q, h, t) == V4_WIRE_NOT_A_MESSAGE, "a length of -1 answers 4");
CHECK(memcmp(&before, &q, sizeof q) == 0, "and changes nothing");
h[6] = 1025;
CHECK(v4_wire_put(&q, h, t) == V4_WIRE_NOT_A_MESSAGE, "a length of 1,025 answers 4");
CHECK(memcmp(&before, &q, sizeof q) == 0, "and changes nothing");
v4_wire_reset(&q);
ok = 1;
put_ok(&q, 1024, 1, &ok); put_ok(&q, 1024, 2, &ok); put_ok(&q, 1024, 3, &ok); put_ok(&q, 900, 4, &ok);
memcpy(&before, &q, sizeof q);
h[6] = 1025;
CHECK(v4_wire_put(&q, h, t) == V4_WIRE_NOT_A_MESSAGE && memcmp(&before, &q, sizeof q) == 0,
"a length that is not a message is 4 and not 1 even when the queue is nearly full");
/* ---- first and next walk the messages; take and drop of the middle one ---- */
v4_wire_reset(&q);
ok = 1;
put_ok(&q, 20, 1, &ok); put_ok(&q, 33, 2, &ok); put_ok(&q, 9, 3, &ok);
v4_wire_first(&q);
CHECK(ok && q.mark == 0 && v4_wire_look(&q, h) == V4_WIRE_DONE && h[0] == 100, "first puts the mark at the front message");
CHECK(v4_wire_next(&q) == V4_WIRE_DONE && v4_wire_look(&q, h) == V4_WIRE_DONE && h[0] == 200, "next, at the second");
CHECK(v4_wire_next(&q) == V4_WIRE_DONE && v4_wire_look(&q, h) == V4_WIRE_DONE && h[0] == 300, "next, at the third");
memcpy(&before, &q, sizeof q);
CHECK(v4_wire_next(&q) == V4_WIRE_NO_MESSAGE, "and then 3");
CHECK(q.mark == q.used && v4_wire_look(&q, h) == V4_WIRE_NO_MESSAGE, "the mark is then at no message");
CHECK(v4_wire_next(&q) == V4_WIRE_NO_MESSAGE && q.mark == q.used, "and stays there");
v4_wire_first(&q);
(void)v4_wire_next(&q); /* the middle one */
CHECK(v4_wire_take(&q, h, t, V4_WIRE_CELLS) == V4_WIRE_DONE && same(h, t, 33, 2), "take of the middle one gives it");
CHECK(q.used == v4_wire_cells(20) + v4_wire_cells(9) && v4_wire_look(&q, h) == V4_WIRE_DONE && h[0] == 300, "the mark is at the third");
v4_wire_first(&q);
CHECK(v4_wire_take(&q, h, t, V4_WIRE_CELLS) == V4_WIRE_DONE && same(h, t, 20, 1), "the first is still first");
CHECK(v4_wire_take(&q, h, t, V4_WIRE_CELLS) == V4_WIRE_DONE && same(h, t, 9, 3), "and the third follows it, whole");
CHECK(q.used == 0, "and nothing is left");
v4_wire_reset(&q);
ok = 1;
put_ok(&q, 20, 1, &ok); put_ok(&q, 33, 2, &ok); put_ok(&q, 9, 3, &ok);
v4_wire_first(&q);
(void)v4_wire_next(&q);
CHECK(v4_wire_drop(&q) == V4_WIRE_DONE && v4_wire_look(&q, h) == V4_WIRE_DONE && h[0] == 300, "drop of the middle one: the mark is at the third");
v4_wire_first(&q);
CHECK(v4_wire_take(&q, h, t, V4_WIRE_CELLS) == V4_WIRE_DONE && same(h, t, 20, 1) && v4_wire_take(&q, h, t, V4_WIRE_CELLS) == V4_WIRE_DONE && same(h, t, 9, 3) && q.used == 0,
"the first and third are left, in order");
/* the last one, dropped, leaves the mark at no message */
ok = 1;
put_ok(&q, 20, 1, &ok); put_ok(&q, 33, 2, &ok);
v4_wire_first(&q);
(void)v4_wire_next(&q);
CHECK(v4_wire_drop(&q) == V4_WIRE_DONE && q.mark == q.used && q.used == v4_wire_cells(20), "drop of the last: the mark is at no message");
/* middle take across the end of the array */
v4_wire_reset(&q);
ok = 1;
put_ok(&q, 1024, 1, &ok); put_ok(&q, 1024, 2, &ok); put_ok(&q, 1024, 3, &ok);
v4_wire_first(&q);
(void)v4_wire_take(&q, h, t, V4_WIRE_CELLS); /* head is at 263 */
put_ok(&q, 1024, 4, &ok); put_ok(&q, 77, 5, &ok); /* the last of them goes round */
CHECK(ok && q.head + q.used > V4_WIRE_CELLS, "messages are across the end of the array");
v4_wire_first(&q);
(void)v4_wire_next(&q); (void)v4_wire_next(&q); /* the 1,024 that came last but one */
CHECK(v4_wire_take(&q, h, t, V4_WIRE_CELLS) == V4_WIRE_DONE && same(h, t, 1024, 4), "the middle one is taken, and the one after it moves down across the end");
CHECK(v4_wire_look(&q, h) == V4_WIRE_DONE && h[0] == 500, "the mark is at the one that followed it");
v4_wire_first(&q);
ok = 1;
ok &= v4_wire_take(&q, h, t, V4_WIRE_CELLS) == V4_WIRE_DONE && same(h, t, 1024, 2);
ok &= v4_wire_take(&q, h, t, V4_WIRE_CELLS) == V4_WIRE_DONE && same(h, t, 1024, 3);
ok &= v4_wire_take(&q, h, t, V4_WIRE_CELLS) == V4_WIRE_DONE && same(h, t, 77, 5);
CHECK(ok && q.used == 0, "and the others come out whole, in order");
/* ---- move ---- */
v4_wire_reset(&q); v4_wire_reset(&other);
ok = 1;
put_ok(&q, 20, 1, &ok); put_ok(&q, 33, 2, &ok); put_ok(&other, 5, 7, &ok);
other.arrived = 0; q.arrived = 0;
v4_wire_first(&q);
(void)v4_wire_next(&q);
CHECK(ok && v4_wire_move(&q, &other) == V4_WIRE_DONE, "the marked message moves to another queue");
CHECK(other.arrived != 0, "which has had a message arrive");
CHECK(v4_wire_look(&q, h) == V4_WIRE_NO_MESSAGE && q.used == v4_wire_cells(20), "it is off the first, whose mark is at no message");
v4_wire_first(&other);
CHECK(v4_wire_take(&other, h, t, V4_WIRE_CELLS) == V4_WIRE_DONE && same(h, t, 5, 7), "the other's own message is first");
CHECK(v4_wire_take(&other, h, t, V4_WIRE_CELLS) == V4_WIRE_DONE && h[3] == 203 - 1 && h[0] == 200 && h[6] == 33 && h[5] == 205,
"and the moved one is at the back, its fourth word one fewer and the rest as they were");
{
v4_cell wh[7], wt[V4_WIRE_CELLS];
unsigned n;
make(wh, wt, 33, 2);
for (n = 0; n < 9 && t[n] == wt[n]; n++) { }
CHECK(n == 9, "and every word of its text");
}
/* no room in the other: 1, and neither changes */
v4_wire_reset(&q); v4_wire_reset(&other);
ok = 1;
put_ok(&q, 100, 1, &ok);
put_ok(&other, 1024, 2, &ok); put_ok(&other, 1024, 3, &ok); put_ok(&other, 1024, 4, &ok);
put_ok(&other, 4 * (226 - 7), 5, &ok);
v4_wire_first(&q);
memcpy(&before, &q, sizeof q); memcpy(&before_other, &other, sizeof other);
CHECK(ok && V4_WIRE_CELLS - other.used < v4_wire_cells(100), "the other has %u cells free, a message of 100 characters takes %u", V4_WIRE_CELLS - other.used, v4_wire_cells(100));
CHECK(v4_wire_move(&q, &other) == V4_WIRE_NO_ROOM, "move to a queue with no room answers 1");
CHECK(memcmp(&before, &q, sizeof q) == 0 && memcmp(&before_other, &other, sizeof other) == 0, "and changes neither");
v4_wire_reset(&other);
v4_wire_reset(&q);
CHECK(v4_wire_move(&q, &other) == V4_WIRE_NO_MESSAGE && other.used == 0, "move with nothing marked answers 3");
/* ---- take with too little room for the text ---- */
v4_wire_reset(&q);
ok = 1;
put_ok(&q, 100, 1, &ok); /* 25 cells of text */
memcpy(&before, &q, sizeof q);
memset(h2, 0, sizeof h2); memset(t2, 0, sizeof t2);
cap_cells = 24;
CHECK(v4_wire_take(&q, h2, t2, cap_cells) == V4_WIRE_NOT_A_MESSAGE, "take with room for 24 cells of a text of 25 answers 4");
CHECK(memcmp(&before, &q, sizeof q) == 0, "the queue is as it was");
ok = 1;
for (i = 0; i < 7; i++) if (h2[i] != 0) ok = 0;
for (i = 0; i < 25; i++) if (t2[i] != 0) ok = 0;
CHECK(ok, "and nothing was copied out");
CHECK(v4_wire_take(&q, h2, t2, 25) == V4_WIRE_DONE && same(h2, t2, 100, 1), "with room for 25 it is taken");
v4_wire_reset(&q);
ok = 1;
put_ok(&q, 0, 1, &ok);
CHECK(ok && v4_wire_take(&q, h2, 0, 0) == V4_WIRE_DONE, "a message with no text needs no room for any");
/* ---- arrived ---- */
v4_wire_reset(&q); v4_wire_reset(&other);
CHECK(q.arrived == 0, "a queue that has had nothing has had nothing arrive");
ok = 1;
put_ok(&q, 4, 1, &ok);
CHECK(ok && q.arrived != 0, "a put sets arrived");
q.arrived = 0;
make(h, t, 4, 2);
h[6] = 2000;
(void)v4_wire_put(&q, h, t);
CHECK(q.arrived == 0, "a put that is refused does not");
put_ok(&other, 4, 3, &ok);
other.arrived = 0;
v4_wire_first(&other);
CHECK(v4_wire_move(&other, &q) == V4_WIRE_DONE && q.arrived != 0, "nor does a move into it, when it is done, fail to");
q.arrived = 0;
v4_wire_first(&q);
(void)v4_wire_take(&q, h, t, V4_WIRE_CELLS);
CHECK(q.arrived == 0, "a take does not set it");
printf(" %d checks, %d failures\n", checks, failures);
return failures != 0;
}