list: improve test coverage to 100%
authorRusty Russell <rusty@rustcorp.com.au>
Sat, 1 Jan 2011 06:42:40 +0000 (17:12 +1030)
committerRusty Russell <rusty@rustcorp.com.au>
Sat, 1 Jan 2011 06:42:40 +0000 (17:12 +1030)
In particular, we test the corruption cases.

ccan/list/list.c
ccan/list/test/run-check-corrupt.c [new file with mode: 0644]
ccan/list/test/run-list_del_from-assert.c [new file with mode: 0644]

index fad40cb80b3e00938ead3ecbe96c415c55a3973d..d876f21e7b06b0bd06a38fdd89b5b35ff6d62d31 100644 (file)
@@ -2,6 +2,20 @@
 #include <stdlib.h>
 #include "list.h"
 
+static void *corrupt(const char *abortstr,
+                    const struct list_node *head,
+                    const struct list_node *node,
+                    unsigned int count)
+{
+       if (abortstr) {
+               fprintf(stderr,
+                       "%s: prev corrupt in node %p (%u) of %p\n",
+                       abortstr, node, count, head);
+               abort();
+       }
+       return NULL;
+}
+
 struct list_node *list_check_node(const struct list_node *node,
                                  const char *abortstr)
 {
@@ -10,31 +24,18 @@ struct list_node *list_check_node(const struct list_node *node,
 
        for (p = node, n = node->next; n != node; p = n, n = n->next) {
                count++;
-               if (n->prev != p) {
-                       if (!abortstr)
-                               return NULL;
-                       fprintf(stderr,
-                               "%s: prev corrupt in node %p (%u) of %p\n",
-                               abortstr, n, count, node);
-                       abort();
-               }
+               if (n->prev != p)
+                       return corrupt(abortstr, node, n, count);
        }
+       /* Check prev on head node. */
+       if (node->prev != p)
+               return corrupt(abortstr, node, node, 0);
+
        return (struct list_node *)node;
 }
 
 struct list_head *list_check(const struct list_head *h, const char *abortstr)
 {
-       if (h->n.next == &h->n) {
-               if (h->n.prev != &h->n) {
-                       if (!abortstr)
-                               return NULL;
-                       fprintf(stderr, "%s: prev corrupt in empty %p\n",
-                               abortstr, h);
-                       abort();
-               }
-               return (struct list_head *)h;
-       }
-
        if (!list_check_node(&h->n, abortstr))
                return NULL;
        return (struct list_head *)h;
diff --git a/ccan/list/test/run-check-corrupt.c b/ccan/list/test/run-check-corrupt.c
new file mode 100644 (file)
index 0000000..5dd9f9c
--- /dev/null
@@ -0,0 +1,89 @@
+#include <setjmp.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <err.h>
+
+/* We don't actually want it to exit... */
+static jmp_buf aborted;
+#define abort() longjmp(aborted, 1)
+
+#define fprintf my_fprintf
+static char printf_buffer[1000];
+
+static int my_fprintf(FILE *stream, const char *format, ...)
+{
+       va_list ap;
+       int ret;
+       va_start(ap, format);
+       ret = vsprintf(printf_buffer, format, ap);
+       va_end(ap);
+       return ret;
+}
+
+#include <ccan/list/list.h>
+#include <ccan/tap/tap.h>
+#include <ccan/list/list.c>
+
+int main(int argc, char *argv[])
+{
+       struct list_head list;
+       struct list_node n1;
+       char expect[100];
+
+       plan_tests(9);
+       /* Empty list. */
+       list.n.next = &list.n;
+       list.n.prev = &list.n;
+       ok1(list_check(&list, NULL) == &list);
+
+       /* Bad back ptr */
+       list.n.prev = &n1;
+       /* Non-aborting version. */
+       ok1(list_check(&list, NULL) == NULL);
+
+       /* Aborting version. */
+       sprintf(expect, "test message: prev corrupt in node %p (0) of %p\n",
+               &list, &list);
+       if (setjmp(aborted) == 0) {
+               list_check(&list, "test message");
+               fail("list_check on empty with bad back ptr didn't fail!");
+       } else {
+               ok1(strcmp(printf_buffer, expect) == 0);
+       }
+
+       /* n1 in list. */
+       list.n.next = &n1;
+       list.n.prev = &n1;
+       n1.prev = &list.n;
+       n1.next = &list.n;
+       ok1(list_check(&list, NULL) == &list);
+       ok1(list_check_node(&n1, NULL) == &n1);
+
+       /* Bad back ptr */
+       n1.prev = &n1;
+       ok1(list_check(&list, NULL) == NULL);
+       ok1(list_check_node(&n1, NULL) == NULL);
+
+       /* Aborting version. */
+       sprintf(expect, "test message: prev corrupt in node %p (1) of %p\n",
+               &n1, &list);
+       if (setjmp(aborted) == 0) {
+               list_check(&list, "test message");
+               fail("list_check on n1 bad back ptr didn't fail!");
+       } else {
+               ok1(strcmp(printf_buffer, expect) == 0);
+       }
+
+       sprintf(expect, "test message: prev corrupt in node %p (0) of %p\n",
+               &n1, &n1);
+       if (setjmp(aborted) == 0) {
+               list_check_node(&n1, "test message");
+               fail("list_check_node on n1 bad back ptr didn't fail!");
+       } else {
+               ok1(strcmp(printf_buffer, expect) == 0);
+       }
+
+       return exit_status();
+}
diff --git a/ccan/list/test/run-list_del_from-assert.c b/ccan/list/test/run-list_del_from-assert.c
new file mode 100644 (file)
index 0000000..05d6cad
--- /dev/null
@@ -0,0 +1,36 @@
+#define CCAN_LIST_DEBUG 1
+#include <ccan/list/list.h>
+#include <ccan/tap/tap.h>
+#include <ccan/list/list.c>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <signal.h>
+
+int main(int argc, char *argv[])
+{
+       struct list_head list1, list2;
+       struct list_node n1, n2, n3;
+       pid_t child;
+       int status;
+
+       plan_tests(1);
+       list_head_init(&list1);
+       list_head_init(&list2);
+       list_add(&list1, &n1);
+       list_add(&list2, &n2);
+       list_add_tail(&list2, &n3);
+
+       child = fork();
+       if (child) {
+               wait(&status);
+       } else {
+               /* This should abort. */
+               list_del_from(&list1, &n3);
+               exit(0);
+       }
+
+       ok1(WIFSIGNALED(status) && WTERMSIG(status) == SIGABRT);
+       list_del_from(&list2, &n3);
+       return exit_status();
+}