tools/llvm: Do not build with symbols
[minix3.git] / external / bsd / atf / dist / atf-c++ / tests.cpp
blob2c351fcb3dbbb3f7966f938b0a40defaa938a4c6
1 //
2 // Automated Testing Framework (atf)
3 //
4 // Copyright (c) 2007 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.
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.
30 extern "C" {
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <sys/time.h>
34 #include <sys/wait.h>
35 #include <signal.h>
36 #include <unistd.h>
39 #include <algorithm>
40 #include <cctype>
41 #include <cerrno>
42 #include <cstdlib>
43 #include <cstring>
44 #include <fstream>
45 #include <iostream>
46 #include <map>
47 #include <memory>
48 #include <sstream>
49 #include <stdexcept>
50 #include <vector>
52 extern "C" {
53 #include "atf-c/error.h"
54 #include "atf-c/tc.h"
55 #include "atf-c/utils.h"
58 #include "noncopyable.hpp"
59 #include "tests.hpp"
61 #include "detail/application.hpp"
62 #include "detail/auto_array.hpp"
63 #include "detail/env.hpp"
64 #include "detail/exceptions.hpp"
65 #include "detail/fs.hpp"
66 #include "detail/parser.hpp"
67 #include "detail/sanity.hpp"
68 #include "detail/text.hpp"
70 namespace impl = atf::tests;
71 namespace detail = atf::tests::detail;
72 #define IMPL_NAME "atf::tests"
74 // ------------------------------------------------------------------------
75 // The "atf_tp_writer" class.
76 // ------------------------------------------------------------------------
78 detail::atf_tp_writer::atf_tp_writer(std::ostream& os) :
79 m_os(os),
80 m_is_first(true)
82 atf::parser::headers_map hm;
83 atf::parser::attrs_map ct_attrs;
84 ct_attrs["version"] = "1";
85 hm["Content-Type"] = atf::parser::header_entry("Content-Type",
86 "application/X-atf-tp", ct_attrs);
87 atf::parser::write_headers(hm, m_os);
90 void
91 detail::atf_tp_writer::start_tc(const std::string& ident)
93 if (!m_is_first)
94 m_os << "\n";
95 m_os << "ident: " << ident << "\n";
96 m_os.flush();
99 void
100 detail::atf_tp_writer::end_tc(void)
102 if (m_is_first)
103 m_is_first = false;
106 void
107 detail::atf_tp_writer::tc_meta_data(const std::string& name,
108 const std::string& value)
110 PRE(name != "ident");
111 m_os << name << ": " << value << "\n";
112 m_os.flush();
115 // ------------------------------------------------------------------------
116 // Free helper functions.
117 // ------------------------------------------------------------------------
119 bool
120 detail::match(const std::string& regexp, const std::string& str)
122 return atf::text::match(str, regexp);
125 // ------------------------------------------------------------------------
126 // The "tc" class.
127 // ------------------------------------------------------------------------
129 static std::map< atf_tc_t*, impl::tc* > wraps;
130 static std::map< const atf_tc_t*, const impl::tc* > cwraps;
132 struct impl::tc_impl : atf::noncopyable {
133 std::string m_ident;
134 atf_tc_t m_tc;
135 bool m_has_cleanup;
137 tc_impl(const std::string& ident, const bool has_cleanup) :
138 m_ident(ident),
139 m_has_cleanup(has_cleanup)
143 static void
144 wrap_head(atf_tc_t *tc)
146 std::map< atf_tc_t*, impl::tc* >::iterator iter = wraps.find(tc);
147 INV(iter != wraps.end());
148 (*iter).second->head();
151 static void
152 wrap_body(const atf_tc_t *tc)
154 std::map< const atf_tc_t*, const impl::tc* >::const_iterator iter =
155 cwraps.find(tc);
156 INV(iter != cwraps.end());
157 try {
158 (*iter).second->body();
159 } catch (const std::exception& e) {
160 (*iter).second->fail("Caught unhandled exception: " + std::string(
161 e.what()));
162 } catch (...) {
163 (*iter).second->fail("Caught unknown exception");
167 static void
168 wrap_cleanup(const atf_tc_t *tc)
170 std::map< const atf_tc_t*, const impl::tc* >::const_iterator iter =
171 cwraps.find(tc);
172 INV(iter != cwraps.end());
173 (*iter).second->cleanup();
177 impl::tc::tc(const std::string& ident, const bool has_cleanup) :
178 pimpl(new tc_impl(ident, has_cleanup))
182 impl::tc::~tc(void)
184 cwraps.erase(&pimpl->m_tc);
185 wraps.erase(&pimpl->m_tc);
187 atf_tc_fini(&pimpl->m_tc);
190 void
191 impl::tc::init(const vars_map& config)
193 atf_error_t err;
195 auto_array< const char * > array(new const char*[(config.size() * 2) + 1]);
196 const char **ptr = array.get();
197 for (vars_map::const_iterator iter = config.begin();
198 iter != config.end(); iter++) {
199 *ptr = (*iter).first.c_str();
200 *(ptr + 1) = (*iter).second.c_str();
201 ptr += 2;
203 *ptr = NULL;
205 wraps[&pimpl->m_tc] = this;
206 cwraps[&pimpl->m_tc] = this;
208 err = atf_tc_init(&pimpl->m_tc, pimpl->m_ident.c_str(), pimpl->wrap_head,
209 pimpl->wrap_body, pimpl->m_has_cleanup ? pimpl->wrap_cleanup : NULL,
210 array.get());
211 if (atf_is_error(err))
212 throw_atf_error(err);
215 bool
216 impl::tc::has_config_var(const std::string& var)
217 const
219 return atf_tc_has_config_var(&pimpl->m_tc, var.c_str());
222 bool
223 impl::tc::has_md_var(const std::string& var)
224 const
226 return atf_tc_has_md_var(&pimpl->m_tc, var.c_str());
229 const std::string
230 impl::tc::get_config_var(const std::string& var)
231 const
233 return atf_tc_get_config_var(&pimpl->m_tc, var.c_str());
236 const std::string
237 impl::tc::get_config_var(const std::string& var, const std::string& defval)
238 const
240 return atf_tc_get_config_var_wd(&pimpl->m_tc, var.c_str(), defval.c_str());
243 const std::string
244 impl::tc::get_md_var(const std::string& var)
245 const
247 return atf_tc_get_md_var(&pimpl->m_tc, var.c_str());
250 const impl::vars_map
251 impl::tc::get_md_vars(void)
252 const
254 vars_map vars;
256 char **array = atf_tc_get_md_vars(&pimpl->m_tc);
257 try {
258 char **ptr;
259 for (ptr = array; *ptr != NULL; ptr += 2)
260 vars[*ptr] = *(ptr + 1);
261 } catch (...) {
262 atf_utils_free_charpp(array);
263 throw;
266 return vars;
269 void
270 impl::tc::set_md_var(const std::string& var, const std::string& val)
272 atf_error_t err = atf_tc_set_md_var(&pimpl->m_tc, var.c_str(), val.c_str());
273 if (atf_is_error(err))
274 throw_atf_error(err);
277 void
278 impl::tc::run(const std::string& resfile)
279 const
281 atf_error_t err = atf_tc_run(&pimpl->m_tc, resfile.c_str());
282 if (atf_is_error(err))
283 throw_atf_error(err);
286 void
287 impl::tc::run_cleanup(void)
288 const
290 atf_error_t err = atf_tc_cleanup(&pimpl->m_tc);
291 if (atf_is_error(err))
292 throw_atf_error(err);
295 void
296 impl::tc::head(void)
300 void
301 impl::tc::cleanup(void)
302 const
306 void
307 impl::tc::require_prog(const std::string& prog)
308 const
310 atf_tc_require_prog(prog.c_str());
313 void
314 impl::tc::pass(void)
316 atf_tc_pass();
319 void
320 impl::tc::fail(const std::string& reason)
322 atf_tc_fail("%s", reason.c_str());
325 void
326 impl::tc::fail_nonfatal(const std::string& reason)
328 atf_tc_fail_nonfatal("%s", reason.c_str());
331 void
332 impl::tc::skip(const std::string& reason)
334 atf_tc_skip("%s", reason.c_str());
337 void
338 impl::tc::check_errno(const char* file, const int line, const int exp_errno,
339 const char* expr_str, const bool result)
341 atf_tc_check_errno(file, line, exp_errno, expr_str, result);
344 void
345 impl::tc::require_errno(const char* file, const int line, const int exp_errno,
346 const char* expr_str, const bool result)
348 atf_tc_require_errno(file, line, exp_errno, expr_str, result);
351 void
352 impl::tc::expect_pass(void)
354 atf_tc_expect_pass();
357 void
358 impl::tc::expect_fail(const std::string& reason)
360 atf_tc_expect_fail("%s", reason.c_str());
363 void
364 impl::tc::expect_exit(const int exitcode, const std::string& reason)
366 atf_tc_expect_exit(exitcode, "%s", reason.c_str());
369 void
370 impl::tc::expect_signal(const int signo, const std::string& reason)
372 atf_tc_expect_signal(signo, "%s", reason.c_str());
375 void
376 impl::tc::expect_death(const std::string& reason)
378 atf_tc_expect_death("%s", reason.c_str());
381 void
382 impl::tc::expect_timeout(const std::string& reason)
384 atf_tc_expect_timeout("%s", reason.c_str());
387 // ------------------------------------------------------------------------
388 // The "tp" class.
389 // ------------------------------------------------------------------------
391 class tp : public atf::application::app {
392 public:
393 typedef std::vector< impl::tc * > tc_vector;
395 private:
396 static const char* m_description;
398 bool m_lflag;
399 atf::fs::path m_resfile;
400 std::string m_srcdir_arg;
401 atf::fs::path m_srcdir;
403 atf::tests::vars_map m_vars;
405 std::string specific_args(void) const;
406 options_set specific_options(void) const;
407 void process_option(int, const char*);
409 void (*m_add_tcs)(tc_vector&);
410 tc_vector m_tcs;
412 void parse_vflag(const std::string&);
413 void handle_srcdir(void);
415 tc_vector init_tcs(void);
417 enum tc_part {
418 BODY,
419 CLEANUP,
422 void list_tcs(void);
423 impl::tc* find_tc(tc_vector, const std::string&);
424 static std::pair< std::string, tc_part > process_tcarg(const std::string&);
425 int run_tc(const std::string&);
427 public:
428 tp(void (*)(tc_vector&));
429 ~tp(void);
431 int main(void);
434 const char* tp::m_description =
435 "This is an independent atf test program.";
437 tp::tp(void (*add_tcs)(tc_vector&)) :
438 app(m_description, "atf-test-program(1)", "atf(7)", false),
439 m_lflag(false),
440 m_resfile("/dev/stdout"),
441 m_srcdir("."),
442 m_add_tcs(add_tcs)
446 tp::~tp(void)
448 for (tc_vector::iterator iter = m_tcs.begin();
449 iter != m_tcs.end(); iter++) {
450 impl::tc* tc = *iter;
452 delete tc;
456 std::string
457 tp::specific_args(void)
458 const
460 return "test_case";
463 tp::options_set
464 tp::specific_options(void)
465 const
467 using atf::application::option;
468 options_set opts;
469 opts.insert(option('l', "", "List test cases and their purpose"));
470 opts.insert(option('r', "resfile", "The file to which the test program "
471 "will write the results of the "
472 "executed test case"));
473 opts.insert(option('s', "srcdir", "Directory where the test's data "
474 "files are located"));
475 opts.insert(option('v', "var=value", "Sets the configuration variable "
476 "`var' to `value'"));
477 return opts;
480 void
481 tp::process_option(int ch, const char* arg)
483 switch (ch) {
484 case 'l':
485 m_lflag = true;
486 break;
488 case 'r':
489 m_resfile = atf::fs::path(arg);
490 break;
492 case 's':
493 m_srcdir_arg = arg;
494 break;
496 case 'v':
497 parse_vflag(arg);
498 break;
500 default:
501 UNREACHABLE;
505 void
506 tp::parse_vflag(const std::string& str)
508 if (str.empty())
509 throw std::runtime_error("-v requires a non-empty argument");
511 std::vector< std::string > ws = atf::text::split(str, "=");
512 if (ws.size() == 1 && str[str.length() - 1] == '=') {
513 m_vars[ws[0]] = "";
514 } else {
515 if (ws.size() != 2)
516 throw std::runtime_error("-v requires an argument of the form "
517 "var=value");
519 m_vars[ws[0]] = ws[1];
523 void
524 tp::handle_srcdir(void)
526 if (m_srcdir_arg.empty()) {
527 m_srcdir = atf::fs::path(m_argv0).branch_path();
528 if (m_srcdir.leaf_name() == ".libs")
529 m_srcdir = m_srcdir.branch_path();
530 } else
531 m_srcdir = atf::fs::path(m_srcdir_arg);
533 if (!atf::fs::exists(m_srcdir / m_prog_name))
534 throw std::runtime_error("Cannot find the test program in the "
535 "source directory `" + m_srcdir.str() + "'");
537 if (!m_srcdir.is_absolute())
538 m_srcdir = m_srcdir.to_absolute();
540 m_vars["srcdir"] = m_srcdir.str();
543 tp::tc_vector
544 tp::init_tcs(void)
546 m_add_tcs(m_tcs);
547 for (tc_vector::iterator iter = m_tcs.begin();
548 iter != m_tcs.end(); iter++) {
549 impl::tc* tc = *iter;
551 tc->init(m_vars);
553 return m_tcs;
557 // An auxiliary unary predicate that compares the given test case's
558 // identifier to the identifier stored in it.
560 class tc_equal_to_ident {
561 const std::string& m_ident;
563 public:
564 tc_equal_to_ident(const std::string& i) :
565 m_ident(i)
569 bool operator()(const impl::tc* tc)
571 return tc->get_md_var("ident") == m_ident;
575 void
576 tp::list_tcs(void)
578 tc_vector tcs = init_tcs();
579 detail::atf_tp_writer writer(std::cout);
581 for (tc_vector::const_iterator iter = tcs.begin();
582 iter != tcs.end(); iter++) {
583 const impl::vars_map vars = (*iter)->get_md_vars();
586 impl::vars_map::const_iterator iter2 = vars.find("ident");
587 INV(iter2 != vars.end());
588 writer.start_tc((*iter2).second);
591 for (impl::vars_map::const_iterator iter2 = vars.begin();
592 iter2 != vars.end(); iter2++) {
593 const std::string& key = (*iter2).first;
594 if (key != "ident")
595 writer.tc_meta_data(key, (*iter2).second);
598 writer.end_tc();
602 impl::tc*
603 tp::find_tc(tc_vector tcs, const std::string& name)
605 std::vector< std::string > ids;
606 for (tc_vector::iterator iter = tcs.begin();
607 iter != tcs.end(); iter++) {
608 impl::tc* tc = *iter;
610 if (tc->get_md_var("ident") == name)
611 return tc;
613 throw atf::application::usage_error("Unknown test case `%s'",
614 name.c_str());
617 std::pair< std::string, tp::tc_part >
618 tp::process_tcarg(const std::string& tcarg)
620 const std::string::size_type pos = tcarg.find(':');
621 if (pos == std::string::npos) {
622 return std::make_pair(tcarg, BODY);
623 } else {
624 const std::string tcname = tcarg.substr(0, pos);
626 const std::string partname = tcarg.substr(pos + 1);
627 if (partname == "body")
628 return std::make_pair(tcname, BODY);
629 else if (partname == "cleanup")
630 return std::make_pair(tcname, CLEANUP);
631 else {
632 using atf::application::usage_error;
633 throw usage_error("Invalid test case part `%s'", partname.c_str());
639 tp::run_tc(const std::string& tcarg)
641 const std::pair< std::string, tc_part > fields = process_tcarg(tcarg);
643 impl::tc* tc = find_tc(init_tcs(), fields.first);
645 if (!atf::env::has("__RUNNING_INSIDE_ATF_RUN") || atf::env::get(
646 "__RUNNING_INSIDE_ATF_RUN") != "internal-yes-value")
648 std::cerr << m_prog_name << ": WARNING: Running test cases without "
649 "atf-run(1) is unsupported\n";
650 std::cerr << m_prog_name << ": WARNING: No isolation nor timeout "
651 "control is being applied; you may get unexpected failures; see "
652 "atf-test-case(4)\n";
655 try {
656 switch (fields.second) {
657 case BODY:
658 tc->run(m_resfile.str());
659 break;
660 case CLEANUP:
661 tc->run_cleanup();
662 break;
663 default:
664 UNREACHABLE;
666 return EXIT_SUCCESS;
667 } catch (const std::runtime_error& e) {
668 std::cerr << "ERROR: " << e.what() << "\n";
669 return EXIT_FAILURE;
674 tp::main(void)
676 using atf::application::usage_error;
678 int errcode;
680 handle_srcdir();
682 if (m_lflag) {
683 if (m_argc > 0)
684 throw usage_error("Cannot provide test case names with -l");
686 list_tcs();
687 errcode = EXIT_SUCCESS;
688 } else {
689 if (m_argc == 0)
690 throw usage_error("Must provide a test case name");
691 else if (m_argc > 1)
692 throw usage_error("Cannot provide more than one test case name");
693 INV(m_argc == 1);
695 errcode = run_tc(m_argv[0]);
698 return errcode;
701 namespace atf {
702 namespace tests {
703 int run_tp(int, char* const*, void (*)(tp::tc_vector&));
708 impl::run_tp(int argc, char* const* argv, void (*add_tcs)(tp::tc_vector&))
710 return tp(add_tcs).run(argc, argv);