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