add line_proto test, enhance others
authorTero Marttila <terom@fixme.fi>
Thu, 12 Mar 2009 21:12:48 +0200
changeset 41 40f7aa051acb
parent 40 51678c7eae03
child 42 13cfc41f76a7
add line_proto test, enhance others
src/error.c
src/line_proto.c
src/line_proto.h
src/sock_test.c
src/sock_test.h
src/test.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)
--- 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                }
 };