xref: /freebsd/contrib/atf/atf-c/tc.c (revision dd41de95a84d979615a2ef11df6850622bf6184e)
1 /* Copyright (c) 2008 The NetBSD Foundation, Inc.
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
14  * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
15  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
16  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
18  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
20  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
22  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
24  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  */
25 
26 #include "atf-c/tc.h"
27 
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <sys/uio.h>
31 
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <stdarg.h>
35 #include <stdbool.h>
36 #include <stdint.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41 
42 #include "atf-c/defs.h"
43 #include "atf-c/detail/env.h"
44 #include "atf-c/detail/fs.h"
45 #include "atf-c/detail/map.h"
46 #include "atf-c/detail/sanity.h"
47 #include "atf-c/detail/text.h"
48 #include "atf-c/error.h"
49 
50 /* ---------------------------------------------------------------------
51  * Auxiliary functions.
52  * --------------------------------------------------------------------- */
53 
54 enum expect_type {
55     EXPECT_PASS,
56     EXPECT_FAIL,
57     EXPECT_EXIT,
58     EXPECT_SIGNAL,
59     EXPECT_DEATH,
60     EXPECT_TIMEOUT,
61 };
62 
63 struct context {
64     const atf_tc_t *tc;
65     const char *resfile;
66     int resfilefd;
67     size_t fail_count;
68 
69     enum expect_type expect;
70     atf_dynstr_t expect_reason;
71     size_t expect_previous_fail_count;
72     size_t expect_fail_count;
73     int expect_exitcode;
74     int expect_signo;
75 };
76 
77 static void context_init(struct context *, const atf_tc_t *, const char *);
78 static void context_set_resfile(struct context *, const char *);
79 static void context_close_resfile(struct context *);
80 static void check_fatal_error(atf_error_t);
81 static void report_fatal_error(const char *, ...)
82     ATF_DEFS_ATTRIBUTE_NORETURN;
83 static atf_error_t write_resfile(const int, const char *, const int,
84                                  const atf_dynstr_t *);
85 static void create_resfile(struct context *, const char *, const int,
86                            atf_dynstr_t *);
87 static void error_in_expect(struct context *, const char *, ...)
88     ATF_DEFS_ATTRIBUTE_NORETURN;
89 static void validate_expect(struct context *);
90 static void expected_failure(struct context *, atf_dynstr_t *)
91     ATF_DEFS_ATTRIBUTE_NORETURN;
92 static void fail_requirement(struct context *, atf_dynstr_t *)
93     ATF_DEFS_ATTRIBUTE_NORETURN;
94 static void fail_check(struct context *, atf_dynstr_t *);
95 static void pass(struct context *)
96     ATF_DEFS_ATTRIBUTE_NORETURN;
97 static void skip(struct context *, atf_dynstr_t *)
98     ATF_DEFS_ATTRIBUTE_NORETURN;
99 static void format_reason_ap(atf_dynstr_t *, const char *, const size_t,
100                              const char *, va_list);
101 static void format_reason_fmt(atf_dynstr_t *, const char *, const size_t,
102                               const char *, ...);
103 static void errno_test(struct context *, const char *, const size_t,
104                        const int, const char *, const bool,
105                        void (*)(struct context *, atf_dynstr_t *));
106 static atf_error_t check_prog_in_dir(const char *, void *);
107 static atf_error_t check_prog(struct context *, const char *);
108 
109 /* No prototype in header for this one, it's a little sketchy (internal). */
110 void atf_tc_set_resultsfile(const char *);
111 
112 static void
113 context_init(struct context *ctx, const atf_tc_t *tc, const char *resfile)
114 {
115 
116     ctx->tc = tc;
117     ctx->resfilefd = -1;
118     context_set_resfile(ctx, resfile);
119     ctx->fail_count = 0;
120     ctx->expect = EXPECT_PASS;
121     check_fatal_error(atf_dynstr_init(&ctx->expect_reason));
122     ctx->expect_previous_fail_count = 0;
123     ctx->expect_fail_count = 0;
124     ctx->expect_exitcode = 0;
125     ctx->expect_signo = 0;
126 }
127 
128 static void
129 context_set_resfile(struct context *ctx, const char *resfile)
130 {
131     atf_error_t err;
132 
133     context_close_resfile(ctx);
134     ctx->resfile = resfile;
135     if (strcmp(resfile, "/dev/stdout") == 0)
136         ctx->resfilefd = STDOUT_FILENO;
137     else if (strcmp(resfile, "/dev/stderr") == 0)
138         ctx->resfilefd = STDERR_FILENO;
139     else
140         ctx->resfilefd = open(resfile, O_WRONLY | O_CREAT | O_TRUNC,
141             S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
142     if (ctx->resfilefd == -1) {
143             err = atf_libc_error(errno,
144                 "Cannot create results file '%s'", resfile);
145             check_fatal_error(err);
146     }
147 
148     ctx->resfile = resfile;
149 }
150 
151 static void
152 context_close_resfile(struct context *ctx)
153 {
154 
155     if (ctx->resfilefd == -1)
156         return;
157     if (ctx->resfilefd != STDOUT_FILENO && ctx->resfilefd != STDERR_FILENO)
158         close(ctx->resfilefd);
159     ctx->resfilefd = -1;
160     ctx->resfile = NULL;
161 }
162 
163 static void
164 check_fatal_error(atf_error_t err)
165 {
166     if (atf_is_error(err)) {
167         char buf[1024];
168         atf_error_format(err, buf, sizeof(buf));
169         fprintf(stderr, "FATAL ERROR: %s\n", buf);
170         atf_error_free(err);
171         abort();
172     }
173 }
174 
175 static void
176 report_fatal_error(const char *msg, ...)
177 {
178     va_list ap;
179     fprintf(stderr, "FATAL ERROR: ");
180 
181     va_start(ap, msg);
182     vfprintf(stderr, msg, ap);
183     va_end(ap);
184 
185     fprintf(stderr, "\n");
186     abort();
187 }
188 
189 /** Writes to a results file.
190  *
191  * The results file is supposed to be already open.
192  *
193  * This function returns an error code instead of exiting in case of error
194  * because the caller needs to clean up the reason object before terminating.
195  */
196 static atf_error_t
197 write_resfile(const int fd, const char *result, const int arg,
198               const atf_dynstr_t *reason)
199 {
200     static char NL[] = "\n", CS[] = ": ";
201     char buf[64];
202     const char *r;
203     struct iovec iov[5];
204     ssize_t ret;
205     int count = 0;
206 
207     INV(arg == -1 || reason != NULL);
208 
209 #define UNCONST(a) ((void *)(uintptr_t)(const void *)(a))
210     iov[count].iov_base = UNCONST(result);
211     iov[count++].iov_len = strlen(result);
212 
213     if (reason != NULL) {
214         if (arg != -1) {
215             iov[count].iov_base = buf;
216             iov[count++].iov_len = snprintf(buf, sizeof(buf), "(%d)", arg);
217         }
218 
219         iov[count].iov_base = CS;
220         iov[count++].iov_len = sizeof(CS) - 1;
221 
222         r = atf_dynstr_cstring(reason);
223         iov[count].iov_base = UNCONST(r);
224         iov[count++].iov_len = strlen(r);
225     }
226 #undef UNCONST
227 
228     iov[count].iov_base = NL;
229     iov[count++].iov_len = sizeof(NL) - 1;
230 
231     while ((ret = writev(fd, iov, count)) == -1 && errno == EINTR)
232         continue; /* Retry. */
233     if (ret != -1)
234         return atf_no_error();
235 
236     return atf_libc_error(
237         errno, "Failed to write results file; result %s, reason %s", result,
238         reason == NULL ? "null" : atf_dynstr_cstring(reason));
239 }
240 
241 /** Creates a results file.
242  *
243  * The input reason is released in all cases.
244  *
245  * An error in this function is considered to be fatal, hence why it does
246  * not return any error code.
247  */
248 static void
249 create_resfile(struct context *ctx, const char *result, const int arg,
250                atf_dynstr_t *reason)
251 {
252     atf_error_t err;
253 
254     /*
255      * We'll attempt to truncate the results file, but only if it's not pointed
256      * at stdout/stderr.  We could just blindly ftruncate() here, but it may
257      * be that stdout/stderr have been redirected to a file that we want to
258      * validate expectations on, for example.  Kyua will want the truncation,
259      * but it will also redirect the results directly to some file and we'll
260      * have no issue here.
261      */
262     if (ctx->resfilefd != STDOUT_FILENO && ctx->resfilefd != STDERR_FILENO &&
263         ftruncate(ctx->resfilefd, 0) != -1)
264         lseek(ctx->resfilefd, 0, SEEK_SET);
265     err = write_resfile(ctx->resfilefd, result, arg, reason);
266 
267     if (reason != NULL)
268         atf_dynstr_fini(reason);
269 
270     check_fatal_error(err);
271 }
272 
273 /** Fails a test case if validate_expect fails. */
274 static void
275 error_in_expect(struct context *ctx, const char *fmt, ...)
276 {
277     atf_dynstr_t reason;
278     va_list ap;
279 
280     va_start(ap, fmt);
281     format_reason_ap(&reason, NULL, 0, fmt, ap);
282     va_end(ap);
283 
284     ctx->expect = EXPECT_PASS;  /* Ensure fail_requirement really fails. */
285     fail_requirement(ctx, &reason);
286 }
287 
288 /** Ensures that the "expect" state is correct.
289  *
290  * Call this function before modifying the current value of expect.
291  */
292 static void
293 validate_expect(struct context *ctx)
294 {
295     if (ctx->expect == EXPECT_DEATH) {
296         error_in_expect(ctx, "Test case was expected to terminate abruptly "
297             "but it continued execution");
298     } else if (ctx->expect == EXPECT_EXIT) {
299         error_in_expect(ctx, "Test case was expected to exit cleanly but it "
300             "continued execution");
301     } else if (ctx->expect == EXPECT_FAIL) {
302         if (ctx->expect_fail_count == ctx->expect_previous_fail_count)
303             error_in_expect(ctx, "Test case was expecting a failure but none "
304                 "were raised");
305         else
306             INV(ctx->expect_fail_count > ctx->expect_previous_fail_count);
307     } else if (ctx->expect == EXPECT_PASS) {
308         /* Nothing to validate. */
309     } else if (ctx->expect == EXPECT_SIGNAL) {
310         error_in_expect(ctx, "Test case was expected to receive a termination "
311             "signal but it continued execution");
312     } else if (ctx->expect == EXPECT_TIMEOUT) {
313         error_in_expect(ctx, "Test case was expected to hang but it continued "
314             "execution");
315     } else
316         UNREACHABLE;
317 }
318 
319 static void
320 expected_failure(struct context *ctx, atf_dynstr_t *reason)
321 {
322     check_fatal_error(atf_dynstr_prepend_fmt(reason, "%s: ",
323         atf_dynstr_cstring(&ctx->expect_reason)));
324     create_resfile(ctx, "expected_failure", -1, reason);
325     context_close_resfile(ctx);
326     exit(EXIT_SUCCESS);
327 }
328 
329 static void
330 fail_requirement(struct context *ctx, atf_dynstr_t *reason)
331 {
332     if (ctx->expect == EXPECT_FAIL) {
333         expected_failure(ctx, reason);
334     } else if (ctx->expect == EXPECT_PASS) {
335         create_resfile(ctx, "failed", -1, reason);
336         context_close_resfile(ctx);
337         exit(EXIT_FAILURE);
338     } else {
339         error_in_expect(ctx, "Test case raised a failure but was not "
340             "expecting one; reason was %s", atf_dynstr_cstring(reason));
341     }
342     UNREACHABLE;
343 }
344 
345 static void
346 fail_check(struct context *ctx, atf_dynstr_t *reason)
347 {
348     if (ctx->expect == EXPECT_FAIL) {
349         fprintf(stderr, "*** Expected check failure: %s: %s\n",
350             atf_dynstr_cstring(&ctx->expect_reason),
351             atf_dynstr_cstring(reason));
352         ctx->expect_fail_count++;
353     } else if (ctx->expect == EXPECT_PASS) {
354         fprintf(stderr, "*** Check failed: %s\n", atf_dynstr_cstring(reason));
355         ctx->fail_count++;
356     } else {
357         error_in_expect(ctx, "Test case raised a failure but was not "
358             "expecting one; reason was %s", atf_dynstr_cstring(reason));
359     }
360 
361     atf_dynstr_fini(reason);
362 }
363 
364 static void
365 pass(struct context *ctx)
366 {
367     if (ctx->expect == EXPECT_FAIL) {
368         error_in_expect(ctx, "Test case was expecting a failure but got "
369             "a pass instead");
370     } else if (ctx->expect == EXPECT_PASS) {
371         create_resfile(ctx, "passed", -1, NULL);
372         context_close_resfile(ctx);
373         exit(EXIT_SUCCESS);
374     } else {
375         error_in_expect(ctx, "Test case asked to explicitly pass but was "
376             "not expecting such condition");
377     }
378     UNREACHABLE;
379 }
380 
381 static void
382 skip(struct context *ctx, atf_dynstr_t *reason)
383 {
384     if (ctx->expect == EXPECT_PASS) {
385         create_resfile(ctx, "skipped", -1, reason);
386         context_close_resfile(ctx);
387         exit(EXIT_SUCCESS);
388     } else {
389         error_in_expect(ctx, "Can only skip a test case when running in "
390             "expect pass mode");
391     }
392     UNREACHABLE;
393 }
394 
395 /** Formats a failure/skip reason message.
396  *
397  * The formatted reason is stored in out_reason.  out_reason is initialized
398  * in this function and is supposed to be released by the caller.  In general,
399  * the reason will eventually be fed to create_resfile, which will release
400  * it.
401  *
402  * Errors in this function are fatal.  Rationale being: reasons are used to
403  * create results files; if we can't format the reason correctly, the result
404  * of the test program will be bogus.  So it's better to just exit with a
405  * fatal error.
406  */
407 static void
408 format_reason_ap(atf_dynstr_t *out_reason,
409                  const char *source_file, const size_t source_line,
410                  const char *reason, va_list ap)
411 {
412     atf_error_t err;
413 
414     if (source_file != NULL) {
415         err = atf_dynstr_init_fmt(out_reason, "%s:%zd: ", source_file,
416                                   source_line);
417     } else {
418         PRE(source_line == 0);
419         err = atf_dynstr_init(out_reason);
420     }
421 
422     if (!atf_is_error(err)) {
423         va_list ap2;
424         va_copy(ap2, ap);
425         err = atf_dynstr_append_ap(out_reason, reason, ap2);
426         va_end(ap2);
427     }
428 
429     check_fatal_error(err);
430 }
431 
432 static void
433 format_reason_fmt(atf_dynstr_t *out_reason,
434                   const char *source_file, const size_t source_line,
435                   const char *reason, ...)
436 {
437     va_list ap;
438 
439     va_start(ap, reason);
440     format_reason_ap(out_reason, source_file, source_line, reason, ap);
441     va_end(ap);
442 }
443 
444 static void
445 errno_test(struct context *ctx, const char *file, const size_t line,
446            const int exp_errno, const char *expr_str,
447            const bool expr_result,
448            void (*fail_func)(struct context *, atf_dynstr_t *))
449 {
450     const int actual_errno = errno;
451 
452     if (expr_result) {
453         if (exp_errno != actual_errno) {
454             atf_dynstr_t reason;
455 
456             format_reason_fmt(&reason, file, line, "Expected errno %d, got %d, "
457                 "in %s", exp_errno, actual_errno, expr_str);
458             fail_func(ctx, &reason);
459         }
460     } else {
461         atf_dynstr_t reason;
462 
463         format_reason_fmt(&reason, file, line, "Expected true value in %s",
464             expr_str);
465         fail_func(ctx, &reason);
466     }
467 }
468 
469 struct prog_found_pair {
470     const char *prog;
471     bool found;
472 };
473 
474 static atf_error_t
475 check_prog_in_dir(const char *dir, void *data)
476 {
477     struct prog_found_pair *pf = data;
478     atf_error_t err;
479 
480     if (pf->found)
481         err = atf_no_error();
482     else {
483         atf_fs_path_t p;
484 
485         err = atf_fs_path_init_fmt(&p, "%s/%s", dir, pf->prog);
486         if (atf_is_error(err))
487             goto out_p;
488 
489         err = atf_fs_eaccess(&p, atf_fs_access_x);
490         if (!atf_is_error(err))
491             pf->found = true;
492         else {
493             atf_error_free(err);
494             INV(!pf->found);
495             err = atf_no_error();
496         }
497 
498 out_p:
499         atf_fs_path_fini(&p);
500     }
501 
502     return err;
503 }
504 
505 static atf_error_t
506 check_prog(struct context *ctx, const char *prog)
507 {
508     atf_error_t err;
509     atf_fs_path_t p;
510 
511     err = atf_fs_path_init_fmt(&p, "%s", prog);
512     if (atf_is_error(err))
513         goto out;
514 
515     if (atf_fs_path_is_absolute(&p)) {
516         err = atf_fs_eaccess(&p, atf_fs_access_x);
517         if (atf_is_error(err)) {
518             atf_dynstr_t reason;
519 
520             atf_error_free(err);
521             atf_fs_path_fini(&p);
522             format_reason_fmt(&reason, NULL, 0, "The required program %s could "
523                 "not be found", prog);
524             skip(ctx, &reason);
525         }
526     } else {
527         const char *path = atf_env_get("PATH");
528         struct prog_found_pair pf;
529         atf_fs_path_t bp;
530 
531         err = atf_fs_path_branch_path(&p, &bp);
532         if (atf_is_error(err))
533             goto out_p;
534 
535         if (strcmp(atf_fs_path_cstring(&bp), ".") != 0) {
536             atf_fs_path_fini(&bp);
537             atf_fs_path_fini(&p);
538 
539             report_fatal_error("Relative paths are not allowed when searching "
540                 "for a program (%s)", prog);
541             UNREACHABLE;
542         }
543 
544         pf.prog = prog;
545         pf.found = false;
546         err = atf_text_for_each_word(path, ":", check_prog_in_dir, &pf);
547         if (atf_is_error(err))
548             goto out_bp;
549 
550         if (!pf.found) {
551             atf_dynstr_t reason;
552 
553             atf_fs_path_fini(&bp);
554             atf_fs_path_fini(&p);
555             format_reason_fmt(&reason, NULL, 0, "The required program %s could "
556                 "not be found in the PATH", prog);
557             fail_requirement(ctx, &reason);
558         }
559 
560 out_bp:
561         atf_fs_path_fini(&bp);
562     }
563 
564 out_p:
565     atf_fs_path_fini(&p);
566 out:
567     return err;
568 }
569 
570 /* ---------------------------------------------------------------------
571  * The "atf_tc" type.
572  * --------------------------------------------------------------------- */
573 
574 struct atf_tc_impl {
575     const char *m_ident;
576 
577     atf_map_t m_vars;
578     atf_map_t m_config;
579 
580     atf_tc_head_t m_head;
581     atf_tc_body_t m_body;
582     atf_tc_cleanup_t m_cleanup;
583 };
584 
585 /*
586  * Constructors/destructors.
587  */
588 
589 atf_error_t
590 atf_tc_init(atf_tc_t *tc, const char *ident, atf_tc_head_t head,
591             atf_tc_body_t body, atf_tc_cleanup_t cleanup,
592             const char *const *config)
593 {
594     atf_error_t err;
595 
596     tc->pimpl = malloc(sizeof(struct atf_tc_impl));
597     if (tc->pimpl == NULL) {
598         err = atf_no_memory_error();
599         goto err;
600     }
601 
602     tc->pimpl->m_ident = ident;
603     tc->pimpl->m_head = head;
604     tc->pimpl->m_body = body;
605     tc->pimpl->m_cleanup = cleanup;
606 
607     err = atf_map_init_charpp(&tc->pimpl->m_config, config);
608     if (atf_is_error(err))
609         goto err;
610 
611     err = atf_map_init(&tc->pimpl->m_vars);
612     if (atf_is_error(err))
613         goto err_vars;
614 
615     err = atf_tc_set_md_var(tc, "ident", ident);
616     if (atf_is_error(err))
617         goto err_map;
618 
619     if (cleanup != NULL) {
620         err = atf_tc_set_md_var(tc, "has.cleanup", "true");
621         if (atf_is_error(err))
622             goto err_map;
623     }
624 
625     /* XXX Should the head be able to return error codes? */
626     if (tc->pimpl->m_head != NULL)
627         tc->pimpl->m_head(tc);
628 
629     if (strcmp(atf_tc_get_md_var(tc, "ident"), ident) != 0) {
630         report_fatal_error("Test case head modified the read-only 'ident' "
631             "property");
632         UNREACHABLE;
633     }
634 
635     INV(!atf_is_error(err));
636     return err;
637 
638 err_map:
639     atf_map_fini(&tc->pimpl->m_vars);
640 err_vars:
641     atf_map_fini(&tc->pimpl->m_config);
642 err:
643     return err;
644 }
645 
646 atf_error_t
647 atf_tc_init_pack(atf_tc_t *tc, const atf_tc_pack_t *pack,
648                  const char *const *config)
649 {
650     return atf_tc_init(tc, pack->m_ident, pack->m_head, pack->m_body,
651                        pack->m_cleanup, config);
652 }
653 
654 void
655 atf_tc_fini(atf_tc_t *tc)
656 {
657     atf_map_fini(&tc->pimpl->m_vars);
658     free(tc->pimpl);
659 }
660 
661 /*
662  * Getters.
663  */
664 
665 const char *
666 atf_tc_get_ident(const atf_tc_t *tc)
667 {
668     return tc->pimpl->m_ident;
669 }
670 
671 const char *
672 atf_tc_get_config_var(const atf_tc_t *tc, const char *name)
673 {
674     const char *val;
675     atf_map_citer_t iter;
676 
677     PRE(atf_tc_has_config_var(tc, name));
678     iter = atf_map_find_c(&tc->pimpl->m_config, name);
679     val = atf_map_citer_data(iter);
680     INV(val != NULL);
681 
682     return val;
683 }
684 
685 const char *
686 atf_tc_get_config_var_wd(const atf_tc_t *tc, const char *name,
687                          const char *defval)
688 {
689     const char *val;
690 
691     if (!atf_tc_has_config_var(tc, name))
692         val = defval;
693     else
694         val = atf_tc_get_config_var(tc, name);
695 
696     return val;
697 }
698 
699 bool
700 atf_tc_get_config_var_as_bool(const atf_tc_t *tc, const char *name)
701 {
702     bool val;
703     const char *strval;
704     atf_error_t err;
705 
706     strval = atf_tc_get_config_var(tc, name);
707     err = atf_text_to_bool(strval, &val);
708     if (atf_is_error(err)) {
709         atf_error_free(err);
710         atf_tc_fail("Configuration variable %s does not have a valid "
711                     "boolean value; found %s", name, strval);
712     }
713 
714     return val;
715 }
716 
717 bool
718 atf_tc_get_config_var_as_bool_wd(const atf_tc_t *tc, const char *name,
719                                  const bool defval)
720 {
721     bool val;
722 
723     if (!atf_tc_has_config_var(tc, name))
724         val = defval;
725     else
726         val = atf_tc_get_config_var_as_bool(tc, name);
727 
728     return val;
729 }
730 
731 long
732 atf_tc_get_config_var_as_long(const atf_tc_t *tc, const char *name)
733 {
734     long val;
735     const char *strval;
736     atf_error_t err;
737 
738     strval = atf_tc_get_config_var(tc, name);
739     err = atf_text_to_long(strval, &val);
740     if (atf_is_error(err)) {
741         atf_error_free(err);
742         atf_tc_fail("Configuration variable %s does not have a valid "
743                     "long value; found %s", name, strval);
744     }
745 
746     return val;
747 }
748 
749 long
750 atf_tc_get_config_var_as_long_wd(const atf_tc_t *tc, const char *name,
751                                  const long defval)
752 {
753     long val;
754 
755     if (!atf_tc_has_config_var(tc, name))
756         val = defval;
757     else
758         val = atf_tc_get_config_var_as_long(tc, name);
759 
760     return val;
761 }
762 
763 const char *
764 atf_tc_get_md_var(const atf_tc_t *tc, const char *name)
765 {
766     const char *val;
767     atf_map_citer_t iter;
768 
769     PRE(atf_tc_has_md_var(tc, name));
770     iter = atf_map_find_c(&tc->pimpl->m_vars, name);
771     val = atf_map_citer_data(iter);
772     INV(val != NULL);
773 
774     return val;
775 }
776 
777 char **
778 atf_tc_get_md_vars(const atf_tc_t *tc)
779 {
780     return atf_map_to_charpp(&tc->pimpl->m_vars);
781 }
782 
783 bool
784 atf_tc_has_config_var(const atf_tc_t *tc, const char *name)
785 {
786     atf_map_citer_t end, iter;
787 
788     iter = atf_map_find_c(&tc->pimpl->m_config, name);
789     end = atf_map_end_c(&tc->pimpl->m_config);
790     return !atf_equal_map_citer_map_citer(iter, end);
791 }
792 
793 bool
794 atf_tc_has_md_var(const atf_tc_t *tc, const char *name)
795 {
796     atf_map_citer_t end, iter;
797 
798     iter = atf_map_find_c(&tc->pimpl->m_vars, name);
799     end = atf_map_end_c(&tc->pimpl->m_vars);
800     return !atf_equal_map_citer_map_citer(iter, end);
801 }
802 
803 /*
804  * Modifiers.
805  */
806 
807 atf_error_t
808 atf_tc_set_md_var(atf_tc_t *tc, const char *name, const char *fmt, ...)
809 {
810     atf_error_t err;
811     char *value;
812     va_list ap;
813 
814     va_start(ap, fmt);
815     err = atf_text_format_ap(&value, fmt, ap);
816     va_end(ap);
817 
818     if (!atf_is_error(err))
819         err = atf_map_insert(&tc->pimpl->m_vars, name, value, true);
820     else
821         free(value);
822 
823     return err;
824 }
825 
826 /* ---------------------------------------------------------------------
827  * Free functions, as they should be publicly but they can't.
828  * --------------------------------------------------------------------- */
829 
830 static void _atf_tc_fail(struct context *, const char *, va_list)
831     ATF_DEFS_ATTRIBUTE_NORETURN;
832 static void _atf_tc_fail_nonfatal(struct context *, const char *, va_list);
833 static void _atf_tc_fail_check(struct context *, const char *, const size_t,
834     const char *, va_list);
835 static void _atf_tc_fail_requirement(struct context *, const char *,
836     const size_t, const char *, va_list) ATF_DEFS_ATTRIBUTE_NORETURN;
837 static void _atf_tc_pass(struct context *) ATF_DEFS_ATTRIBUTE_NORETURN;
838 static void _atf_tc_require_prog(struct context *, const char *);
839 static void _atf_tc_skip(struct context *, const char *, va_list)
840     ATF_DEFS_ATTRIBUTE_NORETURN;
841 static void _atf_tc_check_errno(struct context *, const char *, const size_t,
842     const int, const char *, const bool);
843 static void _atf_tc_require_errno(struct context *, const char *, const size_t,
844     const int, const char *, const bool);
845 static void _atf_tc_expect_pass(struct context *);
846 static void _atf_tc_expect_fail(struct context *, const char *, va_list);
847 static void _atf_tc_expect_exit(struct context *, const int, const char *,
848     va_list);
849 static void _atf_tc_expect_signal(struct context *, const int, const char *,
850     va_list);
851 static void _atf_tc_expect_death(struct context *, const char *,
852     va_list);
853 
854 static void
855 _atf_tc_fail(struct context *ctx, const char *fmt, va_list ap)
856 {
857     va_list ap2;
858     atf_dynstr_t reason;
859 
860     va_copy(ap2, ap);
861     format_reason_ap(&reason, NULL, 0, fmt, ap2);
862     va_end(ap2);
863 
864     fail_requirement(ctx, &reason);
865     UNREACHABLE;
866 }
867 
868 static void
869 _atf_tc_fail_nonfatal(struct context *ctx, const char *fmt, va_list ap)
870 {
871     va_list ap2;
872     atf_dynstr_t reason;
873 
874     va_copy(ap2, ap);
875     format_reason_ap(&reason, NULL, 0, fmt, ap2);
876     va_end(ap2);
877 
878     fail_check(ctx, &reason);
879 }
880 
881 static void
882 _atf_tc_fail_check(struct context *ctx, const char *file, const size_t line,
883                    const char *fmt, va_list ap)
884 {
885     va_list ap2;
886     atf_dynstr_t reason;
887 
888     va_copy(ap2, ap);
889     format_reason_ap(&reason, file, line, fmt, ap2);
890     va_end(ap2);
891 
892     fail_check(ctx, &reason);
893 }
894 
895 static void
896 _atf_tc_fail_requirement(struct context *ctx, const char *file,
897                          const size_t line, const char *fmt, va_list ap)
898 {
899     va_list ap2;
900     atf_dynstr_t reason;
901 
902     va_copy(ap2, ap);
903     format_reason_ap(&reason, file, line, fmt, ap2);
904     va_end(ap2);
905 
906     fail_requirement(ctx, &reason);
907     UNREACHABLE;
908 }
909 
910 static void
911 _atf_tc_pass(struct context *ctx)
912 {
913     pass(ctx);
914     UNREACHABLE;
915 }
916 
917 static void
918 _atf_tc_require_prog(struct context *ctx, const char *prog)
919 {
920     check_fatal_error(check_prog(ctx, prog));
921 }
922 
923 static void
924 _atf_tc_skip(struct context *ctx, const char *fmt, va_list ap)
925 {
926     atf_dynstr_t reason;
927     va_list ap2;
928 
929     va_copy(ap2, ap);
930     format_reason_ap(&reason, NULL, 0, fmt, ap2);
931     va_end(ap2);
932 
933     skip(ctx, &reason);
934 }
935 
936 static void
937 _atf_tc_check_errno(struct context *ctx, const char *file, const size_t line,
938                     const int exp_errno, const char *expr_str,
939                     const bool expr_result)
940 {
941     errno_test(ctx, file, line, exp_errno, expr_str, expr_result, fail_check);
942 }
943 
944 static void
945 _atf_tc_require_errno(struct context *ctx, const char *file, const size_t line,
946                       const int exp_errno, const char *expr_str,
947                       const bool expr_result)
948 {
949     errno_test(ctx, file, line, exp_errno, expr_str, expr_result,
950         fail_requirement);
951 }
952 
953 static void
954 _atf_tc_expect_pass(struct context *ctx)
955 {
956     validate_expect(ctx);
957 
958     ctx->expect = EXPECT_PASS;
959 }
960 
961 static void
962 _atf_tc_expect_fail(struct context *ctx, const char *reason, va_list ap)
963 {
964     va_list ap2;
965 
966     validate_expect(ctx);
967 
968     ctx->expect = EXPECT_FAIL;
969     atf_dynstr_fini(&ctx->expect_reason);
970     va_copy(ap2, ap);
971     check_fatal_error(atf_dynstr_init_ap(&ctx->expect_reason, reason, ap2));
972     va_end(ap2);
973     ctx->expect_previous_fail_count = ctx->expect_fail_count;
974 }
975 
976 static void
977 _atf_tc_expect_exit(struct context *ctx, const int exitcode, const char *reason,
978                     va_list ap)
979 {
980     va_list ap2;
981     atf_dynstr_t formatted;
982 
983     validate_expect(ctx);
984 
985     ctx->expect = EXPECT_EXIT;
986     va_copy(ap2, ap);
987     check_fatal_error(atf_dynstr_init_ap(&formatted, reason, ap2));
988     va_end(ap2);
989 
990     create_resfile(ctx, "expected_exit", exitcode, &formatted);
991 }
992 
993 static void
994 _atf_tc_expect_signal(struct context *ctx, const int signo, const char *reason,
995                       va_list ap)
996 {
997     va_list ap2;
998     atf_dynstr_t formatted;
999 
1000     validate_expect(ctx);
1001 
1002     ctx->expect = EXPECT_SIGNAL;
1003     va_copy(ap2, ap);
1004     check_fatal_error(atf_dynstr_init_ap(&formatted, reason, ap2));
1005     va_end(ap2);
1006 
1007     create_resfile(ctx, "expected_signal", signo, &formatted);
1008 }
1009 
1010 static void
1011 _atf_tc_expect_death(struct context *ctx, const char *reason, va_list ap)
1012 {
1013     va_list ap2;
1014     atf_dynstr_t formatted;
1015 
1016     validate_expect(ctx);
1017 
1018     ctx->expect = EXPECT_DEATH;
1019     va_copy(ap2, ap);
1020     check_fatal_error(atf_dynstr_init_ap(&formatted, reason, ap2));
1021     va_end(ap2);
1022 
1023     create_resfile(ctx, "expected_death", -1, &formatted);
1024 }
1025 
1026 static void
1027 _atf_tc_expect_timeout(struct context *ctx, const char *reason, va_list ap)
1028 {
1029     va_list ap2;
1030     atf_dynstr_t formatted;
1031 
1032     validate_expect(ctx);
1033 
1034     ctx->expect = EXPECT_TIMEOUT;
1035     va_copy(ap2, ap);
1036     check_fatal_error(atf_dynstr_init_ap(&formatted, reason, ap2));
1037     va_end(ap2);
1038 
1039     create_resfile(ctx, "expected_timeout", -1, &formatted);
1040 }
1041 
1042 static void
1043 _atf_tc_set_resultsfile(struct context *ctx, const char *file)
1044 {
1045 
1046     context_set_resfile(ctx, file);
1047 }
1048 
1049 /* ---------------------------------------------------------------------
1050  * Free functions.
1051  * --------------------------------------------------------------------- */
1052 
1053 static struct context Current;
1054 
1055 atf_error_t
1056 atf_tc_run(const atf_tc_t *tc, const char *resfile)
1057 {
1058     context_init(&Current, tc, resfile);
1059 
1060     tc->pimpl->m_body(tc);
1061 
1062     validate_expect(&Current);
1063 
1064     if (Current.fail_count > 0) {
1065         atf_dynstr_t reason;
1066 
1067         format_reason_fmt(&reason, NULL, 0, "%d checks failed; see output for "
1068             "more details", Current.fail_count);
1069         fail_requirement(&Current, &reason);
1070     } else if (Current.expect_fail_count > 0) {
1071         atf_dynstr_t reason;
1072 
1073         format_reason_fmt(&reason, NULL, 0, "%d checks failed as expected; "
1074             "see output for more details", Current.expect_fail_count);
1075         expected_failure(&Current, &reason);
1076     } else {
1077         pass(&Current);
1078     }
1079     UNREACHABLE;
1080     return atf_no_error();
1081 }
1082 
1083 atf_error_t
1084 atf_tc_cleanup(const atf_tc_t *tc)
1085 {
1086     if (tc->pimpl->m_cleanup != NULL)
1087         tc->pimpl->m_cleanup(tc);
1088     return atf_no_error(); /* XXX */
1089 }
1090 
1091 /* ---------------------------------------------------------------------
1092  * Free functions that depend on Current.
1093  * --------------------------------------------------------------------- */
1094 
1095 /*
1096  * All the functions below provide delegates to other internal functions
1097  * (prefixed by _) that take the current test case as an argument to
1098  * prevent them from accessing global state.  This is to keep the side-
1099  * effects of the internal functions clearer and easier to understand.
1100  *
1101  * The public API should never have hid the fact that it needs access to
1102  * the current test case (other than maybe in the macros), but changing it
1103  * is hard.  TODO: Revisit in the future.
1104  */
1105 
1106 void
1107 atf_tc_fail(const char *fmt, ...)
1108 {
1109     va_list ap;
1110 
1111     PRE(Current.tc != NULL);
1112 
1113     va_start(ap, fmt);
1114     _atf_tc_fail(&Current, fmt, ap);
1115     va_end(ap);
1116 }
1117 
1118 void
1119 atf_tc_fail_nonfatal(const char *fmt, ...)
1120 {
1121     va_list ap;
1122 
1123     PRE(Current.tc != NULL);
1124 
1125     va_start(ap, fmt);
1126     _atf_tc_fail_nonfatal(&Current, fmt, ap);
1127     va_end(ap);
1128 }
1129 
1130 void
1131 atf_tc_fail_check(const char *file, const size_t line, const char *fmt, ...)
1132 {
1133     va_list ap;
1134 
1135     PRE(Current.tc != NULL);
1136 
1137     va_start(ap, fmt);
1138     _atf_tc_fail_check(&Current, file, line, fmt, ap);
1139     va_end(ap);
1140 }
1141 
1142 void
1143 atf_tc_fail_requirement(const char *file, const size_t line,
1144                         const char *fmt, ...)
1145 {
1146     va_list ap;
1147 
1148     PRE(Current.tc != NULL);
1149 
1150     va_start(ap, fmt);
1151     _atf_tc_fail_requirement(&Current, file, line, fmt, ap);
1152     va_end(ap);
1153 }
1154 
1155 void
1156 atf_tc_pass(void)
1157 {
1158     PRE(Current.tc != NULL);
1159 
1160     _atf_tc_pass(&Current);
1161 }
1162 
1163 void
1164 atf_tc_require_prog(const char *prog)
1165 {
1166     PRE(Current.tc != NULL);
1167 
1168     _atf_tc_require_prog(&Current, prog);
1169 }
1170 
1171 void
1172 atf_tc_skip(const char *fmt, ...)
1173 {
1174     va_list ap;
1175 
1176     PRE(Current.tc != NULL);
1177 
1178     va_start(ap, fmt);
1179     _atf_tc_skip(&Current, fmt, ap);
1180     va_end(ap);
1181 }
1182 
1183 void
1184 atf_tc_check_errno(const char *file, const size_t line, const int exp_errno,
1185                    const char *expr_str, const bool expr_result)
1186 {
1187     PRE(Current.tc != NULL);
1188 
1189     _atf_tc_check_errno(&Current, file, line, exp_errno, expr_str,
1190                         expr_result);
1191 }
1192 
1193 void
1194 atf_tc_require_errno(const char *file, const size_t line, const int exp_errno,
1195                      const char *expr_str, const bool expr_result)
1196 {
1197     PRE(Current.tc != NULL);
1198 
1199     _atf_tc_require_errno(&Current, file, line, exp_errno, expr_str,
1200                           expr_result);
1201 }
1202 
1203 void
1204 atf_tc_expect_pass(void)
1205 {
1206     PRE(Current.tc != NULL);
1207 
1208     _atf_tc_expect_pass(&Current);
1209 }
1210 
1211 void
1212 atf_tc_expect_fail(const char *reason, ...)
1213 {
1214     va_list ap;
1215 
1216     PRE(Current.tc != NULL);
1217 
1218     va_start(ap, reason);
1219     _atf_tc_expect_fail(&Current, reason, ap);
1220     va_end(ap);
1221 }
1222 
1223 void
1224 atf_tc_expect_exit(const int exitcode, const char *reason, ...)
1225 {
1226     va_list ap;
1227 
1228     PRE(Current.tc != NULL);
1229 
1230     va_start(ap, reason);
1231     _atf_tc_expect_exit(&Current, exitcode, reason, ap);
1232     va_end(ap);
1233 }
1234 
1235 void
1236 atf_tc_expect_signal(const int signo, const char *reason, ...)
1237 {
1238     va_list ap;
1239 
1240     PRE(Current.tc != NULL);
1241 
1242     va_start(ap, reason);
1243     _atf_tc_expect_signal(&Current, signo, reason, ap);
1244     va_end(ap);
1245 }
1246 
1247 void
1248 atf_tc_expect_death(const char *reason, ...)
1249 {
1250     va_list ap;
1251 
1252     PRE(Current.tc != NULL);
1253 
1254     va_start(ap, reason);
1255     _atf_tc_expect_death(&Current, reason, ap);
1256     va_end(ap);
1257 }
1258 
1259 void
1260 atf_tc_expect_timeout(const char *reason, ...)
1261 {
1262     va_list ap;
1263 
1264     PRE(Current.tc != NULL);
1265 
1266     va_start(ap, reason);
1267     _atf_tc_expect_timeout(&Current, reason, ap);
1268     va_end(ap);
1269 }
1270 
1271 /* Internal! */
1272 void
1273 atf_tc_set_resultsfile(const char *file)
1274 {
1275 
1276     PRE(Current.tc != NULL);
1277 
1278     _atf_tc_set_resultsfile(&Current, file);
1279 }
1280