src/test/msg_proto.c
author Tero Marttila <terom@fixme.fi>
Wed, 27 May 2009 23:57:48 +0300
branchnew-lib-errors
changeset 217 7728d6ec3abf
parent 196 873796250c60
permissions -rw-r--r--
nexus.c compiles
#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);
}