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(), "%s", 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
);