/**
* 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 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");
// get the data out
char *data;
size_t len;
log_info("test get_send_data");
sock_test_get_send_data(sock, &data, &len);
// should be the same
assert_strlen(_write_data, len);
assert_strncmp(data, _write_data, len);
// cleanup
free(data);
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);
// cleanup
line_proto_release(lp);
}
static struct irc_conn_callbacks _conn_callbacks = {
.on_registered = NULL,
};
void test_irc_conn (void)
{
struct sock_test *sock;
struct irc_conn *conn;
struct error_info err;
// 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, NULL, &err));
// 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();
}
}