implement fifo (sock_fifo.c) and test_fifo
authorTero Marttila <terom@fixme.fi>
Tue, 07 Apr 2009 19:41:48 +0300
changeset 118 05b8d5150313
parent 117 9cb405164250
child 119 64f50072db9e
implement fifo (sock_fifo.c) and test_fifo
src/CMakeLists.txt
src/log.c
src/log.h
src/sock.c
src/sock.h
src/sock_fd.c
src/sock_fd.h
src/sock_fifo.c
src/sock_gnutls.c
src/sock_internal.h
src/sock_test.c
src/test.c
--- a/src/CMakeLists.txt	Tue Apr 07 18:09:16 2009 +0300
+++ b/src/CMakeLists.txt	Tue Apr 07 19:41:48 2009 +0300
@@ -10,7 +10,7 @@
 
 # define our source code modules
 set (CORE_SOURCES error.c log.c)
-set (SOCK_SOURCES sock.c sock_fd.c sock_tcp.c sock_gnutls.c sock_test.c line_proto.c)
+set (SOCK_SOURCES sock.c sock_fd.c sock_tcp.c sock_gnutls.c sock_test.c sock_fifo.c line_proto.c)
 set (IRC_SOURCES irc_line.c irc_conn.c irc_net.c irc_chan.c chain.c irc_cmd.c irc_proto.c irc_client.c irc_user.c irc_queue.c)
 set (LUA_SOURCES nexus_lua.c lua_objs.c lua_config.c lua_irc.c)
 set (CONSOLE_SOURCES console.c lua_console.c)
--- a/src/log.c	Tue Apr 07 18:09:16 2009 +0300
+++ b/src/log.c	Tue Apr 07 19:41:48 2009 +0300
@@ -3,6 +3,7 @@
 
 #include <stdio.h>
 #include <stdarg.h>
+#include <string.h>
 
 /**
  * The global log level
@@ -106,3 +107,24 @@
     // err_code and newline
     printf(": %s\n", error_msg(err));
 }
+
+void _log_perr (enum log_level level, const char *func, const char *format, ...)
+{
+    va_list vargs;
+
+    // filter out?
+    if (level < _log_level)
+        return;
+ 
+    // header
+    _log_header(level, func);
+    
+    // formatted output
+    va_start(vargs, format);
+    vprintf(format, vargs);
+    va_end(vargs);
+
+    // err_code and newline
+    printf(": %s\n", strerror(errno));
+}
+
--- a/src/log.h	Tue Apr 07 18:09:16 2009 +0300
+++ b/src/log.h	Tue Apr 07 19:41:48 2009 +0300
@@ -59,22 +59,33 @@
 void _log_err_info (enum log_level level, struct error_info *err, const char *func, const char *format, ...)
     __attribute__ ((format (printf, 4, 5)));
 
+/**
+ * Log using errno.
+ */
+void _log_perr (enum log_level level, const char *func, const char *format, ...)
+    __attribute__ ((format (printf, 3, 4)));
+
 #define log_err(err, ...) _log_err(LOG_ERROR, err, __func__, __VA_ARGS__)
 #define log_err_info(err_info, ...) _log_err_info(LOG_ERROR, err_info, __func__, __VA_ARGS__)
 
-/*
+/**
  * log_fatal + exit failure
  */
 #define FATAL(...) do { log_fatal(__VA_ARGS__); exit(EXIT_FAILURE); } while (0)
 
-/*
+/**
  * log_err + exit failure
  */
 #define FATAL_ERR(err_code, ...) do { _log_err(LOG_FATAL, err_code, __func__, __VA_ARGS__); exit(EXIT_FAILURE); } while (0)
 
-/*
+/**
  * log_err_info + exit failure
  */
 #define FATAL_ERROR(err_info, ...) do { _log_err_info(LOG_FATAL, err_info, __func__, __VA_ARGS__); exit(EXIT_FAILURE); } while (0)
 
+/**
+ * log_perr + exit failure
+ */
+#define FATAL_PERROR(...) do { _log_perr(LOG_FATAL, __func__, __VA_ARGS__); exit(EXIT_FAILURE); } while (0)
+
 #endif /* LOG_H */
