+/* Licensed under LGPL - see LICENSE file for details */
#ifndef CCAN_FAILTEST_H
#define CCAN_FAILTEST_H
+#include "config.h"
+#if HAVE_FILE_OFFSET_BITS
+#define _FILE_OFFSET_BITS 64
+#endif
#include <sys/types.h>
#include <stdbool.h>
+#include <fcntl.h>
#include <ccan/compiler/compiler.h>
+#include <ccan/tlist/tlist.h>
/**
* failtest_init - initialize the failtest module
FAILTEST_CALLOC,
FAILTEST_REALLOC,
FAILTEST_OPEN,
+ FAILTEST_CLOSE,
FAILTEST_PIPE,
FAILTEST_READ,
FAILTEST_WRITE,
+ FAILTEST_FCNTL,
+ FAILTEST_MMAP,
+ FAILTEST_LSEEK
};
struct calloc_call {
const char *pathname;
int flags;
mode_t mode;
+ bool always_save;
+ bool closed;
+ /* This is used for O_TRUNC opens on existing files. */
+ struct contents_saved *saved;
+};
+
+struct close_call {
+ int fd;
};
struct pipe_call {
int ret;
int fds[2];
+ bool closed[2];
};
struct read_call {
ssize_t ret;
+ off_t off;
int fd;
void *buf;
size_t count;
int fd;
const void *buf;
size_t count;
+ off_t off;
+ bool is_pwrite;
+ struct failtest_call *opener;
+ struct contents_saved *saved;
+};
+
+struct fcntl_call {
+ int ret;
+ int fd;
+ int cmd;
+ union {
+ struct flock fl;
+ long l;
+ int i;
+ } arg;
+};
+
+struct mmap_call {
+ void *ret;
+ void *addr;
+ size_t length;
+ int prot;
+ int flags;
+ int fd;
+ off_t offset;
+ struct failtest_call *opener;
+ struct contents_saved *saved;
+};
+
+struct lseek_call {
+ ssize_t ret;
+ int fd;
+ off_t offset;
+ int whence;
+ off_t old_off;
};
/**
* failtest_hook, failtest_exit_check
*/
struct failtest_call {
+ /* We're in the history list. */
+ struct list_node list;
enum failtest_call_type type;
/* Where we were called from. */
const char *file;
bool fail;
/* What we set errno to. */
int error;
+ /* How do we clean this up? */
+ void (*cleanup)(void *u, bool restore);
+ /* Should their program have cleaned up? */
+ bool can_leak;
+ /* Backtrace of call chain. */
+ void **backtrace;
+ unsigned int backtrace_num;
/* The actual call data. */
union {
struct calloc_call calloc;
struct malloc_call malloc;
struct realloc_call realloc;
struct open_call open;
+ struct close_call close;
struct pipe_call pipe;
struct read_call read;
struct write_call write;
+ struct fcntl_call fcntl;
+ struct mmap_call mmap;
+ struct lseek_call lseek;
} u;
};
+/* This defines struct tlist_calls. */
+TLIST_TYPE(calls, struct failtest_call);
+
+enum failtest_result {
+ /* Yes try failing this call. */
+ FAIL_OK,
+ /* No, don't try failing this call. */
+ FAIL_DONT_FAIL,
+ /* Try failing this call but don't go too far down that path. */
+ FAIL_PROBE,
+};
+
/**
* failtest_hook - whether a certain call should fail or not.
* @history: the ordered history of all failtest calls.
- * @num: the number of elements in @history (greater than 0)
*
* The default value of this hook is failtest_default_hook(), which returns
- * true (ie. yes, fail the call).
+ * FAIL_OK (ie. yes, fail the call).
*
* You can override it, and avoid failing certain calls. The parameters
* of the call (but not the return value(s)) will be filled in for the last
* call.
*
* Example:
- * static bool dont_fail_allocations(struct failtest_call *history,
- * unsigned num)
+ * static enum failtest_result dont_fail_alloc(struct tlist_calls *history)
* {
- * return history[num-1].type != FAILTEST_MALLOC
- * && history[num-1].type != FAILTEST_CALLOC
- * && history[num-1].type != FAILTEST_REALLOC;
+ * struct failtest_call *call;
+ * call = tlist_tail(history, list);
+ * if (call->type == FAILTEST_MALLOC
+ * || call->type == FAILTEST_CALLOC
+ * || call->type == FAILTEST_REALLOC)
+ * return FAIL_DONT_FAIL;
+ * return FAIL_OK;
* }
* ...
- * failtest_hook = dont_fail_allocations;
+ * failtest_hook = dont_fail_alloc;
*/
-extern bool (*failtest_hook)(struct failtest_call *history, unsigned num);
+extern enum failtest_result (*failtest_hook)(struct tlist_calls *history);
/**
* failtest_exit_check - hook for additional checks on a failed child.
* @history: the ordered history of all failtest calls.
- * @num: the number of elements in @history (greater than 0)
*
* Your program might have additional checks to do on failure, such as
* check that a file is not corrupted, or than an error message has been
* If this returns false, the path to this failure will be printed and the
* overall test will fail.
*/
-extern bool (*failtest_exit_check)(struct failtest_call *history,
- unsigned num);
+extern bool (*failtest_exit_check)(struct tlist_calls *history);
-/* This usually fails the call. */
-bool failtest_default_hook(struct failtest_call *history, unsigned num);
+/**
+ * failtest_has_failed - determine if a failure has occurred.
+ *
+ * Sometimes you want to exit immediately if you've experienced an
+ * injected failure. This is useful when you have four separate tests
+ * in your test suite, and you don't want to do the next one if you've
+ * had a failure in a previous one.
+ */
+extern bool failtest_has_failed(void);
/**
* failtest_timeout_ms - how long to wait before killing child.