Home | History | Annotate | Line # | Download | only in tools
atf-run.cpp revision 1.5
      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.3  jmmv     int run_test(const tools::fs::path&, tools::test_program::atf_tps_writer&,
     85  1.3  jmmv                  const vars_map&);
     86  1.3  jmmv     int run_test_directory(const tools::fs::path&,
     87  1.3  jmmv                            tools::test_program::atf_tps_writer&);
     88  1.3  jmmv     int run_test_program(const tools::fs::path&,
     89  1.3  jmmv                          tools::test_program::atf_tps_writer&,
     90  1.3  jmmv                          const vars_map&);
     91  1.3  jmmv 
     92  1.3  jmmv     tools::test_program::test_case_result get_test_case_result(
     93  1.3  jmmv         const std::string&, const tools::process::status&,
     94  1.3  jmmv         const tools::fs::path&) const;
     95  1.1  jmmv 
     96  1.3  jmmv public:
     97  1.3  jmmv     atf_run(void);
     98  1.1  jmmv 
     99  1.3  jmmv     int main(void);
    100  1.3  jmmv };
    101  1.1  jmmv 
    102  1.3  jmmv static void
    103  1.3  jmmv sanitize_gdb_env(void)
    104  1.3  jmmv {
    105  1.3  jmmv     try {
    106  1.3  jmmv         tools::env::unset("TERM");
    107  1.3  jmmv     } catch (...) {
    108  1.3  jmmv         // Just swallow exceptions here; they cannot propagate into C, which
    109  1.3  jmmv         // is where this function is called from, and even if these exceptions
    110  1.3  jmmv         // appear they are benign.
    111  1.1  jmmv     }
    112  1.3  jmmv }
    113  1.1  jmmv 
    114  1.3  jmmv static void
    115  1.3  jmmv dump_stacktrace(const tools::fs::path& tp, const tools::process::status& s,
    116  1.3  jmmv                 const tools::fs::path& workdir,
    117  1.3  jmmv                 tools::test_program::atf_tps_writer& w)
    118  1.3  jmmv {
    119  1.3  jmmv     assert(s.signaled() && s.coredump());
    120  1.1  jmmv 
    121  1.3  jmmv     w.stderr_tc("Test program crashed; attempting to get stack trace");
    122  1.1  jmmv 
    123  1.3  jmmv     const tools::fs::path corename = workdir /
    124  1.5  jmmv         (tp.leaf_name().substr(0, MAXCOMLEN) + ".core");
    125  1.3  jmmv     if (!tools::fs::exists(corename)) {
    126  1.3  jmmv         w.stderr_tc("Expected file " + corename.str() + " not found");
    127  1.3  jmmv         return;
    128  1.1  jmmv     }
    129  1.1  jmmv 
    130  1.3  jmmv     const tools::fs::path gdb(GDB);
    131  1.3  jmmv     const tools::fs::path gdbout = workdir / "gdb.out";
    132  1.3  jmmv     const tools::process::argv_array args(gdb.leaf_name().c_str(), "-batch",
    133  1.3  jmmv                                         "-q", "-ex", "bt", tp.c_str(),
    134  1.3  jmmv                                         corename.c_str(), NULL);
    135  1.3  jmmv     tools::process::status status = tools::process::exec(
    136  1.3  jmmv         gdb, args,
    137  1.3  jmmv         tools::process::stream_redirect_path(gdbout),
    138  1.3  jmmv         tools::process::stream_redirect_path(tools::fs::path("/dev/null")),
    139  1.3  jmmv         sanitize_gdb_env);
    140  1.3  jmmv     if (!status.exited() || status.exitstatus() != EXIT_SUCCESS) {
    141  1.3  jmmv         w.stderr_tc("Execution of " GDB " failed");
    142  1.3  jmmv         return;
    143  1.1  jmmv     }
    144  1.1  jmmv 
    145  1.3  jmmv     std::ifstream input(gdbout.c_str());
    146  1.3  jmmv     if (input) {
    147  1.3  jmmv         std::string line;
    148  1.3  jmmv         while (std::getline(input, line).good())
    149  1.3  jmmv             w.stderr_tc(line);
    150  1.3  jmmv         input.close();
    151  1.1  jmmv     }
    152  1.1  jmmv 
    153  1.3  jmmv     w.stderr_tc("Stack trace complete");
    154  1.1  jmmv }
    155  1.1  jmmv 
    156  1.1  jmmv const char* atf_run::m_description =
    157  1.1  jmmv     "atf-run is a tool that runs tests programs and collects their "
    158  1.1  jmmv     "results.";
    159  1.1  jmmv 
    160  1.1  jmmv atf_run::atf_run(void) :
    161  1.1  jmmv     app(m_description, "atf-run(1)", "atf(7)")
    162  1.1  jmmv {
    163  1.1  jmmv }
    164  1.1  jmmv 
    165  1.1  jmmv void
    166  1.1  jmmv atf_run::process_option(int ch, const char* arg)
    167  1.1  jmmv {
    168  1.1  jmmv     switch (ch) {
    169  1.1  jmmv     case 'v':
    170  1.1  jmmv         parse_vflag(arg);
    171  1.1  jmmv         break;
    172  1.1  jmmv 
    173  1.1  jmmv     default:
    174  1.3  jmmv         std::abort();
    175  1.1  jmmv     }
    176  1.1  jmmv }
    177  1.1  jmmv 
    178  1.1  jmmv std::string
    179  1.1  jmmv atf_run::specific_args(void)
    180  1.1  jmmv     const
    181  1.1  jmmv {
    182  1.1  jmmv     return "[test-program1 .. test-programN]";
    183  1.1  jmmv }
    184  1.1  jmmv 
    185  1.1  jmmv atf_run::options_set
    186  1.1  jmmv atf_run::specific_options(void)
    187  1.1  jmmv     const
    188  1.1  jmmv {
    189  1.3  jmmv     using tools::application::option;
    190  1.1  jmmv     options_set opts;
    191  1.1  jmmv     opts.insert(option('v', "var=value", "Sets the configuration variable "
    192  1.1  jmmv                                          "`var' to `value'; overrides "
    193  1.1  jmmv                                          "values in configuration files"));
    194  1.1  jmmv     return opts;
    195  1.1  jmmv }
    196  1.1  jmmv 
    197  1.1  jmmv void
    198  1.1  jmmv atf_run::parse_vflag(const std::string& str)
    199  1.1  jmmv {
    200  1.1  jmmv     if (str.empty())
    201  1.1  jmmv         throw std::runtime_error("-v requires a non-empty argument");
    202  1.1  jmmv 
    203  1.3  jmmv     std::vector< std::string > ws = tools::text::split(str, "=");
    204  1.1  jmmv     if (ws.size() == 1 && str[str.length() - 1] == '=') {
    205  1.1  jmmv         m_cmdline_vars[ws[0]] = "";
    206  1.1  jmmv     } else {
    207  1.1  jmmv         if (ws.size() != 2)
    208  1.1  jmmv             throw std::runtime_error("-v requires an argument of the form "
    209  1.1  jmmv                                      "var=value");
    210  1.1  jmmv 
    211  1.1  jmmv         m_cmdline_vars[ws[0]] = ws[1];
    212  1.1  jmmv     }
    213  1.1  jmmv }
    214  1.1  jmmv 
    215  1.1  jmmv int
    216  1.3  jmmv atf_run::run_test(const tools::fs::path& tp,
    217  1.3  jmmv                   tools::test_program::atf_tps_writer& w,
    218  1.3  jmmv                   const vars_map& config)
    219  1.1  jmmv {
    220  1.3  jmmv     tools::fs::file_info fi(tp);
    221  1.1  jmmv 
    222  1.1  jmmv     int errcode;
    223  1.3  jmmv     if (fi.get_type() == tools::fs::file_info::dir_type)
    224  1.1  jmmv         errcode = run_test_directory(tp, w);
    225  1.3  jmmv     else {
    226  1.3  jmmv         const vars_map effective_config =
    227  1.3  jmmv             tools::config_file::merge_configs(config, m_cmdline_vars);
    228  1.3  jmmv 
    229  1.3  jmmv         errcode = run_test_program(tp, w, effective_config);
    230  1.3  jmmv     }
    231  1.1  jmmv     return errcode;
    232  1.1  jmmv }
    233  1.1  jmmv 
    234  1.1  jmmv int
    235  1.3  jmmv atf_run::run_test_directory(const tools::fs::path& tp,
    236  1.3  jmmv                             tools::test_program::atf_tps_writer& w)
    237  1.1  jmmv {
    238  1.3  jmmv     tools::atffile af = tools::read_atffile(tp / "Atffile");
    239  1.1  jmmv 
    240  1.3  jmmv     vars_map test_suite_vars;
    241  1.1  jmmv     {
    242  1.3  jmmv         vars_map::const_iterator iter = af.props().find("test-suite");
    243  1.3  jmmv         assert(iter != af.props().end());
    244  1.3  jmmv         test_suite_vars = tools::config_file::read_config_files((*iter).second);
    245  1.1  jmmv     }
    246  1.1  jmmv 
    247  1.1  jmmv     bool ok = true;
    248  1.1  jmmv     for (std::vector< std::string >::const_iterator iter = af.tps().begin();
    249  1.3  jmmv          iter != af.tps().end(); iter++) {
    250  1.3  jmmv         const bool result = run_test(tp / *iter, w,
    251  1.3  jmmv             tools::config_file::merge_configs(af.conf(), test_suite_vars));
    252  1.3  jmmv         ok &= (result == EXIT_SUCCESS);
    253  1.3  jmmv     }
    254  1.1  jmmv 
    255  1.1  jmmv     return ok ? EXIT_SUCCESS : EXIT_FAILURE;
    256  1.1  jmmv }
    257  1.1  jmmv 
    258  1.3  jmmv tools::test_program::test_case_result
    259  1.3  jmmv atf_run::get_test_case_result(const std::string& broken_reason,
    260  1.3  jmmv                               const tools::process::status& s,
    261  1.3  jmmv                               const tools::fs::path& resfile)
    262  1.3  jmmv     const
    263  1.1  jmmv {
    264  1.3  jmmv     using tools::text::to_string;
    265  1.3  jmmv     using tools::test_program::read_test_case_result;
    266  1.3  jmmv     using tools::test_program::test_case_result;
    267  1.1  jmmv 
    268  1.3  jmmv     if (!broken_reason.empty()) {
    269  1.3  jmmv         test_case_result tcr;
    270  1.1  jmmv 
    271  1.3  jmmv         try {
    272  1.3  jmmv             tcr = read_test_case_result(resfile);
    273  1.1  jmmv 
    274  1.3  jmmv             if (tcr.state() == "expected_timeout") {
    275  1.3  jmmv                 return tcr;
    276  1.3  jmmv             } else {
    277  1.3  jmmv                 return test_case_result("failed", -1, broken_reason);
    278  1.3  jmmv             }
    279  1.3  jmmv         } catch (const std::runtime_error&) {
    280  1.3  jmmv             return test_case_result("failed", -1, broken_reason);
    281  1.3  jmmv         }
    282  1.3  jmmv     }
    283  1.1  jmmv 
    284  1.3  jmmv     if (s.exited()) {
    285  1.3  jmmv         test_case_result tcr;
    286  1.1  jmmv 
    287  1.3  jmmv         try {
    288  1.3  jmmv             tcr = read_test_case_result(resfile);
    289  1.3  jmmv         } catch (const std::runtime_error& e) {
    290  1.3  jmmv             return test_case_result("failed", -1, "Test case exited "
    291  1.3  jmmv                 "normally but failed to create the results file: " +
    292  1.3  jmmv                 std::string(e.what()));
    293  1.1  jmmv         }
    294  1.1  jmmv 
    295  1.3  jmmv         if (tcr.state() == "expected_death") {
    296  1.3  jmmv             return tcr;
    297  1.3  jmmv         } else if (tcr.state() == "expected_exit") {
    298  1.3  jmmv             if (tcr.value() == -1 || s.exitstatus() == tcr.value())
    299  1.3  jmmv                 return tcr;
    300  1.3  jmmv             else
    301  1.3  jmmv                 return test_case_result("failed", -1, "Test case was "
    302  1.3  jmmv                     "expected to exit with a " + to_string(tcr.value()) +
    303  1.3  jmmv                     " error code but returned " + to_string(s.exitstatus()));
    304  1.3  jmmv         } else if (tcr.state() == "expected_failure") {
    305  1.3  jmmv             if (s.exitstatus() == EXIT_SUCCESS)
    306  1.3  jmmv                 return tcr;
    307  1.3  jmmv             else
    308  1.3  jmmv                 return test_case_result("failed", -1, "Test case returned an "
    309  1.3  jmmv                     "error in expected_failure mode but it should not have");
    310  1.3  jmmv         } else if (tcr.state() == "expected_signal") {
    311  1.3  jmmv             return test_case_result("failed", -1, "Test case exited cleanly "
    312  1.3  jmmv                 "but was expected to receive a signal");
    313  1.3  jmmv         } else if (tcr.state() == "failed") {
    314  1.3  jmmv             if (s.exitstatus() == EXIT_SUCCESS)
    315  1.3  jmmv                 return test_case_result("failed", -1, "Test case "
    316  1.3  jmmv                     "exited successfully but reported failure");
    317  1.3  jmmv             else
    318  1.3  jmmv                 return tcr;
    319  1.3  jmmv         } else if (tcr.state() == "passed") {
    320  1.3  jmmv             if (s.exitstatus() == EXIT_SUCCESS)
    321  1.3  jmmv                 return tcr;
    322  1.3  jmmv             else
    323  1.3  jmmv                 return test_case_result("failed", -1, "Test case exited as "
    324  1.3  jmmv                     "passed but reported an error");
    325  1.3  jmmv         } else if (tcr.state() == "skipped") {
    326  1.3  jmmv             if (s.exitstatus() == EXIT_SUCCESS)
    327  1.3  jmmv                 return tcr;
    328  1.3  jmmv             else
    329  1.3  jmmv                 return test_case_result("failed", -1, "Test case exited as "
    330  1.3  jmmv                     "skipped but reported an error");
    331  1.3  jmmv         }
    332  1.3  jmmv     } else if (s.signaled()) {
    333  1.3  jmmv         test_case_result tcr;
    334  1.1  jmmv 
    335  1.3  jmmv         try {
    336  1.3  jmmv             tcr = read_test_case_result(resfile);
    337  1.3  jmmv         } catch (const std::runtime_error&) {
    338  1.3  jmmv             return test_case_result("failed", -1, "Test program received "
    339  1.3  jmmv                 "signal " + tools::text::to_string(s.termsig()) +
    340  1.3  jmmv                 (s.coredump() ? " (core dumped)" : ""));
    341  1.3  jmmv         }
    342  1.1  jmmv 
    343  1.3  jmmv         if (tcr.state() == "expected_death") {
    344  1.3  jmmv             return tcr;
    345  1.3  jmmv         } else if (tcr.state() == "expected_signal") {
    346  1.3  jmmv             if (tcr.value() == -1 || s.termsig() == tcr.value())
    347  1.3  jmmv                 return tcr;
    348  1.3  jmmv             else
    349  1.3  jmmv                 return test_case_result("failed", -1, "Test case was "
    350  1.3  jmmv                     "expected to exit due to a " + to_string(tcr.value()) +
    351  1.3  jmmv                     " signal but got " + to_string(s.termsig()));
    352  1.3  jmmv         } else {
    353  1.3  jmmv             return test_case_result("failed", -1, "Test program received "
    354  1.3  jmmv                 "signal " + tools::text::to_string(s.termsig()) +
    355  1.3  jmmv                 (s.coredump() ? " (core dumped)" : "") + " and created a "
    356  1.3  jmmv                 "bogus results file");
    357  1.1  jmmv         }
    358  1.1  jmmv     }
    359  1.3  jmmv     std::abort();
    360  1.3  jmmv     return test_case_result();
    361  1.1  jmmv }
    362  1.1  jmmv 
    363  1.1  jmmv int
    364  1.3  jmmv atf_run::run_test_program(const tools::fs::path& tp,
    365  1.3  jmmv                           tools::test_program::atf_tps_writer& w,
    366  1.3  jmmv                           const vars_map& config)
    367  1.1  jmmv {
    368  1.3  jmmv     int errcode = EXIT_SUCCESS;
    369  1.1  jmmv 
    370  1.3  jmmv     tools::test_program::metadata md;
    371  1.3  jmmv     try {
    372  1.3  jmmv         md = tools::test_program::get_metadata(tp, config);
    373  1.3  jmmv     } catch (const tools::parser::format_error& e) {
    374  1.3  jmmv         w.start_tp(tp.str(), 0);
    375  1.3  jmmv         w.end_tp("Invalid format for test case list: " + std::string(e.what()));
    376  1.3  jmmv         return EXIT_FAILURE;
    377  1.3  jmmv     } catch (const tools::parser::parse_errors& e) {
    378  1.3  jmmv         const std::string reason = tools::text::join(e, "; ");
    379  1.3  jmmv         w.start_tp(tp.str(), 0);
    380  1.3  jmmv         w.end_tp("Invalid format for test case list: " + reason);
    381  1.3  jmmv         return EXIT_FAILURE;
    382  1.3  jmmv     }
    383  1.3  jmmv 
    384  1.3  jmmv     tools::fs::temp_dir resdir(
    385  1.3  jmmv         tools::fs::path(tools::config::get("atf_workdir")) / "atf-run.XXXXXX");
    386  1.3  jmmv 
    387  1.3  jmmv     w.start_tp(tp.str(), md.test_cases.size());
    388  1.3  jmmv     if (md.test_cases.empty()) {
    389  1.3  jmmv         w.end_tp("Bogus test program: reported 0 test cases");
    390  1.1  jmmv         errcode = EXIT_FAILURE;
    391  1.1  jmmv     } else {
    392  1.3  jmmv         for (std::map< std::string, vars_map >::const_iterator iter
    393  1.3  jmmv              = md.test_cases.begin(); iter != md.test_cases.end(); iter++) {
    394  1.3  jmmv             const std::string& tcname = (*iter).first;
    395  1.3  jmmv             const vars_map& tcmd = (*iter).second;
    396  1.3  jmmv 
    397  1.3  jmmv             w.start_tc(tcname);
    398  1.3  jmmv 
    399  1.3  jmmv             try {
    400  1.3  jmmv                 const std::string& reqfail = tools::check_requirements(
    401  1.3  jmmv                     tcmd, config);
    402  1.3  jmmv                 if (!reqfail.empty()) {
    403  1.3  jmmv                     w.end_tc("skipped", reqfail);
    404  1.3  jmmv                     continue;
    405  1.3  jmmv                 }
    406  1.3  jmmv             } catch (const std::runtime_error& e) {
    407  1.3  jmmv                 w.end_tc("failed", e.what());
    408  1.3  jmmv                 errcode = EXIT_FAILURE;
    409  1.3  jmmv                 continue;
    410  1.3  jmmv             }
    411  1.3  jmmv 
    412  1.3  jmmv             const std::pair< int, int > user = tools::get_required_user(
    413  1.3  jmmv                 tcmd, config);
    414  1.3  jmmv 
    415  1.3  jmmv             tools::fs::path resfile = resdir.get_path() / "tcr";
    416  1.3  jmmv             assert(!tools::fs::exists(resfile));
    417  1.3  jmmv             try {
    418  1.3  jmmv                 const bool has_cleanup = tools::text::to_bool(
    419  1.3  jmmv                     (*tcmd.find("has.cleanup")).second);
    420  1.3  jmmv 
    421  1.3  jmmv                 tools::fs::temp_dir workdir(tools::fs::path(tools::config::get(
    422  1.3  jmmv                     "atf_workdir")) / "atf-run.XXXXXX");
    423  1.3  jmmv                 if (user.first != -1 && user.second != -1) {
    424  1.3  jmmv                     if (::chown(workdir.get_path().c_str(), user.first,
    425  1.3  jmmv                                 user.second) == -1) {
    426  1.3  jmmv                         throw tools::system_error("chown(" +
    427  1.3  jmmv                             workdir.get_path().str() + ")", "chown(2) failed",
    428  1.3  jmmv                             errno);
    429  1.3  jmmv                     }
    430  1.3  jmmv                     resfile = workdir.get_path() / "tcr";
    431  1.3  jmmv                 }
    432  1.3  jmmv 
    433  1.3  jmmv                 std::pair< std::string, const tools::process::status > s =
    434  1.3  jmmv                     tools::test_program::run_test_case(
    435  1.3  jmmv                         tp, tcname, "body", tcmd, config,
    436  1.3  jmmv                         resfile, workdir.get_path(), w);
    437  1.3  jmmv                 if (s.second.signaled() && s.second.coredump())
    438  1.3  jmmv                     dump_stacktrace(tp, s.second, workdir.get_path(), w);
    439  1.3  jmmv                 if (has_cleanup)
    440  1.3  jmmv                     (void)tools::test_program::run_test_case(
    441  1.3  jmmv                         tp, tcname, "cleanup", tcmd,
    442  1.3  jmmv                         config, resfile, workdir.get_path(), w);
    443  1.3  jmmv 
    444  1.3  jmmv                 // TODO: Force deletion of workdir.
    445  1.3  jmmv 
    446  1.3  jmmv                 tools::test_program::test_case_result tcr =
    447  1.3  jmmv                     get_test_case_result(s.first, s.second, resfile);
    448  1.3  jmmv 
    449  1.3  jmmv                 w.end_tc(tcr.state(), tcr.reason());
    450  1.3  jmmv                 if (tcr.state() == "failed")
    451  1.3  jmmv                     errcode = EXIT_FAILURE;
    452  1.3  jmmv             } catch (...) {
    453  1.3  jmmv                 if (tools::fs::exists(resfile))
    454  1.3  jmmv                     tools::fs::remove(resfile);
    455  1.3  jmmv                 throw;
    456  1.3  jmmv             }
    457  1.3  jmmv             if (tools::fs::exists(resfile))
    458  1.3  jmmv                 tools::fs::remove(resfile);
    459  1.3  jmmv 
    460  1.3  jmmv         }
    461  1.3  jmmv         w.end_tp("");
    462  1.1  jmmv     }
    463  1.1  jmmv 
    464  1.1  jmmv     return errcode;
    465  1.1  jmmv }
    466  1.1  jmmv 
    467  1.1  jmmv size_t
    468  1.1  jmmv atf_run::count_tps(std::vector< std::string > tps)
    469  1.1  jmmv     const
    470  1.1  jmmv {
    471  1.1  jmmv     size_t ntps = 0;
    472  1.1  jmmv 
    473  1.1  jmmv     for (std::vector< std::string >::const_iterator iter = tps.begin();
    474  1.1  jmmv          iter != tps.end(); iter++) {
    475  1.3  jmmv         tools::fs::path tp(*iter);
    476  1.3  jmmv         tools::fs::file_info fi(tp);
    477  1.1  jmmv 
    478  1.3  jmmv         if (fi.get_type() == tools::fs::file_info::dir_type) {
    479  1.3  jmmv             tools::atffile af = tools::read_atffile(tp / "Atffile");
    480  1.1  jmmv             std::vector< std::string > aux = af.tps();
    481  1.1  jmmv             for (std::vector< std::string >::iterator i2 = aux.begin();
    482  1.1  jmmv                  i2 != aux.end(); i2++)
    483  1.1  jmmv                 *i2 = (tp / *i2).str();
    484  1.1  jmmv             ntps += count_tps(aux);
    485  1.1  jmmv         } else
    486  1.1  jmmv             ntps++;
    487  1.1  jmmv     }
    488  1.1  jmmv 
    489  1.1  jmmv     return ntps;
    490  1.1  jmmv }
    491  1.1  jmmv 
    492  1.3  jmmv static
    493  1.1  jmmv void
    494  1.3  jmmv call_hook(const std::string& tool, const std::string& hook)
    495  1.1  jmmv {
    496  1.3  jmmv     const tools::fs::path sh(tools::config::get("atf_shell"));
    497  1.3  jmmv     const tools::fs::path hooks =
    498  1.3  jmmv         tools::fs::path(tools::config::get("atf_pkgdatadir")) / (tool + ".hooks");
    499  1.3  jmmv 
    500  1.3  jmmv     const tools::process::status s =
    501  1.3  jmmv         tools::process::exec(sh,
    502  1.3  jmmv                            tools::process::argv_array(sh.c_str(), hooks.c_str(),
    503  1.3  jmmv                                                     hook.c_str(), NULL),
    504  1.3  jmmv                            tools::process::stream_inherit(),
    505  1.3  jmmv                            tools::process::stream_inherit());
    506  1.1  jmmv 
    507  1.1  jmmv 
    508  1.3  jmmv     if (!s.exited() || s.exitstatus() != EXIT_SUCCESS)
    509  1.1  jmmv         throw std::runtime_error("Failed to run the '" + hook + "' hook "
    510  1.3  jmmv                                  "for '" + tool + "'");
    511  1.1  jmmv }
    512  1.1  jmmv 
    513  1.1  jmmv int
    514  1.1  jmmv atf_run::main(void)
    515  1.1  jmmv {
    516  1.3  jmmv     tools::atffile af = tools::read_atffile(tools::fs::path("Atffile"));
    517  1.1  jmmv 
    518  1.1  jmmv     std::vector< std::string > tps;
    519  1.1  jmmv     tps = af.tps();
    520  1.1  jmmv     if (m_argc >= 1) {
    521  1.1  jmmv         // TODO: Ensure that the given test names are listed in the
    522  1.1  jmmv         // Atffile.  Take into account that the file can be using globs.
    523  1.1  jmmv         tps.clear();
    524  1.1  jmmv         for (int i = 0; i < m_argc; i++)
    525  1.1  jmmv             tps.push_back(m_argv[i]);
    526  1.1  jmmv     }
    527  1.1  jmmv 
    528  1.1  jmmv     // Read configuration data for this test suite.
    529  1.3  jmmv     vars_map test_suite_vars;
    530  1.1  jmmv     {
    531  1.3  jmmv         vars_map::const_iterator iter = af.props().find("test-suite");
    532  1.3  jmmv         assert(iter != af.props().end());
    533  1.3  jmmv         test_suite_vars = tools::config_file::read_config_files((*iter).second);
    534  1.1  jmmv     }
    535  1.1  jmmv 
    536  1.3  jmmv     tools::test_program::atf_tps_writer w(std::cout);
    537  1.1  jmmv     call_hook("atf-run", "info_start_hook");
    538  1.1  jmmv     w.ntps(count_tps(tps));
    539  1.1  jmmv 
    540  1.1  jmmv     bool ok = true;
    541  1.1  jmmv     for (std::vector< std::string >::const_iterator iter = tps.begin();
    542  1.3  jmmv          iter != tps.end(); iter++) {
    543  1.3  jmmv         const bool result = run_test(tools::fs::path(*iter), w,
    544  1.3  jmmv             tools::config_file::merge_configs(af.conf(), test_suite_vars));
    545  1.3  jmmv         ok &= (result == EXIT_SUCCESS);
    546  1.3  jmmv     }
    547  1.1  jmmv 
    548  1.1  jmmv     call_hook("atf-run", "info_end_hook");
    549  1.1  jmmv 
    550  1.1  jmmv     return ok ? EXIT_SUCCESS : EXIT_FAILURE;
    551  1.1  jmmv }
    552  1.1  jmmv 
    553  1.1  jmmv int
    554  1.1  jmmv main(int argc, char* const* argv)
    555  1.1  jmmv {
    556  1.1  jmmv     return atf_run().run(argc, argv);
    557  1.1  jmmv }
    558