2 // Automated Testing Framework (atf)
4 // Copyright (c) 2007 The NetBSD Foundation, Inc.
5 // All rights reserved.
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
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.
31 #include <sys/types.h>
53 #include "atf-c/error.h"
55 #include "atf-c/utils.h"
58 #include "noncopyable.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) :
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);
91 detail::atf_tp_writer::start_tc(const std::string& ident)
95 m_os << "ident: " << ident << "\n";
100 detail::atf_tp_writer::end_tc(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";
115 // ------------------------------------------------------------------------
116 // Free helper functions.
117 // ------------------------------------------------------------------------
120 detail::match(const std::string& regexp, const std::string& str)
122 return atf::text::match(str, regexp);
125 // ------------------------------------------------------------------------
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 {
137 tc_impl(const std::string& ident, const bool has_cleanup) :
139 m_has_cleanup(has_cleanup)
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();
152 wrap_body(const atf_tc_t *tc)
154 std::map< const atf_tc_t*, const impl::tc* >::const_iterator iter =
156 INV(iter != cwraps.end());
158 (*iter).second->body();
159 } catch (const std::exception& e) {
160 (*iter).second->fail("Caught unhandled exception: " + std::string(
163 (*iter).second->fail("Caught unknown exception");
168 wrap_cleanup(const atf_tc_t *tc)
170 std::map< const atf_tc_t*, const impl::tc* >::const_iterator iter =
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))
184 cwraps.erase(&pimpl->m_tc);
185 wraps.erase(&pimpl->m_tc);
187 atf_tc_fini(&pimpl->m_tc);
191 impl::tc::init(const vars_map& config)
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();
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,
211 if (atf_is_error(err))
212 throw_atf_error(err);
216 impl::tc::has_config_var(const std::string& var)
219 return atf_tc_has_config_var(&pimpl->m_tc, var.c_str());
223 impl::tc::has_md_var(const std::string& var)
226 return atf_tc_has_md_var(&pimpl->m_tc, var.c_str());
230 impl::tc::get_config_var(const std::string& var)
233 return atf_tc_get_config_var(&pimpl->m_tc, var.c_str());
237 impl::tc::get_config_var(const std::string& var, const std::string& defval)
240 return atf_tc_get_config_var_wd(&pimpl->m_tc, var.c_str(), defval.c_str());
244 impl::tc::get_md_var(const std::string& var)
247 return atf_tc_get_md_var(&pimpl->m_tc, var.c_str());
251 impl::tc::get_md_vars(void)
256 char **array = atf_tc_get_md_vars(&pimpl->m_tc);
259 for (ptr = array; *ptr != NULL; ptr += 2)
260 vars[*ptr] = *(ptr + 1);
262 atf_utils_free_charpp(array);
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);
278 impl::tc::run(const std::string& resfile)
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);
287 impl::tc::run_cleanup(void)
290 atf_error_t err = atf_tc_cleanup(&pimpl->m_tc);
291 if (atf_is_error(err))
292 throw_atf_error(err);
301 impl::tc::cleanup(void)
307 impl::tc::require_prog(const std::string& prog)
310 atf_tc_require_prog(prog.c_str());
320 impl::tc::fail(const std::string& reason)
322 atf_tc_fail("%s", reason.c_str());
326 impl::tc::fail_nonfatal(const std::string& reason)
328 atf_tc_fail_nonfatal("%s", reason.c_str());
332 impl::tc::skip(const std::string& reason)
334 atf_tc_skip("%s", reason.c_str());
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);
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);
352 impl::tc::expect_pass(void)
354 atf_tc_expect_pass();
358 impl::tc::expect_fail(const std::string& reason)
360 atf_tc_expect_fail("%s", reason.c_str());
364 impl::tc::expect_exit(const int exitcode, const std::string& reason)
366 atf_tc_expect_exit(exitcode, "%s", reason.c_str());
370 impl::tc::expect_signal(const int signo, const std::string& reason)
372 atf_tc_expect_signal(signo, "%s", reason.c_str());
376 impl::tc::expect_death(const std::string& reason)
378 atf_tc_expect_death("%s", reason.c_str());
382 impl::tc::expect_timeout(const std::string& reason)
384 atf_tc_expect_timeout("%s", reason.c_str());
387 // ------------------------------------------------------------------------
389 // ------------------------------------------------------------------------
391 class tp : public atf::application::app {
393 typedef std::vector< impl::tc * > tc_vector;
396 static const char* m_description;
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&);
412 void parse_vflag(const std::string&);
413 void handle_srcdir(void);
415 tc_vector init_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&);
428 tp(void (*)(tc_vector&));
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),
440 m_resfile("/dev/stdout"),
448 for (tc_vector::iterator iter = m_tcs.begin();
449 iter != m_tcs.end(); iter++) {
450 impl::tc* tc = *iter;
457 tp::specific_args(void)
464 tp::specific_options(void)
467 using atf::application::option;
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'"));
481 tp::process_option(int ch, const char* arg)
489 m_resfile = atf::fs::path(arg);
506 tp::parse_vflag(const std::string& str)
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] == '=') {
516 throw std::runtime_error("-v requires an argument of the form "
519 m_vars[ws[0]] = ws[1];
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();
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();
547 for (tc_vector::iterator iter = m_tcs.begin();
548 iter != m_tcs.end(); iter++) {
549 impl::tc* tc = *iter;
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;
564 tc_equal_to_ident(const std::string& i) :
569 bool operator()(const impl::tc* tc)
571 return tc->get_md_var("ident") == m_ident;
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;
595 writer.tc_meta_data(key, (*iter2).second);
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)
613 throw atf::application::usage_error("Unknown test case `%s'",
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);
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);
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";
656 switch (fields.second) {
658 tc->run(m_resfile.str());
667 } catch (const std::runtime_error& e) {
668 std::cerr << "ERROR: " << e.what() << "\n";
676 using atf::application::usage_error;
684 throw usage_error("Cannot provide test case names with -l");
687 errcode = EXIT_SUCCESS;
690 throw usage_error("Must provide a test case name");
692 throw usage_error("Cannot provide more than one test case name");
695 errcode = run_tc(m_argv[0]);
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);