33efda80536f8354ebc4e10e6b64fe1299755831
[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 <sys/stat.h>
8 #include <sys/types.h>
9
10 #include <talloc/talloc.h>
11 #include <types/types.h>
12 #include <url/url.h>
13
14 #include "device-handler.h"
15 #include "parser.h"
16 #include "resource.h"
17
18 #include "parser-test.h"
19
20 struct p_item {
21         struct list_item list;
22         struct parser *parser;
23 };
24
25 struct test_file {
26         struct discover_device  *dev;
27         const char              *name;
28         void                    *data;
29         int                     size;
30         struct list_item        list;
31 };
32
33 STATIC_LIST(parsers);
34
35 void __register_parser(struct parser *parser)
36 {
37         struct p_item* i = talloc(NULL, struct p_item);
38
39         i->parser = parser;
40         list_add(&parsers, &i->list);
41 }
42
43 static void __attribute__((destructor)) __cleanup_parsers(void)
44 {
45         struct p_item *item, *tmp;
46
47         list_for_each_entry_safe(&parsers, item, tmp, list)
48                 talloc_free(item);
49 }
50
51 static struct discover_device *test_create_device_simple(
52                 struct parser_test *test)
53 {
54         static int dev_idx;
55         char name[10];
56
57         sprintf(name, "__test%d", dev_idx++);
58
59         return test_create_device(test, name);
60 }
61
62 struct discover_device *test_create_device(struct parser_test *test,
63                 const char *name)
64 {
65         struct discover_device *dev;
66
67         dev = discover_device_create(test->handler, name);
68
69         dev->device->id = talloc_strdup(dev, name);
70         dev->device_path = talloc_asprintf(dev, "/dev/%s", name);
71         dev->mount_path = talloc_asprintf(dev, "/test/mount/%s", name);
72         dev->mounted = true;
73
74         return dev;
75 }
76
77 static struct discover_context *test_create_context(struct parser_test *test)
78 {
79         struct discover_context *ctx;
80
81         ctx = talloc_zero(test, struct discover_context);
82         assert(ctx);
83
84         list_init(&ctx->boot_options);
85         ctx->device = test_create_device_simple(test);
86         ctx->test_data = test;
87         device_handler_add_device(test->handler, ctx->device);
88
89         return ctx;
90 }
91
92 extern struct config *test_config_init(struct parser_test *test);
93
94 struct parser_test *test_init(void)
95 {
96         struct parser_test *test;
97
98         test = talloc_zero(NULL, struct parser_test);
99         test->config = test_config_init(test);
100         test->handler = device_handler_init(NULL, NULL, 0);
101         test->ctx = test_create_context(test);
102         list_init(&test->files);
103
104         return test;
105 }
106
107 void test_fini(struct parser_test *test)
108 {
109         device_handler_destroy(test->handler);
110         talloc_free(test);
111 }
112
113 void __test_read_conf_data(struct parser_test *test,
114                 const char *buf, size_t len)
115 {
116         test->conf.size = len;
117         test->conf.buf = talloc_memdup(test, buf, len);
118 }
119
120 void test_read_conf_file(struct parser_test *test, const char *filename)
121 {
122         struct stat stat;
123         char *path;
124         int fd, rc;
125
126         path = talloc_asprintf(test, "%s/%s", TEST_CONF_BASE, filename);
127
128         fd = open(path, O_RDONLY);
129         if (fd < 0)
130                 err(EXIT_FAILURE, "Can't open test conf file %s\n", path);
131
132         rc = fstat(fd, &stat);
133         assert(!rc);
134         (void)rc;
135
136         test->conf.size = stat.st_size;
137         test->conf.buf = talloc_array(test, char, test->conf.size + 1);
138
139         rc = read(fd, test->conf.buf, test->conf.size);
140         assert(rc == (ssize_t)test->conf.size);
141
142         *(char *)(test->conf.buf + test->conf.size) = '\0';
143
144         close(fd);
145         talloc_free(path);
146 }
147
148 void test_set_conf_source(struct parser_test *test, const char *url)
149 {
150         test->ctx->conf_url = pb_url_parse(test, url);
151         assert(test->ctx->conf_url);
152 }
153
154 void test_add_file_data(struct parser_test *test, struct discover_device *dev,
155                 const char *filename, 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 = data;
163         file->size = size;
164         list_add(&test->files, &file->list);
165 }
166
167
168 int parser_request_file(struct discover_context *ctx,
169                 struct discover_device *dev, const char *filename,
170                 char **buf, int *len)
171 {
172         struct parser_test *test = ctx->test_data;
173         struct test_file *file;
174
175         list_for_each_entry(&test->files, file, list) {
176                 if (file->dev != dev)
177                         continue;
178                 if (strcmp(file->name, filename))
179                         continue;
180
181                 *buf = talloc_memdup(test, file->data, file->size);
182                 *len = file->size;
183                 return 0;
184         }
185
186         return -1;
187 }
188
189 int parser_replace_file(struct discover_context *ctx,
190                 struct discover_device *dev, const char *filename,
191                 char *buf, int len)
192 {
193         struct parser_test *test = ctx->test_data;
194         struct test_file *f, *file;
195
196         list_for_each_entry(&test->files, f, list) {
197                 if (f->dev != dev)
198                         continue;
199                 if (strcmp(f->name, filename))
200                         continue;
201
202                 file = f;
203                 break;
204         }
205
206         if (!file) {
207                 file = talloc_zero(test, struct test_file);
208                 file->dev = dev;
209                 file->name = filename;
210                 list_add(&test->files, &file->list);
211         } else {
212                 talloc_free(file->data);
213         }
214
215         file->data = talloc_memdup(test, buf, len);
216         file->size = len;
217         return 0;
218 }
219 int test_run_parser(struct parser_test *test, const char *parser_name)
220 {
221         struct p_item* i;
222
223         list_for_each_entry(&parsers, i, list) {
224                 if (strcmp(i->parser->name, parser_name))
225                         continue;
226                 test->ctx->parser = i->parser;
227                 return i->parser->parse(test->ctx, test->conf.buf, test->conf.size);
228         }
229
230         errx(EXIT_FAILURE, "%s: parser '%s' not found", __func__, parser_name);
231 }
232
233 bool resource_resolve(struct device_handler *handler, struct parser *parser,
234                 struct resource *resource)
235 {
236         if (!resource)
237                 return true;
238         if (resource->resolved)
239                 return true;
240
241         assert(parser);
242         assert(parser->resolve_resource);
243
244         return parser->resolve_resource(handler, resource);
245 }
246
247 void boot_option_resolve(struct device_handler *handler,
248                 struct discover_boot_option *opt)
249 {
250         resource_resolve(handler, opt->source, opt->boot_image);
251         resource_resolve(handler, opt->source, opt->initrd);
252         resource_resolve(handler, opt->source, opt->icon);
253 }
254
255 void test_hotplug_device(struct parser_test *test, struct discover_device *dev)
256 {
257         struct discover_boot_option *opt;
258
259         device_handler_add_device(test->handler, dev);
260
261         list_for_each_entry(&test->ctx->boot_options, opt, list)
262                 boot_option_resolve(test->handler, opt);
263 }
264
265 struct discover_boot_option *get_boot_option(struct discover_context *ctx,
266                 int idx)
267 {
268         struct discover_boot_option *opt;
269         int i = 0;
270
271         list_for_each_entry(&ctx->boot_options, opt, list) {
272                 if (i++ == idx)
273                         return opt;
274         }
275
276         assert(0);
277
278         return NULL;
279 }
280
281 void __check_boot_option_count(struct discover_context *ctx, int count,
282                 const char *file, int line)
283 {
284         struct discover_boot_option *opt;
285         int defaults = 0, i = 0;
286
287         list_for_each_entry(&ctx->boot_options, opt, list) {
288                 i++;
289                 if (opt->option->is_default)
290                         defaults++;
291         }
292
293         if (defaults > 1) {
294                 fprintf(stderr, "%s:%d: parser returned multiple default "
295                                 "options\n", file, line);
296                 exit(EXIT_FAILURE);
297         }
298
299         if (i == count)
300                 return;
301
302         fprintf(stderr, "%s:%d: boot option count check failed\n", file, line);
303         fprintf(stderr, "expected %d options, got %d:\n", count, i);
304
305         i = 1;
306         list_for_each_entry(&ctx->boot_options, opt, list)
307                 fprintf(stderr, "  %2d: %s [%s]\n", i++, opt->option->name,
308                                 opt->option->id);
309
310         exit(EXIT_FAILURE);
311 }
312
313 void __check_args(struct discover_boot_option *opt, const char *args,
314                 const char *file, int line)
315 {
316         int rc;
317
318         if (!opt->option->boot_args && !args)
319                 return;
320
321         if (!opt->option->boot_args) {
322                 fprintf(stderr, "%s:%d: arg check failed\n", file, line);
323                 fprintf(stderr, "  no arguments parsed\n");
324                 fprintf(stderr, "  expected '%s'\n", args);
325                 exit(EXIT_FAILURE);
326         }
327
328         rc = strcmp(opt->option->boot_args, args);
329         if (rc) {
330                 fprintf(stderr, "%s:%d: arg check failed\n", file, line);
331                 fprintf(stderr, "  got      '%s'\n", opt->option->boot_args);
332                 fprintf(stderr, "  expected '%s'\n", args);
333                 exit(EXIT_FAILURE);
334         }
335 }
336
337 void __check_name(struct discover_boot_option *opt, const char *name,
338                 const char *file, int line)
339 {
340         int rc;
341
342         rc = strcmp(opt->option->name, name);
343         if (rc) {
344                 fprintf(stderr, "%s:%d: name check failed\n", file, line);
345                 fprintf(stderr, "  got      '%s'\n", opt->option->name);
346                 fprintf(stderr, "  expected '%s'\n", name);
347                 exit(EXIT_FAILURE);
348         }
349 }
350
351 void __check_is_default(struct discover_boot_option *opt,
352                 const char *file, int line)
353 {
354         if (opt->option->is_default)
355                 return;
356
357         fprintf(stderr, "%s:%d: default check failed\n", file, line);
358         exit(EXIT_FAILURE);
359 }
360
361 void __check_resolved_local_resource(struct resource *res,
362                 struct discover_device *dev, const char *local_path,
363                 const char *file, int line)
364 {
365         const char *exp_url, *got_url;
366
367         if (!res)
368                 errx(EXIT_FAILURE, "%s:%d: No resource", file, line);
369
370         if (!res->resolved)
371                 errx(EXIT_FAILURE, "%s:%d: Resource is not resolved",
372                                 file, line);
373
374         exp_url = talloc_asprintf(res, "file://%s%s",
375                         dev->mount_path, local_path);
376         got_url = pb_url_to_string(res->url);
377
378         if (strcmp(got_url, exp_url)) {
379                 fprintf(stderr, "%s:%d: Resource mismatch\n", file, line);
380                 fprintf(stderr, "  got      '%s'\n", got_url);
381                 fprintf(stderr, "  expected '%s'\n", exp_url);
382                 exit(EXIT_FAILURE);
383         }
384 }
385
386 void __check_resolved_url_resource(struct resource *res,
387                 const char *url, const char *file, int line)
388 {
389         char *res_url;
390
391         if (!res)
392                 errx(EXIT_FAILURE, "%s:%d: No resource", file, line);
393
394         if (!res->resolved)
395                 errx(EXIT_FAILURE, "%s:%d: Resource is not resolved",
396                                 file, line);
397
398         res_url = pb_url_to_string(res->url);
399         if (strcmp(url, res_url)) {
400                 fprintf(stderr, "%s:%d: Resource mismatch\n", file, line);
401                 fprintf(stderr, "  got      '%s'\n", res_url);
402                 fprintf(stderr, "  expected '%s'\n", url);
403                 exit(EXIT_FAILURE);
404         }
405 }
406 void __check_unresolved_resource(struct resource *res,
407                 const char *file, int line)
408 {
409         if (!res)
410                 errx(EXIT_FAILURE, "%s:%d: No resource", file, line);
411
412         if (res->resolved)
413                 errx(EXIT_FAILURE, "%s:%d: Resource is resolved", file, line);
414 }
415
416 void __check_not_present_resource(struct resource *res,
417                 const char *file, int line)
418 {
419         if (res)
420                 errx(EXIT_FAILURE, "%s:%d: Resource present", file, line);
421 }