summaryrefslogtreecommitdiff
path: root/lib/ccan/list/test
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ccan/list/test')
-rw-r--r--lib/ccan/list/test/compile_ok-constant.c49
-rw-r--r--lib/ccan/list/test/helper.c54
-rw-r--r--lib/ccan/list/test/helper.h7
-rw-r--r--lib/ccan/list/test/run-check-corrupt.c89
-rw-r--r--lib/ccan/list/test/run-list_del_from-assert.c36
-rw-r--r--lib/ccan/list/test/run-single-eval.c168
-rw-r--r--lib/ccan/list/test/run-with-debug.c3
-rw-r--r--lib/ccan/list/test/run.c200
8 files changed, 606 insertions, 0 deletions
diff --git a/lib/ccan/list/test/compile_ok-constant.c b/lib/ccan/list/test/compile_ok-constant.c
new file mode 100644
index 0000000000..c57cdadc31
--- /dev/null
+++ b/lib/ccan/list/test/compile_ok-constant.c
@@ -0,0 +1,49 @@
+#include <ccan/list/list.h>
+#include <ccan/tap/tap.h>
+#include <ccan/list/list.c>
+#include <stdbool.h>
+#include <stdio.h>
+
+struct child {
+ const char *name;
+ struct list_node list;
+};
+
+static bool children(const struct list_head *list)
+{
+ return !list_empty(list);
+}
+
+static const struct child *first_child(const struct list_head *list)
+{
+ return list_top(list, struct child, list);
+}
+
+static const struct child *last_child(const struct list_head *list)
+{
+ return list_tail(list, struct child, list);
+}
+
+static void check_children(const struct list_head *list)
+{
+ list_check(list, "bad child list");
+}
+
+static void print_children(const struct list_head *list)
+{
+ const struct child *c;
+ list_for_each(list, c, list)
+ printf("%s\n", c->name);
+}
+
+int main(void)
+{
+ LIST_HEAD(h);
+
+ children(&h);
+ first_child(&h);
+ last_child(&h);
+ check_children(&h);
+ print_children(&h);
+ return 0;
+}
diff --git a/lib/ccan/list/test/helper.c b/lib/ccan/list/test/helper.c
new file mode 100644
index 0000000000..8903ac1738
--- /dev/null
+++ b/lib/ccan/list/test/helper.c
@@ -0,0 +1,54 @@
+#include <stdlib.h>
+#include <stdbool.h>
+#include <time.h>
+
+#include <ccan/list/list.h>
+#include "helper.h"
+
+#define ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING \
+ (42)
+
+struct opaque {
+ struct list_node list;
+ size_t secret_offset;
+ char secret_drawer[42];
+};
+
+static bool not_randomized = true;
+
+struct opaque *create_opaque_blob(void)
+{
+ struct opaque *blob = calloc(1, sizeof(struct opaque));
+
+ if (not_randomized) {
+ srandom((int)time(NULL));
+ not_randomized = false;
+ }
+
+ blob->secret_offset = random() % (sizeof(blob->secret_drawer));
+ blob->secret_drawer[blob->secret_offset] =
+ ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING;
+
+ return blob;
+}
+
+bool if_blobs_know_the_secret(struct opaque *blob)
+{
+ bool answer = true;
+ int i;
+ for (i = 0; i < sizeof(blob->secret_drawer) /
+ sizeof(blob->secret_drawer[0]); i++)
+ if (i != blob->secret_offset)
+ answer = answer && (blob->secret_drawer[i] == 0);
+ else
+ answer = answer &&
+ (blob->secret_drawer[blob->secret_offset] ==
+ ANSWER_TO_THE_ULTIMATE_QUESTION_OF_LIFE_THE_UNIVERSE_AND_EVERYTHING);
+
+ return answer;
+}
+
+void destroy_opaque_blob(struct opaque *blob)
+{
+ free(blob);
+}
diff --git a/lib/ccan/list/test/helper.h b/lib/ccan/list/test/helper.h
new file mode 100644
index 0000000000..a09a3a997b
--- /dev/null
+++ b/lib/ccan/list/test/helper.h
@@ -0,0 +1,7 @@
+/* These are in a separate C file so we can test undefined structures. */
+struct opaque;
+typedef struct opaque opaque_t;
+
+opaque_t *create_opaque_blob(void);
+bool if_blobs_know_the_secret(opaque_t *blob);
+void destroy_opaque_blob(opaque_t *blob);
diff --git a/lib/ccan/list/test/run-check-corrupt.c b/lib/ccan/list/test/run-check-corrupt.c
new file mode 100644
index 0000000000..5dd9f9cc83
--- /dev/null
+++ b/lib/ccan/list/test/run-check-corrupt.c
@@ -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/lib/ccan/list/test/run-list_del_from-assert.c b/lib/ccan/list/test/run-list_del_from-assert.c
new file mode 100644
index 0000000000..05d6cad62c
--- /dev/null
+++ b/lib/ccan/list/test/run-list_del_from-assert.c
@@ -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();
+}
diff --git a/lib/ccan/list/test/run-single-eval.c b/lib/ccan/list/test/run-single-eval.c
new file mode 100644
index 0000000000..f90eed357a
--- /dev/null
+++ b/lib/ccan/list/test/run-single-eval.c
@@ -0,0 +1,168 @@
+/* Make sure macros only evaluate their args once. */
+#include <ccan/list/list.h>
+#include <ccan/tap/tap.h>
+#include <ccan/list/list.c>
+
+struct parent {
+ const char *name;
+ struct list_head children;
+ unsigned int num_children;
+ int eval_count;
+};
+
+struct child {
+ const char *name;
+ struct list_node list;
+};
+
+static LIST_HEAD(static_list);
+
+#define ref(obj, counter) ((counter)++, (obj))
+
+int main(int argc, char *argv[])
+{
+ struct parent parent;
+ struct child c1, c2, c3, *c, *n;
+ unsigned int i;
+ unsigned int static_count = 0, parent_count = 0, list_count = 0,
+ node_count = 0;
+ struct list_head list = LIST_HEAD_INIT(list);
+
+ plan_tests(74);
+ /* Test LIST_HEAD, LIST_HEAD_INIT, list_empty and check_list */
+ ok1(list_empty(ref(&static_list, static_count)));
+ ok1(static_count == 1);
+ ok1(list_check(ref(&static_list, static_count), NULL));
+ ok1(static_count == 2);
+ ok1(list_empty(ref(&list, list_count)));
+ ok1(list_count == 1);
+ ok1(list_check(ref(&list, list_count), NULL));
+ ok1(list_count == 2);
+
+ parent.num_children = 0;
+ list_head_init(ref(&parent.children, parent_count));
+ ok1(parent_count == 1);
+ /* Test list_head_init */
+ ok1(list_empty(ref(&parent.children, parent_count)));
+ ok1(parent_count == 2);
+ ok1(list_check(ref(&parent.children, parent_count), NULL));
+ ok1(parent_count == 3);
+
+ c2.name = "c2";
+ list_add(ref(&parent.children, parent_count), &c2.list);
+ ok1(parent_count == 4);
+ /* Test list_add and !list_empty. */
+ ok1(!list_empty(ref(&parent.children, parent_count)));
+ ok1(parent_count == 5);
+ ok1(c2.list.next == &parent.children.n);
+ ok1(c2.list.prev == &parent.children.n);
+ ok1(parent.children.n.next == &c2.list);
+ ok1(parent.children.n.prev == &c2.list);
+ /* Test list_check */
+ ok1(list_check(ref(&parent.children, parent_count), NULL));
+ ok1(parent_count == 6);
+
+ c1.name = "c1";
+ list_add(ref(&parent.children, parent_count), &c1.list);
+ ok1(parent_count == 7);
+ /* Test list_add and !list_empty. */
+ ok1(!list_empty(ref(&parent.children, parent_count)));
+ ok1(parent_count == 8);
+ ok1(c2.list.next == &parent.children.n);
+ ok1(c2.list.prev == &c1.list);
+ ok1(parent.children.n.next == &c1.list);
+ ok1(parent.children.n.prev == &c2.list);
+ ok1(c1.list.next == &c2.list);
+ ok1(c1.list.prev == &parent.children.n);
+ /* Test list_check */
+ ok1(list_check(ref(&parent.children, parent_count), NULL));
+ ok1(parent_count == 9);
+
+ c3.name = "c3";
+ list_add_tail(ref(&parent.children, parent_count), &c3.list);
+ ok1(parent_count == 10);
+ /* Test list_add_tail and !list_empty. */
+ ok1(!list_empty(ref(&parent.children, parent_count)));
+ ok1(parent_count == 11);
+ ok1(parent.children.n.next == &c1.list);
+ ok1(parent.children.n.prev == &c3.list);
+ ok1(c1.list.next == &c2.list);
+ ok1(c1.list.prev == &parent.children.n);
+ ok1(c2.list.next == &c3.list);
+ ok1(c2.list.prev == &c1.list);
+ ok1(c3.list.next == &parent.children.n);
+ ok1(c3.list.prev == &c2.list);
+ /* Test list_check */
+ ok1(list_check(ref(&parent.children, parent_count), NULL));
+ ok1(parent_count == 12);
+
+ /* Test list_check_node */
+ ok1(list_check_node(&c1.list, NULL));
+ ok1(list_check_node(&c2.list, NULL));
+ ok1(list_check_node(&c3.list, NULL));
+
+ /* Test list_top */
+ ok1(list_top(ref(&parent.children, parent_count), struct child, list) == &c1);
+ ok1(parent_count == 13);
+
+ /* Test list_tail */
+ ok1(list_tail(ref(&parent.children, parent_count), struct child, list) == &c3);
+ ok1(parent_count == 14);
+
+ /* Test list_for_each. */
+ i = 0;
+ list_for_each(&parent.children, c, list) {
+ switch (i++) {
+ case 0:
+ ok1(c == &c1);
+ break;
+ case 1:
+ ok1(c == &c2);
+ break;
+ case 2:
+ ok1(c == &c3);
+ break;
+ }
+ if (i > 2)
+ break;
+ }
+ ok1(i == 3);
+
+ /* Test list_for_each_safe, list_del and list_del_from. */
+ i = 0;
+ list_for_each_safe(&parent.children, c, n, list) {
+ switch (i++) {
+ case 0:
+ ok1(c == &c1);
+ list_del(ref(&c->list, node_count));
+ ok1(node_count == 1);
+ break;
+ case 1:
+ ok1(c == &c2);
+ list_del_from(ref(&parent.children, parent_count),
+ ref(&c->list, node_count));
+ ok1(node_count == 2);
+ break;
+ case 2:
+ ok1(c == &c3);
+ list_del_from(ref(&parent.children, parent_count),
+ ref(&c->list, node_count));
+ ok1(node_count == 3);
+ break;
+ }
+ ok1(list_check(ref(&parent.children, parent_count), NULL));
+ if (i > 2)
+ break;
+ }
+ ok1(i == 3);
+ ok1(parent_count == 19);
+ ok1(list_empty(ref(&parent.children, parent_count)));
+ ok1(parent_count == 20);
+
+ /* Test list_top/list_tail on empty list. */
+ ok1(list_top(ref(&parent.children, parent_count), struct child, list) == NULL);
+ ok1(parent_count == 21);
+ ok1(list_tail(ref(&parent.children, parent_count), struct child, list) == NULL);
+ ok1(parent_count == 22);
+ return exit_status();
+}
diff --git a/lib/ccan/list/test/run-with-debug.c b/lib/ccan/list/test/run-with-debug.c
new file mode 100644
index 0000000000..d0902421f1
--- /dev/null
+++ b/lib/ccan/list/test/run-with-debug.c
@@ -0,0 +1,3 @@
+/* Just like run.c, but with all debug checks enabled. */
+#define CCAN_LIST_DEBUG 1
+#include <ccan/list/test/run.c>
diff --git a/lib/ccan/list/test/run.c b/lib/ccan/list/test/run.c
new file mode 100644
index 0000000000..952a0e15e6
--- /dev/null
+++ b/lib/ccan/list/test/run.c
@@ -0,0 +1,200 @@
+#include <ccan/list/list.h>
+#include <ccan/tap/tap.h>
+#include <ccan/list/list.c>
+#include "helper.h"
+
+struct parent {
+ const char *name;
+ struct list_head children;
+ unsigned int num_children;
+};
+
+struct child {
+ const char *name;
+ struct list_node list;
+};
+
+static LIST_HEAD(static_list);
+
+int main(int argc, char *argv[])
+{
+ struct parent parent;
+ struct child c1, c2, c3, *c, *n;
+ unsigned int i;
+ struct list_head list = LIST_HEAD_INIT(list);
+ opaque_t *q, *nq;
+ struct list_head opaque_list = LIST_HEAD_INIT(opaque_list);
+
+ plan_tests(65);
+ /* Test LIST_HEAD, LIST_HEAD_INIT, list_empty and check_list */
+ ok1(list_empty(&static_list));
+ ok1(list_check(&static_list, NULL));
+ ok1(list_empty(&list));
+ ok1(list_check(&list, NULL));
+
+ parent.num_children = 0;
+ list_head_init(&parent.children);
+ /* Test list_head_init */
+ ok1(list_empty(&parent.children));
+ ok1(list_check(&parent.children, NULL));
+
+ c2.name = "c2";
+ list_add(&parent.children, &c2.list);
+ /* Test list_add and !list_empty. */
+ ok1(!list_empty(&parent.children));
+ ok1(c2.list.next == &parent.children.n);
+ ok1(c2.list.prev == &parent.children.n);
+ ok1(parent.children.n.next == &c2.list);
+ ok1(parent.children.n.prev == &c2.list);
+ /* Test list_check */
+ ok1(list_check(&parent.children, NULL));
+
+ c1.name = "c1";
+ list_add(&parent.children, &c1.list);
+ /* Test list_add and !list_empty. */
+ ok1(!list_empty(&parent.children));
+ ok1(c2.list.next == &parent.children.n);
+ ok1(c2.list.prev == &c1.list);
+ ok1(parent.children.n.next == &c1.list);
+ ok1(parent.children.n.prev == &c2.list);
+ ok1(c1.list.next == &c2.list);
+ ok1(c1.list.prev == &parent.children.n);
+ /* Test list_check */
+ ok1(list_check(&parent.children, NULL));
+
+ c3.name = "c3";
+ list_add_tail(&parent.children, &c3.list);
+ /* Test list_add_tail and !list_empty. */
+ ok1(!list_empty(&parent.children));
+ ok1(parent.children.n.next == &c1.list);
+ ok1(parent.children.n.prev == &c3.list);
+ ok1(c1.list.next == &c2.list);
+ ok1(c1.list.prev == &parent.children.n);
+ ok1(c2.list.next == &c3.list);
+ ok1(c2.list.prev == &c1.list);
+ ok1(c3.list.next == &parent.children.n);
+ ok1(c3.list.prev == &c2.list);
+ /* Test list_check */
+ ok1(list_check(&parent.children, NULL));
+
+ /* Test list_check_node */
+ ok1(list_check_node(&c1.list, NULL));
+ ok1(list_check_node(&c2.list, NULL));
+ ok1(list_check_node(&c3.list, NULL));
+
+ /* Test list_top */
+ ok1(list_top(&parent.children, struct child, list) == &c1);
+
+ /* Test list_tail */
+ ok1(list_tail(&parent.children, struct child, list) == &c3);
+
+ /* Test list_for_each. */
+ i = 0;
+ list_for_each(&parent.children, c, list) {
+ switch (i++) {
+ case 0:
+ ok1(c == &c1);
+ break;
+ case 1:
+ ok1(c == &c2);
+ break;
+ case 2:
+ ok1(c == &c3);
+ break;
+ }
+ if (i > 2)
+ break;
+ }
+ ok1(i == 3);
+
+ /* Test list_for_each_rev. */
+ i = 0;
+ list_for_each_rev(&parent.children, c, list) {
+ switch (i++) {
+ case 0:
+ ok1(c == &c3);
+ break;
+ case 1:
+ ok1(c == &c2);
+ break;
+ case 2:
+ ok1(c == &c1);
+ break;
+ }
+ if (i > 2)
+ break;
+ }
+ ok1(i == 3);
+
+ /* Test list_for_each_safe, list_del and list_del_from. */
+ i = 0;
+ list_for_each_safe(&parent.children, c, n, list) {
+ switch (i++) {
+ case 0:
+ ok1(c == &c1);
+ list_del(&c->list);
+ break;
+ case 1:
+ ok1(c == &c2);
+ list_del_from(&parent.children, &c->list);
+ break;
+ case 2:
+ ok1(c == &c3);
+ list_del_from(&parent.children, &c->list);
+ break;
+ }
+ ok1(list_check(&parent.children, NULL));
+ if (i > 2)
+ break;
+ }
+ ok1(i == 3);
+ ok1(list_empty(&parent.children));
+
+ /* Test list_for_each_off. */
+ list_add_tail(&opaque_list,
+ (struct list_node *)create_opaque_blob());
+ list_add_tail(&opaque_list,
+ (struct list_node *)create_opaque_blob());
+ list_add_tail(&opaque_list,
+ (struct list_node *)create_opaque_blob());
+
+ i = 0;
+
+ list_for_each_off(&opaque_list, q, 0) {
+ i++;
+ ok1(if_blobs_know_the_secret(q));
+ }
+ ok1(i == 3);
+
+ /* Test list_for_each_safe_off, list_del_off and list_del_from_off. */
+ i = 0;
+ list_for_each_safe_off(&opaque_list, q, nq, 0) {
+ switch (i++) {
+ case 0:
+ ok1(if_blobs_know_the_secret(q));
+ list_del_off(q, 0);
+ destroy_opaque_blob(q);
+ break;
+ case 1:
+ ok1(if_blobs_know_the_secret(q));
+ list_del_from_off(&opaque_list, q, 0);
+ destroy_opaque_blob(q);
+ break;
+ case 2:
+ ok1(c == &c3);
+ list_del_from_off(&opaque_list, q, 0);
+ destroy_opaque_blob(q);
+ break;
+ }
+ ok1(list_check(&opaque_list, NULL));
+ if (i > 2)
+ break;
+ }
+ ok1(i == 3);
+ ok1(list_empty(&opaque_list));
+
+ /* Test list_top/list_tail on empty list. */
+ ok1(list_top(&parent.children, struct child, list) == NULL);
+ ok1(list_tail(&parent.children, struct child, list) == NULL);
+ return exit_status();
+}