src/test.c
author Tero Marttila <terom@fixme.fi>
Thu, 12 Mar 2009 21:44:34 +0200
changeset 43 42f5dc680930
parent 42 13cfc41f76a7
child 44 6bd70113e1ed
permissions -rw-r--r--
improve irc_conn test
/**
 * The main test code entry point
 */
#include "sock_test.h"
#include "line_proto.h"
#include "irc_conn.h"
#include "log.h"
#include "error.h"

#include <stdlib.h>
#include <string.h>
#include <assert.h>

void assert_strcmp (const char *is, const char *should_be)
{
    if (strcmp(is, should_be))
        FATAL("'%s' != '%s'", is, should_be);
}

void assert_strncmp (const char *is, const char *should_be, size_t n)
{
    if (strncmp(is, should_be, n))
        FATAL("'%s':%d != '%s'", is, n, should_be);
}

void assert_strlen (const char *str, size_t n)
{
    if (strlen(str) != n)
        FATAL("strlen('%s') != %u", str, n);
}

void assert_strnull (const char *str)
{
    if (str != NULL)
        FATAL("'%s' != NULL", str);
}

void assert_success (err_t err)
{
    if (err != SUCCESS)
        FATAL("error: %s", error_name(err));

}

void assert_err (err_t err, err_t target)
{
    if (err != target)
        FATAL("error: <%s> != <%s>", error_name(err), error_name(target));
}

void assert_error_info (struct error_info *is, struct error_info *should_be)
{
    if (ERROR_CODE(is) != ERROR_CODE(should_be) || ERROR_EXTRA(is) != ERROR_EXTRA(should_be))
        FATAL("error: <%s> != <%s>", error_msg(is), error_msg(should_be));
}

void assert_sock_read (struct sock_stream *sock, const char *str)
{
    char buf[strlen(str)];

    log_debug("read: %p: '%s'", sock, str);
    
    // read it
    assert(sock_stream_read(sock, buf, strlen(str)) == (int) strlen(str));

    // cmp
    assert_strncmp(buf, str, strlen(str));
}

void assert_sock_write (struct sock_stream *sock, const char *str)
{
    log_debug("write: %p: '%s'", sock, str);

    // write it
    assert(sock_stream_write(sock, str, strlen(str)) == (int) strlen(str));
}

void assert_sock_eof (struct sock_stream *sock)
{
    char buf;

    log_debug("eof: %p", sock);

    assert_err(-sock_stream_read(sock, &buf, 1), ERR_READ_EOF);
}

void assert_sock_send_data (struct sock_test *sock, const char *data)
{
    // get the data out
    char *buf;
    size_t len;
    
    sock_test_get_send_data(sock, &buf, &len);
    
    log_debug("get_send_data: '%*s'", (int) len, buf);
    
    // should be the same
    assert_strncmp(buf, data, len);
    assert_strlen(data, len);

    // cleanup
    free(buf);
}

void test_sock_test (void)
{
    struct sock_test *sock = sock_test_create();
    struct io_vec _read_data[] = {
        {   "foo",      3   },
        {   "barx",     4   }
    };
    const char *_write_data = "test data";
    
    // put the read data
    log_debug("set_recv_buffer: %p, %d", _read_data, 2);
    sock_test_set_recv_buffer(sock, _read_data, 2, true);
    
    // read it out
    log_info("test sock_test_read");

    assert_sock_read(SOCK_TEST_BASE(sock), "foo");
    assert_sock_read(SOCK_TEST_BASE(sock), "ba");
    assert_sock_read(SOCK_TEST_BASE(sock), "rx");
    assert_sock_eof(SOCK_TEST_BASE(sock));

    // write the data in
    log_info("test sock_test_write");

    assert_sock_write(SOCK_TEST_BASE(sock), "test ");
    assert_sock_write(SOCK_TEST_BASE(sock), "data");
    
    // check output
    assert_sock_send_data(sock, _write_data);

    // check output
    assert_sock_send_data(sock, "");

    // cleanup
    sock_test_destroy(sock);
}

void assert_read_line (struct line_proto *lp, const char *line_str)
{
    char *line_buf;
    
    log_debug("expect: '%s'", line_str);

    assert_success(line_proto_recv(lp, &line_buf));

    if (line_str) {
        assert(line_buf != NULL);
        assert_strcmp(line_buf, line_str);

    } else {
        assert_strnull(line_buf);

    }
}

/**
 * Context info for test_line_proto callbacks
 */
struct _lp_test_ctx {
    /** Expected line */
    const char *line;

    /** Expected error */
    struct error_info err;
};

static void _lp_on_line (char *line, void *arg)
{
    struct _lp_test_ctx *ctx = arg;

    log_debug("'%s'", line);

    assert_strcmp(line, ctx->line);

    ctx->line = NULL;
}

