vere: adds tests for newt ipc framing, fixes infinite loop

This commit is contained in:
Joe Bryan 2019-12-09 18:18:07 -08:00
parent baedc50567
commit 1f05927558
4 changed files with 424 additions and 97 deletions

View File

@ -31,11 +31,12 @@ CFLAGS := $(CFLAGS)
all: $(all_exes)
test: build/ames_tests build/hashtable_tests build/jam_tests build/mug_tests build/noun_tests
test: build/ames_tests build/hashtable_tests build/jam_tests build/mug_tests build/newt_tests build/noun_tests
./build/ames_tests
./build/hashtable_tests
./build/jam_tests
./build/mug_tests
./build/newt_tests
./build/noun_tests
clean:
@ -66,6 +67,11 @@ build/mug_tests: $(common_objs) tests/mug_tests.o
@mkdir -p ./build
@$(CC) $^ $(LDFLAGS) -o $@
build/newt_tests: $(common_objs) tests/newt_tests.o
@echo CC -o $@
@mkdir -p ./build
@$(CC) $^ $(LDFLAGS) -o $@
build/noun_tests: $(common_objs) tests/noun_tests.o
@echo CC -o $@
@mkdir -p ./build

View File

@ -257,7 +257,7 @@
/* u3_poke: poke callback function.
*/
typedef void (*u3_poke)(void*, u3_noun);
typedef void (*u3_poke)(void*, u3_atom);
/* u3_bail: bailout callback function.
*/
@ -1172,6 +1172,16 @@
/** Stream messages.
**/
/* u3_newt_encode(): encode an atom to a length-prefixed byte buffer
*/
c3_y*
u3_newt_encode(u3_atom mat, c3_w* len_w);
/* u3_newt_decode(): decode a (partial) length-prefixed byte buffer
*/
void
u3_newt_decode(u3_moat* mot_u, c3_y* buf_y, c3_w len_w);
/* u3_newt_write(): write atom to stream; free atom.
*/
void

View File

