completion
[protos/libecoli.git] / lib / ecoli_test.c
1 /*
2  * Copyright (c) 2016, Olivier MATZ <zer0@droids-corp.org>
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  *     * Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     * Redistributions in binary form must reproduce the above copyright
10  *       notice, this list of conditions and the following disclaimer in the
11  *       documentation and/or other materials provided with the distribution.
12  *     * Neither the name of the University of California, Berkeley nor the
13  *       names of its contributors may be used to endorse or promote products
14  *       derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31
32 #include <ecoli_log.h>
33 #include <ecoli_malloc.h>
34 #include <ecoli_test.h>
35 #include <ecoli_tk.h>
36
37 static struct ec_test_list test_list = TAILQ_HEAD_INITIALIZER(test_list);
38
39 /* register a driver */
40 void ec_test_register(struct ec_test *test)
41 {
42         TAILQ_INSERT_TAIL(&test_list, test, next);
43 }
44
45 int ec_test_check_tk_parse(const struct ec_tk *tk, const char *input,
46         const char *expected)
47 {
48         struct ec_parsed_tk *p;
49         const char *s;
50         int ret = -1;
51
52         p = ec_tk_parse(tk, input);
53         s = ec_parsed_tk_to_string(p);
54         if (s == NULL && expected == NULL)
55                 ret = 0;
56         else if (s != NULL && expected != NULL &&
57                 !strcmp(s, expected))
58                 ret = 0;
59
60         if (expected == NULL && ret != 0)
61                 ec_log(EC_LOG_ERR, "tk should not match but matches <%s>\n", s);
62         if (expected != NULL && ret != 0)
63                 ec_log(EC_LOG_ERR, "tk should match <%s> but matches <%s>\n",
64                         expected, s);
65
66         ec_parsed_tk_free(p);
67
68         return ret;
69 }
70
71 int ec_test_check_tk_complete(const struct ec_tk *tk, const char *input,
72         const char *expected)
73 {
74         struct ec_completed_tk *c;
75         const char *s;
76         int ret = -1;
77
78         c = ec_tk_complete(tk, input);
79         s = ec_completed_tk_smallest_start(c);
80         if (s == NULL && expected == NULL)
81                 ret = 0;
82         else if (s != NULL && expected != NULL &&
83                 !strcmp(s, expected))
84                 ret = 0;
85
86         if (expected == NULL && ret != 0)
87                 ec_log(EC_LOG_ERR,
88                         "tk should not complete but completes with <%s>\n", s);
89         if (expected != NULL && ret != 0)
90                 ec_log(EC_LOG_ERR,
91                         "tk should complete with <%s> but completes with <%s>\n",
92                         expected, s);
93
94         ec_completed_tk_free(c);
95
96         return ret;
97 }
98
99 int ec_test_check_tk_complete_list(const struct ec_tk *tk,
100         const char *input, ...)
101 {
102         struct ec_completed_tk *c = NULL;
103         struct ec_completed_tk_elt *elt;
104         const char *s;
105         int ret = -1;
106         unsigned int count = 0;
107         va_list ap;
108
109         va_start(ap, input);
110
111         c = ec_tk_complete(tk, input);
112         if (c == NULL)
113                 goto out;
114
115         for (s = va_arg(ap, const char *);
116              s != EC_TK_ENDLIST;
117              s = va_arg(ap, const char *)) {
118                 if (s == NULL)
119                         goto out;
120
121                 count++;
122                 TAILQ_FOREACH(elt, &c->elts, next) {
123                         if (strcmp(elt->add, s) == 0)
124                                 break;
125                 }
126
127                 if (elt == NULL) {
128                         ec_log(EC_LOG_ERR,
129                                 "completion <%s> not in list\n", s);
130                         goto out;
131                 }
132         }
133
134         if (count != ec_completed_tk_count(c)) {
135                 ec_log(EC_LOG_ERR,
136                         "nb_completion (%d) does not match (%d)\n",
137                         count, ec_completed_tk_count(c));
138                 goto out;
139         }
140
141         ret = 0;
142
143 out:
144         ec_completed_tk_free(c);
145         va_end(ap);
146         return ret;
147 }
148
149 TAILQ_HEAD(debug_alloc_hdr_list, debug_alloc_hdr);
150 static struct debug_alloc_hdr_list debug_alloc_hdr_list =
151         TAILQ_HEAD_INITIALIZER(debug_alloc_hdr_list);
152
153 struct debug_alloc_hdr {
154         TAILQ_ENTRY(debug_alloc_hdr) next;
155         const char *file;
156         unsigned int line;
157         size_t size;
158         unsigned int cookie;
159 };
160
161 struct debug_alloc_ftr {
162         unsigned int cookie;
163 } __attribute__((packed));
164
165 static void *debug_malloc(size_t size, const char *file, unsigned int line)
166 {
167         struct debug_alloc_hdr *hdr;
168         struct debug_alloc_ftr *ftr;
169         size_t new_size = size + sizeof(*hdr) + sizeof(*ftr);
170         void *ret;
171
172         hdr = malloc(new_size);
173         if (hdr == NULL) {
174                 ret = NULL;
175         } else {
176                 hdr->file = file;
177                 hdr->line = line;
178                 hdr->size = size;
179                 hdr->cookie = 0x12345678;
180                 TAILQ_INSERT_TAIL(&debug_alloc_hdr_list, hdr, next);
181                 ret = hdr + 1;
182                 ftr = (struct debug_alloc_ftr *)(
183                         (char *)hdr + size + sizeof(*hdr));
184                 ftr->cookie = 0x12345678;
185         }
186
187         ec_log(EC_LOG_INFO, "%s:%d: info: malloc(%zd) -> %p\n",
188                 file, line, size, ret);
189
190         return ret;
191 }
192
193 static void debug_free(void *ptr, const char *file, unsigned int line)
194 {
195         struct debug_alloc_hdr *hdr, *h;
196         struct debug_alloc_ftr *ftr;
197
198         (void)file;
199         (void)line;
200
201         ec_log(EC_LOG_INFO, "%s:%d: info: free(%p)\n", file, line, ptr);
202
203         if (ptr == NULL)
204                 return;
205
206         hdr = (ptr - sizeof(*hdr));
207         if (hdr->cookie != 0x12345678) {
208                 ec_log(EC_LOG_ERR, "%s:%d: error: free(%p): bad start cookie\n",
209                         file, line, ptr);
210                 abort();
211         }
212
213         ftr = (ptr + hdr->size);
214         if (ftr->cookie != 0x12345678) {
215                 ec_log(EC_LOG_ERR, "%s:%d: error: free(%p): bad end cookie\n",
216                         file, line, ptr);
217                 abort();
218         }
219
220         TAILQ_FOREACH(h, &debug_alloc_hdr_list, next) {
221                 if (h == hdr)
222                         break;
223         }
224
225         if (h == NULL) {
226                 ec_log(EC_LOG_ERR, "%s:%d: error: free(%p): bad ptr\n",
227                         file, line, ptr);
228                 abort();
229         }
230
231         TAILQ_REMOVE(&debug_alloc_hdr_list, hdr, next);
232         free(hdr);
233 }
234
235 void *debug_realloc(void *ptr, size_t size, const char *file, unsigned int line)
236 {
237         struct debug_alloc_hdr *hdr, *h;
238         struct debug_alloc_ftr *ftr;
239         size_t new_size = size + sizeof(*hdr) + sizeof(unsigned int);
240         void *ret;
241
242         if (ptr != NULL) {
243                 hdr =  (ptr - sizeof(*hdr));
244                 if (hdr->cookie != 0x12345678) {
245                         ec_log(EC_LOG_ERR,
246                                 "%s:%d: error: realloc(%p): bad start cookie\n",
247                                 file, line, ptr);
248                         abort();
249                 }
250
251                 ftr = (ptr + hdr->size);
252                 if (ftr->cookie != 0x12345678) {
253                         ec_log(EC_LOG_ERR,
254                                 "%s:%d: error: realloc(%p): bad end cookie\n",
255                                 file, line, ptr);
256                         abort();
257                 }
258
259                 TAILQ_FOREACH(h, &debug_alloc_hdr_list, next) {
260                         if (h == hdr)
261                                 break;
262                 }
263
264                 if (h == NULL) {
265                         ec_log(EC_LOG_ERR, "%s:%d: error: realloc(%p): bad ptr\n",
266                                 file, line, ptr);
267                         abort();
268                 }
269
270                 TAILQ_REMOVE(&debug_alloc_hdr_list, h, next);
271                 hdr = realloc(hdr, new_size);
272                 if (hdr == NULL) {
273                         TAILQ_INSERT_TAIL(&debug_alloc_hdr_list, h, next);
274                         ret = NULL;
275                 } else {
276                         ret = hdr + 1;
277                 }
278         } else {
279                 hdr = realloc(NULL, new_size);
280                 if (hdr == NULL)
281                         ret = NULL;
282                 else
283                         ret = hdr + 1;
284         }
285
286         if (hdr != NULL) {
287                 hdr->file = file;
288                 hdr->line = line;
289                 hdr->size = size;
290                 hdr->cookie = 0x12345678;
291                 TAILQ_INSERT_TAIL(&debug_alloc_hdr_list, hdr, next);
292                 ftr = (struct debug_alloc_ftr *)(
293                         (char *)hdr + size + sizeof(*hdr));
294                 ftr->cookie = 0x12345678;
295         }
296
297         ec_log(EC_LOG_INFO, "%s:%d: info: realloc(%p, %zd) -> %p\n",
298                 file, line, ptr, size, ret);
299
300         return ret;
301 }
302
303 void debug_alloc_dump(void)
304 {
305         struct debug_alloc_hdr *hdr;
306
307         TAILQ_FOREACH(hdr, &debug_alloc_hdr_list, next) {
308                 ec_log(EC_LOG_ERR, "%s:%d: error: memory leak size=%zd ptr=%p\n",
309                         hdr->file, hdr->line, hdr->size, hdr + 1);
310         }
311 }
312
313 /* XXX todo */
314 /* int ec_test_check_tk_complete_list(const struct ec_tk *tk, */
315 /*      const char *input, ...) */
316
317 int ec_test_all(void)
318 {
319         struct ec_test *test;
320         int ret = 0;
321
322         TAILQ_INIT(&debug_alloc_hdr_list);
323
324         /* register a new malloc to trac memleaks */
325         if (0 && ec_malloc_register(debug_malloc, debug_free, debug_realloc) < 0) {
326                 ec_log(EC_LOG_ERR, "cannot register new malloc\n");
327                 return -1;
328         }
329
330         TAILQ_FOREACH(test, &test_list, next) {
331                 if (test->test() == 0) {
332                         ec_log(EC_LOG_INFO, "== test %-20s success\n",
333                                 test->name);
334                 } else {
335                         ec_log(EC_LOG_INFO, "== test %-20s failed\n",
336                                 test->name);
337                         ret = -1;
338                 }
339         }
340
341         ec_malloc_unregister();
342         debug_alloc_dump();
343
344         return ret;
345 }