tc.c revision 1.1.1.3 1 /*
2 * Automated Testing Framework (atf)
3 *
4 * Copyright (c) 2008, 2009, 2010 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
17 * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
18 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include <sys/types.h>
31 #include <sys/time.h>
32 #include <sys/stat.h>
33 #include <sys/wait.h>
34
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <limits.h>
38 #include <stdarg.h>
39 #include <stdbool.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44
45 #include "atf-c/config.h"
46 #include "atf-c/defs.h"
47 #include "atf-c/env.h"
48 #include "atf-c/error.h"
49 #include "atf-c/fs.h"
50 #include "atf-c/process.h"
51 #include "atf-c/sanity.h"
52 #include "atf-c/tc.h"
53 #include "atf-c/tcr.h"
54 #include "atf-c/text.h"
55 #include "atf-c/user.h"
56
57 /* ---------------------------------------------------------------------
58 * Auxiliary types and functions.
59 * --------------------------------------------------------------------- */
60
61 static atf_error_t check_prog(const char *, void *);
62 static atf_error_t check_prog_in_dir(const char *, void *);
63 static void fail_internal(const char *, int, const char *, const char *,
64 const char *, va_list,
65 void (*)(atf_dynstr_t *),
66 void (*)(const char *, ...));
67 static void tc_fail(atf_dynstr_t *) ATF_DEFS_ATTRIBUTE_NORETURN;
68 static void tc_fail_nonfatal(atf_dynstr_t *);
69
70 /* ---------------------------------------------------------------------
71 * The "atf_tc" type.
72 * --------------------------------------------------------------------- */
73
74 /*
75 * Constructors/destructors.
76 */
77
78 atf_error_t
79 atf_tc_init(atf_tc_t *tc, const char *ident, atf_tc_head_t head,
80 atf_tc_body_t body, atf_tc_cleanup_t cleanup,
81 const atf_map_t *config)
82 {
83 atf_error_t err;
84
85 atf_object_init(&tc->m_object);
86
87 tc->m_ident = ident;
88 tc->m_head = head;
89 tc->m_body = body;
90 tc->m_cleanup = cleanup;
91 tc->m_config = config;
92
93 err = atf_map_init(&tc->m_vars);
94 if (atf_is_error(err))
95 goto err_object;
96
97 err = atf_tc_set_md_var(tc, "ident", ident);
98 if (atf_is_error(err))
99 goto err_map;
100
101 /* XXX Should the head be able to return error codes? */
102 tc->m_head(tc);
103
104 if (strcmp(atf_tc_get_md_var(tc, "ident"), ident) != 0)
105 atf_tc_fail("Test case head modified the read-only 'ident' "
106 "property");
107
108 INV(!atf_is_error(err));
109 return err;
110
111 err_map:
112 atf_map_fini(&tc->m_vars);
113 err_object:
114 atf_object_fini(&tc->m_object);
115
116 return err;
117 }
118
119 atf_error_t
120 atf_tc_init_pack(atf_tc_t *tc, const atf_tc_pack_t *pack,
121 const atf_map_t *config)
122 {
123 return atf_tc_init(tc, pack->m_ident, pack->m_head, pack->m_body,
124 pack->m_cleanup, config);
125 }
126
127 void
128 atf_tc_fini(atf_tc_t *tc)
129 {
130 atf_map_fini(&tc->m_vars);
131
132 atf_object_fini(&tc->m_object);
133 }
134
135 /*
136 * Getters.
137 */
138
139 const char *
140 atf_tc_get_ident(const atf_tc_t *tc)
141 {
142 return tc->m_ident;
143 }
144
145 const char *
146 atf_tc_get_config_var(const atf_tc_t *tc, const char *name)
147 {
148 const char *val;
149 atf_map_citer_t iter;
150
151 PRE(atf_tc_has_config_var(tc, name));
152 iter = atf_map_find_c(tc->m_config, name);
153 val = atf_map_citer_data(iter);
154 INV(val != NULL);
155
156 return val;
157 }
158
159 const char *
160 atf_tc_get_config_var_wd(const atf_tc_t *tc, const char *name,
161 const char *defval)
162 {
163 const char *val;
164
165 if (!atf_tc_has_config_var(tc, name))
166 val = defval;
167 else
168 val = atf_tc_get_config_var(tc, name);
169
170 return val;
171 }
172
173 const char *
174 atf_tc_get_md_var(const atf_tc_t *tc, const char *name)
175 {
176 const char *val;
177 atf_map_citer_t iter;
178
179 PRE(atf_tc_has_md_var(tc, name));
180 iter = atf_map_find_c(&tc->m_vars, name);
181 val = atf_map_citer_data(iter);
182 INV(val != NULL);
183
184 return val;
185 }
186
187 const atf_map_t *
188 atf_tc_get_md_vars(const atf_tc_t *tc)
189 {
190 return &tc->m_vars;
191 }
192
193 bool
194 atf_tc_has_config_var(const atf_tc_t *tc, const char *name)
195 {
196 bool found;
197 atf_map_citer_t end, iter;
198
199 if (tc->m_config == NULL)
200 found = false;
201 else {
202 iter = atf_map_find_c(tc->m_config, name);
203 end = atf_map_end_c(tc->m_config);
204 found = !atf_equal_map_citer_map_citer(iter, end);
205 }
206
207 return found;
208 }
209
210 bool
211 atf_tc_has_md_var(const atf_tc_t *tc, const char *name)
212 {
213 atf_map_citer_t end, iter;
214
215 iter = atf_map_find_c(&tc->m_vars, name);
216 end = atf_map_end_c(&tc->m_vars);
217 return !atf_equal_map_citer_map_citer(iter, end);
218 }
219
220 /*
221 * Modifiers.
222 */
223
224 atf_error_t
225 atf_tc_set_md_var(atf_tc_t *tc, const char *name, const char *fmt, ...)
226 {
227 atf_error_t err;
228 char *value;
229 va_list ap;
230
231 va_start(ap, fmt);
232 err = atf_text_format_ap(&value, fmt, ap);
233 va_end(ap);
234
235 if (!atf_is_error(err))
236 err = atf_map_insert(&tc->m_vars, name, value, true);
237 else
238 free(value);
239
240 return err;
241 }
242
243 /* ---------------------------------------------------------------------
244 * Free functions.
245 * --------------------------------------------------------------------- */
246
247 static const atf_tc_t *current_tc = NULL;
248 static const atf_fs_path_t *current_resfile = NULL;
249 static size_t current_tc_fail_count = 0;
250
251 atf_error_t
252 atf_tc_run(const atf_tc_t *tc, const atf_fs_path_t *resfile)
253 {
254 atf_reset_exit_checks(); /* XXX */
255
256 current_tc = tc;
257 current_resfile = resfile;
258 current_tc_fail_count = 0;
259
260 tc->m_body(tc);
261
262 if (current_tc_fail_count == 0)
263 atf_tc_pass();
264 else
265 atf_tc_fail("%d checks failed; see output for more details",
266 current_tc_fail_count);
267
268 current_tc = NULL;
269 current_resfile = NULL;
270 current_tc_fail_count = 0;
271
272 return atf_no_error();
273 }
274
275 atf_error_t
276 atf_tc_cleanup(const atf_tc_t *tc)
277 {
278 if (tc->m_cleanup != NULL)
279 tc->m_cleanup(tc);
280 return atf_no_error(); /* XXX */
281 }
282
283 struct prog_found_pair {
284 const char *prog;
285 bool found;
286 };
287
288 static
289 atf_error_t
290 check_prog(const char *prog, void *data)
291 {
292 atf_error_t err;
293 atf_fs_path_t p;
294
295 err = atf_fs_path_init_fmt(&p, "%s", prog);
296 if (atf_is_error(err))
297 goto out;
298
299 if (atf_fs_path_is_absolute(&p)) {
300 err = atf_fs_eaccess(&p, atf_fs_access_x);
301 if (atf_is_error(err)) {
302 atf_error_free(err);
303 atf_fs_path_fini(&p);
304 atf_tc_skip("The required program %s could not be found", prog);
305 }
306 } else {
307 const char *path = atf_env_get("PATH");
308 struct prog_found_pair pf;
309 atf_fs_path_t bp;
310
311 err = atf_fs_path_branch_path(&p, &bp);
312 if (atf_is_error(err))
313 goto out_p;
314
315 if (strcmp(atf_fs_path_cstring(&bp), ".") != 0) {
316 atf_fs_path_fini(&bp);
317 atf_fs_path_fini(&p);
318 atf_tc_fail("Relative paths are not allowed when searching for "
319 "a program (%s)", prog);
320 }
321
322 pf.prog = prog;
323 pf.found = false;
324 err = atf_text_for_each_word(path, ":", check_prog_in_dir, &pf);
325 if (atf_is_error(err))
326 goto out_bp;
327
328 if (!pf.found) {
329 atf_fs_path_fini(&bp);
330 atf_fs_path_fini(&p);
331 atf_tc_skip("The required program %s could not be found in "
332 "the PATH", prog);
333 }
334
335 out_bp:
336 atf_fs_path_fini(&bp);
337 }
338
339 out_p:
340 atf_fs_path_fini(&p);
341 out:
342 return err;
343 }
344
345 static
346 atf_error_t
347 check_prog_in_dir(const char *dir, void *data)
348 {
349 struct prog_found_pair *pf = data;
350 atf_error_t err;
351
352 if (pf->found)
353 err = atf_no_error();
354 else {
355 atf_fs_path_t p;
356
357 err = atf_fs_path_init_fmt(&p, "%s/%s", dir, pf->prog);
358 if (atf_is_error(err))
359 goto out_p;
360
361 err = atf_fs_eaccess(&p, atf_fs_access_x);
362 if (!atf_is_error(err))
363 pf->found = true;
364 else {
365 atf_error_free(err);
366 INV(!pf->found);
367 err = atf_no_error();
368 }
369
370 out_p:
371 atf_fs_path_fini(&p);
372 }
373
374 return err;
375 }
376
377 static
378 void
379 tc_fail(atf_dynstr_t *msg)
380 {
381 atf_tcr_t tcr;
382 atf_error_t err;
383
384 PRE(current_tc != NULL);
385
386 err = atf_tcr_init_reason_fmt(&tcr, atf_tcr_failed_state, "%s",
387 atf_dynstr_cstring(msg));
388 if (atf_is_error(err))
389 abort();
390
391 atf_tcr_write(&tcr, current_resfile); /* XXX Handle errors. */
392
393 atf_tcr_fini(&tcr);
394 atf_dynstr_fini(msg);
395
396 exit(EXIT_FAILURE);
397 }
398
399 static
400 void
401 tc_fail_nonfatal(atf_dynstr_t *msg)
402 {
403 fprintf(stderr, "%s\n", atf_dynstr_cstring(msg));
404 atf_dynstr_fini(msg);
405
406 current_tc_fail_count++;
407 }
408
409 void
410 atf_tc_fail(const char *fmt, ...)
411 {
412 va_list ap;
413 atf_tcr_t tcr;
414 atf_error_t err;
415
416 PRE(current_tc != NULL);
417
418 va_start(ap, fmt);
419 err = atf_tcr_init_reason_ap(&tcr, atf_tcr_failed_state, fmt, ap);
420 va_end(ap);
421 if (atf_is_error(err))
422 abort();
423
424 atf_tcr_write(&tcr, current_resfile); /* XXX Handle errors. */
425
426 atf_tcr_fini(&tcr);
427
428 exit(EXIT_FAILURE);
429 }
430
431 void
432 atf_tc_fail_nonfatal(const char *fmt, ...)
433 {
434 va_list ap;
435
436 va_start(ap, fmt);
437 vfprintf(stderr, fmt, ap);
438 va_end(ap);
439 fprintf(stderr, "\n");
440
441 current_tc_fail_count++;
442 }
443
444 void
445 atf_tc_fail_check(const char *file, int line, const char *fmt, ...)
446 {
447 va_list ap;
448
449 va_start(ap, fmt);
450 fail_internal(file, line, "Check failed", "*** ", fmt, ap,
451 tc_fail_nonfatal, atf_tc_fail_nonfatal);
452 va_end(ap);
453 }
454
455 void
456 atf_tc_fail_requirement(const char *file, int line, const char *fmt, ...)
457 {
458 va_list ap;
459
460 atf_reset_exit_checks();
461
462 va_start(ap, fmt);
463 fail_internal(file, line, "Requirement failed", "", fmt, ap,
464 tc_fail, atf_tc_fail);
465 va_end(ap);
466
467 UNREACHABLE;
468 abort();
469 }
470
471 static
472 void
473 fail_internal(const char *file, int line, const char *reason,
474 const char *prefix, const char *fmt, va_list ap,
475 void (*failfunc)(atf_dynstr_t *),
476 void (*backupfunc)(const char *, ...))
477 {
478 va_list ap2;
479 atf_error_t err;
480 atf_dynstr_t msg;
481
482 err = atf_dynstr_init_fmt(&msg, "%s%s:%d: %s: ", prefix, file, line,
483 reason);
484 if (atf_is_error(err))
485 goto backup;
486
487 va_copy(ap2, ap);
488 err = atf_dynstr_append_ap(&msg, fmt, ap2);
489 va_end(ap2);
490 if (atf_is_error(err)) {
491 atf_dynstr_fini(&msg);
492 goto backup;
493 }
494
495 va_copy(ap2, ap);
496 failfunc(&msg);
497 return;
498
499 backup:
500 atf_error_free(err);
501 va_copy(ap2, ap);
502 backupfunc(fmt, ap2);
503 va_end(ap2);
504 }
505
506 void
507 atf_tc_pass(void)
508 {
509 atf_tcr_t tcr;
510 atf_error_t err;
511
512 PRE(current_tc != NULL);
513
514 err = atf_tcr_init(&tcr, atf_tcr_passed_state);
515 if (atf_is_error(err))
516 abort();
517
518 atf_tcr_write(&tcr, current_resfile); /* XXX Handle errors. */
519
520 atf_tcr_fini(&tcr);
521
522 exit(EXIT_SUCCESS);
523 }
524
525 void
526 atf_tc_require_prog(const char *prog)
527 {
528 atf_error_t err;
529
530 err = check_prog(prog, NULL);
531 if (atf_is_error(err)) {
532 atf_error_free(err);
533 atf_tc_fail("atf_tc_require_prog failed"); /* XXX Correct? */
534 }
535 }
536
537 void
538 atf_tc_skip(const char *fmt, ...)
539 {
540 va_list ap;
541 atf_tcr_t tcr;
542 atf_error_t err;
543
544 PRE(current_tc != NULL);
545
546 va_start(ap, fmt);
547 err = atf_tcr_init_reason_ap(&tcr, atf_tcr_skipped_state, fmt, ap);
548 va_end(ap);
549 if (atf_is_error(err))
550 abort();
551
552 atf_tcr_write(&tcr, current_resfile); /* XXX Handle errors. */
553
554 atf_tcr_fini(&tcr);
555
556 exit(EXIT_SUCCESS);
557 }
558