tdb2: change API to return the error value.
[ccan] / ccan / tdb2 / test / run-01-new_database.c
1 #include <ccan/failtest/failtest_override.h>
2 #include <ccan/tdb2/tdb.c>
3 #include <ccan/tdb2/free.c>
4 #include <ccan/tdb2/lock.c>
5 #include <ccan/tdb2/io.c>
6 #include <ccan/tdb2/hash.c>
7 #include <ccan/tdb2/transaction.c>
8 #include <ccan/tdb2/check.c>
9 #include <ccan/tap/tap.h>
10 #include <ccan/failtest/failtest.h>
11 #include "logging.h"
12
13 /* FIXME: Check these! */
14 #define INITIAL_TDB_MALLOC      "tdb.c", 189, FAILTEST_MALLOC
15 #define LOGGING_MALLOC          "tdb.c", 766, FAILTEST_MALLOC
16 #define URANDOM_OPEN            "tdb.c", 49, FAILTEST_OPEN
17 #define URANDOM_READ            "tdb.c", 29, FAILTEST_READ
18
19 static bool failmatch(const struct failtest_call *call,
20                       const char *file, int line, enum failtest_call_type type)
21 {
22         return call->type == type
23                 && call->line == line
24                 && ((strcmp(call->file, file) == 0)
25                     || (strends(call->file, file)
26                         && (call->file[strlen(call->file) - strlen(file) - 1]
27                             == '/')));
28 }
29
30 static const struct failtest_call *
31 find_repeat(const struct failtest_call *start, const struct failtest_call *end,
32             const struct failtest_call *call)
33 {
34         const struct failtest_call *i;
35
36         for (i = start; i < end; i++) {
37                 if (failmatch(i, call->file, call->line, call->type))
38                         return i;
39         }
40         return NULL;
41 }
42
43 static bool is_nonblocking_lock(const struct failtest_call *call)
44 {
45         return call->type == FAILTEST_FCNTL && call->u.fcntl.cmd == F_SETLK;
46 }
47
48 /* Some places we soldier on despite errors: only fail them once. */
49 static enum failtest_result
50 block_repeat_failures(struct failtest_call *history, unsigned num)
51 {
52         const struct failtest_call *i, *last = &history[num-1];
53
54         if (failmatch(last, INITIAL_TDB_MALLOC)
55             || failmatch(last, LOGGING_MALLOC)
56             || failmatch(last, URANDOM_OPEN)
57             || failmatch(last, URANDOM_READ)) {
58                 if (find_repeat(history, last, last))
59                         return FAIL_DONT_FAIL;
60                 return FAIL_PROBE;
61         }
62
63         /* Unlock or non-blocking lock is fail-once. */
64         if (last->type == FAILTEST_FCNTL
65             && last->u.fcntl.arg.fl.l_type == F_UNLCK) {
66                 /* Find a previous unlock at this point? */
67                 for (i = find_repeat(history, last, last);
68                      i;
69                      i = find_repeat(history, i, last)) {
70                         if (i->u.fcntl.arg.fl.l_type == F_UNLCK)
71                                 return FAIL_DONT_FAIL;
72                 }
73                 return FAIL_PROBE;
74         } else if (is_nonblocking_lock(last)) {
75                 /* Find a previous non-blocking lock at this point? */
76                 for (i = find_repeat(history, last, last);
77                      i;
78                      i = find_repeat(history, i, last)) {
79                         if (is_nonblocking_lock(i))
80                                 return FAIL_DONT_FAIL;
81                 }
82                 return FAIL_PROBE;
83         }
84
85         return FAIL_OK;
86 }
87
88 static bool exit_check(struct failtest_call *history, unsigned num)
89 {
90         unsigned int i;
91
92         for (i = 0; i < num; i++) {
93                 if (!history[i].fail)
94                         continue;
95                 /* Failing the /dev/urandom open doesn't count: we fall back. */
96                 if (failmatch(&history[i], URANDOM_OPEN))
97                         continue;
98
99                 /* Similarly with read fail. */
100                 if (failmatch(&history[i], URANDOM_READ))
101                         continue;
102
103                 /* Initial allocation of tdb doesn't log. */
104                 if (failmatch(&history[i], INITIAL_TDB_MALLOC))
105                         continue;
106
107                 /* We don't block "failures" on non-blocking locks. */
108                 if (is_nonblocking_lock(&history[i]))
109                         continue;
110
111                 if (!tap_log_messages)
112                         diag("We didn't log for %u (%s:%u)",
113                              i, history[i].file, history[i].line);
114                 return tap_log_messages != 0;
115         }
116         return true;
117 }
118
119 int main(int argc, char *argv[])
120 {
121         unsigned int i;
122         struct tdb_context *tdb;
123         int flags[] = { TDB_INTERNAL, TDB_DEFAULT, TDB_NOMMAP,
124                         TDB_INTERNAL|TDB_CONVERT, TDB_CONVERT, 
125                         TDB_NOMMAP|TDB_CONVERT };
126
127         failtest_init(argc, argv);
128         failtest_hook = block_repeat_failures;
129         failtest_exit_check = exit_check;
130         plan_tests(sizeof(flags) / sizeof(flags[0]) * 3);
131         for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
132                 tdb = tdb_open("run-new_database.tdb", flags[i],
133                                O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
134                 if (!ok1(tdb))
135                         failtest_exit(exit_status());
136                 if (tdb) {
137                         bool ok = ok1(tdb_check(tdb, NULL, NULL) == 0);
138                         tdb_close(tdb);
139                         if (!ok)
140                                 failtest_exit(exit_status());
141                 }
142                 if (!ok1(tap_log_messages == 0))
143                         break;
144         }
145         failtest_exit(exit_status());
146 }