/* 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 #include 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; }