Files
LithosAnanake/v4/tests/test_store.c
T
rajamesandClaude Opus 5.5 4a505a154d feat(v4.0.0): storage speaks messages -- a device over v3's block chains, common and private
What is on a storage port (v4/system/storage.c): it takes Block read,
Block write and Block data and answers Block data or Block done.  It
holds a view of a common chain, a private chain, or both; private block k
is number 2^32 - 1 - k.  Behind it is v3's block subsystem, reached
through v4/system/store_v3.c, the one v4 file that includes v3's headers.

v3's block code links here with its two device back ends, its log and
its clock, and nothing else of v3.

v4/tests/test_store.c: 36 checks at 64 bits, 35 at 32, and under the
sanitizers.  docs/v4.0.0/MESH.md 8.3, 8.4.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 20:41:07 -04:00

176 lines
9.9 KiB
C

/* test_store.c -- what is on a storage port, spoken to in messages.
* docs/v4.0.0/MESH.md sections 8.3 and 8.4.
*
* No node: the test is the neighbour. The chains are v3's block subsystem
* (v3/src/block_subsystem.c), two of them, each with its fast RAM and a
* device of 64 blocks.
*/
#include "v4/storage.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "block_subsystem.h"
extern void sf_time_init(void);
extern void log_set_level(int level);
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)
#define STORE 90
#define DEVICE_BLOCKS 64u
#define RAM_BYTES ((size_t)BLK_RAM_BLOCKS * BLK_FORTH_SIZE)
static blk_chain_t *make_chain(blk_chain_t *c)
{
blk_chain_t *was = blk_chain_select(c);
int ok = blk_subsys_init((uint8_t *)calloc(1, RAM_BYTES), RAM_BYTES) == BLK_OK
&& blk_subsys_add_raw_device((uint8_t *)calloc(DEVICE_BLOCKS, BLK_FORTH_SIZE), DEVICE_BLOCKS) == BLK_OK;
blk_chain_select(was);
return ok ? c : 0;
}
/* give the device a whole message; 1 if every word was taken */
static int send(v4_store *s, const v4_message *m)
{
unsigned i;
for (i = 0; i < m->count; i++) if (!v4_store_take(s, m->word[i])) return 0;
return 1;
}
static int ask(v4_store *s, v4_cell from, v4_cell type, v4_cell block)
{
v4_message m;
v4_message_text(&m, STORE, from, type, "\0\0\0\0", 4);
m.word[V4_MSG_HEADER] = block;
return send(s, &m);
}
static int data(v4_store *s, v4_cell from, char fill)
{
static char block[V4_STORE_BYTES];
v4_message m;
memset(block, fill, sizeof block);
v4_message_text(&m, STORE, from, V4_MSG_BLOCK_DATA, block, sizeof block);
return send(s, &m);
}
/* the device's next answer, whole; 0 if it has none */
static int answer(v4_store *s, v4_message *m)
{
v4_cell w;
m->count = 0;
if (!v4_store_give(s, &w)) return 0;
while (!v4_message_word(m, w)) if (!v4_store_give(s, &w)) return 0;
return 1;
}
static int is_done(const v4_message *m, v4_cell to, v4_cell status)
{
return v4_message_to(m) == to && v4_message_from(m) == STORE && v4_message_type(m) == V4_MSG_BLOCK_DONE
&& v4_message_length(m) == 4 && m->word[V4_MSG_HEADER] == status;
}
static int is_data(const v4_message *m, v4_cell to, char fill)
{
unsigned i;
if (v4_message_to(m) != to || v4_message_from(m) != STORE || v4_message_type(m) != V4_MSG_BLOCK_DATA
|| v4_message_length(m) != V4_STORE_BYTES) return 0;
for (i = 0; i < V4_STORE_BYTES; i++) if (v4_message_char(m, i) != fill) return 0;
return 1;
}
int main(void)
{
static v4_store both, common_only, private_only;
static v4_message m;
blk_chain_t *common, *own;
v4_cell top = (v4_cell)(v4_ucell)V4_STORE_TOP;
printf("v4 storage tests: V4_CELL_BITS=%d\n", V4_CELL_BITS);
sf_time_init();
log_set_level(-1);
common = make_chain(blk_chain_default());
own = make_chain(blk_chain_new());
CHECK(common && own && common != own, "two chains are made");
if (!common || !own) return 1;
v4_store_init(&both, &v4_store_v3, common, own, STORE);
v4_store_init(&common_only, &v4_store_v3, common, 0, STORE);
v4_store_init(&private_only, &v4_store_v3, 0, own, STORE);
/* ---- a block is written and read ---- */
CHECK(!answer(&both, &m), "with nothing asked there is nothing to read");
CHECK(ask(&both, 11, V4_MSG_BLOCK_WRITE, 2050) && !answer(&both, &m), "a write is not answered until its block has come");
CHECK(data(&both, 11, 'A') && answer(&both, &m) && is_done(&m, 11, V4_STORE_OK), "then it is answered done, to who sent it, from storage");
CHECK(ask(&both, 12, V4_MSG_BLOCK_READ, 2050) && answer(&both, &m) && is_data(&m, 12, 'A'), "another reads what was written");
CHECK(ask(&common_only, 12, V4_MSG_BLOCK_READ, 2050) && answer(&common_only, &m) && is_data(&m, 12, 'A'), "and so does one on another port of the same chain");
CHECK(ask(&both, 12, V4_MSG_BLOCK_WRITE, 20) && data(&both, 12, 'R') && answer(&both, &m) && is_done(&m, 12, V4_STORE_OK)
&& ask(&common_only, 13, V4_MSG_BLOCK_READ, 20) && answer(&common_only, &m) && is_data(&m, 13, 'R'), "a block below 2048 is the common chain's fast RAM");
/* ---- private blocks, from the top down ---- */
CHECK(ask(&both, 13, V4_MSG_BLOCK_WRITE, top) && data(&both, 13, 'P') && answer(&both, &m) && is_done(&m, 13, V4_STORE_OK), "the top number is written");
CHECK(ask(&both, 13, V4_MSG_BLOCK_READ, top) && answer(&both, &m) && is_data(&m, 13, 'P'), "and read back");
CHECK(ask(&private_only, 13, V4_MSG_BLOCK_READ, top) && answer(&private_only, &m) && is_data(&m, 13, 'P'), "it is the private chain's");
{
blk_chain_t *was = blk_chain_select(own);
uint8_t *b = blk_get_buffer((uint32_t)V4_STORE_FIRST, 0);
CHECK(b && b[0] == 'P' && b[1023] == 'P', "private block 0 is that chain's block 2048");
blk_chain_select(common);
b = blk_get_buffer((uint32_t)V4_STORE_FIRST, 0);
CHECK(b && b[0] != 'P', "and is not in the common chain");
blk_chain_select(was);
}
CHECK(ask(&both, 13, V4_MSG_BLOCK_WRITE, top - (v4_cell)(DEVICE_BLOCKS - 1)) && data(&both, 13, 'L') && answer(&both, &m) && is_done(&m, 13, V4_STORE_OK), "the last private block is written");
CHECK(ask(&both, 13, V4_MSG_BLOCK_READ, top - (v4_cell)DEVICE_BLOCKS) && answer(&both, &m) && is_done(&m, 13, V4_STORE_RANGE), "one below the private blocks is no block of the common chain");
CHECK(ask(&common_only, 12, V4_MSG_BLOCK_READ, top) && answer(&common_only, &m) && is_done(&m, 12, V4_STORE_RANGE), "with no private chain the top number is no such block");
CHECK(ask(&private_only, 13, V4_MSG_BLOCK_READ, 2050) && answer(&private_only, &m) && is_done(&m, 13, V4_STORE_NONE), "with no common chain a common number has no storage");
CHECK(ask(&private_only, 13, V4_MSG_BLOCK_WRITE, 20) && data(&private_only, 13, 'r') && answer(&private_only, &m) && is_done(&m, 13, V4_STORE_OK)
&& ask(&private_only, 13, V4_MSG_BLOCK_READ, 20) && answer(&private_only, &m) && is_data(&m, 13, 'r'), "and a number below 2048 is the private chain's fast RAM");
CHECK(ask(&common_only, 12, V4_MSG_BLOCK_READ, 20) && answer(&common_only, &m) && is_data(&m, 12, 'R'), "which is not the common chain's");
/* ---- numbers that are no block ---- */
CHECK(ask(&both, 11, V4_MSG_BLOCK_READ, 0) && answer(&both, &m) && is_done(&m, 11, V4_STORE_RANGE), "0 is no block");
CHECK(ask(&both, 11, V4_MSG_BLOCK_READ, (v4_cell)(V4_STORE_FIRST + DEVICE_BLOCKS)) && answer(&both, &m) && is_done(&m, 11, V4_STORE_RANGE), "one past the end of the common chain is no block");
CHECK(ask(&both, 11, V4_MSG_BLOCK_WRITE, (v4_cell)(V4_STORE_FIRST + DEVICE_BLOCKS)) && data(&both, 11, 'X') && answer(&both, &m) && is_done(&m, 11, V4_STORE_RANGE), "nor can it be written");
#if V4_CELL_BITS > 32
CHECK(ask(&both, 11, V4_MSG_BLOCK_READ, (v4_cell)0x100000000LL) && answer(&both, &m) && is_done(&m, 11, V4_STORE_RANGE), "a number 32 bits do not hold is no block");
CHECK(ask(&both, 11, V4_MSG_BLOCK_READ, -1) && answer(&both, &m) && is_done(&m, 11, V4_STORE_RANGE), "nor is a negative one, on cells wider than 32 bits");
#else
CHECK(top == -1, "on 32-bit cells the top number is -1");
#endif
/* ---- data nobody announced ---- */
CHECK(data(&both, 14, 'Z') && answer(&both, &m) && is_done(&m, 14, V4_STORE_REFUSED), "a block of data with no write before it is refused");
CHECK(ask(&both, 12, V4_MSG_BLOCK_READ, 2050) && answer(&both, &m) && is_data(&m, 12, 'A'), "and nothing was written");
CHECK(ask(&both, 11, V4_MSG_BLOCK_WRITE, 2051) && data(&both, 11, 'B') && answer(&both, &m) && is_done(&m, 11, V4_STORE_OK)
&& data(&both, 11, 'C') && answer(&both, &m) && is_done(&m, 11, V4_STORE_REFUSED)
&& ask(&both, 11, V4_MSG_BLOCK_READ, 2051) && answer(&both, &m) && is_data(&m, 11, 'B'), "a write is for one block of data and no more");
/* ---- two senders at once ---- */
CHECK(ask(&both, 11, V4_MSG_BLOCK_WRITE, 2060) && ask(&both, 12, V4_MSG_BLOCK_WRITE, 2061)
&& data(&both, 12, 'b') && data(&both, 11, 'a'), "two writes are begun before either's block comes, and the blocks come the other way round");
CHECK(answer(&both, &m) && is_done(&m, 12, V4_STORE_OK) && answer(&both, &m) && is_done(&m, 11, V4_STORE_OK), "each is answered, in the order the blocks came");
CHECK(ask(&both, 11, V4_MSG_BLOCK_READ, 2060) && ask(&both, 12, V4_MSG_BLOCK_READ, 2061), "a second request is taken while the answer to the first has not been read");
CHECK(answer(&both, &m) && is_data(&m, 11, 'a') && answer(&both, &m) && is_data(&m, 12, 'b'), "and each block is its own sender's");
/* ---- what is not for storage ---- */
{
v4_message other;
unsigned long before = both.taken;
v4_message_text(&other, 55, 11, V4_MSG_BLOCK_READ, "\0\0\0\0", 4);
other.word[V4_MSG_HEADER] = 2050;
CHECK(send(&both, &other) && both.taken == before + 1 && !answer(&both, &m), "a message for another number is taken and let go");
v4_message_text(&other, STORE, 11, V4_MSG_TEXT, "1 2 +", 5);
CHECK(send(&both, &other) && !answer(&both, &m), "and so is text");
}
/* ---- what v3 wrote reads through the port ---- */
{
blk_chain_t *was = blk_chain_select(common);
uint8_t *b = blk_get_buffer(2070, 1);
CHECK(b != 0, "v3 gives a buffer for block 2070");
if (b) { memset(b, 'V', BLK_FORTH_SIZE); CHECK(blk_update(2070) == BLK_OK && blk_flush(2070) == BLK_OK, "and v3 writes it"); }
blk_chain_select(was);
CHECK(ask(&both, 11, V4_MSG_BLOCK_READ, 2070) && answer(&both, &m) && is_data(&m, 11, 'V'), "what v3's own calls wrote is read through the port");
CHECK(blk_chain_select(0) == was, "the chain selected is the one that was, after the device has been used");
}
printf(" %d checks, %d failures\n", checks, failures);
return failures ? 1 : 0;
}