d1ced73a6365fff837902fd027811a1d0db500e4
[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 test_run_parser(struct parser_test *test, const char *parser_name)
190 {
191         struct p_item* i;
192
193         list_for_each_entry(&parsers, i, list) {
194                 if (strcmp(i->parser->name, parser_name))
195                         continue;
196                 test->ctx->parser = i->parser;
197                 return i->parser->parse(test->ctx, test->conf.buf, test->conf.size);
198         }
199
200         errx(EXIT_FAILURE, "%s: parser '%s' not found", __func__, parser_name);
201 }
202
203 bool resource_resolve(struct device_handler *handler, struct parser *parser,
204                 struct resource *resource)
205 {
206         if (!resource)
207                 return true;
208         if (resource->resolved)
209                 return true;
210
211         assert(parser);
212         assert(parser->resolve_resource);
213
214         return parser->resolve_resource(handler, resource);
215 }
216
217 void boot_option_resolve(struct device_handler *handler,
218                 struct discover_boot_option *opt)
219 {
220         resource_resolve(handler, opt->source, opt->boot_image);
221         resource_resolve(handler, opt->source, opt->initrd);
222         resource_resolve(handler, opt->source, opt->icon);
223 }
224
225 void test_hotplug_device(struct parser_test *test, struct discover_device *dev)
226 {
227         struct discover_boot_option *opt;
228
229         device_handler_add_device(test->handler, dev);
230
231         list_for_each_entry(&test->ctx->boot_options, opt, list)
232                 boot_option_resolve(test->handler, opt);
233 }
234
235 struct discover_boot_option *get_boot_option(struct discover_context *ctx,
236                 int idx)
237 {
238         struct discover_boot_option *opt;
239         int i = 0;
240
241         list_for_each_entry(&ctx->boot_options, opt, list) {
242                 if (i++ == idx)
243                         return opt;
244         }
245
246         assert(0);
247
248         return NULL;
249 }
250
251 void __check_boot_option_count(struct discover_context *ctx, int count,
252                 const char *file, int line)
253 {
254         struct discover_boot_option *opt;
255         int defaults = 0, i = 0;
256
257         list_for_each_entry(&ctx->boot_options, opt, list) {
258                 i++;
259                 if (opt->option->is_default)
260                         defaults++;
261         }
262
263         if (defaults > 1) {
264                 fprintf(stderr, "%s:%d: parser returned multiple default "
265                                 "options\n", file, line);
266                 exit(EXIT_FAILURE);
267         }
268
269         if (i == count)
270                 return;
271
272         fprintf(stderr, "%s:%d: boot option count check failed\n", file, line);
273         fprintf(stderr, "expected %d options, got %d:\n", count, i);
274
275         i = 1;
276         list_for_each_entry(&ctx->boot_options, opt, list)
277                 fprintf(stderr, "  %2d: %s [%s]\n", i++, opt->option->name,
278                                 opt->option->id);
279
280         exit(EXIT_FAILURE);
281 }
282
283 void __check_args(struct discover_boot_option *opt, const char *args,
284                 const char *file, int line)
285 {
286         int rc;
287
288         if (!opt->option->boot_args && !args)
289                 return;
290
291         if (!opt->option->boot_args) {
292                 fprintf(stderr, "%s:%d: arg check failed\n", file, line);
293                 fprintf(stderr, "  no arguments parsed\n");
294                 fprintf(stderr, "  expected '%s'\n", args);
295                 exit(EXIT_FAILURE);
296         }
297
298         rc = strcmp(opt->option->boot_args, args);
299         if (rc) {
300                 fprintf(stderr, "%s:%d: arg check failed\n", file, line);
301                 fprintf(stderr, "  got      '%s'\n", opt->option->boot_args);
302                 fprintf(stderr, "  expected '%s'\n", args);
303                 exit(EXIT_FAILURE);
304         }
305 }
306
307 void __check_name(struct discover_boot_option *opt, const char *name,
308                 const char *file, int line)
309 {
310         int rc;
311
312         rc = strcmp(opt->option->name, name);
313         if (rc) {
314                 fprintf(stderr, "%s:%d: name check failed\n", file, line);
315                 fprintf(stderr, "  got      '%s'\n", opt->option->name);
316                 fprintf(stderr, "  expected '%s'\n", name);
317                 exit(EXIT_FAILURE);
318         }
319 }
320
321 void __check_is_default(struct discover_boot_option *opt,
322                 const char *file, int line)
323 {
324         if (opt->option->is_default)
325                 return;
326
327         fprintf(stderr, "%s:%d: default check failed\n", file, line);
328         exit(EXIT_FAILURE);
329 }
330
331 void __check_resolved_local_resource(struct resource *res,
332                 struct discover_device *dev, const char *local_path,
333                 const char *file, int line)
334 {
335         const char *exp_url, *got_url;
336
337         if (!res)
338                 errx(EXIT_FAILURE, "%s:%d: No resource", file, line);
339
340         if (!res->resolved)
341                 errx(EXIT_FAILURE, "%s:%d: Resource is not resolved",
342                                 file, line);
343
344         exp_url = talloc_asprintf(res, "file://%s%s",
345                         dev->mount_path, local_path);
346         got_url = pb_url_to_string(res->url);
347
348         if (strcmp(got_url, exp_url)) {
349                 fprintf(stderr, "%s:%d: Resource mismatch\n", file, line);
350                 fprintf(stderr, "  got      '%s'\n", got_url);
351                 fprintf(stderr, "  expected '%s'\n", exp_url);
352                 exit(EXIT_FAILURE);
353         }
354 }
355
356 void __check_resolved_url_resource(struct resource *res,
357                 const char *url, const char *file, int line)
358 {
359         char *res_url;
360
361         if (!res)
362                 errx(EXIT_FAILURE, "%s:%d: No resource", file, line);
363
364         if (!res->resolved)
365                 errx(EXIT_FAILURE, "%s:%d: Resource is not resolved",
366                                 file, line);
367
368         res_url = pb_url_to_string(res->url);
369         if (strcmp(url, res_url)) {
370                 fprintf(stderr, "%s:%d: Resource mismatch\n", file, line);
371                 fprintf(stderr, "  got      '%s'\n", res_url);
372                 fprintf(stderr, "  expected '%s'\n", url);
373                 exit(EXIT_FAILURE);
374         }
375 }
376 void __check_unresolved_resource(struct resource *res,
377                 const char *file, int line)
378 {
379         if (!res)
380                 errx(EXIT_FAILURE, "%s:%d: No resource", file, line);
381
382         if (res->resolved)
383                 errx(EXIT_FAILURE, "%s:%d: Resource is resolved", file, line);
384 }
385
386 void __check_not_present_resource(struct resource *res,
387                 const char *file, int line)
388 {
389         if (res)
390                 errx(EXIT_FAILURE, "%s:%d: Resource present", file, line);
391 }