src/test.c
branchnew-transport
changeset 168 a58ad50911fc
parent 167 0d2d8ca879d8
child 169 e6a1ce44aecc
equal deleted inserted replaced
167:0d2d8ca879d8 168:a58ad50911fc
     1 /**
       
     2  * The main test code entry point
       
     3  */
       
     4 #include "transport_test.h"
       
     5 #include "line_proto.h"
       
     6 #include "irc_queue.h"
       
     7 #include "irc_conn.h"
       
     8 #include "irc_net.h"
       
     9 #include "fifo.h"
       
    10 #include "log.h"
       
    11 #include "str.h"
       
    12 #include "error.h"
       
    13 
       
    14 #include <stdlib.h>
       
    15 #include <string.h>
       
    16 #include <getopt.h>
       
    17 #include <assert.h>
       
    18 #include <ctype.h>
       
    19 
       
    20 #define DUMP_STR_BUF 1024
       
    21 #define DUMP_STR_COUNT 8
       
    22 #define DUMP_STR_TAIL 10
       
    23 
       
    24 /**
       
    25  * Global test-running state
       
    26  */
       
    27 struct test_ctx {
       
    28     /** The event_base that we have setup */
       
    29     struct event_base *ev_base;
       
    30 
       
    31 } _test_ctx;
       
    32 
       
    33 
       
    34 /**
       
    35  * This re-formats the given string to escape values, and returns a pointer to an internal static buffer.
       
    36  *
       
    37  * If len is given as >= 0, only the given number of chars will be dumped from str.
       
    38  *
       
    39  * The buffer cycles a bit, so the returned pointers remain valid across DUMP_STR_COUNT calls.
       
    40  *
       
    41  * The resulting string is truncated to (DUMP_STR_BUF - DUMP_STR_TAIL) bytes, not including the ending "...'\0".
       
    42  *
       
    43  * @param str the string to dump, should be NUL-terminated unless len is given
       
    44  * @param len if negative, ignored, otherwise, only this many bytes are dumped from str
       
    45  * @param return a pointer to a static buffer that remains valid across DUMP_STR_COUNT calls to this function
       
    46  */
       
    47 const char *dump_strn (const char *str, ssize_t len)
       
    48 {
       
    49     static char dump_buf[DUMP_STR_COUNT][DUMP_STR_BUF];
       
    50     static size_t dump_idx = 0;
       
    51     
       
    52     // pick a buffer to use
       
    53     char *buf = dump_buf[dump_idx++];
       
    54     
       
    55     // cycle
       
    56     if (dump_idx >= DUMP_STR_COUNT)
       
    57         dump_idx = 0;
       
    58     
       
    59     str_quote(buf, DUMP_STR_BUF, str, len);
       
    60 
       
    61     // ok
       
    62     return buf;
       
    63 }
       
    64 
       
    65 const char *dump_str (const char *str) 
       
    66 {
       
    67     return dump_strn(str, -1);
       
    68 }
       
    69 
       
    70 void assert_null (const void *ptr)
       
    71 {
       
    72     if (ptr)
       
    73         FATAL("%p != NULL", ptr);
       
    74 }
       
    75 
       
    76 void assert_strcmp (const char *is, const char *should_be)
       
    77 {
       
    78     if (!is || strcmp(is, should_be))
       
    79         FATAL("%s != %s", dump_str(is), dump_str(should_be));
       
    80 }
       
    81 
       
    82 void assert_strncmp (const char *is, const char *should_be, size_t n)
       
    83 {   
       
    84     if (!is || strncmp(is, should_be, n))
       
    85         FATAL("%s:%u != %s", dump_strn(is, n), (unsigned) n, dump_strn(should_be, n));
       
    86 }
       
    87 
       
    88 void assert_strlen (const char *str, size_t n)
       
    89 {
       
    90     if (!str || strlen(str) != n)
       
    91         FATAL("strlen(%s) != %u", dump_str(str), (unsigned) n);
       
    92 }
       
    93 
       
    94 void assert_strnull (const char *str)
       
    95 {
       
    96     if (str != NULL)
       
    97         FATAL("%s != NULL", dump_str(str));
       
    98 }
       
    99 
       
   100 void assert_success (err_t err)
       
   101 {
       
   102     if (err != SUCCESS)
       
   103         FATAL("error: %s", error_name(err));
       
   104 }
       
   105 
       
   106 void assert_err (err_t err, err_t target)
       
   107 {
       
   108     if (err != target)
       
   109         FATAL("error: <%s> != <%s>", error_name(err), error_name(target));
       
   110 }
       
   111 
       
   112 void assert_error_info (struct error_info *is, struct error_info *should_be)
       
   113 {
       
   114     if (ERROR_CODE(is) != ERROR_CODE(should_be) || ERROR_EXTRA(is) != ERROR_EXTRA(should_be))
       
   115         FATAL("error: <%s> != <%s>", error_msg(is), error_msg(should_be));
       
   116 }
       
   117 
       
   118 void assert_transport_read (transport_t *transport, const char *str)
       
   119 {
       
   120     size_t len = strlen(str);
       
   121     char buf[len];
       
   122     error_t err;
       
   123 
       
   124     log_debug("read: %p: %s", transport, dump_str(str));
       
   125     
       
   126     // read it
       
   127     assert(transport_read(transport, buf, len, &err) == (int) len);
       
   128 
       
   129     // cmp
       
   130     assert_strncmp(buf, str, len);
       
   131 }
       
   132 
       
   133 void assert_transport_write (transport_t *transport, const char *str)
       
   134 {
       
   135     size_t len = strlen(str);
       
   136     error_t err;
       
   137 
       
   138     log_debug("write: %p: %s", transport, dump_str(str));
       
   139 
       
   140     // write it
       
   141     assert(transport_write(transport, str, len, &err) == (int) len);
       
   142 }
       
   143 
       
   144 void assert_transport_eof (transport_t *transport)
       
   145 {
       
   146     char buf;
       
   147     error_t err;
       
   148 
       
   149     log_debug("eof: %p", transport);
       
   150 
       
   151     assert_err(-transport_read(transport, &buf, 1, &err), ERR_EOF);
       
   152 }
       
   153 
       
   154 void assert_transport_data (struct transport_test *tp, const char *fmt, ...)
       
   155 {
       
   156     char buf[TRANSPORT_TEST_FMT_MAX];
       
   157     va_list vargs;
       
   158     size_t len;
       
   159     
       
   160     va_start(vargs, fmt);
       
   161 
       
   162     if ((len = vsnprintf(buf, TRANSPORT_TEST_FMT_MAX, fmt, vargs)) >= TRANSPORT_TEST_FMT_MAX)
       
   163         FATAL("input too long: %zu bytes", len);
       
   164 
       
   165     va_end(vargs);
       
   166 
       
   167     // get the data out
       
   168     char *out;
       
   169     
       
   170     transport_test_pull_buf(tp, &out, &len);
       
   171     
       
   172     log_debug("pull_buf: %s", dump_strn(out, len));
       
   173     
       
   174     // should be the same
       
   175     assert_strncmp(out, buf, len);
       
   176     assert_strlen(buf, len);
       
   177 
       
   178     // cleanup
       
   179     free(out);
       
   180 }
       
   181 
       
   182 /**
       
   183  * Setup the global sock_stream state
       
   184  */
       
   185 struct event_base* setup_sock (void)
       
   186 {
       
   187     struct event_base *ev_base;
       
   188     struct error_info err;
       
   189 
       
   190     assert((ev_base = event_base_new()));
       
   191     assert_success(sock_init(ev_base, &err));
       
   192 
       
   193     return ev_base;
       
   194 }
       
   195 
       
   196 /**
       
   197  * Create an empty transport_test
       
   198  */
       
   199 struct transport_test* setup_transport_test ()
       
   200 {
       
   201     struct transport_test *tp;
       
   202    
       
   203     assert ((tp = transport_test_create(NULL)) != NULL);
       
   204 
       
   205     return tp;
       
   206 }
       
   207 
       
   208 void assert_str_quote (size_t buf_size, const char *data, ssize_t len, const char *target, size_t out)
       
   209 {
       
   210     char buf[buf_size];
       
   211     
       
   212     size_t ret = str_quote(buf, buf_size, data, len);
       
   213 
       
   214     log_debug("str_quote(%zu, %zd) -> %s:%zu / %s:%zu", buf_size, len, buf, ret, target, out);
       
   215 
       
   216     assert_strcmp(buf, target);
       
   217     assert(ret == out);
       
   218 }
       
   219 
       
   220 void test_str_quote (void)
       
   221 {
       
   222     log_info("testing str_quote()");
       
   223 
       
   224     assert_str_quote(5,     NULL,           -1,     "NULL",         4   );
       
   225     assert_str_quote(16,    "foo",          -1,     "'foo'",        5   );
       
   226     assert_str_quote(16,    "foobar",       3,      "'foo'",        5   );
       
   227     assert_str_quote(16,    "\r\n",         -1,     "'\\r\\n'",     6   );
       
   228     assert_str_quote(16,    "\x13",         -1,     "'\\x13'",      6   );
       
   229     assert_str_quote(16,    "x'y",          -1,     "'x\\'y'",      6   );
       
   230     assert_str_quote(7,     "1234567890",   -1,     "'1'...",       12  );
       
   231     assert_str_quote(9,     "1234567890",   -1,     "'123'...",     12  );
       
   232 }
       
   233 
       
   234 struct str_format_ctx {
       
   235     const char *name;
       
   236 
       
   237     const char *value;
       
   238 };
       
   239 
       
   240 err_t test_str_format_cb (const char *name, const char **value, ssize_t *value_len, void *arg)
       
   241 {
       
   242     struct str_format_ctx *ctx = arg;
       
   243 
       
   244     assert_strcmp(name, ctx->name);
       
   245 
       
   246     *value = ctx->value;
       
   247     *value_len = -1;
       
   248 
       
   249     return SUCCESS;
       
   250 }
       
   251 
       
   252 void assert_str_format (const char *format, const char *name, const char *value, const char *out)
       
   253 {
       
   254     struct str_format_ctx ctx = { name, value };
       
   255     char buf[512];
       
   256 
       
   257     assert_success(str_format(buf, sizeof(buf), format, test_str_format_cb, &ctx));
       
   258     
       
   259     log_debug("str_format(%s), { %s:%s } -> %s / %s", format, name, value, buf, out);
       
   260 
       
   261     assert_strcmp(buf, out);
       
   262 }
       
   263 
       
   264 void test_str_format (void)
       
   265 {
       
   266     log_info("test str_format()");
       
   267 
       
   268     assert_str_format("foo", NULL, NULL, "foo");
       
   269     assert_str_format("foo {bar} quux", "bar", "XXX", "foo XXX quux");
       
   270 }
       
   271 
       
   272 void test_dump_str (void)
       
   273 {
       
   274     log_info("dumping example strings on stdout:");
       
   275 
       
   276     log_debug("normal: %s", dump_str("Hello World"));
       
   277     log_debug("escapes: %s", dump_str("foo\r\nbar\a\001"));
       
   278     log_debug("length: %s", dump_strn("<-->**", 4));
       
   279     log_debug("overflow: %s", dump_str( "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"));
       
   280     log_debug("null: %s", dump_str(NULL));
       
   281     log_debug("quote: %s", dump_str("foo\\bar'quux"));
       
   282 }
       
   283 
       
   284 void test_transport_test (void)
       
   285 {
       
   286     struct transport_info info = { NULL, NULL, 0 };
       
   287     struct transport_test *tp = transport_test_create(&info);
       
   288     transport_t *transport = transport_test_cast(tp);
       
   289 
       
   290     // put the read data
       
   291     log_info("test transport_test_push_*");
       
   292     transport_test_push_buf(tp, "foo", 3);
       
   293     transport_test_push_str(tp, "barx");
       
   294     transport_test_push_fmt(tp, "xx %s xx", "quux");
       
   295     transport_test_push_eof(tp);
       
   296     
       
   297     // read it out
       
   298     log_info("test transport_test_read");
       
   299 
       
   300     assert_transport_read(transport, "foo");
       
   301     assert_transport_read(transport, "ba");
       
   302     assert_transport_read(transport, "rx");
       
   303     assert_transport_read(transport, "xx quux xx");
       
   304     assert_transport_eof(transport);
       
   305 
       
   306     // write some data in
       
   307     log_info("test transport_test_write");
       
   308 
       
   309     assert_transport_write(transport, "test ");
       
   310     assert_transport_write(transport, "data");
       
   311     
       
   312     // check output
       
   313     log_info("test transport_test_pull_*");
       
   314 
       
   315     assert_transport_data(tp, "test data");
       
   316     assert_transport_data(tp, "");
       
   317 
       
   318     // cleanup
       
   319     transport_test_destroy(tp);
       
   320 }
       
   321 
       
   322 void assert_read_line (struct line_proto *lp, const char *line_str)
       
   323 {
       
   324     char *line_buf;
       
   325     
       
   326     log_debug("expect: %s", dump_str(line_str));
       
   327 
       
   328     assert_success(line_proto_recv(lp, &line_buf));
       
   329 
       
   330     if (line_str) {
       
   331         assert_strcmp(line_buf, line_str);
       
   332 
       
   333     } else {
       
   334         assert_strnull(line_buf);
       
   335 
       
   336     }
       
   337 }
       
   338 
       
   339 /**
       
   340  * Context info for test_line_proto callbacks
       
   341  */
       
   342 struct _lp_test_ctx {
       
   343     /** Expected line */
       
   344     const char *line;
       
   345 
       
   346     /** Expected error */
       
   347     struct error_info err;
       
   348 };
       
   349 
       
   350 static void _lp_on_line (char *line, void *arg)
       
   351 {
       
   352     struct _lp_test_ctx *ctx = arg;
       
   353 
       
   354     log_debug("%s", dump_str(line));
       
   355 
       
   356     assert_strcmp(line, ctx->line);
       
   357 
       
   358     ctx->line = NULL;
       
   359 }
       
   360 
       
   361 static void _lp_on_error (struct error_info *err, void *arg)
       
   362 {
       
   363     struct _lp_test_ctx *ctx = arg;
       
   364 
       
   365     assert_error_info(err, &ctx->err);
       
   366 }
       
   367 
       
   368 static struct line_proto_callbacks _lp_callbacks = {
       
   369     .on_line        = &_lp_on_line,
       
   370     .on_error       = &_lp_on_error,
       
   371 };
       
   372 
       
   373 void test_line_proto (void)
       
   374 {
       
   375     struct transport_test *tp = transport_test_create(NULL);
       
   376     transport_t *transport = transport_test_cast(tp);
       
   377     struct line_proto *lp;
       
   378     struct _lp_test_ctx ctx;
       
   379     struct error_info err;
       
   380     
       
   381     // put the read data
       
   382     log_debug("transport_test_push_*");
       
   383     transport_test_push_str(tp, "hello\r\n");
       
   384     transport_test_push_str(tp, "world\n");
       
   385     transport_test_push_str(tp, "this ");
       
   386     transport_test_push_str(tp, "is a line\r");
       
   387     transport_test_push_str(tp, "\nfragment");
       
   388 
       
   389     // create the lp
       
   390     assert_success(line_proto_create(&lp, transport, 128, &_lp_callbacks, &ctx, &err));
       
   391     
       
   392     log_info("test line_proto_recv");
       
   393 
       
   394     // then read some lines from it
       
   395     assert_read_line(lp, "hello");
       
   396     assert_read_line(lp, "world");
       
   397     assert_read_line(lp, "this is a line");
       
   398     assert_read_line(lp, NULL);
       
   399 
       
   400     // then add a final bit to trigger on_line
       
   401     log_info("test on_line");
       
   402 
       
   403     ctx.line = "fragment";
       
   404     transport_test_push_str(tp, "\r\n");
       
   405     assert_strnull(ctx.line);
       
   406 
       
   407     // test writing
       
   408     log_info("test line_proto_send");
       
   409     assert_success(-line_proto_send(lp, "foobar\r\n"));
       
   410     assert_success(-line_proto_send(lp, "quux\r\n"));
       
   411     assert_transport_data(tp, "foobar\r\nquux\r\n");
       
   412 
       
   413     // XXX: test partial writes
       
   414 
       
   415     // cleanup
       
   416     line_proto_destroy(lp);
       
   417 }
       
   418 
       
   419 void test_irc_queue (void)
       
   420 {
       
   421     struct transport_test *tp = transport_test_create(NULL);
       
   422     transport_t *transport = transport_test_cast(tp);
       
   423     struct line_proto *lp;
       
   424     struct irc_queue *queue;
       
   425     struct irc_queue_entry *queue_entry;
       
   426     struct error_info err;
       
   427 
       
   428     // create the lp
       
   429     assert_success(line_proto_create(&lp, transport, 128, &_lp_callbacks, NULL, &err));
       
   430 
       
   431     // create the queue
       
   432     assert_success(irc_queue_create(&queue, _test_ctx.ev_base, lp, &err));
       
   433 
       
   434     struct irc_line line = {
       
   435         NULL, "TEST", { "fooX" }
       
   436     };
       
   437 
       
   438     // then test simple writes, we should be able to push five lines directly
       
   439     log_info("test irc_queue_process (irc_queue_send_direct)");
       
   440     line.args[0] = "foo0"; assert_success(irc_queue_process(queue, &line));
       
   441     line.args[0] = "foo1"; assert_success(irc_queue_process(queue, &line));
       
   442     line.args[0] = "foo2"; assert_success(irc_queue_process(queue, &line));
       
   443     line.args[0] = "foo3"; assert_success(irc_queue_process(queue, &line));
       
   444     line.args[0] = "foo4"; assert_success(irc_queue_process(queue, &line));
       
   445 
       
   446     // they should all be output
       
   447     assert_transport_data(tp,
       
   448             "TEST foo0\r\n"
       
   449             "TEST foo1\r\n"
       
   450             "TEST foo2\r\n"
       
   451             "TEST foo3\r\n"
       
   452             "TEST foo4\r\n"
       
   453     );
       
   454 
       
   455     // then enqueue
       
   456     log_info("test irc_queue_process (irc_queue_put)");
       
   457     line.args[0] = "foo5"; assert_success(irc_queue_process(queue, &line));
       
   458 
       
   459     // ensure it was enqueued
       
   460     assert((queue_entry = TAILQ_FIRST(&queue->list)) != NULL);
       
   461     assert_strcmp(queue_entry->line_buf, "TEST foo5\r\n");
       
   462 
       
   463     // ensure timer is set
       
   464     assert(event_pending(queue->ev, EV_TIMEOUT, NULL));
       
   465 
       
   466     // run the event loop to let the timer run
       
   467     log_info("running the event loop once...");
       
   468     assert(event_base_loop(_test_ctx.ev_base, EVLOOP_ONCE) == 0);
       
   469 
       
   470     // test to check that the line was now sent
       
   471     log_info("checking that the delayed line was sent...");
       
   472     assert_transport_data(tp, "TEST foo5\r\n");
       
   473     assert(TAILQ_EMPTY(&queue->list));
       
   474     assert(!event_pending(queue->ev, EV_TIMEOUT, NULL));
       
   475 
       
   476     // cleanup
       
   477     irc_queue_destroy(queue);
       
   478 }
       
   479 
       
   480 struct test_conn_ctx {
       
   481     /** Callback flags */
       
   482     bool on_registered, on_TEST, on_error, on_quit;
       
   483 };
       
   484 
       
   485 static void _conn_on_registered (struct irc_conn *conn, void *arg)
       
   486 {
       
   487     struct test_conn_ctx *ctx = arg;
       
   488 
       
   489     (void) conn;
       
   490 
       
   491     if (ctx) ctx->on_registered = true;
       
   492 
       
   493     log_debug("registered");
       
   494 }
       
   495 
       
   496 static void _conn_on_error (struct irc_conn *conn, struct error_info *err, void *arg)
       
   497 {
       
   498     struct test_conn_ctx *ctx = arg;
       
   499     
       
   500     (void) conn;
       
   501     (void) err;
       
   502 
       
   503     if (ctx) ctx->on_error = true;
       
   504 
       
   505     log_debug("on_error");
       
   506 }
       
   507 
       
   508 static void _conn_on_quit (struct irc_conn *conn, void *arg)
       
   509 {
       
   510     struct test_conn_ctx *ctx = arg;
       
   511 
       
   512     (void) conn;
       
   513 
       
   514     if (ctx) ctx->on_quit = true;
       
   515 
       
   516     log_debug("on_quit");
       
   517 }
       
   518 
       
   519 static void _conn_on_TEST (const struct irc_line *line, void *arg)
       
   520 {
       
   521     struct test_conn_ctx *ctx = arg;
       
   522 
       
   523     assert(line->source);
       
   524     assert(!line->source->nickname && !line->source->username && line->source->hostname);
       
   525     assert_strcmp(line->command, "TEST");
       
   526     assert_strcmp(line->args[0], "arg0");
       
   527     assert_strnull(line->args[1]);
       
   528 
       
   529     if (ctx) ctx->on_TEST = true;
       
   530 
       
   531     log_debug("on_TEST");
       
   532 }
       
   533 
       
   534 static struct irc_conn_callbacks _conn_callbacks = {
       
   535     .on_registered      = &_conn_on_registered,
       
   536     .on_error           = &_conn_on_error,
       
   537     .on_quit            = &_conn_on_quit,
       
   538 };
       
   539 
       
   540 static struct irc_cmd_handler _conn_handlers[] = {
       
   541     {   "TEST",         &_conn_on_TEST  },
       
   542     {   NULL,           NULL            }
       
   543 };
       
   544 
       
   545 /**
       
   546  * Create and return a new irc_conn with the given ctx (will be initialized to zero).
       
   547  */
       
   548 struct irc_conn* setup_irc_conn (struct transport_test *tp, bool noisy, struct test_conn_ctx *ctx)
       
   549 {
       
   550     struct irc_conn *conn;
       
   551     struct error_info err;
       
   552     struct irc_conn_register_info register_info = {
       
   553         "nick", "user", "realname"
       
   554     };
       
   555 
       
   556     // init the ctx
       
   557     memset(ctx, 0, sizeof(*ctx));
       
   558 
       
   559     // create the irc_conn
       
   560     assert_success(irc_conn_create(&conn, transport_test_cast(tp), &_conn_callbacks, ctx, &err));
       
   561 
       
   562     // test register
       
   563     if (noisy) log_info("test irc_conn_register");
       
   564     assert_success(irc_conn_register(conn, &register_info));
       
   565     assert_transport_data(tp, "NICK nick\r\nUSER user 0 * realname\r\n");
       
   566  
       
   567     // test on_register callback    
       
   568     if (noisy) log_info("test irc_conn_callbacks.on_register");
       
   569     transport_test_push_str(tp, "001 mynick :Blaa blaa blaa\r\n");
       
   570     if (ctx) assert(ctx->on_registered);
       
   571     assert_strcmp(conn->nickname, "mynick");
       
   572    
       
   573     // ok
       
   574     return conn;
       
   575 }
       
   576 
       
   577 void test_irc_conn (void)
       
   578 {
       
   579     struct test_conn_ctx ctx;
       
   580     struct transport_test *tp = setup_transport_test();
       
   581     struct irc_conn *conn = setup_irc_conn(tp, true, &ctx);
       
   582 
       
   583     // add our test handlers
       
   584     assert_success(irc_conn_add_cmd_handlers(conn, _conn_handlers, &ctx));
       
   585 
       
   586     // test on_TEST handler
       
   587     // XXX: come up with a better prefix
       
   588     log_info("test irc_conn.handlers");
       
   589     transport_test_push_str(tp, ":foobar-prefix TEST arg0\r\n");
       
   590     assert(ctx.on_TEST);
       
   591 
       
   592     // test PING/PONG
       
   593     log_info("test PING/PONG");
       
   594     transport_test_push_str(tp, "PING foo\r\n");
       
   595     assert_transport_data(tp, "PONG foo\r\n");
       
   596 
       
   597     // quit nicely
       
   598     log_info("test QUIT");
       
   599     assert_success(irc_conn_QUIT(conn, "bye now"));
       
   600     assert_transport_data(tp, "QUIT :bye now\r\n");
       
   601     assert(conn->quitting);
       
   602 
       
   603     transport_test_push_str(tp, "ERROR :Closing Link: Quit\r\n");
       
   604     transport_test_push_eof(tp);
       
   605     assert(conn->quit && !conn->quitting && !conn->registered);
       
   606     assert(ctx.on_quit);
       
   607     assert(!ctx.on_error);
       
   608 
       
   609     // destroy it
       
   610     irc_conn_destroy(conn);
       
   611 }
       
   612 
       
   613 void test_irc_conn_self_nick (void)
       
   614 {
       
   615     struct test_conn_ctx ctx;
       
   616     struct transport_test *tp = setup_transport_test();
       
   617     struct irc_conn *conn = setup_irc_conn(tp, false, &ctx);
       
   618     
       
   619     log_info("test irc_conn_on_NICK");
       
   620     transport_test_push_fmt(tp, ":mynick!user@somehost NICK mynick2\r\n");
       
   621     assert_strcmp(conn->nickname, "mynick2");
       
   622 
       
   623     // cleanup
       
   624     irc_conn_destroy(conn);
       
   625 }
       
   626 
       
   627 struct test_chan_ctx {
       
   628     /** The channel name */
       
   629     const char *channel;
       
   630 
       
   631     /** The channel we're supposed to be testing */
       
   632     struct irc_chan *chan;
       
   633     
       
   634     /** Flags for callbacks called*/
       
   635     bool on_chan_self_join, on_chan_self_part, on_chan_join, on_chan_part;
       
   636 
       
   637 };
       
   638 
       
   639 void _on_chan_self_join (struct irc_chan *chan, void *arg)
       
   640 {
       
   641     struct test_chan_ctx *ctx = arg;
       
   642 
       
   643     assert(chan == ctx->chan);
       
   644     
       
   645     ctx->on_chan_self_join = true;
       
   646 
       
   647     log_debug("on_self_join");
       
   648 }
       
   649 
       
   650 void _on_chan_join (struct irc_chan *chan, const struct irc_nm *source, void *arg)
       
   651 {
       
   652     struct test_chan_ctx *ctx = arg;
       
   653 
       
   654     assert(chan == ctx->chan);
       
   655 
       
   656     // XXX: verify source
       
   657 
       
   658     ctx->on_chan_join = true;
       
   659 
       
   660     log_debug("on_join");
       
   661 }
       
   662 
       
   663 void _on_chan_part (struct irc_chan *chan, const struct irc_nm *source, const char *msg, void *arg)
       
   664 {
       
   665     struct test_chan_ctx *ctx = arg;
       
   666 
       
   667     assert(chan == ctx->chan);
       
   668 
       
   669     // XXX: verify source
       
   670     // XXX: verify msg
       
   671 
       
   672     ctx->on_chan_part = true;
       
   673 
       
   674     log_debug("on_part");
       
   675 }
       
   676 
       
   677 
       
   678 struct irc_chan_callbacks _chan_callbacks = {
       
   679     .on_self_join       = &_on_chan_self_join,
       
   680     .on_join            = &_on_chan_join,
       
   681     .on_part            = &_on_chan_part,
       
   682 };
       
   683 
       
   684 /**
       
   685  * Setup an irc_net using the given socket, and consume the register request output, but do not push the RPL_WELCOME
       
   686  */
       
   687 struct irc_net* setup_irc_net_unregistered (struct transport_test *tp)
       
   688 {
       
   689     struct irc_net *net;
       
   690     struct irc_net_info net_info = {
       
   691         .register_info = {
       
   692             "nick", "user", "realname"
       
   693         },
       
   694     };
       
   695     struct error_info err;
       
   696 
       
   697     // create the irc_net
       
   698     net_info.transport = transport_test_cast(tp);
       
   699     assert_success(irc_net_create(&net, &net_info, &err));
       
   700 
       
   701     // test register output
       
   702     assert_transport_data(tp, "NICK nick\r\nUSER user 0 * realname\r\n");
       
   703     
       
   704     // ok
       
   705     return net; 
       
   706 }
       
   707 
       
   708 /**
       
   709  * Push to RPL_WELCOME reply, and test state
       
   710  */
       
   711 void do_irc_net_welcome (struct transport_test *tp, struct irc_net *net)
       
   712 {
       
   713     // registration reply
       
   714     transport_test_push_fmt(tp, "001 mynick :Blaa blaa blaa\r\n");
       
   715     assert(net->conn->registered);
       
   716     assert_strcmp(net->conn->nickname, "mynick");
       
   717 
       
   718 }
       
   719 
       
   720 /**
       
   721  * Creates an irc_net and puts it into the registered state
       
   722  */
       
   723 struct irc_net* setup_irc_net (struct transport_test *tp)
       
   724 {
       
   725     struct irc_net *net;   
       
   726 
       
   727     net = setup_irc_net_unregistered(tp);
       
   728     do_irc_net_welcome(tp, net);
       
   729     
       
   730     // ok
       
   731     return net;
       
   732 }
       
   733 
       
   734 /**
       
   735  * General test for irc_net to handle startup
       
   736  */
       
   737 void test_irc_net (void)
       
   738 {
       
   739     struct transport_test *tp = setup_transport_test();
       
   740     
       
   741     // create the network
       
   742     log_info("test irc_net_create");
       
   743     struct irc_net *net = setup_irc_net_unregistered(tp);
       
   744 
       
   745     // send the registration reply
       
   746     log_info("test irc_conn_on_RPL_WELCOME");
       
   747     do_irc_net_welcome(tp, net);
       
   748 
       
   749     // test errors by setting EOF
       
   750     log_info("test irc_net_error");
       
   751     transport_test_push_eof(tp);
       
   752     assert(net->conn == NULL);
       
   753 
       
   754     // cleanup
       
   755     irc_net_destroy(net);
       
   756 }
       
   757 
       
   758 /**
       
   759  * Ensure that an irc_chan_user exists/doesn't exist for the given channel/nickname, and return it
       
   760  */
       
   761 struct irc_chan_user* check_chan_user (struct irc_chan *chan, const char *nickname, bool exists)
       
   762 {
       
   763     struct irc_chan_user *chan_user = irc_chan_get_user(chan, nickname);
       
   764     
       
   765     if (exists && chan_user == NULL)
       
   766         FATAL("user %s not found in channel %s", dump_str(nickname), dump_str(irc_chan_name(chan)));
       
   767     
       
   768     if (!exists && chan_user)
       
   769         FATAL("user %s should not be on channel %s anymore", dump_str(nickname), dump_str(irc_chan_name(chan)));
       
   770 
       
   771     log_debug("%s, exists=%d -> %p: user=%p, nickname=%s", 
       
   772             nickname, exists, chan_user, chan_user ? chan_user->user : NULL, chan_user ? chan_user->user->nickname : NULL);
       
   773     
       
   774     if (chan_user)
       
   775         assert_strcmp(chan_user->user->nickname, nickname);
       
   776 
       
   777     return chan_user;
       
   778 }
       
   779 
       
   780 /**
       
   781  * Creates an irc_chan on the given irc_net, but does not check any output (useful for testing offline add).
       
   782  *
       
   783  * You must pass a test_chan_ctx for use with later operations, this will be initialized for you.
       
   784  */
       
   785 struct irc_chan* setup_irc_chan_raw (struct irc_net *net, const char *channel, struct test_chan_ctx *ctx)
       
   786 {
       
   787     struct irc_chan *chan;
       
   788     struct irc_chan_info chan_info = {
       
   789         .channel        = channel,
       
   790     };
       
   791     struct error_info err;
       
   792     
       
   793     // initialize the given ctx
       
   794     memset(ctx, 0, sizeof(*ctx));
       
   795     ctx->channel = channel;
       
   796 
       
   797     // add a channel
       
   798     assert_success(irc_net_add_chan(net, &chan, &chan_info, &err));
       
   799     assert(!chan->joined);
       
   800     assert_success(irc_chan_add_callbacks(chan, &_chan_callbacks, ctx));
       
   801     ctx->chan = chan;
       
   802     
       
   803     // ok
       
   804     return chan;
       
   805 }
       
   806 
       
   807 /**
       
   808  * Checks that the JOIN request for a channel was sent, and sends the basic JOIN reply
       
   809  */
       
   810 void do_irc_chan_join (struct transport_test *tp, struct test_chan_ctx *ctx)
       
   811 {
       
   812     // JOIN request
       
   813     assert(ctx->chan->joining);
       
   814     assert_transport_data(tp, "JOIN %s\r\n", ctx->channel);
       
   815 
       
   816     // JOIN reply
       
   817     transport_test_push_fmt(tp, ":mynick!user@host JOIN %s\r\n", ctx->channel);
       
   818     assert(!ctx->chan->joining && ctx->chan->joined);
       
   819     assert(ctx->on_chan_self_join);
       
   820 }
       
   821 
       
   822 /**
       
   823  * Sends a short RPL_NAMREPLY/RPL_ENDOFNAMES reply and checks that the users list matches
       
   824  */
       
   825 void do_irc_chan_namreply (struct transport_test *tp, struct test_chan_ctx *ctx)
       
   826 {
       
   827     // RPL_NAMREPLY
       
   828     transport_test_push_fmt(tp, "353 mynick = %s :mynick userA +userB @userC\r\n", ctx->channel);
       
   829     transport_test_push_fmt(tp, "353 mynick = %s :trailingspace \r\n", ctx->channel);
       
   830     transport_test_push_fmt(tp, "366 mynick %s :End of NAMES\r\n", ctx->channel);
       
   831     
       
   832     // XXX: this should be an exclusive test, i.e. these should be the only ones...
       
   833     check_chan_user(ctx->chan, "mynick", true);
       
   834     check_chan_user(ctx->chan, "userA", true);
       
   835     check_chan_user(ctx->chan, "userB", true);
       
   836     check_chan_user(ctx->chan, "userC", true);
       
   837 }
       
   838 
       
   839 /**
       
   840  * Creates an irc_chan on the given irc_net, and checks up to the JOIN reply
       
   841  */
       
   842 struct irc_chan* setup_irc_chan_join (struct transport_test *tp, struct irc_net *net, const char *channel, struct test_chan_ctx *ctx)
       
   843 {
       
   844     setup_irc_chan_raw(net, channel, ctx);
       
   845     do_irc_chan_join(tp, ctx);
       
   846 
       
   847     // ok
       
   848     return ctx->chan;
       
   849 }
       
   850 
       
   851 /**
       
   852  * Creates an irc_chan on the given irc_net, sends the JOIN stuff plus RPL_NAMREPLY
       
   853  */
       
   854 struct irc_chan* setup_irc_chan (struct transport_test *tp, struct irc_net *net, const char *channel, struct test_chan_ctx *ctx)
       
   855 {
       
   856     setup_irc_chan_raw(net, channel, ctx);
       
   857     do_irc_chan_join(tp, ctx);
       
   858     do_irc_chan_namreply(tp, ctx);
       
   859 
       
   860     // ok
       
   861     return ctx->chan;
       
   862 }
       
   863 
       
   864 
       
   865 /**
       
   866  * Call irc_net_add_chan while offline, and ensure that we send the JOIN request after RPL_WELCOME, and handle the join
       
   867  * reply OK.
       
   868  */
       
   869 void test_irc_chan_add_offline (void)
       
   870 {
       
   871     struct test_chan_ctx ctx;
       
   872 
       
   873     struct transport_test *tp = setup_transport_test();
       
   874 
       
   875     log_info("test irc_net_create");
       
   876     struct irc_net *net = setup_irc_net_unregistered(tp);
       
   877 
       
   878     // add an offline channel
       
   879     log_info("test offline irc_net_add_chan");
       
   880     struct irc_chan *chan = setup_irc_chan_raw(net, "#test", &ctx);
       
   881     assert(!chan->joining && !chan->joined);
       
   882 
       
   883     // send the registration reply
       
   884     log_info("test irc_conn_on_RPL_WELCOME");
       
   885     do_irc_net_welcome(tp, net);
       
   886     
       
   887     // test the join sequence
       
   888     log_info("test irc_chan_join/irc_chan_on_JOIN");
       
   889     do_irc_chan_join(tp, &ctx);
       
   890     
       
   891     // cleanup
       
   892     irc_net_destroy(net);
       
   893 }
       
   894 
       
   895 void test_irc_chan_namreply (void)
       
   896 {
       
   897     struct test_chan_ctx ctx;
       
   898     struct transport_test *tp = setup_transport_test();
       
   899     struct irc_net *net = setup_irc_net(tp);
       
   900     setup_irc_chan_join(tp, net, "#test", &ctx);
       
   901 
       
   902     log_info("test irc_chan_on_RPL_NAMREPLY");
       
   903     do_irc_chan_namreply(tp, &ctx);
       
   904 
       
   905     // cleanup
       
   906     irc_net_destroy(net);
       
   907 }
       
   908 
       
   909 void test_irc_chan_user_join (void)
       
   910 {
       
   911     struct test_chan_ctx ctx;
       
   912     struct transport_test *tp = setup_transport_test();
       
   913     struct irc_net *net = setup_irc_net(tp);
       
   914     struct irc_chan *chan = setup_irc_chan(tp, net, "#test", &ctx);
       
   915 
       
   916     // have a user join
       
   917     log_info("test irc_chan_on_JOIN");
       
   918     transport_test_push_fmt(tp, ":newuser!someone@somewhere JOIN %s\r\n", "#test");
       
   919     assert(ctx.on_chan_join);
       
   920     check_chan_user(chan, "newuser", true);
       
   921 
       
   922     // cleanup
       
   923     irc_net_destroy(net);
       
   924 }
       
   925 
       
   926 void test_irc_chan_user_part (void)
       
   927 {
       
   928     struct test_chan_ctx ctx;
       
   929     struct transport_test *tp = setup_transport_test();
       
   930     struct irc_net *net = setup_irc_net(tp);
       
   931     struct irc_chan *chan = setup_irc_chan(tp, net, "#test", &ctx);
       
   932 
       
   933     // have a user join
       
   934     log_info("test irc_chan_on_PART");
       
   935     transport_test_push_fmt(tp, ":userA!someone@somewhere PART %s\r\n", "#test");
       
   936     assert(ctx.on_chan_part); ctx.on_chan_part = NULL;
       
   937     check_chan_user(chan, "userA", false);
       
   938 
       
   939     // cleanup
       
   940     irc_net_destroy(net);
       
   941 }
       
   942 
       
   943 void test_irc_chan_user_kick (void)
       
   944 {
       
   945     struct test_chan_ctx ctx;
       
   946     struct transport_test *tp = setup_transport_test();
       
   947     struct irc_net *net = setup_irc_net(tp);
       
   948     struct irc_chan *chan = setup_irc_chan(tp, net, "#test", &ctx);
       
   949 
       
   950     // kick a user
       
   951     log_info("test irc_chan_on_KICK (other)");
       
   952     transport_test_push_fmt(tp, ":userA!someone@somewhere KICK %s userB\r\n", "#test");
       
   953     check_chan_user(chan, "userA", true);
       
   954     check_chan_user(chan, "userB", false);
       
   955 
       
   956     // cleanup
       
   957     irc_net_destroy(net);
       
   958 }
       
   959 
       
   960 void test_irc_chan_self_kick (void)
       
   961 {
       
   962     struct test_chan_ctx ctx;
       
   963     struct transport_test *tp = setup_transport_test();
       
   964     struct irc_net *net = setup_irc_net(tp);
       
   965     struct irc_chan *chan = setup_irc_chan(tp, net, "#test", &ctx);
       
   966 
       
   967     // kick a user
       
   968     log_info("test irc_chan_on_KICK (self)");
       
   969     transport_test_push_fmt(tp, ":userA!someone@somewhere KICK %s mynick foobar\r\n", "#test");
       
   970     assert(!chan->joined);
       
   971     assert(chan->kicked);
       
   972 
       
   973     // cleanup
       
   974     irc_net_destroy(net);
       
   975 }
       
   976 
       
   977 void test_irc_chan_user_nick (void)
       
   978 {
       
   979     struct test_chan_ctx ctx;
       
   980     struct transport_test *tp = setup_transport_test();
       
   981     struct irc_net *net = setup_irc_net(tp);
       
   982     struct irc_chan *chan = setup_irc_chan(tp, net, "#test", &ctx);
       
   983 
       
   984     // rename one of the users
       
   985     log_info("test irc_net_on_chanuser");
       
   986     transport_test_push_fmt(tp, ":userA!someone@somewhere NICK userA2\r\n");
       
   987     check_chan_user(chan, "userA", false);
       
   988     check_chan_user(chan, "userB", true);
       
   989 
       
   990     // cleanup
       
   991     irc_net_destroy(net);
       
   992 }
       
   993 
       
   994 void test_irc_chan_user_quit (void)
       
   995 {
       
   996     struct test_chan_ctx ctx;
       
   997     struct transport_test *tp = setup_transport_test();
       
   998     struct irc_net *net = setup_irc_net(tp);
       
   999     struct irc_chan *chan = setup_irc_chan(tp, net, "#test", &ctx);
       
  1000 
       
  1001     // rename one of the users
       
  1002     log_info("test irc_net_on_chanuser");
       
  1003     transport_test_push_fmt(tp, ":userA!someone@somewhere QUIT foo\r\n");
       
  1004     check_chan_user(chan, "userA", false);
       
  1005 
       
  1006     // cleanup
       
  1007     irc_net_destroy(net);
       
  1008 }
       
  1009 
       
  1010 void _test_irc_chan_on_CTCP_ACTION (const struct irc_line *line, void *arg)
       
  1011 {
       
  1012     bool *flag = arg;
       
  1013 
       
  1014     log_debug("CTCP ACTION");
       
  1015 
       
  1016     *flag = true;
       
  1017 }
       
  1018 
       
  1019 static struct irc_cmd_handler _test_irc_chan_handlers[] = {
       
  1020     {   "CTCP ACTION",      &_test_irc_chan_on_CTCP_ACTION  },
       
  1021     {   NULL,               NULL                            }
       
  1022 };
       
  1023 
       
  1024 void test_irc_chan_CTCP_ACTION (void)
       
  1025 {
       
  1026     struct test_chan_ctx ctx;
       
  1027     struct transport_test *tp = setup_transport_test();
       
  1028     struct irc_net *net = setup_irc_net(tp);
       
  1029     struct irc_chan *chan = setup_irc_chan(tp, net, "#test", &ctx);
       
  1030     bool cb_ok = false;
       
  1031 
       
  1032     // add our handler
       
  1033     assert_success(irc_cmd_add(&chan->handlers, _test_irc_chan_handlers, &cb_ok));
       
  1034 
       
  1035     // rename one of the users
       
  1036     log_info("test irc_conn_on_CTCP_ACTION");
       
  1037     transport_test_push_fmt(tp, ":userA!someone@somewhere PRIVMSG #test \001ACTION hello  world\001\r\n");
       
  1038     assert(cb_ok);
       
  1039 
       
  1040     // cleanup
       
  1041     irc_net_destroy(net);
       
  1042 }
       
  1043 
       
  1044 void test_irc_chan_privmsg (void)
       
  1045 {
       
  1046     struct test_chan_ctx ctx;
       
  1047     struct transport_test *tp = setup_transport_test();
       
  1048     struct irc_net *net = setup_irc_net(tp);
       
  1049     struct irc_chan *chan = setup_irc_chan(tp, net, "#test", &ctx);
       
  1050 
       
  1051     // rename one of the users
       
  1052     log_info("test irc_chan_PRIVMSG");
       
  1053     assert_success(irc_chan_PRIVMSG(chan, "foobar quux"));
       
  1054     assert_transport_data(tp, "PRIVMSG #test :foobar quux\r\n");
       
  1055 
       
  1056     // cleanup
       
  1057     irc_net_destroy(net);
       
  1058 }
       
  1059 
       
  1060 // XXX: needs to be split off into its own test_fifo.c
       
  1061 #include <sys/types.h>
       
  1062 #include <sys/stat.h>
       
  1063 #include <fcntl.h>
       
  1064 #include <unistd.h>
       
  1065 struct test_fifo_ctx {
       
  1066     /** Path to the fifo */
       
  1067     const char *path;
       
  1068 
       
  1069     /** The write end */
       
  1070     int fd;
       
  1071 
       
  1072     /** callback invoked? */
       
  1073     bool on_read;
       
  1074 
       
  1075     /** Still running? */
       
  1076     bool run;
       
  1077 };
       
  1078 
       
  1079 /**
       
  1080  * Open the FIFO and write the test string to it
       
  1081  */
       
  1082 static void test_fifo_open_write (struct test_fifo_ctx *ctx)
       
  1083 {
       
  1084     // ...raw, for writing
       
  1085     if ((ctx->fd = open(ctx->path, O_WRONLY)) < 0)
       
  1086         FATAL_PERROR("open");
       
  1087 
       
  1088     // write something into it
       
  1089     if (write(ctx->fd, "test", 4) != 4)
       
  1090         FATAL_PERROR("write");
       
  1091  
       
  1092 }
       
  1093 
       
  1094 static void test_fifo_close (struct test_fifo_ctx *ctx)
       
  1095 {
       
  1096     close(ctx->fd);
       
  1097     ctx->fd = -1;
       
  1098 }
       
  1099 
       
  1100 static void test_fifo_on_read (transport_t *fifo, void *arg)
       
  1101 {
       
  1102     int ret;
       
  1103     char buf[16];
       
  1104     struct test_fifo_ctx *ctx = arg;
       
  1105     error_t err;
       
  1106 
       
  1107     // read it back out
       
  1108     log_info("test fifo_read");
       
  1109     if ((ret = transport_read(fifo, buf, 16, &err)) < 0)
       
  1110         assert_success(-ret);
       
  1111 
       
  1112     assert(ret == 4);
       
  1113     assert_strncmp(buf, "test", 4);
       
  1114  
       
  1115     if (ctx->on_read) {
       
  1116         test_fifo_close(ctx);
       
  1117         ctx->run = false;
       
  1118         return;
       
  1119     }
       
  1120    
       
  1121     // re-open the fifo
       
  1122     log_info("test fifo-re-open");
       
  1123     test_fifo_close(ctx);
       
  1124     test_fifo_open_write(ctx);
       
  1125 
       
  1126     ctx->on_read = true;
       
  1127 }
       
  1128 
       
  1129 static struct transport_callbacks test_fifo_callbacks = {
       
  1130     .on_read = test_fifo_on_read,
       
  1131 };
       
  1132 
       
  1133 void test_fifo (void)
       
  1134 {
       
  1135     transport_t *fifo;
       
  1136     struct error_info err;
       
  1137     struct test_fifo_ctx _ctx, *ctx = &_ctx; memset(ctx, 0, sizeof(*ctx));
       
  1138     struct transport_info info = { &test_fifo_callbacks, ctx, TRANSPORT_READ };
       
  1139 
       
  1140     // XXX: requires that this be run in a suitable CWD
       
  1141     ctx->path = "test.fifo";
       
  1142 
       
  1143     // create the fifo
       
  1144     if ((mkfifo(ctx->path, 0600) < 0) && (errno != EEXIST))
       
  1145         FATAL_PERROR("mkfifo");
       
  1146 
       
  1147     // open it
       
  1148     log_info("test fifo_open_read");
       
  1149     assert_success(fifo_open_read(&info, &fifo, _test_ctx.ev_base, ctx->path, &err));
       
  1150     
       
  1151     // put some data into it
       
  1152     test_fifo_open_write(ctx);
       
  1153    
       
  1154     // run the event loop
       
  1155     log_debug("running the event loop...");
       
  1156     ctx->run = true;
       
  1157 
       
  1158     while (ctx->run)
       
  1159         assert(event_base_loop(_test_ctx.ev_base, EVLOOP_ONCE) == 0);
       
  1160 
       
  1161     // check
       
  1162     assert(ctx->fd < 0);
       
  1163     
       
  1164     // cleanup
       
  1165     transport_destroy(fifo);
       
  1166 }
       
  1167 
       
  1168 /**
       
  1169  * Test definition
       
  1170  */
       
  1171 struct test {
       
  1172     /** Test name */
       
  1173     const char *name;
       
  1174 
       
  1175     /** Test func */
       
  1176     void (*func) (void);
       
  1177     
       
  1178     bool optional;
       
  1179 };
       
  1180 
       
  1181 #define DEF_TEST(name) { #name, &test_ ## name, false }
       
  1182 #define DEF_TEST_OPTIONAL(name) { #name, &test_ ## name, true }
       
  1183 #define DEF_TEST_END { NULL, NULL, false }
       
  1184 
       
  1185 static struct test _tests[] = {
       
  1186     DEF_TEST(           str_quote                   ),
       
  1187     DEF_TEST(           str_format                  ),
       
  1188     DEF_TEST(           dump_str                    ),
       
  1189     DEF_TEST(           transport_test              ),
       
  1190     DEF_TEST(           line_proto                  ),
       
  1191     DEF_TEST(           irc_queue                   ),
       
  1192     // XXX: irc_line_parse_invalid_prefix
       
  1193     DEF_TEST(           irc_conn                    ),
       
  1194     DEF_TEST(           irc_conn_self_nick          ),
       
  1195     DEF_TEST(           irc_net                     ),
       
  1196     DEF_TEST(           irc_chan_add_offline        ),
       
  1197     DEF_TEST(           irc_chan_namreply           ),
       
  1198     DEF_TEST(           irc_chan_user_join          ),
       
  1199     DEF_TEST(           irc_chan_user_part          ),
       
  1200     DEF_TEST(           irc_chan_user_kick          ),
       
  1201     DEF_TEST(           irc_chan_self_kick          ),
       
  1202     DEF_TEST(           irc_chan_user_nick          ),
       
  1203     DEF_TEST(           irc_chan_user_quit          ),
       
  1204     DEF_TEST(           irc_chan_CTCP_ACTION        ),
       
  1205     DEF_TEST(           irc_chan_privmsg            ),
       
  1206     DEF_TEST_OPTIONAL(  fifo                        ),
       
  1207     DEF_TEST_END
       
  1208 };
       
  1209 
       
  1210 /**
       
  1211  * Command-line option codes
       
  1212  */
       
  1213 enum option_code {
       
  1214     OPT_HELP            = 'h',
       
  1215     OPT_DEBUG           = 'd',
       
  1216     OPT_QUIET           = 'q',
       
  1217     OPT_LIST            = 'l',
       
  1218     
       
  1219     /** Options without short names */
       
  1220     _OPT_EXT_BEGIN      = 0x00ff,
       
  1221 };
       
  1222 
       
  1223 /**
       
  1224  * Command-line option definitions
       
  1225  */
       
  1226 static struct option options[] = {
       
  1227     {"help",            0,  NULL,   OPT_HELP        },
       
  1228     {"debug",           0,  NULL,   OPT_DEBUG       },
       
  1229     {"quiet",           0,  NULL,   OPT_QUIET       },
       
  1230     {"list",            0,  NULL,   OPT_LIST        },
       
  1231     {0,                 0,  0,      0               },
       
  1232 };
       
  1233 
       
  1234 /**
       
  1235  * Display --help output on stdout
       
  1236  */
       
  1237 static void usage (const char *exe) 
       
  1238 {
       
  1239     printf("Usage: %s [OPTIONS]\n", exe);
       
  1240     printf("\n");
       
  1241     printf(" --help / -h            display this message\n");
       
  1242     printf(" --debug / -d           display DEBUG log messages\n");
       
  1243     printf(" --quiet / -q           supress INFO log messages\n");
       
  1244     printf(" --list / -l            list all tests\n");
       
  1245 }
       
  1246 
       
  1247 static void list_tests (struct test *tests)
       
  1248 {
       
  1249     struct test *test;
       
  1250     
       
  1251     printf("Available tests:\n");
       
  1252 
       
  1253     for (test = tests; test->name; test++) {
       
  1254         printf("\t%s\n", test->name);
       
  1255     }
       
  1256 }
       
  1257 
       
  1258 int main (int argc, char **argv)
       
  1259 {
       
  1260     struct test *test;
       
  1261     size_t test_count = 0;
       
  1262     
       
  1263     int opt, option_index;
       
  1264     const char *filter = NULL;
       
  1265 
       
  1266     // parse options
       
  1267     while ((opt = getopt_long(argc, argv, "hdql", options, &option_index)) != -1) {
       
  1268         switch (opt) {
       
  1269             case OPT_HELP:
       
  1270                 usage(argv[0]);
       
  1271                 exit(EXIT_SUCCESS);
       
  1272             
       
  1273             case OPT_DEBUG:
       
  1274                 set_log_level(LOG_DEBUG);
       
  1275                 break;
       
  1276 
       
  1277             case OPT_QUIET:
       
  1278                 set_log_level(LOG_WARN);
       
  1279                 break;
       
  1280            
       
  1281             case OPT_LIST:
       
  1282                 list_tests(_tests);
       
  1283                 exit(EXIT_SUCCESS);
       
  1284 
       
  1285             case '?':
       
  1286                 usage(argv[0]);
       
  1287                 exit(EXIT_FAILURE);
       
  1288         }
       
  1289     }
       
  1290 
       
  1291     if (optind < argc) {
       
  1292         if (optind == argc - 1) {
       
  1293             // filter
       
  1294             filter = argv[optind];
       
  1295             
       
  1296             log_info("only running tests: %s", filter);
       
  1297         } else {
       
  1298             FATAL("too many arguments");
       
  1299         }
       
  1300     }
       
  1301 
       
  1302     // setup the sockets stuff
       
  1303     _test_ctx.ev_base = setup_sock();
       
  1304 
       
  1305     // run tests
       
  1306     for (test = _tests; test->name; test++) {
       
  1307         if ((filter && strcmp(test->name, filter)) || (!filter && test->optional))
       
  1308             continue;
       
  1309 
       
  1310         log_info("Running test: %s", test->name);
       
  1311         
       
  1312         test_count++;
       
  1313         test->func();
       
  1314     }
       
  1315 
       
  1316     // no tests run?
       
  1317     if (test_count == 0)
       
  1318         FATAL("no tests run");
       
  1319 
       
  1320     log_info("done, ran %zu tests", test_count);
       
  1321 }