--- a/src/sock.c	Tue Apr 07 18:09:16 2009 +0300
+++ b/src/sock.c	Tue Apr 07 19:41:48 2009 +0300
@@ -31,11 +31,15 @@
 
 int sock_stream_read (struct sock_stream *sock, void *buf, size_t len)
 {
-    err_t err;
+    struct error_info *err = SOCK_ERR(sock);
+
+    // XXX: not readable
+    if (!sock->type->methods.read)
+        return -1;
 
     // proxy off to method handler
-    if ((err = sock->type->methods.read(sock, buf, &len)))
-        return -err;
+    if (sock->type->methods.read(sock, buf, &len, err))
+        return -ERROR_CODE(err);
     
     // return updated bytes-read len
     return len;
@@ -43,11 +47,15 @@
 
 int sock_stream_write (struct sock_stream *sock, const void *buf, size_t len)
 {
-    err_t err;
+    struct error_info *err = SOCK_ERR(sock);
+
+    // XXX: not writeable
+    if (!sock->type->methods.write)
+        return -1;
 
     // proxy off to method handler
-    if ((err = sock->type->methods.write(sock, buf, &len)))
-        return -err;
+    if (sock->type->methods.write(sock, buf, &len, err))
+        return -ERROR_CODE(err);
 
     // return updated bytes-written len
     return len;
--- a/src/sock.h	Tue Apr 07 18:09:16 2009 +0300
+++ b/src/sock.h	Tue Apr 07 19:41:48 2009 +0300
@@ -107,7 +107,7 @@
 /**
  * A read-only "socket" based on a FIFO, this provides nonblocking read operations by re-opening the FIFO on EOF.
  */
-err_t fifo_read_open (struct sock_stream **stream_ptr, const char *path, struct error_info *err);
+err_t fifo_open_read (struct sock_stream **stream_ptr, const char *path, struct error_info *err);
 
 /**
  * Read a series of bytes from the socket into the given \a buf (up to \a len bytes). If succesfull, this returns
--- a/src/sock_fd.c	Tue Apr 07 18:09:16 2009 +0300
+++ b/src/sock_fd.c	Tue Apr 07 19:41:48 2009 +0300
@@ -14,7 +14,7 @@
     sock_stream_invoke_callbacks(SOCK_FD_BASE(sock), what);
 }
 
-err_t sock_fd_read (struct sock_stream *base_sock, void *buf, size_t *len)
+err_t sock_fd_read (struct sock_stream *base_sock, void *buf, size_t *len, struct error_info *err)
 {
     struct sock_fd *sock = SOCK_FROM_BASE(base_sock, struct sock_fd);
     int ret;
@@ -22,11 +22,11 @@
     // read(), and detect non-EAGAIN or EOF
     if ((ret = read(sock->fd, buf, *len)) < 0 && errno != EAGAIN)
         // unexpected error
-        RETURN_SET_ERROR_ERRNO(SOCK_FD_ERR(sock), ERR_READ);
+        RETURN_SET_ERROR_ERRNO(err, ERR_READ);
     
     else if (ret == 0)
         // EOF
-        return SET_ERROR(SOCK_FD_ERR(sock), ERR_READ_EOF);
+        return SET_ERROR(err, ERR_READ_EOF);
 
 
     if (ret < 0) {
@@ -42,7 +42,7 @@
     return SUCCESS;
 }
 
-err_t sock_fd_write (struct sock_stream *base_sock, const void *buf, size_t *len)
+err_t sock_fd_write (struct sock_stream *base_sock, const void *buf, size_t *len, struct error_info *err)
 {
     struct sock_fd *sock = SOCK_FROM_BASE(base_sock, struct sock_fd);
     int ret;
@@ -50,11 +50,11 @@
     // write(), and detect non-EAGAIN or EOF
     if ((ret = write(sock->fd, buf, *len)) < 0 && errno != EAGAIN)
         // unexpected error
-        RETURN_SET_ERROR_ERRNO(SOCK_FD_ERR(sock), ERR_WRITE);
+        RETURN_SET_ERROR_ERRNO(err, ERR_WRITE);
     
     else if (ret == 0)
         // EOF
-        return SET_ERROR(SOCK_FD_ERR(sock), ERR_WRITE_EOF);
+        return SET_ERROR(err, ERR_WRITE_EOF);
 
 
     if (ret < 0) {
@@ -120,6 +120,10 @@
 
     // this is initialization
     assert(sock->ev_read == NULL && sock->ev_write == NULL);
+
+    // store
+    sock->ev_cb = ev_cb;
+    sock->ev_arg = cb_arg;
     
     // create new event
     if ((sock->ev_read = event_new(_sock_stream_ctx.ev_base, sock->fd, EV_READ, ev_cb, cb_arg)) == NULL)
@@ -145,7 +149,7 @@
     return SUCCESS;
 }
 
-void sock_fd_deinit_ev (struct sock_fd *sock)
+static void sock_fd_free_ev (struct sock_fd *sock)
 {
     if (sock->ev_read) {
         event_free(sock->ev_read);
@@ -160,6 +164,34 @@
     }
 }
 
+void sock_fd_deinit_ev (struct sock_fd *sock)
+{
+    sock_fd_free_ev(sock);
+    sock->ev_cb = NULL;
+    sock->ev_arg = NULL;
+}
+
+err_t sock_fd_set (struct sock_fd *sock, int fd)
+{
+    // close the old one?
+    if (sock->fd >= 0)
+        // XXX: warn on errors
+        close(sock->fd);
+    
+    // remove any old events
+    sock_fd_free_ev(sock);
+
+    // set the new one
+    sock->fd = fd;
+    
+    // restore them
+    if (sock->ev_cb)
+        return sock_fd_init_ev(sock, sock->ev_cb, sock->ev_arg);
+
+    // ok
+    return SUCCESS;
+}
+
 err_t sock_fd_close (struct sock_fd *sock)
 {
     struct error_info *err = SOCK_FD_ERR(sock);
--- a/src/sock_fd.h	Tue Apr 07 18:09:16 2009 +0300
+++ b/src/sock_fd.h	Tue Apr 07 19:41:48 2009 +0300
@@ -19,6 +19,10 @@
     /** The OS file descriptor */
     int fd;
 
+    /** The callback and arg used for sock_fd_init_ev - required for sock_fd_set */
+    void (*ev_cb) (evutil_socket_t, short, void *);
+    void *ev_arg;
+
     /** The IO events */
     struct event *ev_read, *ev_write;
 
@@ -44,12 +48,12 @@
 /**
  * sock_stream_methods::read implementation.
  */
-err_t sock_fd_read (struct sock_stream *base_sock, void *buf, size_t *len);
+err_t sock_fd_read (struct sock_stream *base_sock, void *buf, size_t *len, struct error_info *err);
 
 /**
  * sock_stream_methods::write implementation.
  */
-err_t sock_fd_write (struct sock_stream *base_sock, const void *buf, size_t *len);
+err_t sock_fd_write (struct sock_stream *base_sock, const void *buf, size_t *len, struct error_info *err);
 
 /**
  * sock_stream_methods::event_init implementation.
@@ -92,6 +96,12 @@
 void sock_fd_deinit_ev (struct sock_fd *sock);
 
 /**
+ * Update a sock_fd's fd, also updating any events set with sock_fd_init_ev. If any events were enabled before, they
+ * are not enabled anymore.
+ */
+err_t sock_fd_set (struct sock_fd *sock, int fd);
+
+/**
  * Close an opened sock_fd, restoring it to a state suitable for sock_fd_init
  */
 err_t sock_fd_close (struct sock_fd *sock);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/sock_fifo.c	Tue Apr 07 19:41:48 2009 +0300
@@ -0,0 +1,153 @@
+/**
+ * @file
+ *
+ * A read-only sock_stream implementation for linux fifo(7).
+ */
+#include "sock_fd.h"
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+
+struct fifo {
+    /** The base fd operations */
+    struct sock_fd base_fd;
+    
+    /** The path to the fifo */
+    char *path;
+};
+
+/**
+ * Get a sock_fd pointer from a sock_fifo pointer
+ */
+#define FIFO_FD(sock_ptr) (&(sock_ptr)->base_fd)
+
+/**
+ * Get a sock_base pointer from a sock_fifo pointer
+ */
+#define FIFO_BASE(sock_ptr) SOCK_FD_BASE(FIFO_FD(sock_ptr))
+
+/**
+ * Get the sock_stream.err pointer from a sock_fifo pointer
+ */
+#define FIFO_ERR(sock_ptr) SOCK_ERR(FIFO_BASE(sock_ptr))
+
+
+
+/**
+ * (re)open the fifo, closing it if already open, and keeping any event callbacks registered.
+ */
+static err_t fifo_open (struct fifo *fifo, struct error_info *err)
+{
+    int fd;
+
+    // open(2) the path in non-blocking mode
+    // XXX: hardoded read-only
+    if ((fd = open(fifo->path, O_RDONLY | O_NONBLOCK)) < 0)
+        RETURN_SET_ERROR_ERRNO(err, ERR_OPEN);
+
+    // set the new fd
+    if ((ERROR_CODE(err) = sock_fd_set(FIFO_FD(fifo), fd)))
+        return ERROR_CODE(err);
+    
+    // ok
+    return SUCCESS;
+}
+
+/**
+ * Destroy the fifo, releasing all resources
+ */
+static void fifo_destroy (struct fifo *fifo)
+{
+    // close if open
+    if (FIFO_FD(fifo)->fd >= 0)
+        sock_fd_close(FIFO_FD(fifo));
+
+    // release the path
+    free(fifo->path);
+    free(fifo);
+}
+
+/**
+ * sock_stream_methods::read implementation.
+ *
+ * Try and do a normal sock_fd_read call, but re-open with EAGAIN on EOF
+ */
+err_t fifo_read (struct sock_stream *base_sock, void *buf, size_t *len, struct error_info *err)
+{
+    struct fifo *fifo = SOCK_FROM_BASE(base_sock, struct fifo);
+
+    // passthru ERR_READ_EOF unless it's READ_EOF
+    if (sock_fd_read(base_sock, buf, len, err) != ERR_READ_EOF)
+        return ERROR_CODE(err);
+    
+    // re-open it
+    // XXX: re-add events?
+    if (fifo_open(fifo, err))
+        goto error;
+
+    // ok, act as if it was EAGAIN
+    *len = 0;
+
+    return SUCCESS;
+
+error:
+    return ERROR_CODE(err);
+}
+
+/**
+ * sock_stream_methods::release implementation
+ */
+static void fifo_release (struct sock_stream *base_sock)
+{
+    struct fifo *fifo = SOCK_FROM_BASE(base_sock, struct fifo);
+    
+    fifo_destroy(fifo);
+}
+
+/*
+ * Our sock_stream_type
+ */
+static struct sock_stream_type fifo_stream_type = {
+    .methods                = {
+        .read               = &fifo_read,
+        .write              = NULL,
+        .event_init         = &sock_fd_event_init,
+        .event_enable       = &sock_fd_event_enable,
+        .release            = &fifo_release,
+    },
+};
+
+err_t fifo_open_read (struct sock_stream **stream_ptr, const char *path, struct error_info *err)
+{
+    struct fifo *fifo;
+
+    // alloc
+    if ((fifo = calloc(1, sizeof(*fifo))) == NULL)
+        return SET_ERROR(err, ERR_CALLOC);
+
+    // copy the path
+    if ((fifo->path = strdup(path)) == NULL)
+        return SET_ERROR(err, ERR_STRDUP);
+
+    // init
+    sock_stream_init(FIFO_BASE(fifo), &fifo_stream_type);
+    sock_fd_init(FIFO_FD(fifo), -1);
+
+    // open the fifo
+    if (fifo_open(fifo, err))
+        goto error;
+
+    // ok
+    *stream_ptr = FIFO_BASE(fifo);
+
+    return SUCCESS;
+
+error:
+    // cleanup
+    fifo_destroy(fifo);
+
+    return ERROR_CODE(err);
+}
--- a/src/sock_gnutls.c	Tue Apr 07 18:09:16 2009 +0300
+++ b/src/sock_gnutls.c	Tue Apr 07 19:41:48 2009 +0300
@@ -4,10 +4,9 @@
 #include <stdlib.h>
 #include <err.h>
 
-static err_t sock_gnutls_read (struct sock_stream *base_sock, void *buf, size_t *len)
+static err_t sock_gnutls_read (struct sock_stream *base_sock, void *buf, size_t *len, struct error_info *err)
 {
     struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls);
-    struct error_info *err = SOCK_GNUTLS_ERR(sock);
     int ret;
     
     // read gnutls record
@@ -34,10 +33,9 @@
     return SUCCESS;
 }
 
-static err_t sock_gnutls_write (struct sock_stream *base_sock, const void *buf, size_t *len)
+static err_t sock_gnutls_write (struct sock_stream *base_sock, const void *buf, size_t *len, struct error_info *err)
 {
     struct sock_gnutls *sock = SOCK_FROM_BASE(base_sock, struct sock_gnutls);
-    struct error_info *err = SOCK_GNUTLS_ERR(sock);
     int ret;
  
     // read gnutls record
--- a/src/sock_internal.h	Tue Apr 07 18:09:16 2009 +0300
+++ b/src/sock_internal.h	Tue Apr 07 19:41:48 2009 +0300
@@ -27,10 +27,10 @@
  */
 struct sock_stream_methods {
     /** As read(2) */
-    err_t (*read) (struct sock_stream *sock, void *buf, size_t *len);
+    err_t (*read) (struct sock_stream *sock, void *buf, size_t *len, struct error_info *err);
 
     /** As write(2) */
-    err_t (*write) (struct sock_stream *sock, const void *buf, size_t *len);
+    err_t (*write) (struct sock_stream *sock, const void *buf, size_t *len, struct error_info *err);
 
     /** Initialize events. cb_info/cb_arg are already updated */
     err_t (*event_init) (struct sock_stream *sock);
--- a/src/sock_test.c	Tue Apr 07 18:09:16 2009 +0300
+++ b/src/sock_test.c	Tue Apr 07 19:41:48 2009 +0300
@@ -43,7 +43,7 @@
     return SUCCESS;
 }
 
-static err_t sock_test_read (struct sock_stream *base_sock, void *buf_ptr, size_t *len)
+static err_t sock_test_read (struct sock_stream *base_sock, void *buf_ptr, size_t *len, struct error_info *err)
 {
     struct sock_test *sock = SOCK_FROM_BASE(base_sock, struct sock_test);
     struct io_buf *buf = &sock->recv_buf;
@@ -58,7 +58,7 @@
 
         } else {
             // EOF!
-            return SET_ERROR(SOCK_TEST_ERR(sock), ERR_READ_EOF);
+            return SET_ERROR(err, ERR_READ_EOF);
         }
     }
     
@@ -92,13 +92,14 @@
     return SUCCESS;
 }
 
