1e2207522Sjmmv //
2e2207522Sjmmv // Automated Testing Framework (atf)
3e2207522Sjmmv //
436b886feSjmmv // Copyright (c) 2007 The NetBSD Foundation, Inc.
5e2207522Sjmmv // All rights reserved.
6e2207522Sjmmv //
7e2207522Sjmmv // Redistribution and use in source and binary forms, with or without
8e2207522Sjmmv // modification, are permitted provided that the following conditions
9e2207522Sjmmv // are met:
10e2207522Sjmmv // 1. Redistributions of source code must retain the above copyright
11e2207522Sjmmv // notice, this list of conditions and the following disclaimer.
12e2207522Sjmmv // 2. Redistributions in binary form must reproduce the above copyright
13e2207522Sjmmv // notice, this list of conditions and the following disclaimer in the
14e2207522Sjmmv // documentation and/or other materials provided with the distribution.
15e2207522Sjmmv //
16e2207522Sjmmv // THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
17e2207522Sjmmv // CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
18e2207522Sjmmv // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19e2207522Sjmmv // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20e2207522Sjmmv // IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
21e2207522Sjmmv // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22e2207522Sjmmv // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23e2207522Sjmmv // GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24e2207522Sjmmv // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25e2207522Sjmmv // IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26e2207522Sjmmv // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27e2207522Sjmmv // IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28e2207522Sjmmv //
29e2207522Sjmmv
30e2207522Sjmmv extern "C" {
31e2207522Sjmmv #include <sys/types.h>
32e2207522Sjmmv #include <sys/stat.h>
33e2207522Sjmmv #include <sys/time.h>
34e2207522Sjmmv #include <sys/wait.h>
35e2207522Sjmmv #include <signal.h>
36e2207522Sjmmv #include <unistd.h>
37e2207522Sjmmv }
38e2207522Sjmmv
39e2207522Sjmmv #include <algorithm>
40e2207522Sjmmv #include <cctype>
41e2207522Sjmmv #include <cerrno>
42e2207522Sjmmv #include <cstdlib>
43e2207522Sjmmv #include <cstring>
44e2207522Sjmmv #include <fstream>
45e2207522Sjmmv #include <iostream>
46e2207522Sjmmv #include <map>
47e2207522Sjmmv #include <memory>
48e2207522Sjmmv #include <sstream>
49e2207522Sjmmv #include <stdexcept>
50e2207522Sjmmv #include <vector>
51e2207522Sjmmv
52e2207522Sjmmv extern "C" {
53e2207522Sjmmv #include "atf-c/error.h"
540bc037cbSjmmv #include "atf-c/tc.h"
550bc037cbSjmmv #include "atf-c/utils.h"
56e2207522Sjmmv }
57e2207522Sjmmv
580bc037cbSjmmv #include "tests.hpp"
590bc037cbSjmmv
600bc037cbSjmmv #include "detail/application.hpp"
616f587c98Sjmmv #include "detail/auto_array.hpp"
620bc037cbSjmmv #include "detail/env.hpp"
630bc037cbSjmmv #include "detail/exceptions.hpp"
640bc037cbSjmmv #include "detail/fs.hpp"
650bc037cbSjmmv #include "detail/sanity.hpp"
660bc037cbSjmmv #include "detail/text.hpp"
67e2207522Sjmmv
68e2207522Sjmmv namespace impl = atf::tests;
698482c0c3Sjmmv namespace detail = atf::tests::detail;
70e2207522Sjmmv #define IMPL_NAME "atf::tests"
71e2207522Sjmmv
72e2207522Sjmmv // ------------------------------------------------------------------------
738482c0c3Sjmmv // The "atf_tp_writer" class.
74e2207522Sjmmv // ------------------------------------------------------------------------
75e2207522Sjmmv
atf_tp_writer(std::ostream & os)768482c0c3Sjmmv detail::atf_tp_writer::atf_tp_writer(std::ostream& os) :
778482c0c3Sjmmv m_os(os),
788482c0c3Sjmmv m_is_first(true)
79e2207522Sjmmv {
80*97bff204Sjmmv m_os << "Content-Type: application/X-atf-tp; version=\"1\"\n\n";
81407d7761Sjmmv }
82407d7761Sjmmv
83407d7761Sjmmv void
start_tc(const std::string & ident)848482c0c3Sjmmv detail::atf_tp_writer::start_tc(const std::string& ident)
85407d7761Sjmmv {
868482c0c3Sjmmv if (!m_is_first)
870bc037cbSjmmv m_os << "\n";
880bc037cbSjmmv m_os << "ident: " << ident << "\n";
898482c0c3Sjmmv m_os.flush();
90407d7761Sjmmv }
91407d7761Sjmmv
92407d7761Sjmmv void
end_tc(void)938482c0c3Sjmmv detail::atf_tp_writer::end_tc(void)
94407d7761Sjmmv {
958482c0c3Sjmmv if (m_is_first)
968482c0c3Sjmmv m_is_first = false;
97407d7761Sjmmv }
988482c0c3Sjmmv
998482c0c3Sjmmv void
tc_meta_data(const std::string & name,const std::string & value)1008482c0c3Sjmmv detail::atf_tp_writer::tc_meta_data(const std::string& name,
1018482c0c3Sjmmv const std::string& value)
1028482c0c3Sjmmv {
1038482c0c3Sjmmv PRE(name != "ident");
1040bc037cbSjmmv m_os << name << ": " << value << "\n";
1058482c0c3Sjmmv m_os.flush();
106407d7761Sjmmv }
107407d7761Sjmmv
108e2207522Sjmmv // ------------------------------------------------------------------------
1090b634f14Sjmmv // Free helper functions.
1100b634f14Sjmmv // ------------------------------------------------------------------------
1110b634f14Sjmmv
1120b634f14Sjmmv bool
match(const std::string & regexp,const std::string & str)1130b634f14Sjmmv detail::match(const std::string& regexp, const std::string& str)
1140b634f14Sjmmv {
1150b634f14Sjmmv return atf::text::match(str, regexp);
1160b634f14Sjmmv }
1170b634f14Sjmmv
1180b634f14Sjmmv // ------------------------------------------------------------------------
119e2207522Sjmmv // The "tc" class.
120e2207522Sjmmv // ------------------------------------------------------------------------
121e2207522Sjmmv
122e2207522Sjmmv static std::map< atf_tc_t*, impl::tc* > wraps;
123e2207522Sjmmv static std::map< const atf_tc_t*, const impl::tc* > cwraps;
124e2207522Sjmmv
125*97bff204Sjmmv struct impl::tc_impl {
126*97bff204Sjmmv private:
127*97bff204Sjmmv // Non-copyable.
128*97bff204Sjmmv tc_impl(const tc_impl&);
129*97bff204Sjmmv tc_impl& operator=(const tc_impl&);
130*97bff204Sjmmv
131*97bff204Sjmmv public:
1320bc037cbSjmmv std::string m_ident;
1330bc037cbSjmmv atf_tc_t m_tc;
1340bc037cbSjmmv bool m_has_cleanup;
135e2207522Sjmmv
tc_implimpl::tc_impl1360bc037cbSjmmv tc_impl(const std::string& ident, const bool has_cleanup) :
1378482c0c3Sjmmv m_ident(ident),
1388482c0c3Sjmmv m_has_cleanup(has_cleanup)
139e2207522Sjmmv {
140e2207522Sjmmv }
141e2207522Sjmmv
1420bc037cbSjmmv static void
wrap_headimpl::tc_impl1430bc037cbSjmmv wrap_head(atf_tc_t *tc)
1440bc037cbSjmmv {
1450bc037cbSjmmv std::map< atf_tc_t*, impl::tc* >::iterator iter = wraps.find(tc);
1460bc037cbSjmmv INV(iter != wraps.end());
1470bc037cbSjmmv (*iter).second->head();
1480bc037cbSjmmv }
1490bc037cbSjmmv
1500bc037cbSjmmv static void
wrap_bodyimpl::tc_impl1510bc037cbSjmmv wrap_body(const atf_tc_t *tc)
1520bc037cbSjmmv {
1530bc037cbSjmmv std::map< const atf_tc_t*, const impl::tc* >::const_iterator iter =
1540bc037cbSjmmv cwraps.find(tc);
1550bc037cbSjmmv INV(iter != cwraps.end());
1560bc037cbSjmmv try {
1570bc037cbSjmmv (*iter).second->body();
1580bc037cbSjmmv } catch (const std::exception& e) {
1590bc037cbSjmmv (*iter).second->fail("Caught unhandled exception: " + std::string(
1600bc037cbSjmmv e.what()));
1610bc037cbSjmmv } catch (...) {
1620bc037cbSjmmv (*iter).second->fail("Caught unknown exception");
1630bc037cbSjmmv }
1640bc037cbSjmmv }
1650bc037cbSjmmv
1660bc037cbSjmmv static void
wrap_cleanupimpl::tc_impl1670bc037cbSjmmv wrap_cleanup(const atf_tc_t *tc)
1680bc037cbSjmmv {
1690bc037cbSjmmv std::map< const atf_tc_t*, const impl::tc* >::const_iterator iter =
1700bc037cbSjmmv cwraps.find(tc);
1710bc037cbSjmmv INV(iter != cwraps.end());
1720bc037cbSjmmv (*iter).second->cleanup();
1730bc037cbSjmmv }
1740bc037cbSjmmv };
1750bc037cbSjmmv
tc(const std::string & ident,const bool has_cleanup)1760bc037cbSjmmv impl::tc::tc(const std::string& ident, const bool has_cleanup) :
1770bc037cbSjmmv pimpl(new tc_impl(ident, has_cleanup))
1780bc037cbSjmmv {
1790bc037cbSjmmv }
1800bc037cbSjmmv
~tc(void)181e2207522Sjmmv impl::tc::~tc(void)
182e2207522Sjmmv {
1830bc037cbSjmmv cwraps.erase(&pimpl->m_tc);
1840bc037cbSjmmv wraps.erase(&pimpl->m_tc);
185e2207522Sjmmv
1860bc037cbSjmmv atf_tc_fini(&pimpl->m_tc);
187e2207522Sjmmv }
188e2207522Sjmmv
189e2207522Sjmmv void
init(const vars_map & config)190e2207522Sjmmv impl::tc::init(const vars_map& config)
191e2207522Sjmmv {
192e2207522Sjmmv atf_error_t err;
193e2207522Sjmmv
1946f587c98Sjmmv auto_array< const char * > array(new const char*[(config.size() * 2) + 1]);
1950bc037cbSjmmv const char **ptr = array.get();
196e2207522Sjmmv for (vars_map::const_iterator iter = config.begin();
197e2207522Sjmmv iter != config.end(); iter++) {
1980bc037cbSjmmv *ptr = (*iter).first.c_str();
1990bc037cbSjmmv *(ptr + 1) = (*iter).second.c_str();
2000bc037cbSjmmv ptr += 2;
2010bc037cbSjmmv }
2020bc037cbSjmmv *ptr = NULL;
203e2207522Sjmmv
2040bc037cbSjmmv wraps[&pimpl->m_tc] = this;
2050bc037cbSjmmv cwraps[&pimpl->m_tc] = this;
2060bc037cbSjmmv
2070bc037cbSjmmv err = atf_tc_init(&pimpl->m_tc, pimpl->m_ident.c_str(), pimpl->wrap_head,
2080bc037cbSjmmv pimpl->wrap_body, pimpl->m_has_cleanup ? pimpl->wrap_cleanup : NULL,
2090bc037cbSjmmv array.get());
2100bc037cbSjmmv if (atf_is_error(err))
211e2207522Sjmmv throw_atf_error(err);
212e2207522Sjmmv }
213e2207522Sjmmv
214e2207522Sjmmv bool
has_config_var(const std::string & var) const215e2207522Sjmmv impl::tc::has_config_var(const std::string& var)
216e2207522Sjmmv const
217e2207522Sjmmv {
2180bc037cbSjmmv return atf_tc_has_config_var(&pimpl->m_tc, var.c_str());
219e2207522Sjmmv }
220e2207522Sjmmv
221e2207522Sjmmv bool
has_md_var(const std::string & var) const222e2207522Sjmmv impl::tc::has_md_var(const std::string& var)
223e2207522Sjmmv const
224e2207522Sjmmv {
2250bc037cbSjmmv return atf_tc_has_md_var(&pimpl->m_tc, var.c_str());
226e2207522Sjmmv }
227e2207522Sjmmv
228e2207522Sjmmv const std::string
get_config_var(const std::string & var) const229e2207522Sjmmv impl::tc::get_config_var(const std::string& var)
230e2207522Sjmmv const
231e2207522Sjmmv {
2320bc037cbSjmmv return atf_tc_get_config_var(&pimpl->m_tc, var.c_str());
233e2207522Sjmmv }
234e2207522Sjmmv
235e2207522Sjmmv const std::string
get_config_var(const std::string & var,const std::string & defval) const236e2207522Sjmmv impl::tc::get_config_var(const std::string& var, const std::string& defval)
237e2207522Sjmmv const
238e2207522Sjmmv {
2390bc037cbSjmmv return atf_tc_get_config_var_wd(&pimpl->m_tc, var.c_str(), defval.c_str());
240e2207522Sjmmv }
241e2207522Sjmmv
242e2207522Sjmmv const std::string
get_md_var(const std::string & var) const243e2207522Sjmmv impl::tc::get_md_var(const std::string& var)
244e2207522Sjmmv const
245e2207522Sjmmv {
2460bc037cbSjmmv return atf_tc_get_md_var(&pimpl->m_tc, var.c_str());
247e2207522Sjmmv }
248e2207522Sjmmv
249407d7761Sjmmv const impl::vars_map
get_md_vars(void) const250407d7761Sjmmv impl::tc::get_md_vars(void)
251407d7761Sjmmv const
252407d7761Sjmmv {
253407d7761Sjmmv vars_map vars;
254407d7761Sjmmv
2550bc037cbSjmmv char **array = atf_tc_get_md_vars(&pimpl->m_tc);
2560bc037cbSjmmv try {
2570bc037cbSjmmv char **ptr;
2580bc037cbSjmmv for (ptr = array; *ptr != NULL; ptr += 2)
2590bc037cbSjmmv vars[*ptr] = *(ptr + 1);
2600bc037cbSjmmv } catch (...) {
2610bc037cbSjmmv atf_utils_free_charpp(array);
2620bc037cbSjmmv throw;
263407d7761Sjmmv }
264407d7761Sjmmv
265407d7761Sjmmv return vars;
266407d7761Sjmmv }
267407d7761Sjmmv
268e2207522Sjmmv void
set_md_var(const std::string & var,const std::string & val)269e2207522Sjmmv impl::tc::set_md_var(const std::string& var, const std::string& val)
270e2207522Sjmmv {
271d29a92a3Sjoerg atf_error_t err = atf_tc_set_md_var(&pimpl->m_tc, var.c_str(), "%s", val.c_str());
272e2207522Sjmmv if (atf_is_error(err))
273e2207522Sjmmv throw_atf_error(err);
274e2207522Sjmmv }
275e2207522Sjmmv
276407d7761Sjmmv void
run(const std::string & resfile) const2770bc037cbSjmmv impl::tc::run(const std::string& resfile)
278e2207522Sjmmv const
279e2207522Sjmmv {
2800bc037cbSjmmv atf_error_t err = atf_tc_run(&pimpl->m_tc, resfile.c_str());
281e2207522Sjmmv if (atf_is_error(err))
282e2207522Sjmmv throw_atf_error(err);
283e2207522Sjmmv }
284e2207522Sjmmv
285407d7761Sjmmv void
run_cleanup(void) const286407d7761Sjmmv impl::tc::run_cleanup(void)
287407d7761Sjmmv const
288407d7761Sjmmv {
2890bc037cbSjmmv atf_error_t err = atf_tc_cleanup(&pimpl->m_tc);
290407d7761Sjmmv if (atf_is_error(err))
291407d7761Sjmmv throw_atf_error(err);
292e2207522Sjmmv }
293e2207522Sjmmv
294e2207522Sjmmv void
head(void)2959b3149ccSjmmv impl::tc::head(void)
2969b3149ccSjmmv {
2979b3149ccSjmmv }
2989b3149ccSjmmv
2999b3149ccSjmmv void
cleanup(void) const300e2207522Sjmmv impl::tc::cleanup(void)
301e2207522Sjmmv const
302e2207522Sjmmv {
303e2207522Sjmmv }
304e2207522Sjmmv
305e2207522Sjmmv void
require_prog(const std::string & prog) const306e2207522Sjmmv impl::tc::require_prog(const std::string& prog)
307e2207522Sjmmv const
308e2207522Sjmmv {
30912aa0b5aSjmmv atf_tc_require_prog(prog.c_str());
310e2207522Sjmmv }
311e2207522Sjmmv
312e2207522Sjmmv void
pass(void)313e2207522Sjmmv impl::tc::pass(void)
314e2207522Sjmmv {
315e2207522Sjmmv atf_tc_pass();
316e2207522Sjmmv }
317e2207522Sjmmv
318e2207522Sjmmv void
fail(const std::string & reason)319e2207522Sjmmv impl::tc::fail(const std::string& reason)
320e2207522Sjmmv {
321e2207522Sjmmv atf_tc_fail("%s", reason.c_str());
322e2207522Sjmmv }
323e2207522Sjmmv
324e2207522Sjmmv void
fail_nonfatal(const std::string & reason)3258482c0c3Sjmmv impl::tc::fail_nonfatal(const std::string& reason)
3268482c0c3Sjmmv {
3278482c0c3Sjmmv atf_tc_fail_nonfatal("%s", reason.c_str());
3288482c0c3Sjmmv }
3298482c0c3Sjmmv
3308482c0c3Sjmmv void
skip(const std::string & reason)331e2207522Sjmmv impl::tc::skip(const std::string& reason)
332e2207522Sjmmv {
333e2207522Sjmmv atf_tc_skip("%s", reason.c_str());
334e2207522Sjmmv }
335e2207522Sjmmv
3368482c0c3Sjmmv void
check_errno(const char * file,const int line,const int exp_errno,const char * expr_str,const bool result)3378482c0c3Sjmmv impl::tc::check_errno(const char* file, const int line, const int exp_errno,
3388482c0c3Sjmmv const char* expr_str, const bool result)
3398482c0c3Sjmmv {
3408482c0c3Sjmmv atf_tc_check_errno(file, line, exp_errno, expr_str, result);
3418482c0c3Sjmmv }
3428482c0c3Sjmmv
3438482c0c3Sjmmv void
require_errno(const char * file,const int line,const int exp_errno,const char * expr_str,const bool result)3448482c0c3Sjmmv impl::tc::require_errno(const char* file, const int line, const int exp_errno,
3458482c0c3Sjmmv const char* expr_str, const bool result)
3468482c0c3Sjmmv {
3478482c0c3Sjmmv atf_tc_require_errno(file, line, exp_errno, expr_str, result);
3488482c0c3Sjmmv }
3498482c0c3Sjmmv
3508482c0c3Sjmmv void
expect_pass(void)3518482c0c3Sjmmv impl::tc::expect_pass(void)
3528482c0c3Sjmmv {
3538482c0c3Sjmmv atf_tc_expect_pass();
3548482c0c3Sjmmv }
3558482c0c3Sjmmv
3568482c0c3Sjmmv void
expect_fail(const std::string & reason)3578482c0c3Sjmmv impl::tc::expect_fail(const std::string& reason)
3588482c0c3Sjmmv {
3598482c0c3Sjmmv atf_tc_expect_fail("%s", reason.c_str());
3608482c0c3Sjmmv }
3618482c0c3Sjmmv
3628482c0c3Sjmmv void
expect_exit(const int exitcode,const std::string & reason)3638482c0c3Sjmmv impl::tc::expect_exit(const int exitcode, const std::string& reason)
3648482c0c3Sjmmv {
3658482c0c3Sjmmv atf_tc_expect_exit(exitcode, "%s", reason.c_str());
3668482c0c3Sjmmv }
3678482c0c3Sjmmv
3688482c0c3Sjmmv void
expect_signal(const int signo,const std::string & reason)3698482c0c3Sjmmv impl::tc::expect_signal(const int signo, const std::string& reason)
3708482c0c3Sjmmv {
3718482c0c3Sjmmv atf_tc_expect_signal(signo, "%s", reason.c_str());
3728482c0c3Sjmmv }
3738482c0c3Sjmmv
3748482c0c3Sjmmv void
expect_death(const std::string & reason)3758482c0c3Sjmmv impl::tc::expect_death(const std::string& reason)
3768482c0c3Sjmmv {
3778482c0c3Sjmmv atf_tc_expect_death("%s", reason.c_str());
3788482c0c3Sjmmv }
3798482c0c3Sjmmv
3808482c0c3Sjmmv void
expect_timeout(const std::string & reason)3818482c0c3Sjmmv impl::tc::expect_timeout(const std::string& reason)
3828482c0c3Sjmmv {
3838482c0c3Sjmmv atf_tc_expect_timeout("%s", reason.c_str());
3848482c0c3Sjmmv }
3858482c0c3Sjmmv
386e2207522Sjmmv // ------------------------------------------------------------------------
387e2207522Sjmmv // The "tp" class.
388e2207522Sjmmv // ------------------------------------------------------------------------
389e2207522Sjmmv
390e2207522Sjmmv class tp : public atf::application::app {
391e2207522Sjmmv public:
392e2207522Sjmmv typedef std::vector< impl::tc * > tc_vector;
393e2207522Sjmmv
394e2207522Sjmmv private:
395e2207522Sjmmv static const char* m_description;
396e2207522Sjmmv
397e2207522Sjmmv bool m_lflag;
398407d7761Sjmmv atf::fs::path m_resfile;
3999b3149ccSjmmv std::string m_srcdir_arg;
400e2207522Sjmmv atf::fs::path m_srcdir;
401e2207522Sjmmv
402e2207522Sjmmv atf::tests::vars_map m_vars;
403e2207522Sjmmv
404e2207522Sjmmv std::string specific_args(void) const;
405e2207522Sjmmv options_set specific_options(void) const;
406e2207522Sjmmv void process_option(int, const char*);
407e2207522Sjmmv
408e2207522Sjmmv void (*m_add_tcs)(tc_vector&);
409e2207522Sjmmv tc_vector m_tcs;
410e2207522Sjmmv
411e2207522Sjmmv void parse_vflag(const std::string&);
412e2207522Sjmmv void handle_srcdir(void);
413e2207522Sjmmv
414e2207522Sjmmv tc_vector init_tcs(void);
415e2207522Sjmmv
416407d7761Sjmmv enum tc_part {
417407d7761Sjmmv BODY,
418407d7761Sjmmv CLEANUP,
419407d7761Sjmmv };
420e2207522Sjmmv
421407d7761Sjmmv void list_tcs(void);
422407d7761Sjmmv impl::tc* find_tc(tc_vector, const std::string&);
423407d7761Sjmmv static std::pair< std::string, tc_part > process_tcarg(const std::string&);
424407d7761Sjmmv int run_tc(const std::string&);
425e2207522Sjmmv
426e2207522Sjmmv public:
427e2207522Sjmmv tp(void (*)(tc_vector&));
428e2207522Sjmmv ~tp(void);
429e2207522Sjmmv
430e2207522Sjmmv int main(void);
431e2207522Sjmmv };
432e2207522Sjmmv
433e2207522Sjmmv const char* tp::m_description =
434e2207522Sjmmv "This is an independent atf test program.";
435e2207522Sjmmv
tp(void (* add_tcs)(tc_vector &))436e2207522Sjmmv tp::tp(void (*add_tcs)(tc_vector&)) :
437*97bff204Sjmmv app(m_description, "atf-test-program(1)"),
438e2207522Sjmmv m_lflag(false),
4398482c0c3Sjmmv m_resfile("/dev/stdout"),
440e2207522Sjmmv m_srcdir("."),
441e2207522Sjmmv m_add_tcs(add_tcs)
442e2207522Sjmmv {
443e2207522Sjmmv }
444e2207522Sjmmv
~tp(void)445e2207522Sjmmv tp::~tp(void)
446e2207522Sjmmv {
447e2207522Sjmmv for (tc_vector::iterator iter = m_tcs.begin();
448e2207522Sjmmv iter != m_tcs.end(); iter++) {
449e2207522Sjmmv impl::tc* tc = *iter;
450e2207522Sjmmv
451e2207522Sjmmv delete tc;
452e2207522Sjmmv }
453e2207522Sjmmv }
454e2207522Sjmmv
455e2207522Sjmmv std::string
specific_args(void) const456e2207522Sjmmv tp::specific_args(void)
457e2207522Sjmmv const
458e2207522Sjmmv {
459407d7761Sjmmv return "test_case";
460e2207522Sjmmv }
461e2207522Sjmmv
462e2207522Sjmmv tp::options_set
specific_options(void) const463e2207522Sjmmv tp::specific_options(void)
464e2207522Sjmmv const
465e2207522Sjmmv {
466e2207522Sjmmv using atf::application::option;
467e2207522Sjmmv options_set opts;
468e2207522Sjmmv opts.insert(option('l', "", "List test cases and their purpose"));
469407d7761Sjmmv opts.insert(option('r', "resfile", "The file to which the test program "
470407d7761Sjmmv "will write the results of the "
471407d7761Sjmmv "executed test case"));
472e2207522Sjmmv opts.insert(option('s', "srcdir", "Directory where the test's data "
473e2207522Sjmmv "files are located"));
474e2207522Sjmmv opts.insert(option('v', "var=value", "Sets the configuration variable "
475e2207522Sjmmv "`var' to `value'"));
476e2207522Sjmmv return opts;
477e2207522Sjmmv }
478e2207522Sjmmv
479e2207522Sjmmv void
process_option(int ch,const char * arg)480e2207522Sjmmv tp::process_option(int ch, const char* arg)
481e2207522Sjmmv {
482e2207522Sjmmv switch (ch) {
483e2207522Sjmmv case 'l':
484e2207522Sjmmv m_lflag = true;
485e2207522Sjmmv break;
486e2207522Sjmmv
487e2207522Sjmmv case 'r':
488407d7761Sjmmv m_resfile = atf::fs::path(arg);
489e2207522Sjmmv break;
490e2207522Sjmmv
491e2207522Sjmmv case 's':
4929b3149ccSjmmv m_srcdir_arg = arg;
493e2207522Sjmmv break;
494e2207522Sjmmv
495e2207522Sjmmv case 'v':
496e2207522Sjmmv parse_vflag(arg);
497e2207522Sjmmv break;
498e2207522Sjmmv
499e2207522Sjmmv default:
500e2207522Sjmmv UNREACHABLE;
501e2207522Sjmmv }
502e2207522Sjmmv }
503e2207522Sjmmv
504e2207522Sjmmv void
parse_vflag(const std::string & str)505e2207522Sjmmv tp::parse_vflag(const std::string& str)
506e2207522Sjmmv {
507e2207522Sjmmv if (str.empty())
508e2207522Sjmmv throw std::runtime_error("-v requires a non-empty argument");
509e2207522Sjmmv
510e2207522Sjmmv std::vector< std::string > ws = atf::text::split(str, "=");
511e2207522Sjmmv if (ws.size() == 1 && str[str.length() - 1] == '=') {
512e2207522Sjmmv m_vars[ws[0]] = "";
513e2207522Sjmmv } else {
514e2207522Sjmmv if (ws.size() != 2)
515e2207522Sjmmv throw std::runtime_error("-v requires an argument of the form "
516e2207522Sjmmv "var=value");
517e2207522Sjmmv
518e2207522Sjmmv m_vars[ws[0]] = ws[1];
519e2207522Sjmmv }
520e2207522Sjmmv }
521e2207522Sjmmv
522e2207522Sjmmv void
handle_srcdir(void)523e2207522Sjmmv tp::handle_srcdir(void)
524e2207522Sjmmv {
5259b3149ccSjmmv if (m_srcdir_arg.empty()) {
5269b3149ccSjmmv m_srcdir = atf::fs::path(m_argv0).branch_path();
5279b3149ccSjmmv if (m_srcdir.leaf_name() == ".libs")
5289b3149ccSjmmv m_srcdir = m_srcdir.branch_path();
5299b3149ccSjmmv } else
5309b3149ccSjmmv m_srcdir = atf::fs::path(m_srcdir_arg);
5319b3149ccSjmmv
532e2207522Sjmmv if (!atf::fs::exists(m_srcdir / m_prog_name))
533e2207522Sjmmv throw std::runtime_error("Cannot find the test program in the "
534e2207522Sjmmv "source directory `" + m_srcdir.str() + "'");
535e2207522Sjmmv
536e2207522Sjmmv if (!m_srcdir.is_absolute())
537e2207522Sjmmv m_srcdir = m_srcdir.to_absolute();
538e2207522Sjmmv
539e2207522Sjmmv m_vars["srcdir"] = m_srcdir.str();
540e2207522Sjmmv }
541e2207522Sjmmv
542e2207522Sjmmv tp::tc_vector
init_tcs(void)543e2207522Sjmmv tp::init_tcs(void)
544e2207522Sjmmv {
545e2207522Sjmmv m_add_tcs(m_tcs);
546e2207522Sjmmv for (tc_vector::iterator iter = m_tcs.begin();
547e2207522Sjmmv iter != m_tcs.end(); iter++) {
548e2207522Sjmmv impl::tc* tc = *iter;
549e2207522Sjmmv
550e2207522Sjmmv tc->init(m_vars);
551e2207522Sjmmv }
552e2207522Sjmmv return m_tcs;
553e2207522Sjmmv }
554e2207522Sjmmv
555e2207522Sjmmv //
556e2207522Sjmmv // An auxiliary unary predicate that compares the given test case's
557e2207522Sjmmv // identifier to the identifier stored in it.
558e2207522Sjmmv //
559e2207522Sjmmv class tc_equal_to_ident {
560e2207522Sjmmv const std::string& m_ident;
561e2207522Sjmmv
562e2207522Sjmmv public:
tc_equal_to_ident(const std::string & i)563e2207522Sjmmv tc_equal_to_ident(const std::string& i) :
564e2207522Sjmmv m_ident(i)
565e2207522Sjmmv {
566e2207522Sjmmv }
567e2207522Sjmmv
operator ()(const impl::tc * tc)568e2207522Sjmmv bool operator()(const impl::tc* tc)
569e2207522Sjmmv {
570e2207522Sjmmv return tc->get_md_var("ident") == m_ident;
571e2207522Sjmmv }
572e2207522Sjmmv };
573e2207522Sjmmv
574407d7761Sjmmv void
list_tcs(void)575407d7761Sjmmv tp::list_tcs(void)
576e2207522Sjmmv {
577407d7761Sjmmv tc_vector tcs = init_tcs();
5788482c0c3Sjmmv detail::atf_tp_writer writer(std::cout);
579e2207522Sjmmv
580407d7761Sjmmv for (tc_vector::const_iterator iter = tcs.begin();
581407d7761Sjmmv iter != tcs.end(); iter++) {
582407d7761Sjmmv const impl::vars_map vars = (*iter)->get_md_vars();
583407d7761Sjmmv
584407d7761Sjmmv {
585407d7761Sjmmv impl::vars_map::const_iterator iter2 = vars.find("ident");
586407d7761Sjmmv INV(iter2 != vars.end());
587407d7761Sjmmv writer.start_tc((*iter2).second);
588407d7761Sjmmv }
589407d7761Sjmmv
590407d7761Sjmmv for (impl::vars_map::const_iterator iter2 = vars.begin();
591407d7761Sjmmv iter2 != vars.end(); iter2++) {
592407d7761Sjmmv const std::string& key = (*iter2).first;
593407d7761Sjmmv if (key != "ident")
594407d7761Sjmmv writer.tc_meta_data(key, (*iter2).second);
595407d7761Sjmmv }
596407d7761Sjmmv
597407d7761Sjmmv writer.end_tc();
598407d7761Sjmmv }
599407d7761Sjmmv }
600407d7761Sjmmv
601407d7761Sjmmv impl::tc*
find_tc(tc_vector tcs,const std::string & name)602407d7761Sjmmv tp::find_tc(tc_vector tcs, const std::string& name)
603407d7761Sjmmv {
604e2207522Sjmmv std::vector< std::string > ids;
605e2207522Sjmmv for (tc_vector::iterator iter = tcs.begin();
606e2207522Sjmmv iter != tcs.end(); iter++) {
607e2207522Sjmmv impl::tc* tc = *iter;
608e2207522Sjmmv
609407d7761Sjmmv if (tc->get_md_var("ident") == name)
610407d7761Sjmmv return tc;
611407d7761Sjmmv }
612407d7761Sjmmv throw atf::application::usage_error("Unknown test case `%s'",
613407d7761Sjmmv name.c_str());
614e2207522Sjmmv }
615e2207522Sjmmv
616407d7761Sjmmv std::pair< std::string, tp::tc_part >
process_tcarg(const std::string & tcarg)617407d7761Sjmmv tp::process_tcarg(const std::string& tcarg)
618407d7761Sjmmv {
619407d7761Sjmmv const std::string::size_type pos = tcarg.find(':');
620407d7761Sjmmv if (pos == std::string::npos) {
621407d7761Sjmmv return std::make_pair(tcarg, BODY);
622407d7761Sjmmv } else {
623407d7761Sjmmv const std::string tcname = tcarg.substr(0, pos);
624e2207522Sjmmv
625407d7761Sjmmv const std::string partname = tcarg.substr(pos + 1);
626407d7761Sjmmv if (partname == "body")
627407d7761Sjmmv return std::make_pair(tcname, BODY);
628407d7761Sjmmv else if (partname == "cleanup")
629407d7761Sjmmv return std::make_pair(tcname, CLEANUP);
630407d7761Sjmmv else {
631407d7761Sjmmv using atf::application::usage_error;
632407d7761Sjmmv throw usage_error("Invalid test case part `%s'", partname.c_str());
633e2207522Sjmmv }
634e2207522Sjmmv }
635e2207522Sjmmv }
636e2207522Sjmmv
637e2207522Sjmmv int
run_tc(const std::string & tcarg)638407d7761Sjmmv tp::run_tc(const std::string& tcarg)
639e2207522Sjmmv {
640407d7761Sjmmv const std::pair< std::string, tc_part > fields = process_tcarg(tcarg);
641e2207522Sjmmv
642407d7761Sjmmv impl::tc* tc = find_tc(init_tcs(), fields.first);
643e2207522Sjmmv
6441d706b81Sjmmv if (!atf::env::has("__RUNNING_INSIDE_ATF_RUN") || atf::env::get(
6451d706b81Sjmmv "__RUNNING_INSIDE_ATF_RUN") != "internal-yes-value")
6461d706b81Sjmmv {
6471d706b81Sjmmv std::cerr << m_prog_name << ": WARNING: Running test cases without "
6481d706b81Sjmmv "atf-run(1) is unsupported\n";
6491d706b81Sjmmv std::cerr << m_prog_name << ": WARNING: No isolation nor timeout "
6501d706b81Sjmmv "control is being applied; you may get unexpected failures; see "
6511d706b81Sjmmv "atf-test-case(4)\n";
6521d706b81Sjmmv }
6531d706b81Sjmmv
654407d7761Sjmmv try {
655407d7761Sjmmv switch (fields.second) {
656407d7761Sjmmv case BODY:
6570bc037cbSjmmv tc->run(m_resfile.str());
658407d7761Sjmmv break;
659407d7761Sjmmv case CLEANUP:
660407d7761Sjmmv tc->run_cleanup();
661407d7761Sjmmv break;
662407d7761Sjmmv default:
663407d7761Sjmmv UNREACHABLE;
664e2207522Sjmmv }
665e2207522Sjmmv return EXIT_SUCCESS;
666407d7761Sjmmv } catch (const std::runtime_error& e) {
6678482c0c3Sjmmv std::cerr << "ERROR: " << e.what() << "\n";
668407d7761Sjmmv return EXIT_FAILURE;
669e2207522Sjmmv }
670e2207522Sjmmv }
671e2207522Sjmmv
672e2207522Sjmmv int
main(void)673e2207522Sjmmv tp::main(void)
674e2207522Sjmmv {
675407d7761Sjmmv using atf::application::usage_error;
676407d7761Sjmmv
677e2207522Sjmmv int errcode;
678e2207522Sjmmv
679e2207522Sjmmv handle_srcdir();
680e2207522Sjmmv
681407d7761Sjmmv if (m_lflag) {
682407d7761Sjmmv if (m_argc > 0)
683407d7761Sjmmv throw usage_error("Cannot provide test case names with -l");
684e2207522Sjmmv
685407d7761Sjmmv list_tcs();
686407d7761Sjmmv errcode = EXIT_SUCCESS;
687407d7761Sjmmv } else {
688407d7761Sjmmv if (m_argc == 0)
689407d7761Sjmmv throw usage_error("Must provide a test case name");
690407d7761Sjmmv else if (m_argc > 1)
691407d7761Sjmmv throw usage_error("Cannot provide more than one test case name");
692407d7761Sjmmv INV(m_argc == 1);
693407d7761Sjmmv
694407d7761Sjmmv errcode = run_tc(m_argv[0]);
695e2207522Sjmmv }
696e2207522Sjmmv
697e2207522Sjmmv return errcode;
698e2207522Sjmmv }
699e2207522Sjmmv
700e2207522Sjmmv namespace atf {
701e2207522Sjmmv namespace tests {
702e2207522Sjmmv int run_tp(int, char* const*, void (*)(tp::tc_vector&));
703e2207522Sjmmv }
704e2207522Sjmmv }
705e2207522Sjmmv
706e2207522Sjmmv int
run_tp(int argc,char * const * argv,void (* add_tcs)(tp::tc_vector &))707e2207522Sjmmv impl::run_tp(int argc, char* const* argv, void (*add_tcs)(tp::tc_vector&))
708e2207522Sjmmv {
709e2207522Sjmmv return tp(add_tcs).run(argc, argv);
710e2207522Sjmmv }
711