#include "../msg_proto.h"
#include "test.h"
#include "transport.h"
#include <stdint.h>
struct test_msg_proto_ctx {
struct msg_proto *proto;
bool on_msg, on_error;
size_t msg_count;
const char *msg;
const error_t *err;
};
static void on_msg (struct msg_proto *proto, void *data, size_t len, void *arg)
{
struct test_msg_proto_ctx *ctx = arg;
log_debug("data=%s, len=%zu", dump_strn(data, len), len);
// check state
assert(proto == ctx->proto);
assert(!ctx->on_msg);
assert(ctx->msg);
// check it's the correct message data
assert_strncmp(data, ctx->msg, len);
assert_strlen(ctx->msg, len);
// mark
if (!ctx->msg_count || !--ctx->msg_count) {
ctx->msg = NULL;
ctx->on_msg = true;
}
}
static void on_error (struct msg_proto *proto, const error_t *err, void *arg)
{
struct test_msg_proto_ctx *ctx = arg;
log_debug("err=%s", dump_str(error_msg(err)));
// check state
assert(proto == ctx->proto);
assert(!ctx->on_error);
assert(ctx->err);
// check it's the right error
assert_error(err, ctx->err);
// mark
ctx->err = NULL;
ctx->on_error = true;
}
static const struct msg_proto_callbacks _callbacks = {
.on_msg = on_msg,
.on_error = on_error,
};
struct msg_proto* setup_msg_proto (struct test_msg_proto_ctx *ctx, struct transport_test *tp)
{
error_t err;
// init
memset(ctx, 0, sizeof(*ctx));
// create it
assert_success(msg_proto_create(&ctx->proto, transport_test_cast(tp), &_callbacks, ctx, &err));
return ctx->proto;
}
void test_msg_proto_recv (void)
{
struct test_msg_proto_ctx _ctx, *ctx = &_ctx;
struct transport_test *tp = setup_transport_test();
struct msg_proto *proto = setup_msg_proto(&_ctx, tp);
// test full message
ctx->msg = "xxxx";
transport_test_push_buf(tp, "\x00\x06xxxx", 6);
assert_true(ctx->on_msg);
ctx->on_msg = false;
// test partial message (header+body)
ctx->msg = "xxxx";
transport_test_push_buf(tp, "\x00\x06", 2);
assert_false(ctx->on_msg);
transport_test_push_buf(tp, "xxxx", 4);
assert_true(ctx->on_msg);
ctx->on_msg = false;
// test partial header
ctx->msg = "xxxx";
transport_test_push_buf(tp, "\x00", 1);
assert_false(ctx->on_msg);
transport_test_push_buf(tp, "\x06xxxx", 5);
assert_true(ctx->on_msg);
ctx->on_msg = false;
// test two messages in one
ctx->msg = "zzzz";
ctx->msg_count = 2;
transport_test_push_buf(tp, "\x00\x06zzzz\x00\x06zzzz", 6*2);
assert_true(ctx->on_msg);
ctx->on_msg = false;
// cleanup
msg_proto_destroy(proto);
}
void test_msg_proto_send (void)
{
struct test_msg_proto_ctx _ctx, *ctx = &_ctx;
struct transport_test *tp = setup_transport_test();
struct msg_proto *proto = setup_msg_proto(&_ctx, tp);
error_t err;
// test full message
ctx->msg = "xxxx";
assert_success(msg_proto_send(proto, ctx->msg, strlen(ctx->msg), &err));
assert_transport_data_buf(tp, "\x00\x06xxxx", 6);
// cleanup
msg_proto_destroy(proto);
// XXX: test write buffering
}
void test_msg_proto_error (void)
{
struct test_msg_proto_ctx _ctx, *ctx = &_ctx;
struct transport_test *tp = setup_transport_test();
struct msg_proto *proto = setup_msg_proto(&_ctx, tp);
error_t err;
(void) ctx;
// XXX: bad way to test uint16_t underflow
// assert_err(msg_proto_send(proto, NULL, ((1 << 16) - 1) - 3, &err), ERR_MISC);
// length overflow
assert_err(msg_proto_send(proto, NULL, ((1 << 16) - 1), &err), ERR_MISC);
// cleanup
msg_proto_destroy(proto);
}
void test_msg_proto_error_transport (void)
{
struct test_msg_proto_ctx _ctx, *ctx = &_ctx;
struct transport_test *tp = setup_transport_test();
struct msg_proto *proto = setup_msg_proto(&_ctx, tp);
error_t err;
// dummy error
SET_ERROR_STR(&err, ERR_MISC, "random fail");
ctx->err = &err;
transport_test_async_error(tp, &err);
assert_true(ctx->on_error);
ctx->on_error = false;
// cleanup
msg_proto_destroy(proto);
}
void test_msg_proto_error_read (void)
{
struct test_msg_proto_ctx _ctx, *ctx = &_ctx;
struct transport_test *tp = setup_transport_test();
struct msg_proto *proto = setup_msg_proto(&_ctx, tp);
error_t err;
// testing with EOF is easiest
SET_ERROR(&err, ERR_EOF);
ctx->err = &err;
transport_test_push_eof(tp);
assert_true(ctx->on_error);
ctx->on_error = false;
// cleanup
msg_proto_destroy(proto);
}
void test_msg_proto_error_recv_invalid (void)
{
struct test_msg_proto_ctx _ctx, *ctx = &_ctx;
struct transport_test *tp = setup_transport_test();
struct msg_proto *proto = setup_msg_proto(&_ctx, tp);
error_t err;
// recieve with invalid header
SET_ERROR_STR(&err, ERR_MISC, "message_header::len");
ctx->err = &err;
transport_test_push_buf(tp, "\x00\x01x", 3);
assert_true(ctx->on_error);
ctx->on_error = false;
// cleanup
msg_proto_destroy(proto);
}