@ -0,0 +1,338 @@
#include "all.h"
#include "vere/vere.h"
/* _setup(): prepare for tests.
*/
static void
_setup(void)
{
u3m_init();
u3m_pave(c3y, c3n);
}
static c3_w pok_w;
static c3_w bal_w;
static void
_moat_poke_cb(void* vod_p, u3_atom a)
{
pok_w++;
u3z(a);
}
static void
_moat_bail_cb(void* vod_p, const c3_c* err_c)
{
bal_w++;
}
/* _test_newt_smol(): various scenarios with small messages
*/
static void
_test_newt_smol(void)
{
// =(2 (jam 0))
//
u3_atom a = u3ke_jam(0);
u3_moat mot_u;
c3_w len_w;
c3_y* buf_y;
memset(&mot_u, 0, sizeof(u3_moat));
mot_u.pok_f = _moat_poke_cb;
mot_u.bal_f = _moat_bail_cb;
// one message one buffer
//
{
pok_w = 0;
bal_w = 0;
buf_y = u3_newt_encode(u3k(a), &len_w);
u3_newt_decode(&mot_u, buf_y, len_w);
if ( 1 != pok_w ) {
fprintf(stderr, "newt smol fail (a)\n");
exit(1);
}
}
// two messages one buffer
//
{
pok_w = 0;
bal_w = 0;
buf_y = u3_newt_encode(u3k(a), &len_w);
buf_y = c3_realloc(buf_y, 2 * len_w);
memcpy(buf_y + len_w, buf_y, len_w);
len_w = 2 * len_w;
u3_newt_decode(&mot_u, buf_y, len_w);
if ( 2 != pok_w ) {
fprintf(stderr, "newt smol fail (b)\n");
exit(1);
}
}
// one message two buffers
//
{
c3_y* end_y;
pok_w = 0;
bal_w = 0;
buf_y = u3_newt_encode(u3k(a), &len_w);
end_y = c3_malloc(1);
end_y[0] = buf_y[len_w - 1];
u3_newt_decode(&mot_u, buf_y, len_w - 1);
if ( 0 != pok_w ) {
fprintf(stderr, "newt smol fail (c)\n");
exit(1);
}
u3_newt_decode(&mot_u, end_y, 1);
if ( 1 != pok_w ) {
fprintf(stderr, "newt smol fail (d)\n");
exit(1);
}
}
// two messages two buffers (overlapping length)
//
{
c3_y* haf_y;
c3_w haf_w, dub_w;
pok_w = 0;
bal_w = 0;
buf_y = u3_newt_encode(u3k(a), &len_w);
dub_w = 2 * len_w;
haf_w = len_w / 2;
// buf_y is all of message one, half of message two (not a full length)
//
buf_y = c3_realloc(buf_y, dub_w - haf_w);
memcpy(buf_y + len_w, buf_y, len_w - haf_w);
// haf_y is the second half of message two
//
haf_y = c3_malloc(haf_w);
memcpy(haf_y, buf_y + (len_w - haf_w), haf_w);
u3_newt_decode(&mot_u, buf_y, dub_w - haf_w);
if ( 1 != pok_w ) {
fprintf(stderr, "newt smol fail (e)\n");
exit(1);
}
u3_newt_decode(&mot_u, haf_y, haf_w);
if ( 2 != pok_w ) {
fprintf(stderr, "newt smol fail (f)\n");
exit(1);
}
}
u3z(a);
}
/* _test_newt_vast(): various scenarios with larger messages
*/
static void
_test_newt_vast(void)
{
// =(53 (met 3 (jam "abcdefghijklmnopqrstuvwxyz")))
//
u3_atom a = u3ke_jam(u3i_tape("abcdefghijklmnopqrstuvwxyz"));
u3_moat mot_u;
c3_w len_w;
c3_y* buf_y;
memset(&mot_u, 0, sizeof(u3_moat));
mot_u.pok_f = _moat_poke_cb;
mot_u.bal_f = _moat_bail_cb;
// one message one buffer
//
{
pok_w = 0;
bal_w = 0;
buf_y = u3_newt_encode(u3k(a), &len_w);
u3_newt_decode(&mot_u, buf_y, len_w);
if ( 1 != pok_w ) {
fprintf(stderr, "newt vast fail (a)\n");
exit(1);
}
}
// two messages one buffer
//
{
pok_w = 0;
bal_w = 0;
buf_y = u3_newt_encode(u3k(a), &len_w);
buf_y = c3_realloc(buf_y, 2 * len_w);
memcpy(buf_y + len_w, buf_y, len_w);
len_w = 2 * len_w;
u3_newt_decode(&mot_u, buf_y, len_w);
if ( 2 != pok_w ) {
fprintf(stderr, "newt vast fail (b)\n");
exit(1);
}
}
// one message many buffers
//
{
pok_w = 0;
bal_w = 0;
buf_y = u3_newt_encode(u3k(a), &len_w);
{
c3_y* cop_y = c3_malloc(len_w);
c3_w haf_w = len_w / 2;
memcpy(cop_y, buf_y, len_w);
u3_newt_decode(&mot_u, buf_y, haf_w);
while ( haf_w < len_w ) {
c3_y* end_y = c3_malloc(1);
end_y[0] = cop_y[haf_w];
if ( 0 != pok_w ) {
fprintf(stderr, "newt vast fail (c) %u\n", haf_w);
exit(1);
}
u3_newt_decode(&mot_u, end_y, 1);
haf_w++;
}
c3_free(cop_y);
}
if ( 1 != pok_w ) {
fprintf(stderr, "newt vast fail (d)\n");
exit(1);
}
}
// two messages two buffers
//
{
c3_y* haf_y;
c3_w haf_w, dub_w;
pok_w = 0;
bal_w = 0;
buf_y = u3_newt_encode(u3k(a), &len_w);
dub_w = 2 * len_w;
haf_w = len_w / 2;
// buf_y is all of message one, half of message two
//
buf_y = c3_realloc(buf_y, dub_w - haf_w);
memcpy(buf_y + len_w, buf_y, len_w - haf_w);
// haf_y is the second half of message two
//
haf_y = c3_malloc(haf_w);
memcpy(haf_y, buf_y + (len_w - haf_w), haf_w);
u3_newt_decode(&mot_u, buf_y, dub_w - haf_w);
if ( 1 != pok_w ) {
fprintf(stderr, "newt vast fail (e)\n");
exit(1);
}
u3_newt_decode(&mot_u, haf_y, haf_w);
if ( 2 != pok_w ) {
fprintf(stderr, "newt vast fail (f)\n");
exit(1);
}
}
// two messages many buffers
//
{
c3_w dub_w;
pok_w = 0;
bal_w = 0;
buf_y = u3_newt_encode(u3k(a), &len_w);
dub_w = 2 * len_w;
// buf_y is two copies of message
//
buf_y = c3_realloc(buf_y, dub_w);
memcpy(buf_y + len_w, buf_y, len_w);
{
c3_y* cop_y = c3_malloc(dub_w);
c3_w haf_w = len_w + 1;
memcpy(cop_y, buf_y, dub_w);
u3_newt_decode(&mot_u, buf_y, haf_w);
while ( haf_w < dub_w ) {
c3_y* end_y = c3_malloc(1);
end_y[0] = cop_y[haf_w];
if ( 1 != pok_w ) {
fprintf(stderr, "newt vast fail (g) %u\n", haf_w);
exit(1);
}
u3_newt_decode(&mot_u, end_y, 1);
haf_w++;
}
c3_free(cop_y);
}
if ( 2 != pok_w ) {
fprintf(stderr, "newt vast fail (h)\n");
exit(1);
}
}
u3z(a);
}
/* main(): run all test cases.
*/
int
main(int argc, char* argv[])
{
_setup();
_test_newt_smol();
_test_newt_vast();
fprintf(stderr, "test_newt: ok\n");
return 0;
}

