From 3d4c75ff37b1c5a7bfd8847a788aa1d09b07d466 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 1 Jan 2011 17:12:40 +1030 Subject: [PATCH] list: improve test coverage to 100% In particular, we test the corruption cases. --- ccan/list/list.c | 39 +++++----- ccan/list/test/run-check-corrupt.c | 89 +++++++++++++++++++++++ ccan/list/test/run-list_del_from-assert.c | 36 +++++++++ 3 files changed, 145 insertions(+), 19 deletions(-) create mode 100644 ccan/list/test/run-check-corrupt.c create mode 100644 ccan/list/test/run-list_del_from-assert.c diff --git a/ccan/list/list.c b/ccan/list/list.c index fad40cb8..d876f21e 100644 --- a/ccan/list/list.c +++ b/ccan/list/list.c @@ -2,6 +2,20 @@ #include #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 index 00000000..5dd9f9cc --- /dev/null +++ b/ccan/list/test/run-check-corrupt.c @@ -0,0 +1,89 @@ +#include +#include +#include +#include +#include +#include + +/* 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 +#include +#include + +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 index 00000000..05d6cad6 --- /dev/null +++ b/ccan/list/test/run-list_del_from-assert.c @@ -0,0 +1,36 @@ +#define CCAN_LIST_DEBUG 1 +#include +#include +#include +#include +#include +#include +#include + +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(); +} -- 2.39.2