1 // Copyright (c) 2007 The NetBSD Foundation, Inc.
2 // All rights reserved.
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions
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.
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.
26 #include "atf-c++/tests.hpp"
28 #if defined(HAVE_CONFIG_H)
33 #include <sys/types.h>
55 #include "atf-c/error.h"
57 #include "atf-c/utils.h"
60 #include "atf-c++/detail/application.hpp"
61 #include "atf-c++/detail/auto_array.hpp"
62 #include "atf-c++/detail/env.hpp"
63 #include "atf-c++/detail/exceptions.hpp"
64 #include "atf-c++/detail/fs.hpp"
65 #include "atf-c++/detail/sanity.hpp"
66 #include "atf-c++/detail/text.hpp"
68 #if defined(HAVE_GNU_GETOPT)
69 # define GETOPT_POSIX "+"
71 # define GETOPT_POSIX ""
74 namespace impl = atf::tests;
75 namespace detail = atf::tests::detail;
76 #define IMPL_NAME "atf::tests"
78 using atf::application::usage_error;
80 // ------------------------------------------------------------------------
81 // The "atf_tp_writer" class.
82 // ------------------------------------------------------------------------
84 detail::atf_tp_writer::atf_tp_writer(std::ostream& os) :
88 m_os << "Content-Type: application/X-atf-tp; version=\"1\"\n\n";
92 detail::atf_tp_writer::start_tc(const std::string& ident)
96 m_os << "ident: " << ident << "\n";
101 detail::atf_tp_writer::end_tc(void)
108 detail::atf_tp_writer::tc_meta_data(const std::string& name,
109 const std::string& value)
111 PRE(name != "ident");
112 m_os << name << ": " << value << "\n";
116 // ------------------------------------------------------------------------
117 // Free helper functions.
118 // ------------------------------------------------------------------------
120 std::string Program_Name;
123 set_program_name(const char* argv0)
125 const std::string program_name = atf::fs::path(argv0).leaf_name();
126 // Libtool workaround: if running from within the source tree (binaries
127 // that are not installed yet), skip the "lt-" prefix added to files in
128 // the ".libs" directory to show the real (not temporary) name.
129 if (program_name.substr(0, 3) == "lt-")
130 Program_Name = program_name.substr(3);
132 Program_Name = program_name;
136 detail::match(const std::string& regexp, const std::string& str)
138 return atf::text::match(str, regexp);
141 // ------------------------------------------------------------------------
143 // ------------------------------------------------------------------------
145 static std::map< atf_tc_t*, impl::tc* > wraps;
146 static std::map< const atf_tc_t*, const impl::tc* > cwraps;
148 struct impl::tc_impl {
151 tc_impl(const tc_impl&);
152 tc_impl& operator=(const tc_impl&);
159 tc_impl(const std::string& ident, const bool has_cleanup) :
161 m_has_cleanup(has_cleanup)
166 wrap_head(atf_tc_t *tc)
168 std::map< atf_tc_t*, impl::tc* >::iterator iter = wraps.find(tc);
169 INV(iter != wraps.end());
170 (*iter).second->head();
174 wrap_body(const atf_tc_t *tc)
176 std::map< const atf_tc_t*, const impl::tc* >::const_iterator iter =
178 INV(iter != cwraps.end());
179 (*iter).second->body();
183 wrap_cleanup(const atf_tc_t *tc)
185 std::map< const atf_tc_t*, const impl::tc* >::const_iterator iter =
187 INV(iter != cwraps.end());
188 (*iter).second->cleanup();
192 impl::tc::tc(const std::string& ident, const bool has_cleanup) :
193 pimpl(new tc_impl(ident, has_cleanup))
199 cwraps.erase(&pimpl->m_tc);
200 wraps.erase(&pimpl->m_tc);
202 atf_tc_fini(&pimpl->m_tc);
206 impl::tc::init(const vars_map& config)
210 auto_array< const char * > array(new const char*[(config.size() * 2) + 1]);
211 const char **ptr = array.get();
212 for (vars_map::const_iterator iter = config.begin();
213 iter != config.end(); iter++) {
214 *ptr = (*iter).first.c_str();
215 *(ptr + 1) = (*iter).second.c_str();
220 wraps[&pimpl->m_tc] = this;
221 cwraps[&pimpl->m_tc] = this;
223 err = atf_tc_init(&pimpl->m_tc, pimpl->m_ident.c_str(), pimpl->wrap_head,
224 pimpl->wrap_body, pimpl->m_has_cleanup ? pimpl->wrap_cleanup : NULL,
226 if (atf_is_error(err))
227 throw_atf_error(err);
231 impl::tc::has_config_var(const std::string& var)
234 return atf_tc_has_config_var(&pimpl->m_tc, var.c_str());
238 impl::tc::has_md_var(const std::string& var)
241 return atf_tc_has_md_var(&pimpl->m_tc, var.c_str());
245 impl::tc::get_config_var(const std::string& var)
248 return atf_tc_get_config_var(&pimpl->m_tc, var.c_str());
252 impl::tc::get_config_var(const std::string& var, const std::string& defval)
255 return atf_tc_get_config_var_wd(&pimpl->m_tc, var.c_str(), defval.c_str());
259 impl::tc::get_md_var(const std::string& var)
262 return atf_tc_get_md_var(&pimpl->m_tc, var.c_str());
266 impl::tc::get_md_vars(void)
271 char **array = atf_tc_get_md_vars(&pimpl->m_tc);
274 for (ptr = array; *ptr != NULL; ptr += 2)
275 vars[*ptr] = *(ptr + 1);
277 atf_utils_free_charpp(array);
285 impl::tc::set_md_var(const std::string& var, const std::string& val)
287 atf_error_t err = atf_tc_set_md_var(&pimpl->m_tc, var.c_str(), val.c_str());
288 if (atf_is_error(err))
289 throw_atf_error(err);
293 impl::tc::run(const std::string& resfile)
296 atf_error_t err = atf_tc_run(&pimpl->m_tc, resfile.c_str());
297 if (atf_is_error(err))
298 throw_atf_error(err);
302 impl::tc::run_cleanup(void)
305 atf_error_t err = atf_tc_cleanup(&pimpl->m_tc);
306 if (atf_is_error(err))
307 throw_atf_error(err);
316 impl::tc::cleanup(void)
322 impl::tc::require_prog(const std::string& prog)
325 atf_tc_require_prog(prog.c_str());
335 impl::tc::fail(const std::string& reason)
337 atf_tc_fail("%s", reason.c_str());
341 impl::tc::fail_nonfatal(const std::string& reason)
343 atf_tc_fail_nonfatal("%s", reason.c_str());
347 impl::tc::skip(const std::string& reason)
349 atf_tc_skip("%s", reason.c_str());
353 impl::tc::check_errno(const char* file, const int line, const int exp_errno,
354 const char* expr_str, const bool result)
356 atf_tc_check_errno(file, line, exp_errno, expr_str, result);
360 impl::tc::require_errno(const char* file, const int line, const int exp_errno,
361 const char* expr_str, const bool result)
363 atf_tc_require_errno(file, line, exp_errno, expr_str, result);
367 impl::tc::expect_pass(void)
369 atf_tc_expect_pass();
373 impl::tc::expect_fail(const std::string& reason)
375 atf_tc_expect_fail("%s", reason.c_str());
379 impl::tc::expect_exit(const int exitcode, const std::string& reason)
381 atf_tc_expect_exit(exitcode, "%s", reason.c_str());
385 impl::tc::expect_signal(const int signo, const std::string& reason)
387 atf_tc_expect_signal(signo, "%s", reason.c_str());
391 impl::tc::expect_death(const std::string& reason)
393 atf_tc_expect_death("%s", reason.c_str());
397 impl::tc::expect_timeout(const std::string& reason)
399 atf_tc_expect_timeout("%s", reason.c_str());
402 // ------------------------------------------------------------------------
403 // Test program main code.
404 // ------------------------------------------------------------------------
408 typedef std::vector< impl::tc * > tc_vector;
410 enum tc_part { BODY, CLEANUP };
413 parse_vflag(const std::string& str, atf::tests::vars_map& vars)
416 throw std::runtime_error("-v requires a non-empty argument");
418 std::vector< std::string > ws = atf::text::split(str, "=");
419 if (ws.size() == 1 && str[str.length() - 1] == '=') {
423 throw std::runtime_error("-v requires an argument of the form "
431 handle_srcdir(const char* argv0, const std::string& srcdir_arg)
433 atf::fs::path srcdir(".");
435 if (srcdir_arg.empty()) {
436 srcdir = atf::fs::path(argv0).branch_path();
437 if (srcdir.leaf_name() == ".libs")
438 srcdir = srcdir.branch_path();
440 srcdir = atf::fs::path(srcdir_arg);
442 if (!atf::fs::exists(srcdir / Program_Name))
443 throw usage_error("Cannot find the test program in the source "
444 "directory `%s'", srcdir.c_str());
446 if (!srcdir.is_absolute())
447 srcdir = srcdir.to_absolute();
453 init_tcs(void (*add_tcs)(tc_vector&), tc_vector& tcs,
454 const atf::tests::vars_map& vars)
457 for (tc_vector::iterator iter = tcs.begin(); iter != tcs.end(); iter++) {
458 impl::tc* tc = *iter;
465 list_tcs(const tc_vector& tcs)
467 detail::atf_tp_writer writer(std::cout);
469 for (tc_vector::const_iterator iter = tcs.begin();
470 iter != tcs.end(); iter++) {
471 const impl::vars_map vars = (*iter)->get_md_vars();
474 impl::vars_map::const_iterator iter2 = vars.find("ident");
475 INV(iter2 != vars.end());
476 writer.start_tc((*iter2).second);
479 for (impl::vars_map::const_iterator iter2 = vars.begin();
480 iter2 != vars.end(); iter2++) {
481 const std::string& key = (*iter2).first;
483 writer.tc_meta_data(key, (*iter2).second);
493 find_tc(tc_vector tcs, const std::string& name)
495 std::vector< std::string > ids;
496 for (tc_vector::iterator iter = tcs.begin();
497 iter != tcs.end(); iter++) {
498 impl::tc* tc = *iter;
500 if (tc->get_md_var("ident") == name)
503 throw usage_error("Unknown test case `%s'", name.c_str());
506 static std::pair< std::string, tc_part >
507 process_tcarg(const std::string& tcarg)
509 const std::string::size_type pos = tcarg.find(':');
510 if (pos == std::string::npos) {
511 return std::make_pair(tcarg, BODY);
513 const std::string tcname = tcarg.substr(0, pos);
515 const std::string partname = tcarg.substr(pos + 1);
516 if (partname == "body")
517 return std::make_pair(tcname, BODY);
518 else if (partname == "cleanup")
519 return std::make_pair(tcname, CLEANUP);
521 throw usage_error("Invalid test case part `%s'", partname.c_str());
527 run_tc(tc_vector& tcs, const std::string& tcarg, const atf::fs::path& resfile)
529 const std::pair< std::string, tc_part > fields = process_tcarg(tcarg);
531 impl::tc* tc = find_tc(tcs, fields.first);
533 if (!atf::env::has("__RUNNING_INSIDE_ATF_RUN") || atf::env::get(
534 "__RUNNING_INSIDE_ATF_RUN") != "internal-yes-value")
536 std::cerr << Program_Name << ": WARNING: Running test cases outside "
537 "of kyua(1) is unsupported\n";
538 std::cerr << Program_Name << ": WARNING: No isolation nor timeout "
539 "control is being applied; you may get unexpected failures; see "
540 "atf-test-case(4)\n";
543 switch (fields.second) {
545 tc->run(resfile.str());
557 safe_main(int argc, char** argv, void (*add_tcs)(tc_vector&))
559 const char* argv0 = argv[0];
562 atf::fs::path resfile("/dev/stdout");
563 std::string srcdir_arg;
564 atf::tests::vars_map vars;
571 while ((ch = ::getopt(argc, argv, GETOPT_POSIX ":lr:s:v:")) != -1) {
578 resfile = atf::fs::path(::optarg);
582 srcdir_arg = ::optarg;
586 parse_vflag(::optarg, vars);
590 throw usage_error("Option -%c requires an argument.", ::optopt);
595 throw usage_error("Unknown option -%c.", ::optopt);
601 // Clear getopt state just in case the test wants to use it.
602 ::opterr = old_opterr;
604 #if defined(HAVE_OPTRESET)
608 vars["srcdir"] = handle_srcdir(argv0, srcdir_arg).str();
615 throw usage_error("Cannot provide test case names with -l");
617 init_tcs(add_tcs, tcs, vars);
618 errcode = list_tcs(tcs);
621 throw usage_error("Must provide a test case name");
623 throw usage_error("Cannot provide more than one test case name");
626 init_tcs(add_tcs, tcs, vars);
627 errcode = run_tc(tcs, argv[0], resfile);
629 for (tc_vector::iterator iter = tcs.begin(); iter != tcs.end(); iter++) {
630 impl::tc* tc = *iter;
638 } // anonymous namespace
642 int run_tp(int, char**, void (*)(tc_vector&));
647 impl::run_tp(int argc, char** argv, void (*add_tcs)(tc_vector&))
650 set_program_name(argv[0]);
651 return ::safe_main(argc, argv, add_tcs);
652 } catch (const usage_error& e) {
654 << Program_Name << ": ERROR: " << e.what() << '\n'
655 << Program_Name << ": See atf-test-program(1) for usage details.\n";