b80e0e102025430f4ebaece59540481d44fb5814
[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, const char *conf_file,
116                 const char *buf, size_t len)
117 {
118         test_add_file_data(test, test->ctx->device, conf_file, buf, len);
119 }
120
121 void test_read_conf_file(struct parser_test *test, const char *filename,
122                 const char *conf_file)
123 {
124         struct stat stat;
125         size_t size;
126         char *path;
127         int fd, rc;
128         char *buf;
129
130         path = talloc_asprintf(test, "%s/%s", TEST_CONF_BASE, filename);
131
132         fd = open(path, O_RDONLY);
133         if (fd < 0)
134                 err(EXIT_FAILURE, "Can't open test conf file %s\n", path);
135
136         rc = fstat(fd, &stat);
137         assert(!rc);
138         (void)rc;
139
140         size = stat.st_size;
141         buf = talloc_array(test, char, size + 1);
142
143         rc = read(fd, buf, size);
144         assert(rc == (ssize_t)size);
145
146         *(buf + size) = '\0';
147
148         close(fd);
149         talloc_free(path);
150
151         test_add_file_data(test, test->ctx->device, conf_file, buf, size);
152 }
153
154 void test_add_file_data(struct parser_test *test, struct discover_device *dev,
155                 const char *filename, const void *data, int size)
156 {
157         struct test_file *file;
158
159         file = talloc_zero(test, struct test_file);
160         file->dev = dev;
161         file->name = filename;
162         file->data = talloc_memdup(test, data, size);
163         file->size = size;
164         list_add(&test->files, &file->list);
165 }
166
167 void test_set_event_source(struct parser_test *test)
168 {
169         test->ctx->event = talloc_zero(test->ctx, struct event);
170 }
171
172 void test_set_event_param(struct event *event, const char *name,
173                 const char *value)
174 {
175         event_set_param(event, name, value);
176 }
177
178 int parser_request_file(struct discover_context *ctx,
179                 struct discover_device *dev, const char *filename,
180                 char **buf, int *len)
181 {
182         struct parser_test *test = ctx->test_data;
183         struct test_file *file;
184         char *tmp;
185
186         list_for_each_entry(&test->files, file, list) {
187                 if (file->dev != dev)
188                         continue;
189                 if (strcmp(file->name, filename))
190                         continue;
191
192                 /* the read_file() interface always adds a trailing null
193                  * for string-safety; do the same here */
194                 tmp = talloc_array(test, char, file->size + 1);
195                 memcpy(tmp, file->data, file->size);
196                 tmp[file->size] = '\0';
197                 *buf = tmp;
198                 *len = file->size;
199                 return 0;
200         }
201
202         return -1;
203 }
204
205 int parser_replace_file(struct discover_context *ctx,
206                 struct discover_device *dev, const char *filename,
207                 char *buf, int len)
208 {
209         struct parser_test *test = ctx->test_data;
210         struct test_file *f, *file;
211
212         list_for_each_entry(&test->files, f, list) {
213                 if (f->dev != dev)
214                         continue;
215                 if (strcmp(f->name, filename))
216                         continue;
217
218                 file = f;
219                 break;
220         }
221
222         if (!file) {
223                 file = talloc_zero(test, struct test_file);
224                 file->dev = dev;
225                 file->name = filename;
226                 list_add(&test->files, &file->list);
227         }
228
229         file->data = talloc_memdup(test, buf, len);
230         file->size = len;
231         return 0;
232 }
233
234 int parser_request_url(struct discover_context *ctx, struct pb_url *url,
235                 char **buf, int *len)
236 {
237         struct parser_test *test = ctx->test_data;
238         struct test_file *file;
239         char *tmp;
240
241         list_for_each_entry(&test->files, file, list) {
242                 if (strcmp(file->name, url->file))
243                         continue;
244
245                 /* the read_file() interface always adds a trailing null
246                  * for string-safety; do the same here */
247                 tmp = talloc_array(test, char, file->size + 1);
248                 memcpy(tmp, file->data, file->size);
249                 tmp[file->size] = '\0';
250                 *buf = tmp;
251                 *len = file->size;
252                 return 0;
253         }
254
255         return -1;
256 }
257
258 int test_run_parser(struct parser_test *test, const char *parser_name)
259 {
260         struct p_item* i;
261
262         list_for_each_entry(&parsers, i, list) {
263                 if (strcmp(i->parser->name, parser_name))
264                         continue;
265                 test->ctx->parser = i->parser;
266                 return i->parser->parse(test->ctx);
267         }
268
269         errx(EXIT_FAILURE, "%s: parser '%s' not found", __func__, parser_name);
270 }
271
272 bool resource_resolve(struct device_handler *handler, struct parser *parser,
273                 struct resource *resource)
274 {
275         if (!resource)
276                 return true;
277         if (resource->resolved)
278                 return true;
279
280         assert(parser);
281         assert(parser->resolve_resource);
282
283         return parser->resolve_resource(handler, resource);
284 }
285
286 void boot_option_resolve(struct device_handler *handler,
287                 struct discover_boot_option *opt)
288 {
289         resource_resolve(handler, opt->source, opt->boot_image);
290         resource_resolve(handler, opt->source, opt->initrd);
291         resource_resolve(handler, opt->source, opt->icon);
292 }
293
294 void test_hotplug_device(struct parser_test *test, struct discover_device *dev)
295 {
296         struct discover_boot_option *opt;
297
298         device_handler_add_device(test->handler, dev);
299
300         list_for_each_entry(&test->ctx->boot_options, opt, list)
301                 boot_option_resolve(test->handler, opt);
302 }
303
304 struct discover_boot_option *get_boot_option(struct discover_context *ctx,
305                 int idx)
306 {
307         struct discover_boot_option *opt;
308         int i = 0;
309
310         list_for_each_entry(&ctx->boot_options, opt, list) {
311                 if (i++ == idx)
312                         return opt;
313         }
314
315         assert(0);
316
317         return NULL;
318 }
319
320 void __check_boot_option_count(struct discover_context *ctx, int count,
321                 const char *file, int line)
322 {
323         struct discover_boot_option *opt;
324         int defaults = 0, i = 0;
325
326         list_for_each_entry(&ctx->boot_options, opt, list) {
327                 i++;
328                 if (opt->option->is_default)
329                         defaults++;
330         }
331
332         if (defaults > 1) {
333                 fprintf(stderr, "%s:%d: parser returned multiple default "
334                                 "options\n", file, line);
335                 exit(EXIT_FAILURE);
336         }
337
338         if (i == count)
339                 return;
340
341         fprintf(stderr, "%s:%d: boot option count check failed\n", file, line);
342         fprintf(stderr, "expected %d options, got %d:\n", count, i);
343
344         i = 1;
345         list_for_each_entry(&ctx->boot_options, opt, list)
346                 fprintf(stderr, "  %2d: %s [%s]\n", i++, opt->option->name,
347                                 opt->option->id);
348
349         exit(EXIT_FAILURE);
350 }
351
352 void __check_args(struct discover_boot_option *opt, const char *args,
353                 const char *file, int line)
354 {
355         int rc;
356
357         if (!opt->option->boot_args && !args)
358                 return;
359
360         if (!opt->option->boot_args) {
361                 fprintf(stderr, "%s:%d: arg check failed\n", file, line);
362                 fprintf(stderr, "  no arguments parsed\n");
363                 fprintf(stderr, "  expected '%s'\n", args);
364                 exit(EXIT_FAILURE);
365         }
366
367         rc = strcmp(opt->option->boot_args, args);
368         if (rc) {
369                 fprintf(stderr, "%s:%d: arg check failed\n", file, line);
370                 fprintf(stderr, "  got      '%s'\n", opt->option->boot_args);
371                 fprintf(stderr, "  expected '%s'\n", args);
372                 exit(EXIT_FAILURE);
373         }
374 }
375
376 void __check_name(struct discover_boot_option *opt, const char *name,
377                 const char *file, int line)
378 {
379         int rc;
380
381         rc = strcmp(opt->option->name, name);
382         if (rc) {
383                 fprintf(stderr, "%s:%d: name check failed\n", file, line);
384                 fprintf(stderr, "  got      '%s'\n", opt->option->name);
385                 fprintf(stderr, "  expected '%s'\n", name);
386                 exit(EXIT_FAILURE);
387         }
388 }
389
390 void __check_is_default(struct discover_boot_option *opt,
391                 const char *file, int line)
392 {
393         if (opt->option->is_default)
394                 return;
395
396         fprintf(stderr, "%s:%d: default check failed\n", file, line);
397         exit(EXIT_FAILURE);
398 }
399
400 void __check_resolved_local_resource(struct resource *res,
401                 struct discover_device *dev, const char *local_path,
402                 const char *file, int line)
403 {
404         const char *exp_url, *got_url;
405
406         if (!res)
407                 errx(EXIT_FAILURE, "%s:%d: No resource", file, line);
408
409         if (!res->resolved)
410                 errx(EXIT_FAILURE, "%s:%d: Resource is not resolved",
411                                 file, line);
412
413         exp_url = talloc_asprintf(res, "file://%s%s",
414                         dev->mount_path, local_path);
415         got_url = pb_url_to_string(res->url);
416
417         if (strcmp(got_url, exp_url)) {
418                 fprintf(stderr, "%s:%d: Resource mismatch\n", file, line);
419                 fprintf(stderr, "  got      '%s'\n", got_url);
420                 fprintf(stderr, "  expected '%s'\n", exp_url);
421                 exit(EXIT_FAILURE);
422         }
423 }
424
425 void __check_resolved_url_resource(struct resource *res,
426                 const char *url, const char *file, int line)
427 {
428         char *res_url;
429
430         if (!res)
431                 errx(EXIT_FAILURE, "%s:%d: No resource", file, line);
432
433         if (!res->resolved)
434                 errx(EXIT_FAILURE, "%s:%d: Resource is not resolved",
435                                 file, line);
436
437         res_url = pb_url_to_string(res->url);
438         if (strcmp(url, res_url)) {
439                 fprintf(stderr, "%s:%d: Resource mismatch\n", file, line);
440                 fprintf(stderr, "  got      '%s'\n", res_url);
441                 fprintf(stderr, "  expected '%s'\n", url);
442                 exit(EXIT_FAILURE);
443         }
444 }
445 void __check_unresolved_resource(struct resource *res,
446                 const char *file, int line)
447 {
448         if (!res)
449                 errx(EXIT_FAILURE, "%s:%d: No resource", file, line);
450
451         if (res->resolved)
452                 errx(EXIT_FAILURE, "%s:%d: Resource is resolved", file, line);
453 }
454
455 void __check_not_present_resource(struct resource *res,
456                 const char *file, int line)
457 {
458         if (res)
459                 errx(EXIT_FAILURE, "%s:%d: Resource present", file, line);
460 }
461
462 static void dump_file_data(const void *buf, int len)
463 {
464         int i, j, hex_len = strlen("00 ");
465         const int row_len = 16;
466
467         for (i = 0; i < len; i += row_len) {
468                 char hbuf[row_len * hex_len + 1];
469                 char cbuf[row_len + strlen("|") + 1];
470
471                 for (j = 0; (j < row_len) && ((i+j) < len); j++) {
472                         char c = ((const char *)buf)[i + j];
473
474                         snprintf(hbuf + j * hex_len, hex_len + 1, "%02x ", c);
475
476                         if (!isprint(c))
477                                 c = '.';
478
479                         snprintf(cbuf + j, hex_len + 1, "%c", c);
480                 }
481
482                 strcat(cbuf, "|");
483
484                 fprintf(stderr, "%08x  %*s |%s\n", i,
485                                 0 - (int)sizeof(hbuf) + 1, hbuf, cbuf);
486         }
487 }
488
489 void __check_file_contents(struct parser_test *test,
490                 struct discover_device *dev, const char *filename,
491                 const char *buf, int len,
492                 const char *srcfile, int srcline)
493 {
494         struct test_file *f, *file = NULL;
495
496         list_for_each_entry(&test->files, f, list) {
497                 if (f->dev != dev)
498                         continue;
499                 if (strcmp(f->name, filename))
500                         continue;
501
502                 file = f;
503                 break;
504         }
505
506         if (!file)
507                 errx(EXIT_FAILURE, "%s:%d: File '%s' not found",
508                                 srcfile, srcline, filename);
509
510         if (file->size != len || memcmp(file->data, buf, len)) {
511                 fprintf(stderr, "%s:%d: File '%s' data/size mismatch\n",
512                                 srcfile, srcline, filename);
513                 fprintf(stderr, "Expected:\n");
514                 dump_file_data(buf, len);
515                 fprintf(stderr, "Got:\n");
516                 dump_file_data(file->data, file->size);
517                 exit(EXIT_FAILURE);
518         }
519 }