Home | History | Annotate | Line # | Download | only in tools
atf-run.cpp revision 1.6
      1  1.1  jmmv //
      2  1.1  jmmv // Automated Testing Framework (atf)
      3  1.1  jmmv //
      4  1.3  jmmv // Copyright (c) 2007 The NetBSD Foundation, Inc.
      5  1.1  jmmv // All rights reserved.
      6  1.1  jmmv //
      7  1.1  jmmv // Redistribution and use in source and binary forms, with or without
      8  1.1  jmmv // modification, are permitted provided that the following conditions
      9  1.1  jmmv // are met:
     10  1.1  jmmv // 1. Redistributions of source code must retain the above copyright
     11  1.1  jmmv //    notice, this list of conditions and the following disclaimer.
     12  1.1  jmmv // 2. Redistributions in binary form must reproduce the above copyright
     13  1.1  jmmv //    notice, this list of conditions and the following disclaimer in the
     14  1.1  jmmv //    documentation and/or other materials provided with the distribution.
     15  1.1  jmmv //
     16  1.1  jmmv // THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
     17  1.1  jmmv // CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
     18  1.1  jmmv // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     19  1.1  jmmv // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     20  1.1  jmmv // IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
     21  1.1  jmmv // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     22  1.1  jmmv // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
     23  1.1  jmmv // GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     24  1.1  jmmv // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
     25  1.1  jmmv // IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     26  1.1  jmmv // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
     27  1.1  jmmv // IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     28  1.1  jmmv //
     29  1.1  jmmv 
     30  1.1  jmmv extern "C" {
     31  1.1  jmmv #include <sys/types.h>
     32  1.3  jmmv #include <sys/param.h>
     33  1.3  jmmv #include <sys/stat.h>
     34  1.1  jmmv #include <sys/wait.h>
     35  1.1  jmmv #include <unistd.h>
     36  1.1  jmmv }
     37  1.1  jmmv 
     38  1.3  jmmv #include <algorithm>
     39  1.3  jmmv #include <cassert>
     40  1.1  jmmv #include <cerrno>
     41  1.1  jmmv #include <cstdlib>
     42  1.1  jmmv #include <cstring>
     43  1.1  jmmv #include <fstream>
     44  1.1  jmmv #include <iostream>
     45  1.1  jmmv #include <map>
     46  1.1  jmmv #include <string>
     47  1.1  jmmv 
     48  1.3  jmmv #include "application.hpp"
     49  1.3  jmmv #include "atffile.hpp"
     50  1.3  jmmv #include "config.hpp"
     51  1.3  jmmv #include "config_file.hpp"
     52  1.3  jmmv #include "env.hpp"
     53  1.3  jmmv #include "exceptions.hpp"
     54  1.3  jmmv #include "fs.hpp"
     55  1.3  jmmv #include "parser.hpp"
     56  1.3  jmmv #include "process.hpp"
     57  1.3  jmmv #include "requirements.hpp"
     58  1.3  jmmv #include "test-program.hpp"
     59  1.3  jmmv #include "text.hpp"
     60  1.3  jmmv 
     61  1.3  jmmv namespace {
     62  1.3  jmmv 
     63  1.3  jmmv typedef std::map< std::string, std::string > vars_map;
     64  1.3  jmmv 
     65  1.3  jmmv } // anonymous namespace
     66  1.3  jmmv 
     67  1.3  jmmv class atf_run : public tools::application::app {
     68  1.3  jmmv     static const char* m_description;
     69  1.1  jmmv 
     70  1.3  jmmv     vars_map m_cmdline_vars;
     71  1.1  jmmv 
     72  1.3  jmmv     static vars_map::value_type parse_var(const std::string&);
     73  1.1  jmmv 
     74  1.3  jmmv     void process_option(int, const char*);
     75  1.3  jmmv     std::string specific_args(void) const;
     76  1.3  jmmv     options_set specific_options(void) const;
     77  1.1  jmmv 
     78  1.3  jmmv     void parse_vflag(const std::string&);
     79  1.1  jmmv 
     80  1.3  jmmv     std::vector< std::string > conf_args(void) const;
     81  1.1  jmmv 
     82  1.3  jmmv     size_t count_tps(std::vector< std::string >) const;
     83  1.1  jmmv 
     84  1.6  gson     int run_test(const tools::fs::path&, const std::string &,
     85  1.6  gson                  tools::test_program::atf_tps_writer&,
     86  1.3  jmmv                  const vars_map&);
     87  1.3  jmmv     int run_test_directory(const tools::fs::path&,
     88  1.3  jmmv                            tools::test_program::atf_tps_writer&);
     89  1.3  jmmv     int run_test_program(const tools::fs::path&,
     90  1.6  gson                          const std::string tc,
     91  1.3  jmmv                          tools::test_program::atf_tps_writer&,
     92  1.3  jmmv                          const vars_map&);
     93  1.3  jmmv 
     94  1.3  jmmv     tools::test_program::test_case_result get_test_case_result(
     95  1.3  jmmv         const std::string&, const tools::process::status&,
     96  1.3  jmmv         const tools::fs::path&) const;
     97  1.1  jmmv 
     98  1.3  jmmv public:
     99  1.3  jmmv     atf_run(void);
    100  1.1  jmmv 
    101  1.3  jmmv     int main(void);
    102  1.3  jmmv };
    103  1.1  jmmv 
    104  1.3  jmmv static void
    105  1.3  jmmv sanitize_gdb_env(void)
    106  1.3  jmmv {
    107  1.3  jmmv     try {
    108  1.3  jmmv         tools::env::unset("TERM");
    109  1.3  jmmv     } catch (...) {
    110  1.3  jmmv         // Just swallow exceptions here; they cannot propagate into C, which
    111  1.3  jmmv         // is where this function is called from, and even if these exceptions
    112  1.3  jmmv         // appear they are benign.
    113  1.1  jmmv     }
    114  1.3  jmmv }
    115  1.1  jmmv 
    116  1.3  jmmv static void
    117  1.3  jmmv dump_stacktrace(const tools::fs::path& tp, const tools::process::status& s,
    118  1.3  jmmv                 const tools::fs::path& workdir,
    119  1.3  jmmv                 tools::test_program::atf_tps_writer& w)
    120  1.3  jmmv {
    121  1.3  jmmv     assert(s.signaled() && s.coredump());
    122  1.1  jmmv 
    123  1.3  jmmv     w.stderr_tc("Test program crashed; attempting to get stack trace");
    124  1.1  jmmv 
    125  1.3  jmmv     const tools::fs::path corename = workdir /
    126  1.5  jmmv         (tp.leaf_name().substr(0, MAXCOMLEN) + ".core");
    127  1.3  jmmv     if (!tools::fs::exists(corename)) {
    128  1.3  jmmv         w.stderr_tc("Expected file " + corename.str() + " not found");
    129  1.3  jmmv         return;
    130  1.1  jmmv     }
    131  1.1  jmmv 
    132  1.3  jmmv     const tools::fs::path gdb(GDB);
    133  1.3  jmmv     const tools::fs::path gdbout = workdir / "gdb.out";
    134  1.3  jmmv     const tools::process::argv_array args(gdb.leaf_name().c_str(), "-batch",
    135  1.3  jmmv                                         "-q", "-ex", "bt", tp.c_str(),
    136  1.3  jmmv                                         corename.c_str(), NULL);
    137  1.3  jmmv     tools::process::status status = tools::process::exec(
    138  1.3  jmmv         gdb, args,
    139  1.3  jmmv         tools::process::stream_redirect_path(gdbout),
    140  1.3  jmmv         tools::process::stream_redirect_path(tools::fs::path("/dev/null")),
    141  1.3  jmmv         sanitize_gdb_env);
    142  1.3  jmmv     if (!status.exited() || status.exitstatus() != EXIT_SUCCESS) {
    143  1.3  jmmv         w.stderr_tc("Execution of " GDB " failed");
    144  1.3  jmmv         return;
    145  1.1  jmmv     }
    146  1.1  jmmv 
    147  1.3  jmmv     std::ifstream input(gdbout.c_str());
    148  1.3  jmmv     if (input) {
    149  1.3  jmmv         std::string line;
    150  1.3  jmmv         while (std::getline(input, line).good())
    151  1.3  jmmv             w.stderr_tc(line);
    152  1.3  jmmv         input.close();
    153  1.1  jmmv     }
    154  1.1  jmmv 
    155  1.3  jmmv     w.stderr_tc("Stack trace complete");
    156  1.1  jmmv }
    157  1.1  jmmv 
    158  1.1  jmmv const char* atf_run::m_description =
    159  1.1  jmmv     "atf-run is a tool that runs tests programs and collects their "
    160  1.1  jmmv     "results.";
    161  1.1  jmmv 
    162  1.1  jmmv atf_run::atf_run(void) :
    163  1.1  jmmv     app(m_description, "atf-run(1)", "atf(7)")
    164  1.1  jmmv {
    165  1.1  jmmv }
    166  1.1  jmmv 
    167  1.1  jmmv void
    168  1.1  jmmv atf_run::process_option(int ch, const char* arg)
    169  1.1  jmmv {
    170  1.1  jmmv     switch (ch) {
    171  1.1  jmmv     case 'v':
    172  1.1  jmmv         parse_vflag(arg);
    173  1.1  jmmv         break;
    174  1.1  jmmv 
    175  1.1  jmmv     default:
    176  1.3  jmmv         std::abort();
    177  1.1  jmmv     }
    178  1.1  jmmv }
    179  1.1  jmmv 
    180  1.1  jmmv std::string
    181  1.1  jmmv atf_run::specific_args(void)
    182  1.1  jmmv     const
    183  1.1  jmmv {
    184  1.6  gson     return "[test1 .. testN]";
    185  1.1  jmmv }
    186  1.1  jmmv 
    187  1.1  jmmv atf_run::options_set
    188  1.1  jmmv atf_run::specific_options(void)
    189  1.1  jmmv     const
    190  1.1  jmmv {
    191  1.3  jmmv     using tools::application::option;
    192  1.1  jmmv     options_set opts;
    193  1.1  jmmv     opts.insert(option('v', "var=value", "Sets the configuration variable "
    194  1.1  jmmv                                          "`var' to `value'; overrides "
    195  1.1  jmmv                                          "values in configuration files"));
    196  1.1  jmmv     return opts;
    197  1.1  jmmv }
    198  1.1  jmmv 
    199  1.1  jmmv void
    200  1.1  jmmv atf_run::parse_vflag(const std::string& str)
    201  1.1  jmmv {
    202  1.1  jmmv     if (str.empty())
    203  1.1  jmmv         throw std::runtime_error("-v requires a non-empty argument");
    204  1.1  jmmv 
    205  1.3  jmmv     std::vector< std::string > ws = tools::text::split(str, "=");
    206  1.1  jmmv     if (ws.size() == 1 && str[str.length() - 1] == '=') {
    207  1.1  jmmv         m_cmdline_vars[ws[0]] = "";
    208  1.1  jmmv     } else {
    209  1.1  jmmv         if (ws.size() != 2)
    210  1.1  jmmv             throw std::runtime_error("-v requires an argument of the form "
    211  1.1  jmmv                                      "var=value");
    212  1.1  jmmv 
    213  1.1  jmmv         m_cmdline_vars[ws[0]] = ws[1];
    214  1.1  jmmv     }
    215  1.1  jmmv }
    216  1.1  jmmv 
    217  1.1  jmmv int
    218  1.3  jmmv atf_run::run_test(const tools::fs::path& tp,
    219  1.6  gson                   const std::string &tc,
    220  1.3  jmmv                   tools::test_program::atf_tps_writer& w,
    221  1.3  jmmv                   const vars_map& config)
    222  1.1  jmmv {
    223  1.3  jmmv     tools::fs::file_info fi(tp);
    224  1.1  jmmv 
    225  1.1  jmmv     int errcode;
    226  1.3  jmmv     if (fi.get_type() == tools::fs::file_info::dir_type)
    227  1.1  jmmv         errcode = run_test_directory(tp, w);
    228  1.3  jmmv     else {
    229  1.3  jmmv         const vars_map effective_config =
    230  1.3  jmmv             tools::config_file::merge_configs(config, m_cmdline_vars);
    231  1.3  jmmv 
    232  1.6  gson         errcode = run_test_program(tp, tc, w, effective_config);
    233  1.3  jmmv     }
    234  1.1  jmmv     return errcode;
    235  1.1  jmmv }
    236  1.1  jmmv 
    237  1.1  jmmv int
    238  1.3  jmmv atf_run::run_test_directory(const tools::fs::path& tp,
    239  1.3  jmmv                             tools::test_program::atf_tps_writer& w)
    240  1.1  jmmv {
    241  1.3  jmmv     tools::atffile af = tools::read_atffile(tp / "Atffile");
    242  1.1  jmmv 
    243  1.3  jmmv     vars_map test_suite_vars;
    244  1.1  jmmv     {
    245  1.3  jmmv         vars_map::const_iterator iter = af.props().find("test-suite");
    246  1.3  jmmv         assert(iter != af.props().end());
    247  1.3  jmmv         test_suite_vars = tools::config_file::read_config_files((*iter).second);
    248  1.1  jmmv     }
    249  1.1  jmmv 
    250  1.1  jmmv     bool ok = true;
    251  1.1  jmmv     for (std::vector< std::string >::const_iterator iter = af.tps().begin();
    252  1.3  jmmv          iter != af.tps().end(); iter++) {
    253  1.6  gson         const bool result = run_test(tp / *iter, "", w,
    254  1.3  jmmv             tools::config_file::merge_configs(af.conf(), test_suite_vars));
    255  1.3  jmmv         ok &= (result == EXIT_SUCCESS);
    256  1.3  jmmv     }
    257  1.1  jmmv 
    258  1.1  jmmv     return ok ? EXIT_SUCCESS : EXIT_FAILURE;
    259  1.1  jmmv }
    260  1.1  jmmv 
    261  1.3  jmmv tools::test_program::test_case_result
    262  1.3  jmmv atf_run::get_test_case_result(const std::string& broken_reason,
    263  1.3  jmmv                               const tools::process::status& s,
    264  1.3  jmmv                               const tools::fs::path& resfile)
    265  1.3  jmmv     const
    266  1.1  jmmv {
    267  1.3  jmmv     using tools::text::to_string;
    268  1.3  jmmv     using tools::test_program::read_test_case_result;
    269  1.3  jmmv     using tools::test_program::test_case_result;
    270  1.1  jmmv 
    271  1.3  jmmv     if (!broken_reason.empty()) {
    272  1.3  jmmv         test_case_result tcr;
    273  1.1  jmmv 
    274  1.3  jmmv         try {
    275  1.3  jmmv             tcr = read_test_case_result(resfile);
    276  1.1  jmmv 
    277  1.3  jmmv             if (tcr.state() == "expected_timeout") {
    278  1.3  jmmv                 return tcr;
    279  1.3  jmmv             } else {
    280  1.3  jmmv                 return test_case_result("failed", -1, broken_reason);
    281  1.3  jmmv             }
    282  1.3  jmmv         } catch (const std::runtime_error&) {
    283  1.3  jmmv             return test_case_result("failed", -1, broken_reason);
    284  1.3  jmmv         }
    285  1.3  jmmv     }
    286  1.1  jmmv 
    287  1.3  jmmv     if (s.exited()) {
    288  1.3  jmmv         test_case_result tcr;
    289  1.1  jmmv 
    290  1.3  jmmv         try {
    291  1.3  jmmv             tcr = read_test_case_result(resfile);
    292  1.3  jmmv         } catch (const std::runtime_error& e) {
    293  1.3  jmmv             return test_case_result("failed", -1, "Test case exited "
    294  1.3  jmmv                 "normally but failed to create the results file: " +
    295  1.3  jmmv                 std::string(e.what()));
    296  1.1  jmmv         }
    297  1.1  jmmv 
    298  1.3  jmmv         if (tcr.state() == "expected_death") {
    299  1.3  jmmv             return tcr;
    300  1.3  jmmv         } else if (tcr.state() == "expected_exit") {
    301  1.3  jmmv             if (tcr.value() == -1 || s.exitstatus() == tcr.value())
    302  1.3  jmmv                 return tcr;
    303  1.3  jmmv             else
    304  1.3  jmmv                 return test_case_result("failed", -1, "Test case was "
    305  1.3  jmmv                     "expected to exit with a " + to_string(tcr.value()) +
    306  1.3  jmmv                     " error code but returned " + to_string(s.exitstatus()));
    307  1.3  jmmv         } else if (tcr.state() == "expected_failure") {
    308  1.3  jmmv             if (s.exitstatus() == EXIT_SUCCESS)
    309  1.3  jmmv                 return tcr;
    310  1.3  jmmv             else
    311  1.3  jmmv                 return test_case_result("failed", -1, "Test case returned an "
    312  1.3  jmmv                     "error in expected_failure mode but it should not have");
    313  1.3  jmmv         } else if (tcr.state() == "expected_signal") {
    314  1.3  jmmv             return test_case_result("failed", -1, "Test case exited cleanly "
    315  1.3  jmmv                 "but was expected to receive a signal");
    316  1.3  jmmv         } else if (tcr.state() == "failed") {
    317  1.3  jmmv             if (s.exitstatus() == EXIT_SUCCESS)
    318  1.3  jmmv                 return test_case_result("failed", -1, "Test case "
    319  1.3  jmmv                     "exited successfully but reported failure");
    320  1.3  jmmv             else
    321  1.3  jmmv                 return tcr;
    322  1.3  jmmv         } else if (tcr.state() == "passed") {
    323  1.3  jmmv             if (s.exitstatus() == EXIT_SUCCESS)
    324  1.3  jmmv                 return tcr;
    325  1.3  jmmv             else
    326  1.3  jmmv                 return test_case_result("failed", -1, "Test case exited as "
    327  1.3  jmmv                     "passed but reported an error");
    328  1.3  jmmv         } else if (tcr.state() == "skipped") {
    329  1.3  jmmv             if (s.exitstatus() == EXIT_SUCCESS)
    330  1.3  jmmv                 return tcr;
    331  1.3  jmmv             else
    332  1.3  jmmv                 return test_case_result("failed", -1, "Test case exited as "
    333  1.3  jmmv                     "skipped but reported an error");
    334  1.3  jmmv         }
    335  1.3  jmmv     } else if (s.signaled()) {
    336  1.3  jmmv         test_case_result tcr;
    337  1.1  jmmv 
    338  1.3  jmmv         try {
    339  1.3  jmmv             tcr = read_test_case_result(resfile);
    340  1.3  jmmv         } catch (const std::runtime_error&) {
    341  1.3  jmmv             return test_case_result("failed", -1, "Test program received "
    342  1.3  jmmv                 "signal " + tools::text::to_string(s.termsig()) +
    343  1.3  jmmv                 (s.coredump() ? " (core dumped)" : ""));
    344  1.3  jmmv         }
    345  1.1  jmmv 
    346  1.3  jmmv         if (tcr.state() == "expected_death") {
    347  1.3  jmmv             return tcr;
    348  1.3  jmmv         } else if (tcr.state() == "expected_signal") {
    349  1.3  jmmv             if (tcr.value() == -1 || s.termsig() == tcr.value())
    350  1.3  jmmv                 return tcr;
    351  1.3  jmmv             else
    352  1.3  jmmv                 return test_case_result("failed", -1, "Test case was "
    353  1.3  jmmv                     "expected to exit due to a " + to_string(tcr.value()) +
    354  1.3  jmmv                     " signal but got " + to_string(s.termsig()));
    355  1.3  jmmv         } else {
    356  1.3  jmmv             return test_case_result("failed", -1, "Test program received "
    357  1.3  jmmv                 "signal " + tools::text::to_string(s.termsig()) +
    358  1.3  jmmv                 (s.coredump() ? " (core dumped)" : "") + " and created a "
    359  1.3  jmmv                 "bogus results file");
    360  1.1  jmmv         }
    361  1.1  jmmv     }
    362  1.3  jmmv     std::abort();
    363  1.3  jmmv     return test_case_result();
    364  1.1  jmmv }
    365  1.1  jmmv 
    366  1.1  jmmv int
    367  1.3  jmmv atf_run::run_test_program(const tools::fs::path& tp,
    368  1.6  gson                           const std::string tc,
    369  1.3  jmmv                           tools::test_program::atf_tps_writer& w,
    370  1.3  jmmv                           const vars_map& config)
    371  1.1  jmmv {
    372  1.3  jmmv     int errcode = EXIT_SUCCESS;
    373  1.1  jmmv 
    374  1.3  jmmv     tools::test_program::metadata md;
    375  1.3  jmmv     try {
    376  1.3  jmmv         md = tools::test_program::get_metadata(tp, config);
    377  1.3  jmmv     } catch (const tools::parser::format_error& e) {
    378  1.3  jmmv         w.start_tp(tp.str(), 0);
    379  1.3  jmmv         w.end_tp("Invalid format for test case list: " + std::string(e.what()));
    380  1.3  jmmv         return EXIT_FAILURE;
    381  1.3  jmmv     } catch (const tools::parser::parse_errors& e) {
    382  1.3  jmmv         const std::string reason = tools::text::join(e, "; ");
    383  1.3  jmmv         w.start_tp(tp.str(), 0);
    384  1.3  jmmv         w.end_tp("Invalid format for test case list: " + reason);
    385  1.3  jmmv         return EXIT_FAILURE;
    386  1.3  jmmv     }
    387  1.3  jmmv 
    388  1.3  jmmv     tools::fs::temp_dir resdir(
    389  1.3  jmmv         tools::fs::path(tools::config::get("atf_workdir")) / "atf-run.XXXXXX");
    390  1.3  jmmv 
    391  1.3  jmmv     w.start_tp(tp.str(), md.test_cases.size());
    392  1.3  jmmv     if (md.test_cases.empty()) {
    393  1.3  jmmv         w.end_tp("Bogus test program: reported 0 test cases");
    394  1.1  jmmv         errcode = EXIT_FAILURE;
    395  1.1  jmmv     } else {
    396  1.3  jmmv         for (std::map< std::string, vars_map >::const_iterator iter
    397  1.3  jmmv              = md.test_cases.begin(); iter != md.test_cases.end(); iter++) {
    398  1.3  jmmv             const std::string& tcname = (*iter).first;
    399  1.3  jmmv             const vars_map& tcmd = (*iter).second;
    400  1.3  jmmv 
    401  1.6  gson             if (! tc.empty() && tcname != tc)
    402  1.6  gson                 continue;
    403  1.6  gson 
    404  1.3  jmmv             w.start_tc(tcname);
    405  1.3  jmmv 
    406  1.3  jmmv             try {
    407  1.3  jmmv                 const std::string& reqfail = tools::check_requirements(
    408  1.3  jmmv                     tcmd, config);
    409  1.3  jmmv                 if (!reqfail.empty()) {
    410  1.3  jmmv                     w.end_tc("skipped", reqfail);
    411  1.3  jmmv                     continue;
    412  1.3  jmmv                 }
    413  1.3  jmmv             } catch (const std::runtime_error& e) {
    414  1.3  jmmv                 w.end_tc("failed", e.what());
    415  1.3  jmmv                 errcode = EXIT_FAILURE;
    416  1.3  jmmv                 continue;
    417  1.3  jmmv             }
    418  1.3  jmmv 
    419  1.3  jmmv             const std::pair< int, int > user = tools::get_required_user(
    420  1.3  jmmv                 tcmd, config);
    421  1.3  jmmv 
    422  1.3  jmmv             tools::fs::path resfile = resdir.get_path() / "tcr";
    423  1.3  jmmv             assert(!tools::fs::exists(resfile));
    424  1.3  jmmv             try {
    425  1.3  jmmv                 const bool has_cleanup = tools::text::to_bool(
    426  1.3  jmmv                     (*tcmd.find("has.cleanup")).second);
    427  1.3  jmmv 
    428  1.3  jmmv                 tools::fs::temp_dir workdir(tools::fs::path(tools::config::get(
    429  1.3  jmmv                     "atf_workdir")) / "atf-run.XXXXXX");
    430  1.3  jmmv                 if (user.first != -1 && user.second != -1) {
    431  1.3  jmmv                     if (::chown(workdir.get_path().c_str(), user.first,
    432  1.3  jmmv                                 user.second) == -1) {
    433  1.3  jmmv                         throw tools::system_error("chown(" +
    434  1.3  jmmv                             workdir.get_path().str() + ")", "chown(2) failed",
    435  1.3  jmmv                             errno);
    436  1.3  jmmv                     }
    437  1.3  jmmv                     resfile = workdir.get_path() / "tcr";
    438  1.3  jmmv                 }
    439  1.3  jmmv 
    440  1.3  jmmv                 std::pair< std::string, const tools::process::status > s =
    441  1.3  jmmv                     tools::test_program::run_test_case(
    442  1.3  jmmv                         tp, tcname, "body", tcmd, config,
    443  1.3  jmmv                         resfile, workdir.get_path(), w);
    444  1.3  jmmv                 if (s.second.signaled() && s.second.coredump())
    445  1.3  jmmv                     dump_stacktrace(tp, s.second, workdir.get_path(), w);
    446  1.3  jmmv                 if (has_cleanup)
    447  1.3  jmmv                     (void)tools::test_program::run_test_case(
    448  1.3  jmmv                         tp, tcname, "cleanup", tcmd,
    449  1.3  jmmv                         config, resfile, workdir.get_path(), w);
    450  1.3  jmmv 
    451  1.3  jmmv                 // TODO: Force deletion of workdir.
    452  1.3  jmmv 
    453  1.3  jmmv                 tools::test_program::test_case_result tcr =
    454  1.3  jmmv                     get_test_case_result(s.first, s.second, resfile);
    455  1.3  jmmv 
    456  1.3  jmmv                 w.end_tc(tcr.state(), tcr.reason());
    457  1.3  jmmv                 if (tcr.state() == "failed")
    458  1.3  jmmv                     errcode = EXIT_FAILURE;
    459  1.3  jmmv             } catch (...) {
    460  1.3  jmmv                 if (tools::fs::exists(resfile))
    461  1.3  jmmv                     tools::fs::remove(resfile);
    462  1.3  jmmv                 throw;
    463  1.3  jmmv             }
    464  1.3  jmmv             if (tools::fs::exists(resfile))
    465  1.3  jmmv                 tools::fs::remove(resfile);
    466  1.3  jmmv 
    467  1.3  jmmv         }
    468  1.3  jmmv         w.end_tp("");
    469  1.1  jmmv     }
    470  1.1  jmmv 
    471  1.1  jmmv     return errcode;
    472  1.1  jmmv }
    473  1.1  jmmv 
    474  1.6  gson static void
    475  1.6  gson colon_split(const std::string &s, std::string &tp, std::string &tc)
    476  1.6  gson {
    477  1.6  gson     size_t colon_pos = s.rfind(':');
    478  1.6  gson     if (colon_pos != std::string::npos && colon_pos < s.size() - 1) {
    479  1.6  gson         tp = s.substr(0, colon_pos);
    480  1.6  gson         tc = s.substr(colon_pos + 1);
    481  1.6  gson     } else {
    482  1.6  gson         tp = s;
    483  1.6  gson         tc = "";
    484  1.6  gson     }
    485  1.6  gson }
    486  1.6  gson 
    487  1.1  jmmv size_t
    488  1.1  jmmv atf_run::count_tps(std::vector< std::string > tps)
    489  1.1  jmmv     const
    490  1.1  jmmv {
    491  1.1  jmmv     size_t ntps = 0;
    492  1.1  jmmv 
    493  1.1  jmmv     for (std::vector< std::string >::const_iterator iter = tps.begin();
    494  1.1  jmmv          iter != tps.end(); iter++) {
    495  1.6  gson         std::string tpname, tcname;
    496  1.6  gson         colon_split(*iter, tpname, tcname);
    497  1.6  gson         tools::fs::path tp(tpname);
    498  1.3  jmmv         tools::fs::file_info fi(tp);
    499  1.1  jmmv 
    500  1.3  jmmv         if (fi.get_type() == tools::fs::file_info::dir_type) {
    501  1.3  jmmv             tools::atffile af = tools::read_atffile(tp / "Atffile");
    502  1.1  jmmv             std::vector< std::string > aux = af.tps();
    503  1.1  jmmv             for (std::vector< std::string >::iterator i2 = aux.begin();
    504  1.1  jmmv                  i2 != aux.end(); i2++)
    505  1.1  jmmv                 *i2 = (tp / *i2).str();
    506  1.1  jmmv             ntps += count_tps(aux);
    507  1.1  jmmv         } else
    508  1.1  jmmv             ntps++;
    509  1.1  jmmv     }
    510  1.1  jmmv 
    511  1.1  jmmv     return ntps;
    512  1.1  jmmv }
    513  1.1  jmmv 
    514  1.3  jmmv static
    515  1.1  jmmv void
    516  1.3  jmmv call_hook(const std::string& tool, const std::string& hook)
    517  1.1  jmmv {
    518  1.3  jmmv     const tools::fs::path sh(tools::config::get("atf_shell"));
    519  1.3  jmmv     const tools::fs::path hooks =
    520  1.3  jmmv         tools::fs::path(tools::config::get("atf_pkgdatadir")) / (tool + ".hooks");
    521  1.3  jmmv 
    522  1.3  jmmv     const tools::process::status s =
    523  1.3  jmmv         tools::process::exec(sh,
    524  1.3  jmmv                            tools::process::argv_array(sh.c_str(), hooks.c_str(),
    525  1.3  jmmv                                                     hook.c_str(), NULL),
    526  1.3  jmmv                            tools::process::stream_inherit(),
    527  1.3  jmmv                            tools::process::stream_inherit());
    528  1.1  jmmv 
    529  1.1  jmmv 
    530  1.3  jmmv     if (!s.exited() || s.exitstatus() != EXIT_SUCCESS)
    531  1.1  jmmv         throw std::runtime_error("Failed to run the '" + hook + "' hook "
    532  1.3  jmmv                                  "for '" + tool + "'");
    533  1.1  jmmv }
    534  1.1  jmmv 
    535  1.1  jmmv int
    536  1.1  jmmv atf_run::main(void)
    537  1.1  jmmv {
    538  1.3  jmmv     tools::atffile af = tools::read_atffile(tools::fs::path("Atffile"));
    539  1.1  jmmv 
    540  1.1  jmmv     std::vector< std::string > tps;
    541  1.1  jmmv     tps = af.tps();
    542  1.1  jmmv     if (m_argc >= 1) {
    543  1.1  jmmv         // TODO: Ensure that the given test names are listed in the
    544  1.1  jmmv         // Atffile.  Take into account that the file can be using globs.
    545  1.1  jmmv         tps.clear();
    546  1.1  jmmv         for (int i = 0; i < m_argc; i++)
    547  1.1  jmmv             tps.push_back(m_argv[i]);
    548  1.1  jmmv     }
    549  1.1  jmmv 
    550  1.1  jmmv     // Read configuration data for this test suite.
    551  1.3  jmmv     vars_map test_suite_vars;
    552  1.1  jmmv     {
    553  1.3  jmmv         vars_map::const_iterator iter = af.props().find("test-suite");
    554  1.3  jmmv         assert(iter != af.props().end());
    555  1.3  jmmv         test_suite_vars = tools::config_file::read_config_files((*iter).second);
    556  1.1  jmmv     }
    557  1.1  jmmv 
    558  1.3  jmmv     tools::test_program::atf_tps_writer w(std::cout);
    559  1.1  jmmv     call_hook("atf-run", "info_start_hook");
    560  1.1  jmmv     w.ntps(count_tps(tps));
    561  1.1  jmmv 
    562  1.1  jmmv     bool ok = true;
    563  1.1  jmmv     for (std::vector< std::string >::const_iterator iter = tps.begin();
    564  1.3  jmmv          iter != tps.end(); iter++) {
    565  1.6  gson         std::string tp, tc;
    566  1.6  gson         colon_split(*iter, tp, tc);
    567  1.6  gson         const bool result = run_test(tools::fs::path(tp), tc, w,
    568  1.3  jmmv             tools::config_file::merge_configs(af.conf(), test_suite_vars));
    569  1.3  jmmv         ok &= (result == EXIT_SUCCESS);
    570  1.3  jmmv     }
    571  1.1  jmmv 
    572  1.1  jmmv     call_hook("atf-run", "info_end_hook");
    573  1.1  jmmv 
    574  1.1  jmmv     return ok ? EXIT_SUCCESS : EXIT_FAILURE;
    575  1.1  jmmv }
    576  1.1  jmmv 
    577  1.1  jmmv int
    578  1.1  jmmv main(int argc, char* const* argv)
    579  1.1  jmmv {
    580  1.1  jmmv     return atf_run().run(argc, argv);
    581  1.1  jmmv }
    582