xref: /dragonfly/usr.bin/dfregress/testcase.c (revision f9673872)
1 /*
2  * Copyright (c) 2011 Alex Hornung <alex@alexhornung.com>.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in
13  *    the documentation and/or other materials provided with the
14  *    distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
20  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
22  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
26  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/resource.h>
31 #include <sys/time.h>
32 #include <sys/types.h>
33 #include <sys/wait.h>
34 
35 #include <errno.h>
36 #include <signal.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <stdint.h>
40 #include <string.h>
41 #include <unistd.h>
42 #include <pwd.h>
43 
44 #include <err.h>
45 
46 #include <libprop/proplib.h>
47 
48 #include "parser.h"
49 #include "testcase.h"
50 #include "runlist.h"
51 #include "config.h"
52 #include <dfregress.h>
53 
54 prop_dictionary_t
55 testcase_from_struct(struct testcase *testcase)
56 {
57 	int i, r;
58 	prop_dictionary_t dict, testcase_dict;
59 	prop_array_t a;
60 	char *s;
61 
62 	testcase_dict = prop_dictionary_create();
63 	if (testcase_dict == NULL)
64 		err(1, "could not create testcase dict");
65 	r = prop_dictionary_set_cstring(testcase_dict, "name", testcase->name);
66 	if (r == 0)
67 		err(1, "prop_dictionary operation failed");
68 	r = prop_dictionary_set_cstring(testcase_dict, "type", testcase->type_str);
69 	if (r == 0)
70 		err(1, "prop_dictionary operation failed");
71 
72 	r = prop_dictionary_set_int32(testcase_dict, "argc",
73 	    (int32_t)testcase->argc);
74 	if (r == 0)
75 		err(1, "prop_dictionary operation failed");
76 
77 	a = prop_array_create_with_capacity(testcase->argc+1);
78 	if (a == NULL)
79 		err(1, "prop_array_create for argv failed");
80 
81 	s = strrchr(testcase->name, '/');
82 	r = prop_array_set_cstring(a, 0, (s == NULL) ? testcase->name : s+1);
83 	if (r == 0)
84 		err(1, "prop_array_set_cstring operation failed");
85 
86 	for (i = 1; i <= testcase->argc; i++) {
87 		r = prop_array_set_cstring(a, i, testcase->argv[i-1]);
88 		if (r == 0)
89 			err(1, "prop_array_set_cstring operation failed");
90 	}
91 
92 	r = prop_dictionary_set(testcase_dict, "args", a);
93 	if (r == 0)
94 		err(1, "prop_dictionary_set \"args\" failed");
95 
96 	dict = prop_dictionary_create();
97 	if (dict == NULL)
98 		err(1, "could not create dict");
99 
100 	r = prop_dictionary_set_int32(dict, "timeout_in_secs",
101 	    (int32_t)testcase->opts.timeout_in_secs);
102 	if (r == 0)
103 		err(1, "prop_dictionary operation failed");
104 
105 	r = prop_dictionary_set_int32(dict, "rc",
106 	    (int32_t)testcase->opts.rc);
107 	if (r == 0)
108 		err(1, "prop_dictionary operation failed");
109 
110 	r = prop_dictionary_set_uint32(dict, "flags", testcase->opts.flags);
111 	if (r == 0)
112 		err(1, "prop_dictionary operation failed");
113 
114 	if (testcase->opts.pre_cmd != NULL) {
115 		r = prop_dictionary_set_cstring(dict, "pre_cmd",
116 		    testcase->opts.pre_cmd);
117 		if (r == 0)
118 			err(1, "prop_dictionary operation failed");
119 	}
120 
121 	if (testcase->opts.post_cmd != NULL) {
122 		r = prop_dictionary_set_cstring(dict, "post_cmd",
123 		    testcase->opts.post_cmd);
124 		if (r == 0)
125 			err(1, "prop_dictionary operation failed");
126 	}
127 
128 	if (testcase->opts.interpreter != NULL) {
129 		r = prop_dictionary_set_cstring(dict, "interpreter",
130 		    testcase->opts.interpreter);
131 		if (r == 0)
132 			err(1, "prop_dictionary operation failed");
133 	}
134 
135 	r = prop_dictionary_set_uint32(dict, "runas_uid",
136 	    (uint32_t)testcase->opts.runas_uid);
137 	if (r == 0)
138 		err(1, "prop_dictionary operation failed");
139 
140 	r = prop_dictionary_set_cstring(dict, "make_cmd",
141 	    (testcase->opts.make_cmd != NULL) ? testcase->opts.make_cmd : "make");
142 	if (r == 0)
143 		err(1, "prop_dictionary operation failed");
144 
145 	r = prop_dictionary_set(testcase_dict, "opts", dict);
146 	if (r == 0)
147 		err(1, "prop_dictionary operation failed");
148 
149 	return testcase_dict;
150 }
151 
152 struct timeval *
153 testcase_get_timeout(prop_dictionary_t testcase)
154 {
155 	static struct timeval tv;
156 	int32_t val;
157 	int r;
158 
159 	r = prop_dictionary_get_int32(prop_dictionary_get(testcase, "opts"),
160 	    "timeout_in_secs", &val);
161 	if (r == 0)
162 		err(1, "prop_dictionary operation failed");
163 
164 	tv.tv_usec = 0;
165 	tv.tv_sec = (long)val;
166 
167 	return &tv;
168 }
169 
170 int
171 testcase_get_type(prop_dictionary_t testcase)
172 {
173 	const char *type;
174 	int r;
175 
176 	r = prop_dictionary_get_cstring_nocopy(testcase, "type", &type);
177 	if (r == 0)
178 		err(1, "prop_dictionary operation failed");
179 
180 	if (strcmp(type, "userland") == 0)
181 		return TESTCASE_TYPE_USERLAND;
182 	else if (strcmp(type, "kernel") == 0)
183 		return TESTCASE_TYPE_KERNEL;
184 	else if (strcmp(type, "buildonly") == 0)
185 		return TESTCASE_TYPE_BUILDONLY;
186 
187 	return 0;
188 }
189 
190 const char *
191 testcase_get_type_desc(prop_dictionary_t testcase)
192 {
193 	const char *str;
194 	int r;
195 
196 	r = prop_dictionary_get_cstring_nocopy(testcase, "type", &str);
197 	if (r == 0)
198 		err(1, "prop_dictionary operation failed");
199 
200 	return str;
201 }
202 
203 const char *
204 testcase_get_name(prop_dictionary_t testcase)
205 {
206 	const char *str;
207 	int r;
208 
209 	r = prop_dictionary_get_cstring_nocopy(testcase, "name", &str);
210 	if (r == 0)
211 		err(1, "prop_dictionary operation failed");
212 
213 	return str;
214 }
215 
216 int
217 testcase_get_argc(prop_dictionary_t testcase)
218 {
219 	int32_t argc;
220 	int r;
221 
222 	r = prop_dictionary_get_int32(testcase, "argc", &argc);
223 	if (r == 0)
224 		err(1, "prop_dictionary operation failed for argc");
225 
226 	return argc;
227 }
228 
229 const char **
230 testcase_get_args(prop_dictionary_t testcase)
231 {
232 	/* Sane limit of 63 arguments... who wants more than that? */
233 	static const char *argv[64];
234 	unsigned int i, count;
235 	prop_array_t a;
236 	int r;
237 
238 	a = prop_dictionary_get(testcase, "args");
239 	if (a == NULL)
240 		err(1, "testcase_get_args NULL array");
241 
242 	count = prop_array_count(a);
243 
244 	for (i = 0; i < count; i++) {
245 		r = prop_array_get_cstring_nocopy(a, i, &argv[i]);
246 		if (r == 0)
247 			err(1, "error building argv");
248 	}
249 
250 	argv[i] = NULL;
251 
252 	return argv;
253 }
254 
255 uint32_t
256 testcase_get_flags(prop_dictionary_t testcase)
257 {
258 	uint32_t flags;
259 	int r;
260 
261 	r = prop_dictionary_get_uint32(prop_dictionary_get(testcase, "opts"),
262 	    "flags", &flags);
263 	if (r == 0)
264 		err(1, "prop_dictionary operation failed");
265 
266 	return flags;
267 }
268 
269 int
270 testcase_get_precmd_type(prop_dictionary_t testcase)
271 {
272 	uint32_t flags = testcase_get_flags(testcase);
273 
274 	return (flags & (TESTCASE_INT_PRE | TESTCASE_CUSTOM_PRE));
275 }
276 
277 int
278 testcase_get_rc(prop_dictionary_t testcase)
279 {
280 	int32_t rc;
281 	int r;
282 
283 	r = prop_dictionary_get_int32(prop_dictionary_get(testcase, "opts"),
284 	    "rc", &rc);
285 	if (r == 0)
286 		err(1, "prop_dictionary operation failed for rc");
287 
288 	return rc;
289 }
290 
291 int
292 testcase_get_postcmd_type(prop_dictionary_t testcase)
293 {
294 	uint32_t flags = testcase_get_flags(testcase);
295 
296 	return (flags & (TESTCASE_INT_POST | TESTCASE_CUSTOM_POST));
297 }
298 
299 int
300 testcase_needs_setuid(prop_dictionary_t testcase)
301 {
302 	uint32_t flags = testcase_get_flags(testcase);
303 
304 	return (flags & TESTCASE_RUN_AS);
305 }
306 
307 uid_t
308 testcase_get_runas_uid(prop_dictionary_t testcase)
309 {
310 	uint32_t uid = 0;
311 	int r;
312 
313 	r = prop_dictionary_get_uint32(
314 	    prop_dictionary_get(testcase, "opts"), "runas_uid", &uid);
315 	if (r == 0)
316 		err(1, "prop_dictionary operation failed");
317 
318 	return (uid_t)uid;
319 }
320 
321 const char *
322 testcase_get_custom_precmd(prop_dictionary_t testcase)
323 {
324 	const char *str;
325 	int r;
326 
327 	r = prop_dictionary_get_cstring_nocopy(
328 	    prop_dictionary_get(testcase, "opts"), "pre_cmd", &str);
329 	if (r == 0)
330 		err(1, "prop_dictionary operation failed");
331 
332 	return str;
333 }
334 
335 const char *
336 testcase_get_custom_postcmd(prop_dictionary_t testcase)
337 {
338 	const char *str;
339 	int r;
340 
341 	r = prop_dictionary_get_cstring_nocopy(
342 	    prop_dictionary_get(testcase, "opts"), "pre_cmd", &str);
343 	if (r == 0)
344 		err(1, "prop_dictionary operation failed");
345 
346 	return str;
347 }
348 
349 static const char *
350 _testcase_get_interpreter(prop_dictionary_t testcase, bool fatal)
351 {
352 	const char *str;
353 	int r;
354 
355 	r = prop_dictionary_get_cstring_nocopy(
356 	    prop_dictionary_get(testcase, "opts"), "interpreter", &str);
357 	if (r == 0) {
358 		if (fatal)
359 			err(1, "prop_dictionary operation failed for interpreter");
360 		else
361 			return NULL;
362 	}
363 
364 	return str;
365 }
366 
367 const char *
368 testcase_get_interpreter(prop_dictionary_t testcase)
369 {
370 	return _testcase_get_interpreter(testcase, true);
371 }
372 
373 const char *
374 testcase_get_interpreter_noexit(prop_dictionary_t testcase)
375 {
376 	return _testcase_get_interpreter(testcase, false);
377 }
378 
379 const char *
380 testcase_get_make_cmd(prop_dictionary_t testcase)
381 {
382 	const char *str;
383 	int r;
384 
385 	r = prop_dictionary_get_cstring_nocopy(
386 	    prop_dictionary_get(testcase, "opts"), "make_cmd", &str);
387 	if (r == 0)
388 		err(1, "prop_dictionary operation failed");
389 
390 	return str;
391 }
392 
393 prop_dictionary_t
394 testcase_get_result_dict(prop_dictionary_t testcase)
395 {
396 	prop_dictionary_t result_dict;
397 	int r;
398 
399 	result_dict = prop_dictionary_get(testcase, "result");
400 	if (result_dict == NULL) {
401 		result_dict = prop_dictionary_create();
402 		if (result_dict == NULL)
403 			err(1, "could not allocate new result dict");
404 
405 		r = prop_dictionary_set(testcase, "result", result_dict);
406 		if (r == 0)
407 			err(1, "prop_dictionary operation failed");
408 	}
409 
410 	return result_dict;
411 }
412 
413 int
414 testcase_set_build_buf(prop_dictionary_t testcase, const char *buf)
415 {
416 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
417 
418 	return !prop_dictionary_set_cstring(dict, "build_buf", buf);
419 }
420 
421 int
422 testcase_set_cleanup_buf(prop_dictionary_t testcase, const char *buf)
423 {
424 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
425 
426 	return !prop_dictionary_set_cstring(dict, "cleanup_buf", buf);
427 }
428 
429 int
430 testcase_set_sys_buf(prop_dictionary_t testcase, const char *buf)
431 {
432 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
433 
434 	return !prop_dictionary_set_cstring(dict, "sys_buf", buf);
435 }
436 
437 int
438 testcase_set_precmd_buf(prop_dictionary_t testcase, const char *buf)
439 {
440 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
441 
442 	return !prop_dictionary_set_cstring(dict, "precmd_buf", buf);
443 }
444 
445 int
446 testcase_set_postcmd_buf(prop_dictionary_t testcase, const char *buf)
447 {
448 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
449 
450 	return !prop_dictionary_set_cstring(dict, "postcmd_buf", buf);
451 }
452 
453 int
454 testcase_set_stdout_buf(prop_dictionary_t testcase, const char *buf)
455 {
456 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
457 
458 	return !prop_dictionary_set_cstring(dict, "stdout_buf", buf);
459 }
460 
461 int
462 testcase_set_stderr_buf(prop_dictionary_t testcase, const char *buf)
463 {
464 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
465 
466 	return !prop_dictionary_set_cstring(dict, "stderr_buf", buf);
467 }
468 
469 int
470 testcase_set_result(prop_dictionary_t testcase, int result)
471 {
472 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
473 
474 	return !prop_dictionary_set_int32(dict, "result", result);
475 }
476 
477 int
478 testcase_set_exit_value(prop_dictionary_t testcase, int exitval)
479 {
480 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
481 
482 	return !prop_dictionary_set_int32(dict, "exit_value", exitval);
483 }
484 
485 int
486 testcase_set_signal(prop_dictionary_t testcase, int sig)
487 {
488 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
489 
490 	return !prop_dictionary_set_int32(dict, "signal", sig);
491 }
492 
493 const char *
494 testcase_get_build_buf(prop_dictionary_t testcase)
495 {
496 	const char *str = "";
497 
498 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
499 	prop_dictionary_get_cstring_nocopy(dict, "build_buf", &str);
500 
501 	return str;
502 }
503 
504 const char *
505 testcase_get_cleanup_buf(prop_dictionary_t testcase)
506 {
507 	const char *str = "";
508 
509 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
510 	prop_dictionary_get_cstring_nocopy(dict, "cleanup_buf", &str);
511 
512 	return str;
513 }
514 
515 const char *
516 testcase_get_sys_buf(prop_dictionary_t testcase)
517 {
518 	const char *str = "";
519 
520 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
521 	prop_dictionary_get_cstring_nocopy(dict, "sys_buf", &str);
522 
523 	return str;
524 }
525 
526 const char *
527 testcase_get_precmd_buf(prop_dictionary_t testcase)
528 {
529 	const char *str = "";
530 
531 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
532 	prop_dictionary_get_cstring_nocopy(dict, "precmd_buf", &str);
533 
534 	return str;
535 }
536 
537 const char *
538 testcase_get_postcmd_buf(prop_dictionary_t testcase)
539 {
540 	const char *str = "";
541 
542 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
543 	prop_dictionary_get_cstring_nocopy(dict, "postcmd_buf", &str);
544 
545 	return str;
546 }
547 
548 const char *
549 testcase_get_stdout_buf(prop_dictionary_t testcase)
550 {
551 	const char *str = "";
552 
553 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
554 	prop_dictionary_get_cstring_nocopy(dict, "stdout_buf", &str);
555 
556 	return str;
557 }
558 
559 const char *
560 testcase_get_stderr_buf(prop_dictionary_t testcase)
561 {
562 	const char *str = "";
563 
564 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
565 	prop_dictionary_get_cstring_nocopy(dict, "stderr_buf", &str);
566 
567 	return str;
568 }
569 
570 int
571 testcase_get_result(prop_dictionary_t testcase)
572 {
573 	int32_t result = RESULT_NOTRUN;
574 
575 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
576 	prop_dictionary_get_int32(dict, "result", &result);
577 
578 	return (int)result;
579 }
580 
581 const char *
582 testcase_get_result_desc(prop_dictionary_t testcase)
583 {
584 	int result = testcase_get_result(testcase);
585 
586 	switch(result) {
587 	case RESULT_TIMEOUT:	return "TIMEOUT";
588 	case RESULT_SIGNALLED:	return "SIGNALLED";
589 	case RESULT_NOTRUN:	return "NOT RUN";
590 	case RESULT_FAIL:	return "FAIL";
591 	case RESULT_PASS:	return "PASS";
592 	case RESULT_PREFAIL:	return "PREFAIL";
593 	case RESULT_POSTFAIL:	return "POSTFAIL";
594 	case RESULT_BUILDFAIL:	return "BUILDFAIL";
595 	default:		return "UNKNOWN";
596 	}
597 }
598 
599 int
600 testcase_get_exit_value(prop_dictionary_t testcase)
601 {
602 	int32_t exitval;
603 	int r;
604 
605 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
606 	r = prop_dictionary_get_int32(dict, "exit_value", &exitval);
607 	if (r == 0)
608 		err(1, "prop_dictionary operation failed");
609 
610 	return (int)exitval;
611 }
612 
613 int
614 testcase_get_signal(prop_dictionary_t testcase)
615 {
616 	int32_t sig;
617 	int r;
618 
619 	prop_dictionary_t dict = testcase_get_result_dict(testcase);
620 	r = prop_dictionary_get_int32(dict, "signal", &sig);
621 	if (r == 0)
622 		err(1, "prop_dictionary operation failed");
623 
624 	return (int)sig;
625 }
626 
627 int
628 parse_testcase_option(struct testcase_options *opts, char *option)
629 {
630 	struct passwd *pwd;
631 	char	*parameter, *endptr;
632 	long	lval;
633 	int	noparam = 0;
634 
635 	parameter = strchr(option, '=');
636 	noparam = (parameter == NULL);
637 	if (!noparam)
638 	{
639 		*parameter = '\0';
640 		++parameter;
641 	}
642 
643 	if (strcmp(option, "timeout") == 0) {
644 		if (noparam)
645 			syntax_error("The option 'timeout' needs a parameter");
646 			/* NOTREACHED */
647 
648 		lval = strtol(parameter, &endptr, 10);
649 		if (*endptr != '\0')
650 			syntax_error("The option 'timeout' expects an integer "
651 			    "parameter, not '%s'", parameter);
652 			/* NOTREACHED */
653 
654 		opts->timeout_in_secs = (long int)lval;
655 	} else if (strcmp(option, "rc") == 0) {
656 		if (noparam)
657 			syntax_error("The option 'timeout' needs a parameter");
658 		/* NOTREACHED */
659 
660 		lval = strtol(parameter, &endptr, 10);
661 		if (*endptr != '\0')
662 			syntax_error("The option 'timeout' expects an integer "
663 			    "parameter, not '%s'", parameter);
664 			/* NOTREACHED */
665 
666 		opts->rc = (int)lval;
667 	} else if (strcmp(option, "intpre") == 0) {
668 		opts->flags |= TESTCASE_INT_PRE;
669 	} else if (strcmp(option, "intpost") == 0) {
670 		opts->flags |= TESTCASE_INT_POST;
671 	} else if (strcmp(option, "pre") == 0) {
672 		if (noparam)
673 			syntax_error("The option 'pre' needs a parameter");
674 			/* NOTREACHED */
675 
676 		opts->flags |= TESTCASE_CUSTOM_PRE;
677 		opts->pre_cmd = strdup(parameter);
678 	} else if (strcmp(option, "post") == 0) {
679 		if (noparam)
680 			syntax_error("The option 'post' needs a parameter");
681 			/* NOTREACHED */
682 
683 		opts->flags |= TESTCASE_CUSTOM_POST;
684 		opts->post_cmd = strdup(parameter);
685 	} else if (strcmp(option, "runas") == 0) {
686 		if (noparam)
687 			syntax_error("The option 'runas' needs a parameter");
688 			/* NOTREACHED */
689 
690 		if ((pwd = getpwnam(parameter))) {
691 			opts->runas_uid = pwd->pw_uid;
692 			opts->flags |= TESTCASE_RUN_AS;
693 		} else {
694 			syntax_error("invalid user name for 'runas': %s",
695 			    parameter);
696 		}
697 	} else if (strcmp(option, "nobuild") == 0) {
698 		opts->flags |= TESTCASE_NOBUILD;
699 	} else if (strcmp(option, "interpreter") == 0) {
700 		if (noparam)
701 			syntax_error("The option 'interpreter' needs a parameter");
702 			/* NOTREACHED */
703 		opts->interpreter = strdup(parameter);
704 	} else if (strcmp(option, "make") == 0) {
705 		if (noparam)
706 			syntax_error("The option 'make' needs a parameter");
707 			/* NOTREACHED */
708 
709 		opts->make_cmd = strdup(parameter);
710 	} else if (strcmp(option, "defaults") == 0) {
711 		/* Valid option, does nothing */
712 	} else {
713 		syntax_error("Unknown option: %s", option);
714 		/* NOTREACHED */
715 	}
716 
717 	return 0;
718 }
719 
720 void
721 testcase_entry_parser(void *arg, char **tokens)
722 {
723 	prop_array_t runlist;
724 	prop_dictionary_t testcase_dict;
725 	struct testcase *testcase;
726 	char *options[256];
727 	int i, r, nopts;
728 
729 	runlist = (prop_array_t)arg;
730 
731 	testcase = malloc(sizeof(struct testcase));
732 	if (testcase == NULL)
733 		err(1, "could not malloc testcase memory");
734 
735 	bzero(testcase, sizeof(struct testcase));
736 
737 	entry_check_num_args(tokens, 3);
738 
739 	testcase->argv = &tokens[3];
740 	for (testcase->argc = 0; testcase->argv[testcase->argc] != NULL;
741 	     testcase->argc++)
742 		;
743 
744 	nopts = parse_options(tokens[2], options);
745 
746 	testcase->name = tokens[0];
747 
748 	if (strcmp(tokens[1], "userland") == 0) {
749 		testcase->type = TESTCASE_TYPE_USERLAND;
750 	} else if (strcmp(tokens[1], "kernel") == 0) {
751 		testcase->type = TESTCASE_TYPE_KERNEL;
752 	} else if (strcmp(tokens[1], "buildonly") == 0) {
753 		testcase->type = TESTCASE_TYPE_BUILDONLY;
754 	} else {
755 		syntax_error("Unknown type: %s", tokens[1]);
756 		/* NOTREACHED */
757 	}
758 
759 	testcase->type_str = tokens[1];
760 
761 	config_get_defaults(&testcase->opts);
762 
763 	for (i = 0; i < nopts; i++)
764 		parse_testcase_option(&testcase->opts, options[i]);
765 
766 	if ((testcase->type != TESTCASE_TYPE_USERLAND) &&
767 	    (testcase->opts.flags & (TESTCASE_INT_PRE | TESTCASE_INT_POST)))
768 		syntax_error("'intpre' and 'intpost' options are only valid "
769 		    "with testcase type 'userland'");
770 
771 	if ((testcase->type == TESTCASE_TYPE_BUILDONLY) &&
772 	    (testcase->opts.flags & TESTCASE_NOBUILD))
773 		syntax_error("'nobuild' option is incompatible with type "
774 		    "'buildonly'");
775 
776 	testcase_dict = testcase_from_struct(testcase);
777 	if (testcase->opts.pre_cmd != NULL)
778 		free(testcase->opts.pre_cmd);
779 	if (testcase->opts.post_cmd != NULL)
780 		free(testcase->opts.post_cmd);
781 	if (testcase->opts.interpreter != NULL)
782 		free(testcase->opts.interpreter);
783 	if (testcase->opts.make_cmd != NULL)
784 		free(testcase->opts.make_cmd);
785 	free(testcase);
786 
787 	r = prop_array_add(runlist, testcase_dict);
788 	if (r == 0)
789 		err(1, "prop_array_add failed");
790 }
791