diff --git a/v4/tests/test_host_unit.c b/v4/tests/test_host_unit.c index 5d1502c3..96bfd512 100644 --- a/v4/tests/test_host_unit.c +++ b/v4/tests/test_host_unit.c @@ -371,6 +371,15 @@ static unsigned going_from(v4_cell number, unsigned port) return 0; } +/* 1 if the node numbered `number` has been put to sleep by the fabric (NODE-SLEEP) */ +static v4_cell hera_lost; /* Hera's (LOST) when the step 7 section begins */ +static int paused(v4_cell number) +{ + unsigned k; + for (k = 0; k < PLACES; k++) if (v4_fabric_node_at(&f, k) && v4_fabric_node_at(&f, k)->n.mem[ME] == number) return places[k].asleep; + return 0; +} + int main(void) { unsigned i; @@ -619,6 +628,96 @@ int main(void) "nothing was removed, and Hera goes on"); } + /* ---- STEP 7 (MESH.md 9a.6): A SECOND UNIT WHILE THE FIRST RUNS; PAUSE, RESUME; GONE DOWN THE GRAPH ---- */ + step_limit = 400000000ul; + { + /* What Hera had let go before this section -- in the kills above -- is not this section's. */ + hera_lost = node_numbered(10)->mem[LOST]; + } + { + unsigned born = born_count; + CHECK(told(10, "10 UNIT") && born_count == born + 4, "Hera births her unit again: 11 to 14 are free once more: \"%s\"", printed); + } + CHECK(strstr(tell(10, "11 6 6 BIRTH"), "Argument out of range") != NULL && ended_how == V4_TEXT_ERROR, "a birth of a number that is live is refused: \"%s\"", printed); + + /* acceptance 1: it scales while running */ + spinning = 1u << 11; + (void)tell(11, ": BUSY 3000000 0 DO LOOP ; BUSY"); + CHECK(!waiting(11) && ended == 0, "node 11 of the first unit is busy"); + { + unsigned born = born_count, p = parity_count, k; + const char *r = tell(10, "20 CENTRE"); + CHECK(ended == 1 && ended_how == V4_TEXT_COMPLETED && ended_from == 10, "Hera's 20 CENTRE: a second centre, and it births its own unit: \"%s\"", r); + CHECK(born_count == born + 5 && parity_count == p + 5 && parity_number[p] == 20 && parity_number[p + 1] == 21 && parity_number[p + 4] == 24, + "five nodes were born and reported, 20 by Hera and 21 to 24 by node 20: %u", born_count - born); + for (k = 1; k <= 4 && parity_hash[p + k] == parity_hash[0]; k++) { } + CHECK(k == 5, "the four outer nodes of the second unit have the dictionary hash of the first's"); + CHECK(node_numbered(20)->rom == MESH_ROM && node_numbered(24)->rom == MESH_ROM, "they carry a mesh node's ROM"); + } + CHECK(!waiting(11), "node 11 was busy all the while"); + spinning = 0; + CHECK(strcmp(tell(11, "11 100 * ."), "1100 ") == 0, "and goes on when its loop is done"); + for (i = 21; i <= 24; i++) { + char text[32], want[16]; + snprintf(text, sizeof text, "%u 100 * .", i); + snprintf(want, sizeof want, "%u ", i * 100); + CHECK(strcmp(tell((v4_cell)i, text), want) == 0 && printed_from == (v4_cell)i, "text for node %u goes by Hera and node 20, and what it prints comes back: \"%s\"", i, printed); + } + CHECK(strstr(tell(12, ": X22 S\" 7 6 * .\" 22 SEND 22 AWAIT . ; X22"), "42 ") != NULL && strstr(printed, "1 ") != NULL && ended_how == V4_TEXT_COMPLETED, + "node 12 of the first unit sends node 22 of the second text and waits for it: it is done, and its answer comes back: \"%s\"", printed); + CHECK(strcmp(tell(23, ": T S\" 65 EMIT\" 24 SEND ; T"), "A") == 0 && printed_from == 24, "the second unit's nodes talk across their own square: \"%s\"", printed); + + /* acceptance 2: Hera manages */ + CHECK(waiting(10) && waiting(11) && waiting(12) && waiting(13) && waiting(14) && waiting(20) && waiting(21) && waiting(22) && waiting(23) && waiting(24), "all ten are waiting"); + { + v4_uheat_t c[10]; + static const v4_cell who[10] = { 10, 11, 12, 13, 14, 20, 21, 22, 23, 24 }; + for (i = 0; i < 10; i++) c[i] = clock_of(who[i]); + for (i = 0; i < 5000; i++) (void)v4_fabric_step(&f); + for (i = 0; i < 10 && clock_of(who[i]) == c[i]; i++) { } + CHECK(i == 10, "and none executes anything while it waits: the anti-clocks do not move"); + } + spinning = 1u << 13; + (void)tell(13, ": LONG 2000000 0 DO LOOP ; LONG"); + CHECK(!waiting(13) && ended == 0, "node 13 is in a long loop"); + CHECK(told(10, "13 PAUSE") && paused(13), "Hera puts it to sleep, by its number"); + { + v4_uheat_t c = clock_of(13); + for (i = 0; i < 100000; i++) (void)v4_fabric_step(&f); + CHECK(clock_of(13) == c, "asleep, it executes nothing"); + CHECK(told(11, ": T13 S\" 5 .\" 13 SEND ; T13") && going_from(11, 4) > 0, "text node 11 sends it waits on the wire to it: %u cells", going_from(11, 4)); + CHECK(told(10, "13 RESUME") && !paused(13), "Hera wakes it"); + spinning = 0; + CHECK(strstr(tell(10, "1 DROP"), "5 ") != NULL && clock_of(13) != c && waiting(13), "its loop goes on to its end, and then the text is done: \"%s\"", printed); + } + CHECK(strstr(tell(10, "99 PAUSE"), "PAUSE: no such node") != NULL && strstr(tell(10, "10 RESUME"), "RESUME: no such node") != NULL, "PAUSE and RESUME of what is not a node, or of Hera, say so"); + + /* acceptance 4: GONE down the graph */ + (void)tell(21, ": W12 12 AWAIT . ; W12"); + CHECK(ended == 0 && node_numbered(21)->mem[AWAIT_FROM] == 12 && waiting(21), "node 21 of the second unit waits on node 12 of the first"); + (void)tell(10, "12 KILL"); + CHECK(node_numbered(12) == NULL && strstr(printed, "Node gone") != NULL && node_numbered(21)->mem[AWAIT_FROM] == 0, + "Hera kills node 12: her GONE goes to node 20, which tells its own, and node 21's wait ends: \"%s\"", printed); + { + v4_cell ways = node_numbered(22)->mem[ROUTE_COUNT]; + CHECK(told(22, "88 3 ROUTE") && told(21, "88 22 GONE") && node_numbered(22)->mem[ROUTE_COUNT] == ways + 1, + "a GONE from node 21, which is not node 22's centre, is not believed"); + } + + /* acceptance 1, the end: the second unit is removed and the first carries on */ + { + unsigned born = born_count; + CHECK(told(10, "20 UNIT-KILL") && node_numbered(20) == NULL && node_numbered(21) == NULL && node_numbered(24) == NULL && born_count == born, + "Hera's 20 UNIT-KILL: node 20's children and then node 20 are removed: \"%s\"", printed); + CHECK(strcmp(tell(10, "20 NODE-PLACE . 21 NODE-PLACE . 24 NODE-PLACE ."), "-1 -1 -1 ") == 0, "the kernel has none of them: \"%s\"", printed); + CHECK(strstr(tell(10, ": H21 S\" 1\" 21 SEND ; H21"), "Argument out of range") != NULL, "Hera has no way to them"); + } + CHECK(strcmp(tell(10, "1 2 + ."), "3 ") == 0 && strcmp(tell(11, "1 2 + ."), "3 ") == 0 && strcmp(tell(13, "1 2 + ."), "3 ") == 0 + && strcmp(tell(14, "1 2 + ."), "3 ") == 0, "and the first unit carries on: every node answers"); + CHECK(node_numbered(10)->mem[LOST] == hera_lost && node_numbered(11)->mem[LOST] == 0 && node_numbered(13)->mem[LOST] == 0 && node_numbered(14)->mem[LOST] == 0 + && waiting(10) && waiting(11) && waiting(13) && waiting(14), "nothing was let go in all of this, and all wait again: Hera %ld (%ld before)", + (long)node_numbered(10)->mem[LOST], (long)hera_lost); + printf(" %d checks, %d failures\n", checks, failures); return failures != 0; }