# HG changeset patch # User Tero Marttila # Date 1236885168 -7200 # Node ID 40f7aa051acb9c976dc28b79af227ce5cb218e2b # Parent 51678c7eae03fcdca595e69fb4b987bcefd9646b add line_proto test, enhance others diff -r 51678c7eae03 -r 40f7aa051acb src/error.c --- a/src/error.c Thu Mar 12 20:00:48 2009 +0200 +++ b/src/error.c Thu Mar 12 21:12:48 2009 +0200 @@ -67,7 +67,7 @@ struct error_desc **desc_table, *desc = NULL; // iterate over each defined error_desc array - for (desc_table = _desc_tables; desc_table; desc_table++) { + for (desc_table = _desc_tables; *desc_table; desc_table++) { for (desc = *desc_table; desc->code && desc->name; desc++) { // compare code if (desc->code == code) diff -r 51678c7eae03 -r 40f7aa051acb src/line_proto.c --- a/src/line_proto.c Thu Mar 12 20:00:48 2009 +0200 +++ b/src/line_proto.c Thu Mar 12 21:12:48 2009 +0200 @@ -214,7 +214,7 @@ // move trailing data from previous line to front of buffer if (lp->tail_offset) { - // move to front + // move to front, no-op if tail_len is zero memmove(lp->in_buf, lp->in_buf + lp->tail_offset, lp->tail_len); // reset diff -r 51678c7eae03 -r 40f7aa051acb src/line_proto.h --- a/src/line_proto.h Thu Mar 12 20:00:48 2009 +0200 +++ b/src/line_proto.h Thu Mar 12 21:12:48 2009 +0200 @@ -30,10 +30,12 @@ * * The incoming lines are buffered in a buffer of \a buf_size bytes. This imposes a maximum limit on the line length. * + * Note that the given callbacks struct is copied. + * * @param lp_ptr a pointer to the new line_proto will be returned via this pointer * @param sock the sock_stream to use * @param buf_size the incoming/outgoing buffer size, should be enough to hold the biggest possible line - * @param callbacks the callback struct to use + * @param callbacks the callbacks to use, a copy is stored * @param cb_arg the read_cb callback argument * @param err error information is returned via this pointer */ diff -r 51678c7eae03 -r 40f7aa051acb src/sock_test.c --- a/src/sock_test.c Thu Mar 12 20:00:48 2009 +0200 +++ b/src/sock_test.c Thu Mar 12 21:12:48 2009 +0200 @@ -4,23 +4,63 @@ #include #include +/** + * Grow buf->vecs as needed, to ensure that buf->write_vec is valid + */ +static err_t sock_test_grow_buf (struct io_buf *buf) +{ + size_t read_vec_offset = buf->read_vec ? (buf->read_vec - buf->vecs) : 0; + size_t write_vec_offset = buf->write_vec ? (buf->write_vec - buf->vecs) : 0; + struct io_vec *v; + struct io_vec *vecs_tmp = buf->vecs; + + // don't grow if not full + if (buf->vecs && buf->write_vec < buf->vecs + buf->count) + return SUCCESS; + + // new size + buf->count = buf->count * 2 + 1; + + // grow + if ((buf->vecs = realloc(buf->vecs, buf->count * sizeof(struct io_vec))) == NULL) { + // restore old value + buf->vecs = vecs_tmp; + + return ERR_CALLOC; + } + + // set vec + buf->write_vec = buf->vecs + write_vec_offset; + buf->read_vec = buf->vecs + read_vec_offset; + + // zero new vecs + for (v = buf->write_vec; v < buf->vecs + buf->count; v++) { + v->buf = NULL; + v->len = 0; + } + + // ok + return SUCCESS; +} + static err_t sock_test_read (struct sock_stream *base_sock, void *buf_ptr, size_t *len) { struct sock_test *sock = SOCK_FROM_BASE(base_sock, struct sock_test); struct io_buf *buf = &sock->recv_buf; - struct io_vec *vec = buf->vec; - - // no current vector -> EOF - if (!vec) - return ERR_READ_EOF; + struct io_vec *vec = buf->read_vec; + + // EOF/nonblock if we're past the end of the last vector + if (!vec || vec == buf->vecs + buf->count || buf->off >= vec->len) { + if (sock->nonblocking && !sock->eof) { + // wait for more to be fed in + *len = 0; + return SUCCESS; - // vector past the end -> EOF - if (vec == &buf->vecs[buf->count]) - return ERR_READ_EOF; - - // no data left in current vector -> EOF - if (buf->off >= vec->len) - return ERR_READ_EOF; + } else { + // EOF! + return SET_ERROR(SOCK_TEST_ERR(sock), ERR_READ_EOF); + } + } // amount of data available in this iovec size_t available = vec->len - buf->off; @@ -42,7 +82,7 @@ } else { // next vector - buf->vec++; + buf->read_vec++; } // update len @@ -56,28 +96,12 @@ { struct sock_test *sock = SOCK_FROM_BASE(base_sock, struct sock_test); struct io_buf *buf = &sock->send_buf; - struct io_vec *vec = buf->vec; - // vectors full? - if (buf->vecs == NULL || vec == &buf->vecs[buf->count]) { - size_t vec_offset = vec ? (vec - buf->vecs) : 0; - struct io_vec *v; - - // new size - buf->count = buf->count * 2 + 1; - - // grow - assert((buf->vecs = realloc(buf->vecs, buf->count * sizeof(struct io_vec)))); - - // set vec - vec = buf->vec = buf->vecs + vec_offset; - - // zero - for (v = vec; v < &buf->vecs[buf->count]; v++) { - v->buf = NULL; - v->len = 0; - } - } + // ensure there's room + assert(sock_test_grow_buf(buf) == SUCCESS); + + // the next buffer + struct io_vec *vec = buf->write_vec; // store vec->len = *len; @@ -85,7 +109,7 @@ memcpy(vec->buf, buf_ptr, vec->len); // move vec onwards - buf->vec++; + buf->write_vec++; // ok return SUCCESS; @@ -93,19 +117,26 @@ static err_t sock_test_event_init (struct sock_stream *base_sock) { - + struct sock_test *sock = SOCK_FROM_BASE(base_sock, struct sock_test); + + // set the nonblocking flag + sock->nonblocking = true; + return SUCCESS; } static err_t sock_test_event_enable (struct sock_stream *base_sock, short mask) { - + struct sock_test *sock = SOCK_FROM_BASE(base_sock, struct sock_test); + return SUCCESS; } static void sock_test_release (struct sock_stream *base_sock) { + struct sock_test *sock = SOCK_FROM_BASE(base_sock, struct sock_test); + sock_test_destroy(sock); } /* @@ -135,12 +166,52 @@ return sock; } -void sock_test_set_recv_buffer (struct sock_test *sock, struct io_vec *vecs, size_t count) +void sock_test_destroy (struct sock_test *sock) { - sock->recv_buf.vecs = vecs; - sock->recv_buf.count = count; - sock->recv_buf.vec = vecs; - sock->recv_buf.off = 0; + size_t i; + struct io_buf *sbuf = &sock->send_buf, *rbuf = &sock->recv_buf; + + // free the send buffer + for (i = 0; i < sbuf->count; i++) { + free(sbuf->vecs[i].buf); + } + + // free the buffer vector lists + free(sbuf->vecs); + free(rbuf->vecs); + + // free the sock itself + free(sock); +} + +void sock_test_set_recv_buffer (struct sock_test *sock, struct io_vec *vecs, size_t count, bool eof) +{ + struct io_buf *buf = &sock->recv_buf; + + // allocate + copy + assert((buf->vecs = calloc(count, sizeof(struct io_vec)))); + memcpy(buf->vecs, vecs, count * sizeof(struct io_vec)); + + // set + buf->count = count; + buf->read_vec = buf->vecs; + buf->write_vec = buf->vecs + count; + buf->off = 0; + + // set EOF flag? + if (eof) + sock->eof = true; +} + +void sock_test_add_recv_vec (struct sock_test *sock, struct io_vec new_vec) +{ + struct io_buf *buf = &sock->recv_buf; + + // ensure there's room + assert(sock_test_grow_buf(buf) == SUCCESS); + + // copy + *(buf->write_vec++) = new_vec; } void sock_test_get_send_data (struct sock_test *sock, char **buf_ptr, size_t *len_ptr) diff -r 51678c7eae03 -r 40f7aa051acb src/sock_test.h --- a/src/sock_test.h Thu Mar 12 20:00:48 2009 +0200 +++ b/src/sock_test.h Thu Mar 12 21:12:48 2009 +0200 @@ -7,6 +7,7 @@ * Dummy sock_stream implemention for local testing. */ #include "sock_internal.h" +#include /** * IO vector @@ -29,8 +30,8 @@ /** The number of io_vecs */ size_t count; - /** Current vector */ - struct io_vec *vec; + /** Current read/write vector */ + struct io_vec *read_vec, *write_vec; /** Offset into current vector */ size_t off; @@ -45,6 +46,12 @@ /** The send/recieve buffers */ struct io_buf send_buf, recv_buf; + + /** non-blocking mode? */ + bool nonblocking; + + /** No more data is going to be added, return EOF once all the rest is consumed */ + bool eof; }; /** @@ -53,16 +60,33 @@ #define SOCK_TEST_BASE(sock_ptr) (&(sock_ptr)->base) /** + * Get the sock_stream.err pointer from a sock_tcp pointer + */ +#define SOCK_TEST_ERR(sock_ptr) SOCK_ERR(SOCK_TEST_BASE(sock_ptr)) + +/** * A dummy stream socket intended for testing purposes. */ struct sock_test* sock_test_create (void); /** + * Destroy the sock buffer, releasing any resource we allocated ourself + */ +void sock_test_destroy (struct sock_test *sock); + +/** * Set the recieve buffer contents. * - * The data is not copied, but the vectors are stored as-is. + * The vectors themselves are copied, but the data they contain is not. + * + * If the EOF flag is given, it indicates that no more data will be added, otherwise the eof status is unchanged. */ -void sock_test_set_recv_buffer (struct sock_test *sock, struct io_vec *vecs, size_t count); +void sock_test_set_recv_buffer (struct sock_test *sock, struct io_vec *vecs, size_t count, bool eof); + +/** + * Add some data to the recieve buffer + */ +void sock_test_add_recv_vec (struct sock_test *sock, struct io_vec vec); /** * Get the send buffer contents as a single string, free() after use if you care about that diff -r 51678c7eae03 -r 40f7aa051acb src/test.c --- a/src/test.c Thu Mar 12 20:00:48 2009 +0200 +++ b/src/test.c Thu Mar 12 21:12:48 2009 +0200 @@ -2,12 +2,52 @@ * 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 #include #include +void assert_strcmp (const char *a, const char *b) +{ + if (strcmp(a, b)) + FATAL("'%s' != '%s'", a, b); +} + +void assert_strncmp (const char *a, const char *b, size_t n) +{ + if (strncmp(a, b, n)) + FATAL("'%s':%d != '%s'", a, n, b); +} + +void assert_strlen (const char *str, size_t n) +{ + if (strlen(str) != n) + FATAL("strlen('%s') != %u", str, n); +} + +void assert_strnul (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> != target <%s>", error_name(err), error_name(target)); +} + void assert_sock_read (struct sock_stream *sock, const char *str) { char buf[strlen(str)]; @@ -18,7 +58,7 @@ assert(sock_stream_read(sock, buf, strlen(str)) == (int) strlen(str)); // cmp - assert(strncmp(buf, str, strlen(str)) == 0); + assert_strncmp(buf, str, strlen(str)); } void assert_sock_write (struct sock_stream *sock, const char *str) @@ -29,6 +69,15 @@ 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(); @@ -40,14 +89,19 @@ // put the read data log_debug("set_recv_buffer: %p, %d", _read_data, 2); - sock_test_set_recv_buffer(sock, _read_data, 2); + sock_test_set_recv_buffer(sock, _read_data, 2, true); + + // read it out + log_info("test sock_test_read"); - // read it out 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"); @@ -55,13 +109,78 @@ char *data; size_t len; + log_info("test get_send_data"); + sock_test_get_send_data(sock, &data, &len); - log_debug("get_send_data: %u: '%s'", len, data); + // should be the same + assert_strlen(_write_data, len); + assert_strncmp(data, _write_data, len); - // should be the same - assert(len == strlen(_write_data)); - assert(strncmp(data, _write_data, len) == 0); + // 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_strnul(line_buf); + + } +} + +static struct line_proto_callbacks _lp_callbacks = { + .on_line = NULL, + .on_error = NULL, +}; + +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 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, NULL, &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 + sock_test_add_recv_vec(sock, _trailing_data); + + // read the final bit + assert_read_line(lp, "fragment"); + + // cleanup + line_proto_release(lp); } static struct irc_conn_callbacks _conn_callbacks = { @@ -78,7 +197,7 @@ assert((sock = sock_test_create())); // create the irc_conn - assert(irc_conn_create(&conn, SOCK_TEST_BASE(sock), &_conn_callbacks, NULL, &err) == SUCCESS); + assert_success(irc_conn_create(&conn, SOCK_TEST_BASE(sock), &_conn_callbacks, NULL, &err)); // destroy it irc_conn_destroy(conn); @@ -96,6 +215,7 @@ } _tests[] = { { "sock_test", &test_sock_test }, + { "line_proto", &test_line_proto }, { "irc_conn", &test_irc_conn }, { NULL, NULL } };