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