--- 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)
--- 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
--- 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
*/
--- 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 <string.h>
#include <assert.h>
+/**
+ * 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)
--- 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 <stdbool.h>
/**
* 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
--- 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 <stdlib.h>
#include <string.h>
#include <assert.h>
+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 }
};