test(v4): a second unit while the first runs; PAUSE, RESUME; GONE down the graph (MESH.md 9a.6)

test_host_unit: 125 checks.  Shown to fail with the GONE hook taken out of
centre.4th, and with the Hera-only check taken out of manage.c.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rajames
2026-10-16 13:08:43 -04:00
co-authored by Claude Opus 5.5
parent c88af25e1e
commit 3506747d5a
+99
View File
@@ -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;
}