tal: Make tal_resize() easier to use.
[ccan] / ccan / tal / test / run-allocfail.c
1 #include <ccan/tal/tal.h>
2 #include <ccan/tal/tal.c>
3 #include <ccan/tap/tap.h>
4
5 static int alloc_count, when_to_fail, err_count;
6 static bool stealing;
7
8 static void *failing_alloc(size_t len)
9 {
10         if (alloc_count++ == when_to_fail)
11                 return NULL;
12         /* once we've failed once, it shouldn't ask again (steal can though). */
13         assert(stealing || alloc_count <= when_to_fail);
14
15         return malloc(len);
16 }
17
18 static void *failing_realloc(void *p, size_t len)
19 {
20         if (alloc_count++ == when_to_fail)
21                 return NULL;
22
23         return realloc(p, len);
24 }
25
26
27 static void nofail_on_error(const char *msg)
28 {
29         diag("ERROR: %s", msg);
30         err_count++;
31 }
32
33 static void destroy_p(void *p)
34 {
35 }
36
37 int main(void)
38 {
39         char *p, *c1, *c2;
40         bool success;
41
42         plan_tests(25);
43
44         tal_set_backend(failing_alloc, failing_realloc, NULL, nofail_on_error);
45
46         /* Fail at each possible point in an allocation. */
47         when_to_fail = err_count = 0;
48         do {
49                 alloc_count = 0;
50                 p = tal(NULL, char);
51                 when_to_fail++;
52         } while (!p);
53         ok1(alloc_count >= 1);
54         ok1(when_to_fail > 1);
55         ok1(err_count == when_to_fail - 1);
56
57         /* Do it again. */
58         when_to_fail = err_count = 0;
59         do {
60                 alloc_count = 0;
61                 c1 = tal(p, char);
62                 when_to_fail++;
63         } while (!c1);
64         ok1(alloc_count >= 1);
65         ok1(when_to_fail > 1);
66         ok1(err_count == when_to_fail - 1);
67
68         /* Now during resize. */
69         c2 = c1;
70         when_to_fail = err_count = 0;
71         for (;;) {
72                 alloc_count = 0;
73                 if (tal_resize(&c1, 100))
74                         break;
75                 /* Failing alloc will not change pointer. */
76                 ok1(c1 == c2);
77                 when_to_fail++;
78         };
79         ok1(alloc_count == 1);
80         ok1(when_to_fail == 1);
81         ok1(err_count == 1);
82         /* Make sure it's really resized. */
83         memset(c1, 1, 100);
84
85         /* Now for second child. */
86         when_to_fail = err_count = 0;
87         do {
88                 alloc_count = 0;
89                 c2 = tal(p, char);
90                 when_to_fail++;
91         } while (!c2);
92         ok1(alloc_count >= 1);
93         ok1(when_to_fail > 1);
94         /* Note: adding a child will fall through if group alloc fails. */
95         ok1 (err_count == when_to_fail - 1 || err_count == when_to_fail);
96
97         /* Now while adding a destructor. */
98         when_to_fail = err_count = 0;
99         do {
100                 alloc_count = 0;
101                 success = tal_add_destructor(p, destroy_p);
102                 when_to_fail++;
103         } while (!success);
104         ok1(alloc_count >= 1);
105         ok1(when_to_fail > 1);
106         ok1(err_count == when_to_fail - 1);
107
108         /* Now while adding a name. */
109         when_to_fail = err_count = 0;
110         do {
111                 const char name[] = "some name";
112                 alloc_count = 0;
113                 success = tal_set_name(p, name);
114                 when_to_fail++;
115         } while (!success);
116         ok1(alloc_count >= 1);
117         ok1(when_to_fail > 1);
118         ok1(err_count == when_to_fail - 1);
119
120         /* Now while stealing. */
121         stealing = true;
122         when_to_fail = err_count = 0;
123         do {
124                 alloc_count = 0;
125                 success = tal_steal(c2, c1) != NULL;
126                 when_to_fail++;
127         } while (!success);
128         ok1(alloc_count >= 1);
129         ok1(when_to_fail > 1);
130         ok1(err_count == when_to_fail - 1);
131
132         /* Now stealing with more children (more coverage). */
133         when_to_fail = 1000;
134         (void)tal(p, char);
135         c1 = tal(p, char);
136         c2 = tal(p, char);
137         (void)tal(p, char);
138
139         /* Now steal again. */
140         when_to_fail = err_count = 0;
141         do {
142                 alloc_count = 0;
143                 success = tal_steal(c2, c1) != NULL;
144                 when_to_fail++;
145         } while (!success);
146         ok1(alloc_count >= 1);
147         ok1(when_to_fail > 1);
148         ok1(err_count == when_to_fail - 1);
149
150         tal_free(p);
151         return exit_status();
152 }