-static err_t sock_test_write (struct sock_stream *base_sock, const void *buf_ptr, size_t *len)
+static err_t sock_test_write (struct sock_stream *base_sock, const void *buf_ptr, size_t *len, struct error_info *err)
 {
     struct sock_test *sock = SOCK_FROM_BASE(base_sock, struct sock_test);
     struct io_buf *buf = &sock->send_buf;
-    
+
     // ensure there's room
-    assert(sock_test_grow_buf(buf) == SUCCESS);
+    if ((ERROR_CODE(err) = sock_test_grow_buf(buf)))
+        goto error;
     
     // the next buffer
     struct io_vec *vec = buf->write_vec;
@@ -113,6 +114,9 @@
 
     // ok
     return SUCCESS;
+
+error:
+    return ERROR_CODE(err);
 }
 
 static err_t sock_test_event_init (struct sock_stream *base_sock)
--- a/src/test.c	Tue Apr 07 18:09:16 2009 +0300
+++ b/src/test.c	Tue Apr 07 19:41:48 2009 +0300
@@ -1079,36 +1079,154 @@
     irc_net_destroy(net);
 }
 
+// XXX: needs to be split off into its own test_fifo.c
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+struct test_fifo_ctx {
+    /** Path to the fifo */
+    const char *path;
+
+    /** The write end */
+    int fd;
+
+    /** callback invoked? */
+    bool on_read;
+
+    /** Still running? */
+    bool run;
+};
+
+/**
+ * Open the FIFO and write the test string to it
+ */
+static void test_fifo_open_write (struct test_fifo_ctx *ctx)
+{
+    // ...raw, for writing
+    if ((ctx->fd = open(ctx->path, O_WRONLY)) < 0)
+        FATAL_PERROR("open");
+
+    // write something into it
+    if (write(ctx->fd, "test", 4) != 4)
+        FATAL_PERROR("write");
+ 
+}
+
+static void test_fifo_close (struct test_fifo_ctx *ctx)
+{
+    close(ctx->fd);
+    ctx->fd = -1;
+}
+
+static void test_fifo_on_read (struct sock_stream *fifo, void *arg)
+{
+    int ret;
+    char buf[16];
+    struct test_fifo_ctx *ctx = arg;
+
+    // read it back out
+    log_info("test fifo_read");
+    if ((ret = sock_stream_read(fifo, buf, 16)) < 0)
+        assert_success(-ret);
+
+    assert(ret == 4);
+    assert_strncmp(buf, "test", 4);
+ 
+    if (ctx->on_read) {
+        test_fifo_close(ctx);
+        ctx->run = false;
+        return;
+    }
+   
+    // re-open the fifo
+    log_info("test fifo-re-open");
+    test_fifo_close(ctx);
+    test_fifo_open_write(ctx);
+
+    assert_success(sock_stream_event_enable(fifo, EV_READ));
+        
+    ctx->on_read = true;
+}
+
+static struct sock_stream_callbacks test_fifo_callbacks = {
+    .on_read = test_fifo_on_read,
+};
+
+void test_fifo (void)
+{
+    struct sock_stream *fifo;
+    struct error_info err;
+    struct test_fifo_ctx _ctx, *ctx = &_ctx; memset(ctx, 0, sizeof(*ctx));
+
+    // XXX: requires that this be run in a suitable CWD
+    ctx->path = "test.fifo";
+
+    // create the fifo
+    if ((mkfifo(ctx->path, 0600) < 0) && (errno != EEXIST))
+        FATAL_PERROR("mkfifo");
+
+    // open it
+    log_info("test fifo_open_read");
+    assert_success(fifo_open_read(&fifo, ctx->path, &err));
+    assert_success(sock_stream_event_init(fifo, &test_fifo_callbacks, ctx));
+    assert_success(sock_stream_event_enable(fifo, EV_READ));
+    
+    // put some data into it
+    test_fifo_open_write(ctx);
+   
+    // run the event loop
+    log_debug("running the event loop...");
+    ctx->run = true;
+
+    while (ctx->run)
+        assert(event_base_loop(_test_ctx.ev_base, EVLOOP_ONCE) == 0);
+
+    // check
+    assert(ctx->fd < 0);
+    
+    // cleanup
+    sock_stream_release(fifo);
+}
+
 /**
  * Test definition
  */
