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"
60 #include "detail/application.hpp"
61 #include "detail/auto_array.hpp"
62 #include "detail/env.hpp"
63 #include "detail/exceptions.hpp"
64 #include "detail/fs.hpp"
65 #include "detail/sanity.hpp"
66 #include "detail/text.hpp"
68 namespace impl = atf::tests;
69 namespace detail = atf::tests::detail;
70 #define IMPL_NAME "atf::tests"
72 // ------------------------------------------------------------------------
73 // The "atf_tp_writer" class.
74 // ------------------------------------------------------------------------
76 detail::atf_tp_writer::atf_tp_writer(std::ostream& os) :
80 m_os << "Content-Type: application/X-atf-tp; version=\"1\"\n\n";
84 detail::atf_tp_writer::start_tc(const std::string& ident)
88 m_os << "ident: " << ident << "\n";
93 detail::atf_tp_writer::end_tc(void)
100 detail::atf_tp_writer::tc_meta_data(const std::string& name,
101 const std::string& value)
103 PRE(name != "ident");
104 m_os << name << ": " << value << "\n";
108 // ------------------------------------------------------------------------
109 // Free helper functions.
110 // ------------------------------------------------------------------------
113 detail::match(const std::string& regexp, const std::string& str)
115 return atf::text::match(str, regexp);
118 // ------------------------------------------------------------------------
120 // ------------------------------------------------------------------------
122 static std::map< atf_tc_t*, impl::tc* > wraps;
123 static std::map< const atf_tc_t*, const impl::tc* > cwraps;
125 struct impl::tc_impl {
128 tc_impl(const tc_impl&);
129 tc_impl& operator=(const tc_impl&);
136 tc_impl(const std::string& ident, const bool has_cleanup) :
138 m_has_cleanup(has_cleanup)
143 wrap_head(atf_tc_t *tc)
145 std::map< atf_tc_t*, impl::tc* >::iterator iter = wraps.find(tc);
146 INV(iter != wraps.end());
147 (*iter).second->head();
151 wrap_body(const atf_tc_t *tc)
153 std::map< const atf_tc_t*, const impl::tc* >::const_iterator iter =
155 INV(iter != cwraps.end());
157 (*iter).second->body();
158 } catch (const std::exception& e) {
159 (*iter).second->fail("Caught unhandled exception: " + std::string(
162 (*iter).second->fail("Caught unknown exception");
167 wrap_cleanup(const atf_tc_t *tc)
169 std::map< const atf_tc_t*, const impl::tc* >::const_iterator iter =
171 INV(iter != cwraps.end());
172 (*iter).second->cleanup();
176 impl::tc::tc(const std::string& ident, const bool has_cleanup) :
177 pimpl(new tc_impl(ident, has_cleanup))
183 cwraps.erase(&pimpl->m_tc);
184 wraps.erase(&pimpl->m_tc);
186 atf_tc_fini(&pimpl->m_tc);
190 impl::tc::init(const vars_map& config)
194 auto_array< const char * > array(new const char*[(config.size() * 2) + 1]);
195 const char **ptr = array.get();
196 for (vars_map::const_iterator iter = config.begin();
197 iter != config.end(); iter++) {
198 *ptr = (*iter).first.c_str();
199 *(ptr + 1) = (*iter).second.c_str();
204 wraps[&pimpl->m_tc] = this;
205 cwraps[&pimpl->m_tc] = this;
207 err = atf_tc_init(&pimpl->m_tc, pimpl->m_ident.c_str(), pimpl->wrap_head,
208 pimpl->wrap_body, pimpl->m_has_cleanup ? pimpl->wrap_cleanup : NULL,
210 if (atf_is_error(err))
211 throw_atf_error(err);
215 impl::tc::has_config_var(const std::string& var)
218 return atf_tc_has_config_var(&pimpl->m_tc, var.c_str());
222 impl::tc::has_md_var(const std::string& var)
225 return atf_tc_has_md_var(&pimpl->m_tc, var.c_str());
229 impl::tc::get_config_var(const std::string& var)
232 return atf_tc_get_config_var(&pimpl->m_tc, var.c_str());
236 impl::tc::get_config_var(const std::string& var, const std::string& defval)
239 return atf_tc_get_config_var_wd(&pimpl->m_tc, var.c_str(), defval.c_str());
243 impl::tc::get_md_var(const std::string& var)
246 return atf_tc_get_md_var(&pimpl->m_tc, var.c_str());
250 impl::tc::get_md_vars(void)
255 char **array = atf_tc_get_md_vars(&pimpl->m_tc);
258 for (ptr = array; *ptr != NULL; ptr += 2)
259 vars[*ptr] = *(ptr + 1);
261 atf_utils_free_charpp(array);
269 impl::tc::set_md_var(const std::string& var, const std::string& val)
271 atf_error_t err = atf_tc_set_md_var(&pimpl->m_tc, var.c_str(), val.c_str());
272 if (atf_is_error(err))
273 throw_atf_error(err);
277 impl::tc::run(const std::string& resfile)
280 atf_error_t err = atf_tc_run(&pimpl->m_tc, resfile.c_str());
281 if (atf_is_error(err))
282 throw_atf_error(err);
286 impl::tc::run_cleanup(void)
289 atf_error_t err = atf_tc_cleanup(&pimpl->m_tc);
290 if (atf_is_error(err))
291 throw_atf_error(err);
300 impl::tc::cleanup(void)
306 impl::tc::require_prog(const std::string& prog)
309 atf_tc_require_prog(prog.c_str());
319 impl::tc::fail(const std::string& reason)
321 atf_tc_fail("%s", reason.c_str());
325 impl::tc::fail_nonfatal(const std::string& reason)
327 atf_tc_fail_nonfatal("%s", reason.c_str());
331 impl::tc::skip(const std::string& reason)
333 atf_tc_skip("%s", reason.c_str());
337 impl::tc::check_errno(const char* file, const int line, const int exp_errno,
338 const char* expr_str, const bool result)
340 atf_tc_check_errno(file, line, exp_errno, expr_str, result);
344 impl::tc::require_errno(const char* file, const int line, const int exp_errno,
345 const char* expr_str, const bool result)
347 atf_tc_require_errno(file, line, exp_errno, expr_str, result);
351 impl::tc::expect_pass(void)
353 atf_tc_expect_pass();
357 impl::tc::expect_fail(const std::string& reason)
359 atf_tc_expect_fail("%s", reason.c_str());
363 impl::tc::expect_exit(const int exitcode, const std::string& reason)
365 atf_tc_expect_exit(exitcode, "%s", reason.c_str());
369 impl::tc::expect_signal(const int signo, const std::string& reason)
371 atf_tc_expect_signal(signo, "%s", reason.c_str());
375 impl::tc::expect_death(const std::string& reason)
377 atf_tc_expect_death("%s", reason.c_str());
381 impl::tc::expect_timeout(const std::string& reason)
383 atf_tc_expect_timeout("%s", reason.c_str());
386 // ------------------------------------------------------------------------
388 // ------------------------------------------------------------------------
390 class tp : public atf::application::app {
392 typedef std::vector< impl::tc * > tc_vector;
395 static const char* m_description;
398 atf::fs::path m_resfile;
399 std::string m_srcdir_arg;
400 atf::fs::path m_srcdir;
402 atf::tests::vars_map m_vars;
404 std::string specific_args(void) const;
405 options_set specific_options(void) const;
406 void process_option(int, const char*);
408 void (*m_add_tcs)(tc_vector&);
411 void parse_vflag(const std::string&);
412 void handle_srcdir(void);
414 tc_vector init_tcs(void);
422 impl::tc* find_tc(tc_vector, const std::string&);
423 static std::pair< std::string, tc_part > process_tcarg(const std::string&);
424 int run_tc(const std::string&);
427 tp(void (*)(tc_vector&));
433 const char* tp::m_description =
434 "This is an independent atf test program.";
436 tp::tp(void (*add_tcs)(tc_vector&)) :
437 app(m_description, "atf-test-program(1)"),
439 m_resfile("/dev/stdout"),
447 for (tc_vector::iterator iter = m_tcs.begin();
448 iter != m_tcs.end(); iter++) {
449 impl::tc* tc = *iter;
456 tp::specific_args(void)
463 tp::specific_options(void)
466 using atf::application::option;
468 opts.insert(option('l', "", "List test cases and their purpose"));
469 opts.insert(option('r', "resfile", "The file to which the test program "
470 "will write the results of the "
471 "executed test case"));
472 opts.insert(option('s', "srcdir", "Directory where the test's data "
473 "files are located"));
474 opts.insert(option('v', "var=value", "Sets the configuration variable "
475 "`var' to `value'"));
480 tp::process_option(int ch, const char* arg)
488 m_resfile = atf::fs::path(arg);
505 tp::parse_vflag(const std::string& str)
508 throw std::runtime_error("-v requires a non-empty argument");
510 std::vector< std::string > ws = atf::text::split(str, "=");
511 if (ws.size() == 1 && str[str.length() - 1] == '=') {
515 throw std::runtime_error("-v requires an argument of the form "
518 m_vars[ws[0]] = ws[1];
523 tp::handle_srcdir(void)
525 if (m_srcdir_arg.empty()) {
526 m_srcdir = atf::fs::path(m_argv0).branch_path();
527 if (m_srcdir.leaf_name() == ".libs")
528 m_srcdir = m_srcdir.branch_path();
530 m_srcdir = atf::fs::path(m_srcdir_arg);
532 if (!atf::fs::exists(m_srcdir / m_prog_name))
533 throw std::runtime_error("Cannot find the test program in the "
534 "source directory `" + m_srcdir.str() + "'");
536 if (!m_srcdir.is_absolute())
537 m_srcdir = m_srcdir.to_absolute();
539 m_vars["srcdir"] = m_srcdir.str();
546 for (tc_vector::iterator iter = m_tcs.begin();
547 iter != m_tcs.end(); iter++) {
548 impl::tc* tc = *iter;
556 // An auxiliary unary predicate that compares the given test case's
557 // identifier to the identifier stored in it.
559 class tc_equal_to_ident {
560 const std::string& m_ident;
563 tc_equal_to_ident(const std::string& i) :
568 bool operator()(const impl::tc* tc)
570 return tc->get_md_var("ident") == m_ident;
577 tc_vector tcs = init_tcs();
578 detail::atf_tp_writer writer(std::cout);
580 for (tc_vector::const_iterator iter = tcs.begin();
581 iter != tcs.end(); iter++) {
582 const impl::vars_map vars = (*iter)->get_md_vars();
585 impl::vars_map::const_iterator iter2 = vars.find("ident");
586 INV(iter2 != vars.end());
587 writer.start_tc((*iter2).second);
590 for (impl::vars_map::const_iterator iter2 = vars.begin();
591 iter2 != vars.end(); iter2++) {
592 const std::string& key = (*iter2).first;
594 writer.tc_meta_data(key, (*iter2).second);
602 tp::find_tc(tc_vector tcs, const std::string& name)
604 std::vector< std::string > ids;
605 for (tc_vector::iterator iter = tcs.begin();
606 iter != tcs.end(); iter++) {
607 impl::tc* tc = *iter;
609 if (tc->get_md_var("ident") == name)
612 throw atf::application::usage_error("Unknown test case `%s'",
616 std::pair< std::string, tp::tc_part >
617 tp::process_tcarg(const std::string& tcarg)
619 const std::string::size_type pos = tcarg.find(':');
620 if (pos == std::string::npos) {
621 return std::make_pair(tcarg, BODY);
623 const std::string tcname = tcarg.substr(0, pos);
625 const std::string partname = tcarg.substr(pos + 1);
626 if (partname == "body")
627 return std::make_pair(tcname, BODY);
628 else if (partname == "cleanup")
629 return std::make_pair(tcname, CLEANUP);
631 using atf::application::usage_error;
632 throw usage_error("Invalid test case part `%s'", partname.c_str());
638 tp::run_tc(const std::string& tcarg)
640 const std::pair< std::string, tc_part > fields = process_tcarg(tcarg);
642 impl::tc* tc = find_tc(init_tcs(), fields.first);
644 if (!atf::env::has("__RUNNING_INSIDE_ATF_RUN") || atf::env::get(
645 "__RUNNING_INSIDE_ATF_RUN") != "internal-yes-value")
647 std::cerr << m_prog_name << ": WARNING: Running test cases without "
648 "atf-run(1) is unsupported\n";
649 std::cerr << m_prog_name << ": WARNING: No isolation nor timeout "
650 "control is being applied; you may get unexpected failures; see "
651 "atf-test-case(4)\n";
655 switch (fields.second) {
657 tc->run(m_resfile.str());
666 } catch (const std::runtime_error& e) {
667 std::cerr << "ERROR: " << e.what() << "\n";
675 using atf::application::usage_error;
683 throw usage_error("Cannot provide test case names with -l");
686 errcode = EXIT_SUCCESS;
689 throw usage_error("Must provide a test case name");
691 throw usage_error("Cannot provide more than one test case name");
694 errcode = run_tc(m_argv[0]);
702 int run_tp(int, char* const*, void (*)(tp::tc_vector&));
707 impl::run_tp(int argc, char* const* argv, void (*add_tcs)(tp::tc_vector&))
709 return tp(add_tcs).run(argc, argv);