View File

@ -31,8 +31,6 @@
#include "all.h"
#include "vere/vere.h"
#undef NEWT_VERBOSE
/* _newt_gain_meat(): add a block to an existing message
*/
static void
@ -47,17 +45,6 @@ _newt_gain_meat(u3_moat* mot_u)
met_u->len_d = mot_u->len_d;
memcpy(met_u->hun_y, mot_u->rag_y, mot_u->len_d);
#ifdef NEWT_VERBOSE
u3l_log("newt: %d: create: msg %p, new block %p, len %"
PRIu64 ", has %" PRIu64 ", needs %" PRIu64 "\r\n",
getpid(),
mot_u->mes_u,
met_u,
met_u->len_d,
mot_u->mes_u->has_d,
mot_u->mes_u->len_d);
#endif
// enqueue block
//
if ( !mot_u->mes_u->meq_u ) {
@ -105,12 +92,6 @@ _newt_gain_mess(u3_moat* mot_u)
nel_d);
}
#ifdef NEWT_VERBOSE
u3l_log("newt: %d: parsed length %" PRIu64 "\r\n",
getpid(),
nel_d);
#endif
mot_u->len_d -= 8ULL;
mot_u->mes_u = c3_malloc(sizeof(u3_mess));
@ -202,11 +183,29 @@ _newt_poke_mess(u3_moat* mot_u)
}
}
/* _newt_consume(): advance buffer processing.
/* u3_newt_decode(): decode a (partial) length-prefixed byte buffer
*/
static void
_newt_consume(u3_moat* mot_u)
void
u3_newt_decode(u3_moat* mot_u, c3_y* buf_y, c3_w len_w)
{
// grow read buffer by `len_d` bytes
//
if ( mot_u->rag_y ) {
// XX check SIZE_MAX?
//
c3_d nel_d = mot_u->len_d + len_w;
mot_u->rag_y = c3_realloc(mot_u->rag_y, nel_d);
memcpy(mot_u->rag_y + mot_u->len_d, buf_y, len_w);
mot_u->len_d = nel_d;
c3_free(buf_y);
}
else {
mot_u->rag_y = buf_y;
mot_u->len_d = (c3_d)len_w;
}
// process stray bytes, trying to create a new message
// or add a block to an existing one.
//
@ -219,6 +218,9 @@ _newt_consume(u3_moat* mot_u)
if ( 8ULL <= mot_u->len_d ) {
_newt_gain_mess(mot_u);
}
else {
break;
}
}
else {
// there is a live message, add a block to the queue.
@ -256,6 +258,7 @@ _newt_read_cb(uv_stream_t* str_u,
u3_moat* mot_u = (void *)str_u;
if ( 0 > len_i ) {
c3_free(buf_u->base);
uv_read_stop(str_u);
mot_u->bal_f(mot_u->vod_p, uv_strerror(len_i));
}
@ -265,37 +268,7 @@ _newt_read_cb(uv_stream_t* str_u,
c3_free(buf_u->base);
}
else {
c3_d len_d = (c3_d)len_i;
#ifdef NEWT_VERBOSE
u3l_log("newt: %d: read %ld\r\n", getpid(), len_i);
u3l_log("newt: %d: <bytes>", getpid());
for ( int i = 0; i < len_i; i++) {
if (0 == (i % 16)) u3l_log("\r\n");
u3l_log(" %02x", (unsigned) buf_u->base[i]);
}
u3l_log("\r\nnewt: %d: </bytes>\r\n", getpid());
#endif
// grow read buffer by `len_d` bytes
//
if ( mot_u->rag_y ) {
c3_d nel_d = mot_u->len_d + len_d;
mot_u->rag_y = c3_realloc(mot_u->rag_y, nel_d);
memcpy(mot_u->rag_y + mot_u->len_d, buf_u->base, len_d);
mot_u->len_d = nel_d;
c3_free(buf_u->base);
}
else {
mot_u->rag_y = (c3_y *)buf_u->base;
mot_u->len_d = len_d;
}
_newt_consume(mot_u);
u3_newt_decode(mot_u, (c3_y*)buf_u->base, (c3_w)len_i);
}
}
@ -319,23 +292,23 @@ u3_newt_read(u3_moat* mot_u)
}
}
/* write request for newt
/* u3_write_t: write request for newt
*/
struct _u3_write_t {
uv_write_t wri_u;
u3_mojo* moj_u;
void* vod_p;
c3_y* buf_y;
};
typedef struct _u3_write_t {
uv_write_t wri_u;
u3_mojo* moj_u;
void* vod_p;
c3_y* buf_y;
} u3_write_t;
/* _newt_write_cb(): generic write callback.
*/
static void
_newt_write_cb(uv_write_t* wri_u, c3_i sas_i)
{
struct _u3_write_t* req_u = (struct _u3_write_t*)wri_u;
void* vod_p = req_u->vod_p;
u3_mojo* moj_u = req_u->moj_u;
u3_write_t* req_u = (struct _u3_write_t*)wri_u;
void* vod_p = req_u->vod_p;
u3_mojo* moj_u = req_u->moj_u;
free(req_u->buf_y);
free(req_u);
@ -346,6 +319,31 @@ _newt_write_cb(uv_write_t* wri_u, c3_i sas_i)
}
}
/* u3_newt_encode(): encode an atom to a length-prefixed byte buffer
*/
c3_y*
u3_newt_encode(u3_atom mat, c3_w* len_w)
{
c3_w met_w = u3r_met(3, mat);
c3_y* buf_y;
*len_w = 8 + met_w;
buf_y = c3_malloc(*len_w);
// write header; c3_d is futureproofing
//
buf_y[0] = ((met_w >> 0) & 0xff);
buf_y[1] = ((met_w >> 8) & 0xff);
buf_y[2] = ((met_w >> 16) & 0xff);
buf_y[3] = ((met_w >> 24) & 0xff);
buf_y[4] = buf_y[5] = buf_y[6] = buf_y[7] = 0;
u3r_bytes(0, met_w, buf_y + 8, mat);
u3z(mat);
return buf_y;
}
/* u3_newt_write(): write atom to stream; free atom.
*/
void
@ -353,44 +351,19 @@ u3_newt_write(u3_mojo* moj_u,
u3_atom mat,
void* vod_p)
{
c3_w len_w = u3r_met(3, mat);
c3_y* buf_y = c3_malloc(len_w + 8);
struct _u3_write_t* req_u = c3_malloc(sizeof(*req_u));
uv_buf_t buf_u;
c3_i err_i;
/* write header; c3_d is futureproofing
*/
buf_y[0] = ((len_w >> 0) & 0xff);
buf_y[1] = ((len_w >> 8) & 0xff);
buf_y[2] = ((len_w >> 16) & 0xff);
buf_y[3] = ((len_w >> 24) & 0xff);
buf_y[4] = buf_y[5] = buf_y[6] = buf_y[7] = 0;
u3r_bytes(0, len_w, buf_y + 8, mat);
u3z(mat);
u3_write_t* req_u = c3_malloc(sizeof(*req_u));
c3_w len_w;
c3_y* buf_y = u3_newt_encode(mat, &len_w);
uv_buf_t buf_u;
c3_i err_i;
req_u->moj_u = moj_u;
req_u->buf_y = buf_y;
buf_u.base = (c3_c*) buf_y;
buf_u.len = len_w + 8;
#ifdef NEWT_VERBOSE
u3l_log("newt: %d: write %d\n", getpid(), len_w + 8);
#endif
#ifdef NEWT_VERBOSE
u3l_log("newt: %d: <bytes>", getpid());
for ( int i = 0; i < len_w+8; i++) {
if (0 == (i % 16)) u3l_log("\r\n");
u3l_log(" %02x", (unsigned) buf_u.base[i]);
}
u3l_log("\r\nnewt: %d: </bytes>\r\n", getpid());
#endif
buf_u = uv_buf_init((c3_c*)buf_y, len_w);
if ( 0 != (err_i = uv_write((uv_write_t*)req_u,
(uv_stream_t*)&moj_u->pyp_u,
&buf_u,
1,
&buf_u, 1,
_newt_write_cb)) )
{
moj_u->bal_f(moj_u, uv_strerror(err_i));