67401abc9e0d8fa5d95cfb2ed9f1b27ba2fce35e
[petitboot] / test / parser / utils.c
1
2 #include <assert.h>
3 #include <err.h>
4 #include <fcntl.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <ctype.h>
8 #include <sys/stat.h>
9 #include <sys/types.h>
10
11 #include <talloc/talloc.h>
12 #include <types/types.h>
13 #include <url/url.h>
14
15 #include "device-handler.h"
16 #include "parser.h"
17 #include "resource.h"
18 #include "event.h"
19
20 #include "parser-test.h"
21
22 struct p_item {
23         struct list_item list;
24         struct parser *parser;
25 };
26
27 struct test_file {
28         struct discover_device  *dev;
29         const char              *name;
30         void                    *data;
31         int                     size;
32         struct list_item        list;
33 };
34
35 STATIC_LIST(parsers);
36
37 void __register_parser(struct parser *parser)
38 {
39         struct p_item* i = talloc(NULL, struct p_item);
40
41         i->parser = parser;
42         list_add(&parsers, &i->list);
43 }
44
45 static void __attribute__((destructor)) __cleanup_parsers(void)
46 {
47         struct p_item *item, *tmp;
48
49         list_for_each_entry_safe(&parsers, item, tmp, list)
50                 talloc_free(item);
51 }
52
53 static struct discover_device *test_create_device_simple(
54                 struct parser_test *test)
55 {
56         static int dev_idx;
57         char name[10];
58
59         sprintf(name, "__test%d", dev_idx++);
60
61         return test_create_device(test, name);
62 }
63
64 struct discover_device *test_create_device(struct parser_test *test,
65                 const char *name)
66 {
67         struct discover_device *dev;
68
69         dev = discover_device_create(test->handler, name);
70
71         dev->device->id = talloc_strdup(dev, name);
72         dev->device_path = talloc_asprintf(dev, "/dev/%s", name);
73         dev->mount_path = talloc_asprintf(dev, "/test/mount/%s", name);
74         dev->mounted = true;
75
76         return dev;
77 }
78
79 static struct discover_context *test_create_context(struct parser_test *test)
80 {
81         struct discover_context *ctx;
82
83         ctx = talloc_zero(test, struct discover_context);
84         assert(ctx);
85
86         list_init(&ctx->boot_options);
87         ctx->device = test_create_device_simple(test);
88         ctx->test_data = test;
89         device_handler_add_device(test->handler, ctx->device);
90
91         return ctx;
92 }
93
94 extern struct config *test_config_init(struct parser_test *test);
95
96 struct parser_test *test_init(void)
97 {
98         struct parser_test *test;
99
100         test = talloc_zero(NULL, struct parser_test);
101         test->config = test_config_init(test);
102         test->handler = device_handler_init(NULL, NULL, 0);
103         test->ctx = test_create_context(test);
104         list_init(&test->files);
105
106         return test;
107 }
108
109 void test_fini(struct parser_test *test)
110 {
111         device_handler_destroy(test->handler);
112         talloc_free(test);
113 }
114
115 void __test_read_conf_data(struct parser_test *test,
116                 struct discover_device *dev, const char *conf_file,
117                 const char *buf, size_t len)
118 {
119         test_add_file_data(test, dev, conf_file, buf, len);
120 }
121
122 void test_read_conf_file(struct parser_test *test, const char *filename,
123                 const char *conf_file)
124 {
125         struct stat stat;
126         size_t size;
127         char *path;
128         int fd, rc;
129         char *buf;
130
131         path = talloc_asprintf(test, "%s/%s", TEST_CONF_BASE, filename);
132
133         fd = open(path, O_RDONLY);
134         if (fd < 0)
135                 err(EXIT_FAILURE, "Can't open test conf file %s\n", path);
136
137         rc = fstat(fd, &stat);
138         assert(!rc);
139         (void)rc;
140
141         size = stat.st_size;
142         buf = talloc_array(test, char, size + 1);
143
144         rc = read(fd, buf, size);
145         assert(rc == (ssize_t)size);
146
147         *(buf + size) = '\0';
148
149         close(fd);
150         talloc_free(path);
151
152         test_add_file_data(test, test->ctx->device, conf_file, buf, size);
153 }
154
155 void test_add_file_data(struct parser_test *test, struct discover_device *dev,
156                 const char *filename, const void *data, int size)
157 {
158         struct test_file *file;
159
160         file = talloc_zero(test, struct test_file);
161         file->dev = dev;
162         file->name = filename;
163         file->data = talloc_memdup(test, data, size);
164         file->size = size;
165         list_add(&test->files, &file->list);
166 }
167
168 void test_set_event_source(struct parser_test *test)
169 {
170         test->ctx->event = talloc_zero(test->ctx, struct event);
171 }
172
173 void test_set_event_param(struct event *event, const char *name,
174                 const char *value)
175 {
176         event_set_param(event, name, value);
177 }
178
179 int parser_request_file(struct discover_context *ctx,
180                 struct discover_device *dev, const char *filename,
181                 char **buf, int *len)
182 {
183         struct parser_test *test = ctx->test_data;
184         struct test_file *file;
185         char *tmp;
186
187         list_for_each_entry(&test->files, file, list) {
188                 if (file->dev != dev)
189                         continue;
190                 if (strcmp(file->name, filename))
191                         continue;
192
193                 /* the read_file() interface always adds a trailing null
194                  * for string-safety; do the same here */
195                 tmp = talloc_array(test, char, file->size + 1);
196                 memcpy(tmp, file->data, file->size);
197                 tmp[file->size] = '\0';
198                 *buf = tmp;
199                 *len = file->size;
200                 return 0;
201         }
202
203         return -1;
204 }
205
206 int parser_replace_file(struct discover_context *ctx,
207                 struct discover_device *dev, const char *filename,
208                 char *buf, int len)
209 {
210         struct parser_test *test = ctx->test_data;
211         struct test_file *f, *file;
212
213         list_for_each_entry(&test->files, f, list) {
214                 if (f->dev != dev)
215                         continue;
216                 if (strcmp(f->name, filename))
217                         continue;
218
219                 file = f;
220                 break;
221         }
222
223         if (!file) {
224                 file = talloc_zero(test, struct test_file);
225                 file->dev = dev;
226                 file->name = filename;
227                 list_add(&test->files, &file->list);
228         }
229
230         file->data = talloc_memdup(test, buf, len);
231         file->size = len;
232         return 0;
233 }
234
235 int parser_request_url(struct discover_context *ctx, struct pb_url *url,
236                 char **buf, int *len)
237 {
238         struct parser_test *test = ctx->test_data;
239         struct test_file *file;
240         char *tmp;
241
242         list_for_each_entry(&test->files, file, list) {
243                 if (file->dev)
244                         continue;
245
246                 if (strcmp(file->name, url->full))
247                         continue;
248
249                 /* the read_file() interface always adds a trailing null
250                  * for string-safety; do the same here */
251                 tmp = talloc_array(test, char, file->size + 1);
252                 memcpy(tmp, file->data, file->size);
253                 tmp[file->size] = '\0';
254                 *buf = tmp;
255                 *len = file->size;
256                 return 0;
257         }
258
259         return -1;
260 }
261
262 int test_run_parser(struct parser_test *test, const char *parser_name)
263 {
264         struct p_item* i;
265
266         list_for_each_entry(&parsers, i, list) {
267                 if (strcmp(i->parser->name, parser_name))
268                         continue;
269                 test->ctx->parser = i->parser;
270                 return i->parser->parse(test->ctx);
271         }
272
273         errx(EXIT_FAILURE, "%s: parser '%s' not found", __func__, parser_name);
274 }
275
276 bool resource_resolve(struct device_handler *handler, struct parser *parser,
277                 struct resource *resource)
278 {
279         if (!resource)
280                 return true;
281         if (resource->resolved)
282                 return true;
283
284         assert(parser);
285         assert(parser->resolve_resource);
286
287         return parser->resolve_resource(handler, resource);
288 }
289
290 void boot_option_resolve(struct device_handler *handler,
291                 struct discover_boot_option *opt)
292 {
293         resource_resolve(handler, opt->source, opt->boot_image);
294         resource_resolve(handler, opt->source, opt->initrd);
295         resource_resolve(handler, opt->source, opt->icon);
296 }
297
298 void test_hotplug_device(struct parser_test *test, struct discover_device *dev)
299 {
300         struct discover_boot_option *opt;
301
302         device_handler_add_device(test->handler, dev);
303
304         list_for_each_entry(&test->ctx->boot_options, opt, list)
305                 boot_option_resolve(test->handler, opt);
306 }
307
308 void test_remove_device(struct parser_test *test, struct discover_device *dev)
309 {
310         struct discover_boot_option *opt, *tmp;
311
312         if (dev == test->ctx->device) {
313                 list_for_each_entry_safe(&test->ctx->boot_options,
314                                 opt, tmp, list) {
315                         list_remove(&opt->list);
316                         talloc_free(opt);
317                 }
318         }
319
320         device_handler_remove(test->handler, dev);
321 }
322
323 struct discover_boot_option *get_boot_option(struct discover_context *ctx,
324                 int idx)
325 {
326         struct discover_boot_option *opt;
327         int i = 0;
328
329         list_for_each_entry(&ctx->boot_options, opt, list) {
330                 if (i++ == idx)
331                         return opt;
332         }
333
334         assert(0);
335
336         return NULL;
337 }
338
339 void __check_boot_option_count(struct discover_context *ctx, int count,
340                 const char *file, int line)
341 {
342         struct discover_boot_option *opt;
343         int defaults = 0, i = 0;
344
345         list_for_each_entry(&ctx->boot_options, opt, list) {
346                 i++;
347                 if (opt->option->is_default)
348                         defaults++;
349         }
350
351         if (defaults > 1) {
352                 fprintf(stderr, "%s:%d: parser returned multiple default "
353                                 "options\n", file, line);
354                 exit(EXIT_FAILURE);
355         }
356
357         if (i == count)
358                 return;
359
360         fprintf(stderr, "%s:%d: boot option count check failed\n", file, line);
361         fprintf(stderr, "expected %d options, got %d:\n", count, i);
362
363         i = 1;
364         list_for_each_entry(&ctx->boot_options, opt, list)
365                 fprintf(stderr, "  %2d: %s [%s]\n", i++, opt->option->name,
366                                 opt->option->id);
367
368         exit(EXIT_FAILURE);
369 }
370
371 void __check_args(struct discover_boot_option *opt, const char *args,
372                 const char *file, int line)
373 {
374         int rc;
375
376         if (!opt->option->boot_args && !args)
377                 return;
378
379         if (!opt->option->boot_args) {
380                 fprintf(stderr, "%s:%d: arg check failed\n", file, line);
381                 fprintf(stderr, "  no arguments parsed\n");
382                 fprintf(stderr, "  expected '%s'\n", args);
383                 exit(EXIT_FAILURE);
384         }
385
386         rc = strcmp(opt->option->boot_args, args);
387         if (rc) {
388                 fprintf(stderr, "%s:%d: arg check failed\n", file, line);
389                 fprintf(stderr, "  got      '%s'\n", opt->option->boot_args);
390                 fprintf(stderr, "  expected '%s'\n", args);
391                 exit(EXIT_FAILURE);
392         }
393 }
394
395 void __check_name(struct discover_boot_option *opt, const char *name,
396                 const char *file, int line)
397 {
398         int rc;
399
400         rc = strcmp(opt->option->name, name);
401         if (rc) {
402                 fprintf(stderr, "%s:%d: name check failed\n", file, line);
403                 fprintf(stderr, "  got      '%s'\n", opt->option->name);
404                 fprintf(stderr, "  expected '%s'\n", name);
405                 exit(EXIT_FAILURE);
406         }
407 }
408
409 void __check_is_default(struct discover_boot_option *opt,
410                 const char *file, int line)
411 {
412         if (opt->option->is_default)
413                 return;
414
415         fprintf(stderr, "%s:%d: default check failed\n", file, line);
416         exit(EXIT_FAILURE);
417 }
418
419 void __check_resolved_local_resource(struct resource *res,
420                 struct discover_device *dev, const char *local_path,
421                 const char *file, int line)
422 {
423         const char *exp_url, *got_url;
424
425         if (!res)
426                 errx(EXIT_FAILURE, "%s:%d: No resource", file, line);
427
428         if (!res->resolved)
429                 errx(EXIT_FAILURE, "%s:%d: Resource is not resolved",
430                                 file, line);
431
432         exp_url = talloc_asprintf(res, "file://%s%s",
433                         dev->mount_path, local_path);
434         got_url = pb_url_to_string(res->url);
435
436         if (strcmp(got_url, exp_url)) {
437                 fprintf(stderr, "%s:%d: Resource mismatch\n", file, line);
438                 fprintf(stderr, "  got      '%s'\n", got_url);
439                 fprintf(stderr, "  expected '%s'\n", exp_url);
440                 exit(EXIT_FAILURE);
441         }
442 }
443
444 void __check_resolved_url_resource(struct resource *res,
445                 const char *url, const char *file, int line)
446 {
447         char *res_url;
448
449         if (!res)
450                 errx(EXIT_FAILURE, "%s:%d: No resource", file, line);
451
452         if (!res->resolved)
453                 errx(EXIT_FAILURE, "%s:%d: Resource is not resolved",
454                                 file, line);
455
456         res_url = pb_url_to_string(res->url);
457         if (strcmp(url, res_url)) {
458                 fprintf(stderr, "%s:%d: Resource mismatch\n", file, line);
459                 fprintf(stderr, "  got      '%s'\n", res_url);
460                 fprintf(stderr, "  expected '%s'\n", url);
461                 exit(EXIT_FAILURE);
462         }
463 }
464 void __check_unresolved_resource(struct resource *res,
465                 const char *file, int line)
466 {
467         if (!res)
468                 errx(EXIT_FAILURE, "%s:%d: No resource", file, line);
469
470         if (res->resolved)
471                 errx(EXIT_FAILURE, "%s:%d: Resource is resolved", file, line);
472 }
473
474 void __check_not_present_resource(struct resource *res,
475                 const char *file, int line)
476 {
477         if (res)
478                 errx(EXIT_FAILURE, "%s:%d: Resource present", file, line);
479 }
480
481 static void dump_file_data(const void *buf, int len)
482 {
483         int i, j, hex_len = strlen("00 ");
484         const int row_len = 16;
485
486         for (i = 0; i < len; i += row_len) {
487                 char hbuf[row_len * hex_len + 1];
488                 char cbuf[row_len + strlen("|") + 1];
489
490                 for (j = 0; (j < row_len) && ((i+j) < len); j++) {
491                         char c = ((const char *)buf)[i + j];
492
493                         snprintf(hbuf + j * hex_len, hex_len + 1, "%02x ", c);
494
495                         if (!isprint(c))
496                                 c = '.';
497
498                         snprintf(cbuf + j, hex_len + 1, "%c", c);
499                 }
500
501                 strcat(cbuf, "|");
502
503                 fprintf(stderr, "%08x  %*s |%s\n", i,
504                                 0 - (int)sizeof(hbuf) + 1, hbuf, cbuf);
505         }
506 }
507
508 void __check_file_contents(struct parser_test *test,
509                 struct discover_device *dev, const char *filename,
510                 const char *buf, int len,
511                 const char *srcfile, int srcline)
512 {
513         struct test_file *f, *file = NULL;
514
515         list_for_each_entry(&test->files, f, list) {
516                 if (f->dev != dev)
517                         continue;
518                 if (strcmp(f->name, filename))
519                         continue;
520
521                 file = f;
522                 break;
523         }
524
525         if (!file)
526                 errx(EXIT_FAILURE, "%s:%d: File '%s' not found",
527                                 srcfile, srcline, filename);
528
529         if (file->size != len || memcmp(file->data, buf, len)) {
530                 fprintf(stderr, "%s:%d: File '%s' data/size mismatch\n",
531                                 srcfile, srcline, filename);
532                 fprintf(stderr, "Expected:\n");
533                 dump_file_data(buf, len);
534                 fprintf(stderr, "Got:\n");
535                 dump_file_data(file->data, file->size);
536                 exit(EXIT_FAILURE);
537         }
538 }