diff options
Diffstat (limited to 'lib/ccan/failtest/test')
-rw-r--r-- | lib/ccan/failtest/test/run-failpath.c | 39 | ||||
-rw-r--r-- | lib/ccan/failtest/test/run-history.c | 183 | ||||
-rw-r--r-- | lib/ccan/failtest/test/run-locking.c | 134 | ||||
-rw-r--r-- | lib/ccan/failtest/test/run-malloc.c | 116 | ||||
-rw-r--r-- | lib/ccan/failtest/test/run-open.c | 72 | ||||
-rw-r--r-- | lib/ccan/failtest/test/run-write.c | 51 |
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(); +} |