summaryrefslogtreecommitdiff
path: root/lib/ccan/failtest/test
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ccan/failtest/test')
-rw-r--r--lib/ccan/failtest/test/run-failpath.c39
-rw-r--r--lib/ccan/failtest/test/run-history.c183
-rw-r--r--lib/ccan/failtest/test/run-locking.c134
-rw-r--r--lib/ccan/failtest/test/run-malloc.c116
-rw-r--r--lib/ccan/failtest/test/run-open.c72
-rw-r--r--lib/ccan/failtest/test/run-write.c51
6 files changed, 595 insertions, 0 deletions
diff --git a/lib/ccan/failtest/test/run-failpath.c b/lib/ccan/failtest/test/run-failpath.c
new file mode 100644
index 0000000000..9795ee9d05
--- /dev/null
+++ b/lib/ccan/failtest/test/run-failpath.c
@@ -0,0 +1,39 @@
+#include <ccan/failtest/failtest.c>
+#include <stdlib.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <ccan/tap/tap.h>
+
+int main(void)
+{
+ int fds[2], fd;
+ void *p;
+
+ plan_tests(14);
+ failtest_init(0, NULL);
+
+ failpath = "mceopwrMCEOPWR";
+
+ ok1((p = failtest_malloc(10, "run-failpath.c", 1)) != NULL);
+ ok1(failtest_calloc(10, 5, "run-failpath.c", 1) != NULL);
+ ok1((p = failtest_realloc(p, 100, "run-failpath.c", 1)) != NULL);
+ ok1((fd = failtest_open("failpath-scratch", "run-failpath.c", 1,
+ O_RDWR|O_CREAT, 0600)) >= 0);
+ ok1(failtest_pipe(fds, "run-failpath.c", 1) == 0);
+ ok1(failtest_write(fd, "xxxx", 4, "run-failpath.c", 1) == 4);
+ lseek(fd, 0, SEEK_SET);
+ ok1(failtest_read(fd, p, 5, "run-failpath.c", 1) == 4);
+
+ /* Now we're into the failures. */
+ ok1(failtest_malloc(10, "run-failpath.c", 1) == NULL);
+ ok1(failtest_calloc(10, 5, "run-failpath.c", 1) == NULL);
+ ok1(failtest_realloc(p, 100, "run-failpath.c", 1) == NULL);
+ ok1(failtest_open("failpath-scratch", "run-failpath.c", 1,
+ O_RDWR|O_CREAT, 0600) == -1);
+ ok1(failtest_pipe(fds, "run-failpath.c", 1) == -1);
+ ok1(failtest_write(fd, "xxxx", 4, "run-failpath.c", 1) == -1);
+ lseek(fd, 0, SEEK_SET);
+ ok1(failtest_read(fd, p, 5, "run-failpath.c", 1) == -1);
+ return exit_status();
+}
diff --git a/lib/ccan/failtest/test/run-history.c b/lib/ccan/failtest/test/run-history.c
new file mode 100644
index 0000000000..b78682f5d5
--- /dev/null
+++ b/lib/ccan/failtest/test/run-history.c
@@ -0,0 +1,183 @@
+/* Include the C files directly. */
+#include <ccan/failtest/failtest.c>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <ccan/tap/tap.h>
+
+int main(void)
+{
+ struct failtest_call *call;
+ struct calloc_call calloc_call;
+ struct malloc_call malloc_call;
+ struct realloc_call realloc_call;
+ struct open_call open_call;
+ struct pipe_call pipe_call;
+ struct read_call read_call;
+ struct write_call write_call;
+ struct mmap_call mmap_call;
+ char buf[20];
+ unsigned int i;
+ char *path;
+
+ /* This is how many tests you plan to run */
+ plan_tests(69);
+
+ calloc_call.ret = calloc(1, 2);
+ calloc_call.nmemb = 1;
+ calloc_call.size = 2;
+ call = add_history(FAILTEST_CALLOC, true,
+ "run-history.c", 1, &calloc_call);
+ /* Normally should_fail would set this. */
+ call->fail = false;
+ ok1(call->type == FAILTEST_CALLOC);
+ ok1(call->can_leak == true);
+ ok1(strcmp(call->file, "run-history.c") == 0);
+ ok1(call->line == 1);
+ ok1(call->u.calloc.ret == calloc_call.ret);
+ ok1(call->u.calloc.nmemb == calloc_call.nmemb);
+ ok1(call->u.calloc.size == calloc_call.size);
+
+ malloc_call.ret = malloc(2);
+ malloc_call.size = 2;
+ call = add_history(FAILTEST_MALLOC, true,
+ "run-history.c", 2, &malloc_call);
+ /* Normally should_fail would set this. */
+ call->fail = false;
+ ok1(call->type == FAILTEST_MALLOC);
+ ok1(call->can_leak == true);
+ ok1(strcmp(call->file, "run-history.c") == 0);
+ ok1(call->line == 2);
+ ok1(call->u.malloc.ret == malloc_call.ret);
+ ok1(call->u.malloc.size == malloc_call.size);
+
+ realloc_call.ret = realloc(malloc_call.ret, 3);
+ realloc_call.ptr = malloc_call.ret;
+ realloc_call.size = 3;
+ call = add_history(FAILTEST_REALLOC, true, "run-history.c", 3,
+ &realloc_call);
+ /* Normally should_fail would set this. */
+ call->fail = false;
+ ok1(call->type == FAILTEST_REALLOC);
+ ok1(call->can_leak == true);
+ ok1(strcmp(call->file, "run-history.c") == 0);
+ ok1(call->line == 3);
+ ok1(call->u.realloc.ret == realloc_call.ret);
+ ok1(call->u.realloc.ptr == realloc_call.ptr);
+ ok1(call->u.realloc.size == realloc_call.size);
+
+ open_call.ret = open("test/run-history.c", O_RDONLY);
+ open_call.pathname = "test/run-history.c";
+ open_call.flags = O_RDONLY;
+ open_call.mode = 0;
+ open_call.closed = false;
+ call = add_history(FAILTEST_OPEN, true, "run-history.c", 4, &open_call);
+ /* Normally should_fail would set this. */
+ call->fail = false;
+ ok1(call->type == FAILTEST_OPEN);
+ ok1(call->can_leak == true);
+ ok1(strcmp(call->file, "run-history.c") == 0);
+ ok1(call->line == 4);
+ ok1(call->u.open.ret == open_call.ret);
+ ok1(strcmp(call->u.open.pathname, open_call.pathname) == 0);
+ ok1(call->u.open.flags == open_call.flags);
+ ok1(call->u.open.mode == open_call.mode);
+
+ pipe_call.ret = pipe(pipe_call.fds);
+ call = add_history(FAILTEST_PIPE, true, "run-history.c", 5, &pipe_call);
+ /* Normally should_fail would set this. */
+ call->fail = false;
+ ok1(call->type == FAILTEST_PIPE);
+ ok1(strcmp(call->file, "run-history.c") == 0);
+ ok1(call->can_leak == true);
+ ok1(call->line == 5);
+ ok1(call->u.pipe.ret == pipe_call.ret);
+ ok1(call->u.pipe.fds[0] == pipe_call.fds[0]);
+ ok1(call->u.pipe.fds[1] == pipe_call.fds[1]);
+
+ read_call.ret = read(open_call.ret, buf, 20);
+ read_call.buf = buf;
+ read_call.fd = open_call.ret;
+ read_call.count = 20;
+ call = add_history(FAILTEST_READ, false, "run-history.c", 6, &read_call);
+ /* Normally should_fail would set this. */
+ call->fail = false;
+ ok1(call->type == FAILTEST_READ);
+ ok1(call->can_leak == false);
+ ok1(strcmp(call->file, "run-history.c") == 0);
+ ok1(call->line == 6);
+ ok1(call->u.read.ret == read_call.ret);
+ ok1(call->u.read.buf == read_call.buf);
+ ok1(call->u.read.fd == read_call.fd);
+ ok1(call->u.read.count == read_call.count);
+
+ write_call.ret = 20;
+ write_call.buf = buf;
+ write_call.fd = open_call.ret;
+ write_call.count = 20;
+ write_call.opener = NULL;
+ call = add_history(FAILTEST_WRITE, false, "run-history.c", 7,
+ &write_call);
+ /* Normally should_fail would set this. */
+ call->fail = false;
+ ok1(call->type == FAILTEST_WRITE);
+ ok1(call->can_leak == false);
+ ok1(strcmp(call->file, "run-history.c") == 0);
+ ok1(call->line == 7);
+ ok1(call->u.write.ret == write_call.ret);
+ ok1(call->u.write.buf == write_call.buf);
+ ok1(call->u.write.fd == write_call.fd);
+ ok1(call->u.write.count == write_call.count);
+ ok1(call->u.write.opener == write_call.opener);
+
+ mmap_call.ret = &mmap_call;
+ mmap_call.addr = NULL;
+ mmap_call.length = 4096;
+ mmap_call.prot = PROT_READ;
+ mmap_call.flags = 0;
+ mmap_call.fd = open_call.ret;
+ mmap_call.offset = 0;
+ mmap_call.opener = opener_of(open_call.ret);
+ ok1(mmap_call.opener->type == FAILTEST_OPEN);
+ mmap_call.saved = NULL;
+
+ call = add_history(FAILTEST_MMAP, false, "run-history.c", 8,
+ &mmap_call);
+ /* Normally should_fail would set this. */
+ call->fail = false;
+ ok1(call->type == FAILTEST_MMAP);
+ ok1(call->can_leak == false);
+ ok1(strcmp(call->file, "run-history.c") == 0);
+ ok1(call->line == 8);
+ ok1(call->u.mmap.ret == mmap_call.ret);
+ ok1(call->u.mmap.addr == mmap_call.addr);
+ ok1(call->u.mmap.length == mmap_call.length);
+ ok1(call->u.mmap.prot == mmap_call.prot);
+ ok1(call->u.mmap.flags == mmap_call.flags);
+ ok1(call->u.mmap.fd == mmap_call.fd);
+ ok1(call->u.mmap.offset == mmap_call.offset);
+ ok1(call->u.mmap.opener == mmap_call.opener);
+ ok1(call->u.mmap.saved == mmap_call.saved);
+
+ i = 0;
+ tlist_for_each(&history, call, list)
+ i++;
+
+ ok1(i == 8);
+
+ tlist_for_each(&history, call, list)
+ call->fail = false;
+
+ path = failpath_string();
+ ok1(streq(path, "cmeoprwa"));
+ free(path);
+
+ tlist_for_each(&history, call, list)
+ call->fail = true;
+
+ path = failpath_string();
+ ok1(streq(path, "CMEOPRWA"));
+ free(path);
+
+ return exit_status();
+}
diff --git a/lib/ccan/failtest/test/run-locking.c b/lib/ccan/failtest/test/run-locking.c
new file mode 100644
index 0000000000..da0ee70fac
--- /dev/null
+++ b/lib/ccan/failtest/test/run-locking.c
@@ -0,0 +1,134 @@
+/* Include the C files directly. */
+#include <ccan/failtest/failtest.c>
+#include <stdlib.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <ccan/tap/tap.h>
+
+#define SIZE 8
+
+/* We don't want to fork and fail; we're just testing lock recording. */
+static enum failtest_result dont_fail(struct tlist_calls *history)
+{
+ return FAIL_DONT_FAIL;
+}
+
+static bool place_lock(int fd, char lockarr[], unsigned pos, unsigned size,
+ int type)
+{
+ struct flock fl;
+
+ /* Update record keeping. */
+ if (type == F_RDLCK)
+ memset(lockarr+pos, 1, size);
+ else if (type == F_WRLCK)
+ memset(lockarr+pos, 2, size);
+ else
+ memset(lockarr+pos, 0, size);
+
+ fl.l_whence = SEEK_SET;
+ fl.l_type = type;
+ fl.l_start = pos;
+ fl.l_len = size;
+ return failtest_fcntl(fd, "run-locking.c", 1, F_SETLK, &fl) == 0;
+}
+
+static char lock_lookup(int fd, unsigned pos)
+{
+ char ret = 0;
+ unsigned int i;
+ struct lock_info *l;
+
+ for (i = 0; i < lock_num; i++) {
+ l = &locks[i];
+
+ if (l->fd != fd)
+ continue;
+
+ if (pos >= l->start && pos <= l->end) {
+ if (ret)
+ ret = 3;
+ else if (l->type == F_RDLCK)
+ ret = 1;
+ else
+ ret = 2;
+ }
+ }
+ return ret;
+}
+
+static bool test(int fd,
+ unsigned p1, unsigned s1,
+ unsigned p2, unsigned s2,
+ unsigned p3, unsigned s3)
+{
+ unsigned int i;
+ char lockarr[SIZE];
+
+ memset(lockarr, 0, sizeof(lockarr));
+
+ if (!place_lock(fd, lockarr, p1, s1, F_WRLCK))
+ return false;
+
+ if (!place_lock(fd, lockarr, p2, s2, F_RDLCK))
+ return false;
+
+ if (!place_lock(fd, lockarr, p3, s3, F_UNLCK))
+ return false;
+
+ for (i = 0; i < SIZE; i++) {
+ if (lock_lookup(fd, i) != lockarr[i])
+ return false;
+ }
+
+ /* Reset lock info. */
+ lock_num = 0;
+ return true;
+}
+
+int main(void)
+{
+ int fd;
+ long flags;
+ unsigned int isize;
+
+ plan_tests(5835);
+ failtest_init(0, NULL);
+ failtest_hook = dont_fail;
+
+ fd = open("run-locking-scratch", O_RDWR|O_CREAT, 0600);
+ /* GETFL and SETFL wrappers should pass through. */
+ flags = fcntl(fd, F_GETFL);
+ ok1(failtest_fcntl(fd, "run-locking.c", 1, F_GETFL) == flags);
+ flags |= O_NONBLOCK;
+ ok1(failtest_fcntl(fd, "run-locking.c", 1, F_SETFL, flags) == 0);
+ ok1(failtest_fcntl(fd, "run-locking.c", 1, F_GETFL) == flags);
+
+ for (isize = 1; isize < 4; isize++) {
+ unsigned int ipos;
+ for (ipos = 0; ipos + isize < SIZE; ipos++) {
+ unsigned int jsize;
+ for (jsize = 1; jsize < 4; jsize++) {
+ unsigned int jpos;
+ for (jpos = 0; jpos + jsize < SIZE; jpos++) {
+ unsigned int ksize;
+ for (ksize = 1; ksize < 4; ksize++) {
+ unsigned int kpos;
+ for (kpos = 0;
+ kpos + ksize < SIZE;
+ kpos++) {
+ ok1(test(fd,
+ ipos, isize,
+ jpos, jsize,
+ kpos, ksize));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return exit_status();
+}
diff --git a/lib/ccan/failtest/test/run-malloc.c b/lib/ccan/failtest/test/run-malloc.c
new file mode 100644
index 0000000000..96641b8525
--- /dev/null
+++ b/lib/ccan/failtest/test/run-malloc.c
@@ -0,0 +1,116 @@
+#include "config.h"
+#include <stdlib.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <ccan/tap/tap.h>
+
+/* We don't actually want it to exit... */
+static jmp_buf exited;
+#define exit(status) longjmp(exited, (status) + 1)
+
+#define printf saved_printf
+static int saved_printf(const char *fmt, ...);
+
+#define fprintf saved_fprintf
+static int saved_fprintf(FILE *ignored, const char *fmt, ...);
+
+#define vfprintf saved_vfprintf
+static int saved_vfprintf(FILE *ignored, const char *fmt, va_list ap);
+
+/* Hack to avoid a memory leak which valgrind complains about. */
+#define realloc set_realloc
+static void *set_realloc(void *ptr, size_t size);
+
+#define free set_free
+static void set_free(void *ptr);
+
+/* Include the C files directly. */
+#include <ccan/failtest/failtest.c>
+
+#undef realloc
+#undef free
+
+static char *buffer;
+static void *set_realloc(void *ptr, size_t size)
+{
+ return buffer = realloc(ptr, size);
+}
+
+static void set_free(void *ptr)
+{
+ if (ptr == buffer)
+ buffer = NULL;
+ free(ptr);
+}
+
+static char *output = NULL;
+
+static int saved_vprintf(const char *fmt, va_list ap)
+{
+ int ret;
+ int len = 0;
+ va_list ap2;
+
+ va_copy(ap2, ap);
+ ret = vsnprintf(NULL, 0, fmt, ap2);
+ va_end(ap2);
+
+ if (output)
+ len = strlen(output);
+
+ output = realloc(output, len + ret + 1);
+ return vsprintf(output + len, fmt, ap);
+}
+
+static int saved_vfprintf(FILE *ignored, const char *fmt, va_list ap)
+{
+ return saved_vprintf(fmt, ap);
+}
+
+static int saved_printf(const char *fmt, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, fmt);
+ ret = saved_vprintf(fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+static int saved_fprintf(FILE *ignored, const char *fmt, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, fmt);
+ ret = saved_vprintf(fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+int main(void)
+{
+ int status;
+
+ plan_tests(3);
+ failtest_init(0, NULL);
+
+ status = setjmp(exited);
+ if (status == 0) {
+ char *p = failtest_malloc(1, "run-malloc.c", 1);
+ /* If we just segv, valgrind counts that as a failure.
+ * So kill ourselves creatively. */
+ if (!p)
+ kill(getpid(), SIGSEGV);
+ fail("Expected child to crash!");
+ } else {
+ ok1(status == 2);
+ ok1(strstr(output, "Killed by signal"));
+ ok1(strstr(output, "--failpath=M\n"));
+ }
+ free(buffer);
+ return exit_status();
+}
diff --git a/lib/ccan/failtest/test/run-open.c b/lib/ccan/failtest/test/run-open.c
new file mode 100644
index 0000000000..0166506480
--- /dev/null
+++ b/lib/ccan/failtest/test/run-open.c
@@ -0,0 +1,72 @@
+/* Include the C files directly. */
+#include <ccan/failtest/failtest.c>
+#include <stdlib.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <ccan/tap/tap.h>
+
+int main(void)
+{
+ int fd, pfd[2], err;
+ char buf[] = "Hello world!";
+ struct stat st;
+
+ plan_tests(12);
+ failtest_init(0, NULL);
+
+ if (pipe(pfd))
+ abort();
+ fd = failtest_open("run-open-scratchpad", "run-open.c", 1,
+ O_RDWR|O_CREAT, 0600);
+ if (fd == -1) {
+ /* We are the child: write error code for parent to check. */
+ err = errno;
+ if (write(pfd[1], &err, sizeof(err)) != sizeof(err))
+ abort();
+ failtest_exit(0);
+ }
+ /* Check it is read-write. */
+ ok1(write(fd, buf, strlen(buf)) == strlen(buf));
+ lseek(fd, SEEK_SET, 0);
+ ok1(read(fd, buf, strlen("Hello world!")) == strlen("Hello world!"));
+ ok1(strcmp(buf, "Hello world!") == 0);
+
+ /* Check name and perms. */
+ ok1(stat("run-open-scratchpad", &st) == 0);
+ ok1(st.st_size == strlen(buf));
+ ok1(S_ISREG(st.st_mode));
+ ok1((st.st_mode & 0777) == 0600);
+
+ /* Check child got correct errno. */
+ ok1(read(pfd[0], &err, sizeof(err)) == sizeof(err));
+ ok1(err == EACCES);
+
+ /* Clean up. */
+ failtest_close(fd, "run-open.c", 1);
+ close(pfd[0]);
+ close(pfd[1]);
+
+ /* Two-arg open. */
+ if (pipe(pfd) != 0)
+ abort();
+ fd = failtest_open("run-open-scratchpad", "run-open.c", 1, O_RDONLY);
+ if (fd == -1) {
+ /* We are the child: write error code for parent to check. */
+ err = errno;
+ if (write(pfd[1], &err, sizeof(err)) != sizeof(err))
+ abort();
+ failtest_exit(0);
+ }
+ /* Check it is read-only. */
+ ok1(write(fd, buf, strlen(buf)) == -1);
+ ok1(read(fd, buf, strlen("Hello world!")) == strlen("Hello world!"));
+ ok1(strcmp(buf, "Hello world!") == 0);
+ /* Clean up. */
+ failtest_close(fd, "run-open.c", 1);
+ close(pfd[0]);
+ close(pfd[1]);
+
+ return exit_status();
+}
diff --git a/lib/ccan/failtest/test/run-write.c b/lib/ccan/failtest/test/run-write.c
new file mode 100644
index 0000000000..6a712fec53
--- /dev/null
+++ b/lib/ccan/failtest/test/run-write.c
@@ -0,0 +1,51 @@
+/* Include the C files directly. */
+#include <ccan/failtest/failtest.c>
+#include <stdlib.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <ccan/tap/tap.h>
+
+int main(int argc, char *argv[])
+{
+ int fd;
+ char *p;
+ char buf[] = "Hello world!";
+
+ plan_tests(5);
+ failtest_init(argc, argv);
+
+ fd = failtest_open("run-write-scratchpad", __FILE__, __LINE__,
+ O_RDWR|O_CREAT, 0600);
+ /* Child will fail, ignore. */
+ if (fd < 0)
+ failtest_exit(0);
+ if (write(fd, buf, strlen(buf)) != strlen(buf))
+ abort();
+ ok1(lseek(fd, 0, SEEK_CUR) == strlen(buf));
+
+ p = failtest_malloc(100, __FILE__, __LINE__);
+ if (!p) {
+ /* We are the child. Do a heap of writes. */
+ unsigned int i;
+
+ for (i = 0; i < strlen(buf)+1; i++)
+ if (failtest_write(fd, "x", 1, __FILE__, __LINE__)
+ == 1)
+ break;
+ failtest_close(fd, __FILE__, __LINE__);
+ failtest_exit(0);
+ }
+
+ /* Seek pointer should be left alone! */
+ ok1(lseek(fd, 0, SEEK_CUR) == strlen(buf));
+ /* Length should be restored. */
+ ok1(lseek(fd, 0, SEEK_END) == strlen(buf));
+ lseek(fd, 0, SEEK_SET);
+ ok1(read(fd, buf, strlen(buf)) == strlen("Hello world!"));
+ ok1(strcmp(buf, "Hello world!") == 0);
+ failtest_close(fd, __FILE__, __LINE__);
+
+ return exit_status();
+}