xref: /netbsd-src/external/bsd/atf/dist/atf-c++/tests.cpp (revision 97bff2045d87545e2f12a7c860a6fb0570afc22b)
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