option, shlex
[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 #include <assert.h>
32
33 #include <ecoli_log.h>
34 #include <ecoli_malloc.h>
35 #include <ecoli_test.h>
36 #include <ecoli_tk.h>
37
38 static struct ec_test_list test_list = TAILQ_HEAD_INITIALIZER(test_list);
39
40 /* register a driver */
41 void ec_test_register(struct ec_test *test)
42 {
43         TAILQ_INSERT_TAIL(&test_list, test, next);
44 }
45
46 int ec_test_check_tk_parse(const struct ec_tk *tk, const char *input,
47         const char *expected)
48 {
49         struct ec_parsed_tk *p;
50         const char *s;
51         int ret = -1;
52
53         p = ec_tk_parse(tk, input);
54         s = ec_parsed_tk_to_string(p);
55         if (s == NULL && expected == NULL)
56                 ret = 0;
57         else if (s != NULL && expected != NULL &&
58                 !strcmp(s, expected))
59                 ret = 0;
60
61         if (expected == NULL && ret != 0)
62                 ec_log(EC_LOG_ERR, "tk should not match but matches <%s>\n", s);
63         if (expected != NULL && ret != 0)
64                 ec_log(EC_LOG_ERR, "tk should match <%s> but matches <%s>\n",
65                         expected, s);
66
67         ec_parsed_tk_free(p);
68
69         return ret;
70 }
71
72 int ec_test_check_tk_complete(const struct ec_tk *tk, const char *input,
73         const char *expected)
74 {
75         struct ec_completed_tk *c;
76         const char *s;
77         int ret = -1;
78
79         assert(expected != NULL);
80
81         c = ec_tk_complete(tk, input);
82         s = ec_completed_tk_smallest_start(c);
83         if (!strcmp(s, expected))
84                 ret = 0;
85
86         if (ret != 0)
87                 ec_log(EC_LOG_ERR,
88                         "should complete with <%s> but completes with <%s>\n",
89                         expected, s);
90
91         ec_completed_tk_free(c);
92
93         return ret;
94 }
95
96 int ec_test_check_tk_complete_list(const struct ec_tk *tk,
97         const char *input, ...)
98 {
99         struct ec_completed_tk *c = NULL;
100         struct ec_completed_tk_elt *elt;
101         const char *s;
102         int ret = -1;
103         unsigned int count = 0;
104         va_list ap;
105
106         va_start(ap, input);
107
108         c = ec_tk_complete(tk, input);
109         if (c == NULL)
110                 goto out;
111
112         for (s = va_arg(ap, const char *);
113              s != EC_TK_ENDLIST;
114              s = va_arg(ap, const char *)) {
115                 if (s == NULL)
116                         goto out;
117
118                 count++;
119                 TAILQ_FOREACH(elt, &c->elts, next) {
120                         if (strcmp(elt->add, s) == 0)
121                                 break;
122                 }
123
124                 if (elt == NULL) {
125                         ec_log(EC_LOG_ERR,
126                                 "completion <%s> not in list\n", s);
127                         goto out;
128                 }
129         }
130
131         if (count != ec_completed_tk_count(c)) {
132                 ec_log(EC_LOG_ERR,
133                         "nb_completion (%d) does not match (%d)\n",
134                         count, ec_completed_tk_count(c));
135                 ec_completed_tk_dump(stdout, c);
136                 goto out;
137         }
138
139         ret = 0;
140
141 out:
142         ec_completed_tk_free(c);
143         va_end(ap);
144         return ret;
145 }
146
147 TAILQ_HEAD(debug_alloc_hdr_list, debug_alloc_hdr);
148 static struct debug_alloc_hdr_list debug_alloc_hdr_list =
149         TAILQ_HEAD_INITIALIZER(debug_alloc_hdr_list);
150
151 struct debug_alloc_hdr {
152         TAILQ_ENTRY(debug_alloc_hdr) next;
153         const char *file;
154         unsigned int line;
155         size_t size;
156         unsigned int cookie;
157 };
158
159 struct debug_alloc_ftr {
160         unsigned int cookie;
161 } __attribute__((packed));
162
163 static void *debug_malloc(size_t size, const char *file, unsigned int line)
164 {
165         struct debug_alloc_hdr *hdr;
166         struct debug_alloc_ftr *ftr;
167         size_t new_size = size + sizeof(*hdr) + sizeof(*ftr);
168         void *ret;
169
170         hdr = malloc(new_size);
171         if (hdr == NULL) {
172                 ret = NULL;
173         } else {
174                 hdr->file = file;
175                 hdr->line = line;
176                 hdr->size = size;
177                 hdr->cookie = 0x12345678;
178                 TAILQ_INSERT_TAIL(&debug_alloc_hdr_list, hdr, next);
179                 ret = hdr + 1;
180                 ftr = (struct debug_alloc_ftr *)(
181                         (char *)hdr + size + sizeof(*hdr));
182                 ftr->cookie = 0x12345678;
183         }
184
185         ec_log(EC_LOG_INFO, "%s:%d: info: malloc(%zd) -> %p\n",
186                 file, line, size, ret);
187
188         return ret;
189 }
190
191 static void debug_free(void *ptr, const char *file, unsigned int line)
192 {
193         struct debug_alloc_hdr *hdr, *h;
194         struct debug_alloc_ftr *ftr;
195
196         (void)file;
197         (void)line;
198
199         ec_log(EC_LOG_INFO, "%s:%d: info: free(%p)\n", file, line, ptr);
200
201         if (ptr == NULL)
202                 return;
203
204         hdr = (ptr - sizeof(*hdr));
205         if (hdr->cookie != 0x12345678) {
206                 ec_log(EC_LOG_ERR, "%s:%d: error: free(%p): bad start cookie\n",
207                         file, line, ptr);
208                 abort();
209         }
210
211         ftr = (ptr + hdr->size);
212         if (ftr->cookie != 0x12345678) {
213                 ec_log(EC_LOG_ERR, "%s:%d: error: free(%p): bad end cookie\n",
214                         file, line, ptr);
215                 abort();
216         }
217
218         TAILQ_FOREACH(h, &debug_alloc_hdr_list, next) {
219                 if (h == hdr)
220                         break;
221         }
222
223         if (h == NULL) {
224                 ec_log(EC_LOG_ERR, "%s:%d: error: free(%p): bad ptr\n",
225                         file, line, ptr);
226                 abort();
227         }
228
229         TAILQ_REMOVE(&debug_alloc_hdr_list, hdr, next);
230         free(hdr);
231 }
232
233 void *debug_realloc(void *ptr, size_t size, const char *file, unsigned int line)
234 {
235         struct debug_alloc_hdr *hdr, *h;
236         struct debug_alloc_ftr *ftr;
237         size_t new_size = size + sizeof(*hdr) + sizeof(unsigned int);
238         void *ret;
239
240         if (ptr != NULL) {
241                 hdr =  (ptr - sizeof(*hdr));
242                 if (hdr->cookie != 0x12345678) {
243                         ec_log(EC_LOG_ERR,
244                                 "%s:%d: error: realloc(%p): bad start cookie\n",
245                                 file, line, ptr);
246                         abort();
247                 }
248
249                 ftr = (ptr + hdr->size);
250                 if (ftr->cookie != 0x12345678) {
251                         ec_log(EC_LOG_ERR,
252                                 "%s:%d: error: realloc(%p): bad end cookie\n",
253                                 file, line, ptr);
254                         abort();
255                 }
256
257                 TAILQ_FOREACH(h, &debug_alloc_hdr_list, next) {
258                         if (h == hdr)
259                                 break;
260                 }
261
262                 if (h == NULL) {
263                         ec_log(EC_LOG_ERR, "%s:%d: error: realloc(%p): bad ptr\n",
264                                 file, line, ptr);
265                         abort();
266                 }
267
268                 TAILQ_REMOVE(&debug_alloc_hdr_list, h, next);
269                 hdr = realloc(hdr, new_size);
270                 if (hdr == NULL) {
271                         TAILQ_INSERT_TAIL(&debug_alloc_hdr_list, h, next);
272                         ret = NULL;
273                 } else {
274                         ret = hdr + 1;
275                 }
276         } else {
277                 hdr = realloc(NULL, new_size);
278                 if (hdr == NULL)
279                         ret = NULL;
280                 else
281                         ret = hdr + 1;
282         }
283
284         if (hdr != NULL) {
285                 hdr->file = file;
286                 hdr->line = line;
287                 hdr->size = size;
288                 hdr->cookie = 0x12345678;
289                 TAILQ_INSERT_TAIL(&debug_alloc_hdr_list, hdr, next);
290                 ftr = (struct debug_alloc_ftr *)(
291                         (char *)hdr + size + sizeof(*hdr));
292                 ftr->cookie = 0x12345678;
293         }
294
295         ec_log(EC_LOG_INFO, "%s:%d: info: realloc(%p, %zd) -> %p\n",
296                 file, line, ptr, size, ret);
297
298         return ret;
299 }
300
301 void debug_alloc_dump(void)
302 {
303         struct debug_alloc_hdr *hdr;
304
305         TAILQ_FOREACH(hdr, &debug_alloc_hdr_list, next) {
306                 ec_log(EC_LOG_ERR, "%s:%d: error: memory leak size=%zd ptr=%p\n",
307                         hdr->file, hdr->line, hdr->size, hdr + 1);
308         }
309 }
310
311 int ec_test_all(void)
312 {
313         struct ec_test *test;
314         int ret = 0;
315
316         TAILQ_INIT(&debug_alloc_hdr_list);
317
318         /* register a new malloc to trac memleaks */
319         if (ec_malloc_register(debug_malloc, debug_free, debug_realloc) < 0) {
320                 ec_log(EC_LOG_ERR, "cannot register new malloc\n");
321                 return -1;
322         }
323
324         TAILQ_FOREACH(test, &test_list, next) {
325                 if (test->test() == 0) {
326                         ec_log(EC_LOG_INFO, "== test %-20s success\n",
327                                 test->name);
328                 } else {
329                         ec_log(EC_LOG_INFO, "== test %-20s failed\n",
330                                 test->name);
331                         ret = -1;
332                 }
333         }
334
335         ec_malloc_unregister();
336         debug_alloc_dump();
337
338         return ret;
339 }