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
);