static void _lp_on_error (struct error_info *err, void *arg)
{
    struct _lp_test_ctx *ctx = arg;

    assert_error_info(err, &ctx->err);
}

static struct line_proto_callbacks _lp_callbacks = {
    .on_line        = &_lp_on_line,
    .on_error       = &_lp_on_error,
};

void test_line_proto (void)
{
    struct sock_test *sock = sock_test_create();
    struct io_vec _read_data[] = {
        {   "hello\r\n",    7   },
        {   "world\n",      6   },
        {   "this ",        5   },
        {   "is a line\r",  10  },
        {   "\nfragment",   9   },
    }, _trailing_data = {   "\r\n",     2 };
    struct line_proto *lp;
    struct _lp_test_ctx ctx;
    struct error_info err;
    
    // put the read data
    log_debug("set_recv_buffer: %p, %d", _read_data, 5);
    sock_test_set_recv_buffer(sock, _read_data, 5, false);
    
    // create the lp
    assert_success(line_proto_create(&lp, SOCK_TEST_BASE(sock), 128, &_lp_callbacks, &ctx, &err));
    
    log_info("test line_proto_recv");

    // then read some lines from it
    assert_read_line(lp, "hello");
    assert_read_line(lp, "world");
    assert_read_line(lp, "this is a line");
    assert_read_line(lp, NULL);

    // then add a final bit to trigger on_line
    log_info("test on_line");

    ctx.line = "fragment";
    sock_test_add_recv_vec(sock, _trailing_data);
    assert_strnull(ctx.line);

    // test writing
    log_info("test line_proto_send");
    assert_success(-line_proto_send(lp, "foobar\r\n"));
    assert_success(-line_proto_send(lp, "quux\r\n"));
    assert_sock_send_data(sock, "foobar\r\nquux\r\n");

    // XXX: test partial writes

    // cleanup
    line_proto_release(lp);
}

struct _test_irc_conn_ctx {
    bool on_registered, on_TEST;
};

static void _conn_on_registered (struct irc_conn *conn, void *arg)
{
    struct _test_irc_conn_ctx *ctx = arg;

    (void) conn;

    ctx->on_registered = true;

    log_debug("registered");
}

static void _conn_on_TEST (const struct irc_line *line, void *arg)
{
    struct _test_irc_conn_ctx *ctx = arg;

    assert_strcmp(line->prefix, "foobar-prefix");
    assert_strcmp(line->command, "TEST");
    assert_strcmp(line->args[0], "arg0");
    assert_strnull(line->args[1]);

    ctx->on_TEST = true;

    log_debug("on_TEST");
}

static struct irc_conn_callbacks _conn_callbacks = {
    .on_registered      = &_conn_on_registered,
};

static struct irc_cmd_handler _conn_handlers[] = {
    {   "TEST",         &_conn_on_TEST  },
    {   NULL,           NULL            }
};

void test_irc_conn (void)
{
    struct sock_test *sock;
    struct irc_conn *conn;
    struct error_info err;
    struct irc_conn_register_info register_info = {
        "nick", "user", "realname"
    };
    struct _test_irc_conn_ctx ctx = { false, false };

    // create the test socket
    assert((sock = sock_test_create()));
    
    // create the irc_conn
    assert_success(irc_conn_create(&conn, SOCK_TEST_BASE(sock), &_conn_callbacks, &ctx, &err));
    assert_success(irc_conn_add_cmd_handlers(conn, _conn_handlers, &ctx));

    // test register
    log_info("test irc_conn_register");
    assert_success(irc_conn_register(conn, &register_info));
    assert_sock_send_data(sock, "NICK nick\r\nUSER user 0 * realname\r\n");
    
    // test on_register callback    
    log_info("test irc_conn_callbacks.on_register");
    sock_test_add_recv_str(sock, "001 mynick :Blaa blaa blaa\r\n");
    assert(ctx.on_registered);
    assert_strcmp(conn->nickname, "mynick");

    // test on_TEST handler
    log_info("test irc_conn.handlers");
    sock_test_add_recv_str(sock, ":foobar-prefix TEST arg0\r\n");
    assert(ctx.on_TEST);

    // destroy it
    irc_conn_destroy(conn);
}

/**
 * Test definition
 */
static struct test {
    /** Test name */
    const char *name;

    /** Test func */
    void (*func) (void);

} _tests[] = {
    {   "sock_test",    &test_sock_test     },
    {   "line_proto",   &test_line_proto    },
    {   "irc_conn",     &test_irc_conn      },
    {   NULL,           NULL                }
};

int main (int argc, char **argv)
{
    struct test *test;

    (void) argv;
    
    // no arguments
    assert(argc == 1);

    // run tests
    for (test = _tests; test->name; test++) {
        log_info("Running test: %s", test->name);

        test->func();
    }
}