-static struct test {
+struct test {
     /** Test name */
     const char *name;
 
     /** Test func */
     void (*func) (void);
+    
+    bool optional;
+};
 
-} _tests[] = {
-    {   "dump_str",             &test_dump_str              },
-    {   "sock_test",            &test_sock_test             },
-    {   "line_proto",           &test_line_proto            },
-    {   "irc_queue",            &test_irc_queue             },
+#define DEF_TEST(name) { #name, &test_ ## name, false }
+#define DEF_TEST_OPTIONAL(name) { #name, &test_ ## name, true }
+#define DEF_TEST_END { NULL, NULL, false }
+
+static struct test _tests[] = {
+    DEF_TEST(            dump_str                   ),
+    DEF_TEST(            sock_test                  ),
+    DEF_TEST(            line_proto                 ),
+    DEF_TEST(            irc_queue                  ),
     // XXX: irc_line_parse_invalid_prefix
-    {   "irc_conn",             &test_irc_conn              },
-    {   "irc_conn_self_nick",   &test_irc_conn_self_nick    },
-    {   "irc_net",              &test_irc_net               },
-    {   "irc_chan_add_offline", &test_irc_chan_add_offline  },
-    {   "irc_chan_namreply",    &test_irc_chan_namreply     },
-    {   "irc_chan_user_join",   &test_irc_chan_user_join    },
-    {   "irc_chan_user_part",   &test_irc_chan_user_part    },
-    {   "irc_chan_user_kick",   &test_irc_chan_user_kick    },
-    {   "irc_chan_self_kick",   &test_irc_chan_self_kick    },
-    {   "irc_chan_user_nick",   &test_irc_chan_user_nick    },
-    {   "irc_chan_user_quit",   &test_irc_chan_user_quit    },
-    {   "irc_chan_CTCP_ACTION", &test_irc_chan_CTCP_ACTION  },
-    {   "irc_chan_privmsg",     &test_irc_chan_privmsg      },
-    {   NULL,                   NULL                        }
+    DEF_TEST(            irc_conn                   ),
+    DEF_TEST(            irc_conn_self_nick         ),
+    DEF_TEST(            irc_net                    ),
+    DEF_TEST(            irc_chan_add_offline       ),
+    DEF_TEST(            irc_chan_namreply          ),
+    DEF_TEST(            irc_chan_user_join         ),
+    DEF_TEST(            irc_chan_user_part         ),
+    DEF_TEST(            irc_chan_user_kick         ),
+    DEF_TEST(            irc_chan_self_kick         ),
+    DEF_TEST(            irc_chan_user_nick         ),
+    DEF_TEST(            irc_chan_user_quit         ),
+    DEF_TEST(            irc_chan_CTCP_ACTION       ),
+    DEF_TEST(            irc_chan_privmsg           ),
+    DEF_TEST_OPTIONAL(   fifo                       ),
+    DEF_TEST_END
 };
 
 /**
@@ -1208,7 +1326,7 @@
 
     // run tests
     for (test = _tests; test->name; test++) {
-        if (filter && strcmp(test->name, filter))
+        if ((filter && strcmp(test->name, filter)) || (!filter && test->optional))
             continue;
 
         log_info("Running test: %s", test->name);