xref: /netbsd-src/external/bsd/atf/dist/tools/atf-run.cpp (revision 0033969404891b68474dd04b9301546a29ce80c8)
197bff204Sjmmv //
297bff204Sjmmv // Automated Testing Framework (atf)
397bff204Sjmmv //
497bff204Sjmmv // Copyright (c) 2007 The NetBSD Foundation, Inc.
597bff204Sjmmv // All rights reserved.
697bff204Sjmmv //
797bff204Sjmmv // Redistribution and use in source and binary forms, with or without
897bff204Sjmmv // modification, are permitted provided that the following conditions
997bff204Sjmmv // are met:
1097bff204Sjmmv // 1. Redistributions of source code must retain the above copyright
1197bff204Sjmmv //    notice, this list of conditions and the following disclaimer.
1297bff204Sjmmv // 2. Redistributions in binary form must reproduce the above copyright
1397bff204Sjmmv //    notice, this list of conditions and the following disclaimer in the
1497bff204Sjmmv //    documentation and/or other materials provided with the distribution.
1597bff204Sjmmv //
1697bff204Sjmmv // THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
1797bff204Sjmmv // CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
1897bff204Sjmmv // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
1997bff204Sjmmv // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2097bff204Sjmmv // IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
2197bff204Sjmmv // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2297bff204Sjmmv // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
2397bff204Sjmmv // GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2497bff204Sjmmv // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
2597bff204Sjmmv // IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
2697bff204Sjmmv // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
2797bff204Sjmmv // IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2897bff204Sjmmv //
2997bff204Sjmmv 
3097bff204Sjmmv extern "C" {
3197bff204Sjmmv #include <sys/types.h>
3297bff204Sjmmv #include <sys/param.h>
3397bff204Sjmmv #include <sys/stat.h>
3497bff204Sjmmv #include <sys/wait.h>
3597bff204Sjmmv #include <unistd.h>
3697bff204Sjmmv }
3797bff204Sjmmv 
3897bff204Sjmmv #include <algorithm>
3997bff204Sjmmv #include <cassert>
4097bff204Sjmmv #include <cerrno>
4197bff204Sjmmv #include <cstdlib>
4297bff204Sjmmv #include <cstring>
4397bff204Sjmmv #include <fstream>
4497bff204Sjmmv #include <iostream>
4597bff204Sjmmv #include <map>
4697bff204Sjmmv #include <string>
4797bff204Sjmmv 
4897bff204Sjmmv #include "application.hpp"
4997bff204Sjmmv #include "atffile.hpp"
5097bff204Sjmmv #include "config.hpp"
5197bff204Sjmmv #include "config_file.hpp"
5297bff204Sjmmv #include "env.hpp"
5397bff204Sjmmv #include "exceptions.hpp"
5497bff204Sjmmv #include "fs.hpp"
5597bff204Sjmmv #include "parser.hpp"
5697bff204Sjmmv #include "process.hpp"
5797bff204Sjmmv #include "requirements.hpp"
5897bff204Sjmmv #include "test-program.hpp"
5997bff204Sjmmv #include "text.hpp"
6097bff204Sjmmv 
6197bff204Sjmmv namespace {
6297bff204Sjmmv 
6397bff204Sjmmv typedef std::map< std::string, std::string > vars_map;
6497bff204Sjmmv 
6597bff204Sjmmv } // anonymous namespace
6697bff204Sjmmv 
6797bff204Sjmmv class atf_run : public tools::application::app {
6897bff204Sjmmv     static const char* m_description;
6997bff204Sjmmv 
7097bff204Sjmmv     vars_map m_cmdline_vars;
7197bff204Sjmmv 
7297bff204Sjmmv     static vars_map::value_type parse_var(const std::string&);
7397bff204Sjmmv 
7497bff204Sjmmv     void process_option(int, const char*);
7597bff204Sjmmv     std::string specific_args(void) const;
7697bff204Sjmmv     options_set specific_options(void) const;
7797bff204Sjmmv 
7897bff204Sjmmv     void parse_vflag(const std::string&);
7997bff204Sjmmv 
8097bff204Sjmmv     std::vector< std::string > conf_args(void) const;
8197bff204Sjmmv 
8297bff204Sjmmv     size_t count_tps(std::vector< std::string >) const;
8397bff204Sjmmv 
84e4942545Sgson     int run_test(const tools::fs::path&, const std::string &,
85e4942545Sgson                  tools::test_program::atf_tps_writer&,
8697bff204Sjmmv                  const vars_map&);
8797bff204Sjmmv     int run_test_directory(const tools::fs::path&,
8897bff204Sjmmv                            tools::test_program::atf_tps_writer&);
8997bff204Sjmmv     int run_test_program(const tools::fs::path&,
90e4942545Sgson                          const std::string tc,
9197bff204Sjmmv                          tools::test_program::atf_tps_writer&,
9297bff204Sjmmv                          const vars_map&);
9397bff204Sjmmv 
9497bff204Sjmmv     tools::test_program::test_case_result get_test_case_result(
9597bff204Sjmmv         const std::string&, const tools::process::status&,
9697bff204Sjmmv         const tools::fs::path&) const;
9797bff204Sjmmv 
9897bff204Sjmmv public:
9997bff204Sjmmv     atf_run(void);
10097bff204Sjmmv 
10197bff204Sjmmv     int main(void);
10297bff204Sjmmv };
10397bff204Sjmmv 
10497bff204Sjmmv static void
sanitize_gdb_env(void)10597bff204Sjmmv sanitize_gdb_env(void)
10697bff204Sjmmv {
10797bff204Sjmmv     try {
10897bff204Sjmmv         tools::env::unset("TERM");
10997bff204Sjmmv     } catch (...) {
11097bff204Sjmmv         // Just swallow exceptions here; they cannot propagate into C, which
11197bff204Sjmmv         // is where this function is called from, and even if these exceptions
11297bff204Sjmmv         // appear they are benign.
11397bff204Sjmmv     }
11497bff204Sjmmv }
11597bff204Sjmmv 
11697bff204Sjmmv static void
dump_stacktrace(const tools::fs::path & tp,const tools::process::status & s,const tools::fs::path & workdir,tools::test_program::atf_tps_writer & w)11797bff204Sjmmv dump_stacktrace(const tools::fs::path& tp, const tools::process::status& s,
11897bff204Sjmmv                 const tools::fs::path& workdir,
11997bff204Sjmmv                 tools::test_program::atf_tps_writer& w)
12097bff204Sjmmv {
12197bff204Sjmmv     assert(s.signaled() && s.coredump());
12297bff204Sjmmv 
12397bff204Sjmmv     w.stderr_tc("Test program crashed; attempting to get stack trace");
12497bff204Sjmmv 
12597bff204Sjmmv     const tools::fs::path corename = workdir /
126646fe152Sjmmv         (tp.leaf_name().substr(0, MAXCOMLEN) + ".core");
12797bff204Sjmmv     if (!tools::fs::exists(corename)) {
12897bff204Sjmmv         w.stderr_tc("Expected file " + corename.str() + " not found");
12997bff204Sjmmv         return;
13097bff204Sjmmv     }
13197bff204Sjmmv 
13297bff204Sjmmv     const tools::fs::path gdb(GDB);
13397bff204Sjmmv     const tools::fs::path gdbout = workdir / "gdb.out";
13497bff204Sjmmv     const tools::process::argv_array args(gdb.leaf_name().c_str(), "-batch",
13597bff204Sjmmv                                         "-q", "-ex", "bt", tp.c_str(),
13697bff204Sjmmv                                         corename.c_str(), NULL);
13797bff204Sjmmv     tools::process::status status = tools::process::exec(
13897bff204Sjmmv         gdb, args,
13997bff204Sjmmv         tools::process::stream_redirect_path(gdbout),
14097bff204Sjmmv         tools::process::stream_redirect_path(tools::fs::path("/dev/null")),
14197bff204Sjmmv         sanitize_gdb_env);
14297bff204Sjmmv     if (!status.exited() || status.exitstatus() != EXIT_SUCCESS) {
14397bff204Sjmmv         w.stderr_tc("Execution of " GDB " failed");
14497bff204Sjmmv         return;
14597bff204Sjmmv     }
14697bff204Sjmmv 
14797bff204Sjmmv     std::ifstream input(gdbout.c_str());
14897bff204Sjmmv     if (input) {
14997bff204Sjmmv         std::string line;
15097bff204Sjmmv         while (std::getline(input, line).good())
15197bff204Sjmmv             w.stderr_tc(line);
15297bff204Sjmmv         input.close();
15397bff204Sjmmv     }
15497bff204Sjmmv 
15597bff204Sjmmv     w.stderr_tc("Stack trace complete");
15697bff204Sjmmv }
15797bff204Sjmmv 
15897bff204Sjmmv const char* atf_run::m_description =
15997bff204Sjmmv     "atf-run is a tool that runs tests programs and collects their "
16097bff204Sjmmv     "results.";
16197bff204Sjmmv 
atf_run(void)16297bff204Sjmmv atf_run::atf_run(void) :
16397bff204Sjmmv     app(m_description, "atf-run(1)", "atf(7)")
16497bff204Sjmmv {
16597bff204Sjmmv }
16697bff204Sjmmv 
16797bff204Sjmmv void
process_option(int ch,const char * arg)16897bff204Sjmmv atf_run::process_option(int ch, const char* arg)
16997bff204Sjmmv {
17097bff204Sjmmv     switch (ch) {
17197bff204Sjmmv     case 'v':
17297bff204Sjmmv         parse_vflag(arg);
17397bff204Sjmmv         break;
17497bff204Sjmmv 
17597bff204Sjmmv     default:
17697bff204Sjmmv         std::abort();
17797bff204Sjmmv     }
17897bff204Sjmmv }
17997bff204Sjmmv 
18097bff204Sjmmv std::string
specific_args(void) const18197bff204Sjmmv atf_run::specific_args(void)
18297bff204Sjmmv     const
18397bff204Sjmmv {
184e4942545Sgson     return "[test1 .. testN]";
18597bff204Sjmmv }
18697bff204Sjmmv 
18797bff204Sjmmv atf_run::options_set
specific_options(void) const18897bff204Sjmmv atf_run::specific_options(void)
18997bff204Sjmmv     const
19097bff204Sjmmv {
19197bff204Sjmmv     using tools::application::option;
19297bff204Sjmmv     options_set opts;
19397bff204Sjmmv     opts.insert(option('v', "var=value", "Sets the configuration variable "
19497bff204Sjmmv                                          "`var' to `value'; overrides "
19597bff204Sjmmv                                          "values in configuration files"));
19697bff204Sjmmv     return opts;
19797bff204Sjmmv }
19897bff204Sjmmv 
19997bff204Sjmmv void
parse_vflag(const std::string & str)20097bff204Sjmmv atf_run::parse_vflag(const std::string& str)
20197bff204Sjmmv {
20297bff204Sjmmv     if (str.empty())
20397bff204Sjmmv         throw std::runtime_error("-v requires a non-empty argument");
20497bff204Sjmmv 
20597bff204Sjmmv     std::vector< std::string > ws = tools::text::split(str, "=");
20697bff204Sjmmv     if (ws.size() == 1 && str[str.length() - 1] == '=') {
20797bff204Sjmmv         m_cmdline_vars[ws[0]] = "";
20897bff204Sjmmv     } else {
20997bff204Sjmmv         if (ws.size() != 2)
21097bff204Sjmmv             throw std::runtime_error("-v requires an argument of the form "
21197bff204Sjmmv                                      "var=value");
21297bff204Sjmmv 
21397bff204Sjmmv         m_cmdline_vars[ws[0]] = ws[1];
21497bff204Sjmmv     }
21597bff204Sjmmv }
21697bff204Sjmmv 
21797bff204Sjmmv int
run_test(const tools::fs::path & tp,const std::string & tc,tools::test_program::atf_tps_writer & w,const vars_map & config)21897bff204Sjmmv atf_run::run_test(const tools::fs::path& tp,
219e4942545Sgson                   const std::string &tc,
22097bff204Sjmmv                   tools::test_program::atf_tps_writer& w,
22197bff204Sjmmv                   const vars_map& config)
22297bff204Sjmmv {
22397bff204Sjmmv     tools::fs::file_info fi(tp);
22497bff204Sjmmv 
22597bff204Sjmmv     int errcode;
22697bff204Sjmmv     if (fi.get_type() == tools::fs::file_info::dir_type)
22797bff204Sjmmv         errcode = run_test_directory(tp, w);
22897bff204Sjmmv     else {
22997bff204Sjmmv         const vars_map effective_config =
23097bff204Sjmmv             tools::config_file::merge_configs(config, m_cmdline_vars);
23197bff204Sjmmv 
232e4942545Sgson         errcode = run_test_program(tp, tc, w, effective_config);
23397bff204Sjmmv     }
23497bff204Sjmmv     return errcode;
23597bff204Sjmmv }
23697bff204Sjmmv 
23797bff204Sjmmv int
run_test_directory(const tools::fs::path & tp,tools::test_program::atf_tps_writer & w)23897bff204Sjmmv atf_run::run_test_directory(const tools::fs::path& tp,
23997bff204Sjmmv                             tools::test_program::atf_tps_writer& w)
24097bff204Sjmmv {
24197bff204Sjmmv     tools::atffile af = tools::read_atffile(tp / "Atffile");
24297bff204Sjmmv 
24397bff204Sjmmv     vars_map test_suite_vars;
24497bff204Sjmmv     {
24597bff204Sjmmv         vars_map::const_iterator iter = af.props().find("test-suite");
24697bff204Sjmmv         assert(iter != af.props().end());
24797bff204Sjmmv         test_suite_vars = tools::config_file::read_config_files((*iter).second);
24897bff204Sjmmv     }
24997bff204Sjmmv 
25097bff204Sjmmv     bool ok = true;
25197bff204Sjmmv     for (std::vector< std::string >::const_iterator iter = af.tps().begin();
25297bff204Sjmmv          iter != af.tps().end(); iter++) {
253e4942545Sgson         const bool result = run_test(tp / *iter, "", w,
25497bff204Sjmmv             tools::config_file::merge_configs(af.conf(), test_suite_vars));
25597bff204Sjmmv         ok &= (result == EXIT_SUCCESS);
25697bff204Sjmmv     }
25797bff204Sjmmv 
25897bff204Sjmmv     return ok ? EXIT_SUCCESS : EXIT_FAILURE;
25997bff204Sjmmv }
26097bff204Sjmmv 
26197bff204Sjmmv tools::test_program::test_case_result
get_test_case_result(const std::string & broken_reason,const tools::process::status & s,const tools::fs::path & resfile) const26297bff204Sjmmv atf_run::get_test_case_result(const std::string& broken_reason,
26397bff204Sjmmv                               const tools::process::status& s,
26497bff204Sjmmv                               const tools::fs::path& resfile)
26597bff204Sjmmv     const
26697bff204Sjmmv {
26797bff204Sjmmv     using tools::text::to_string;
26897bff204Sjmmv     using tools::test_program::read_test_case_result;
26997bff204Sjmmv     using tools::test_program::test_case_result;
27097bff204Sjmmv 
27197bff204Sjmmv     if (!broken_reason.empty()) {
27297bff204Sjmmv         test_case_result tcr;
27397bff204Sjmmv 
27497bff204Sjmmv         try {
27597bff204Sjmmv             tcr = read_test_case_result(resfile);
27697bff204Sjmmv 
27797bff204Sjmmv             if (tcr.state() == "expected_timeout") {
27897bff204Sjmmv                 return tcr;
27997bff204Sjmmv             } else {
28097bff204Sjmmv                 return test_case_result("failed", -1, broken_reason);
28197bff204Sjmmv             }
28297bff204Sjmmv         } catch (const std::runtime_error&) {
28397bff204Sjmmv             return test_case_result("failed", -1, broken_reason);
28497bff204Sjmmv         }
28597bff204Sjmmv     }
28697bff204Sjmmv 
28797bff204Sjmmv     if (s.exited()) {
28897bff204Sjmmv         test_case_result tcr;
28997bff204Sjmmv 
29097bff204Sjmmv         try {
29197bff204Sjmmv             tcr = read_test_case_result(resfile);
29297bff204Sjmmv         } catch (const std::runtime_error& e) {
29397bff204Sjmmv             return test_case_result("failed", -1, "Test case exited "
29497bff204Sjmmv                 "normally but failed to create the results file: " +
29597bff204Sjmmv                 std::string(e.what()));
29697bff204Sjmmv         }
29797bff204Sjmmv 
29897bff204Sjmmv         if (tcr.state() == "expected_death") {
29997bff204Sjmmv             return tcr;
30097bff204Sjmmv         } else if (tcr.state() == "expected_exit") {
30197bff204Sjmmv             if (tcr.value() == -1 || s.exitstatus() == tcr.value())
30297bff204Sjmmv                 return tcr;
30397bff204Sjmmv             else
30497bff204Sjmmv                 return test_case_result("failed", -1, "Test case was "
30597bff204Sjmmv                     "expected to exit with a " + to_string(tcr.value()) +
30697bff204Sjmmv                     " error code but returned " + to_string(s.exitstatus()));
30797bff204Sjmmv         } else if (tcr.state() == "expected_failure") {
30897bff204Sjmmv             if (s.exitstatus() == EXIT_SUCCESS)
30997bff204Sjmmv                 return tcr;
31097bff204Sjmmv             else
31197bff204Sjmmv                 return test_case_result("failed", -1, "Test case returned an "
31297bff204Sjmmv                     "error in expected_failure mode but it should not have");
31397bff204Sjmmv         } else if (tcr.state() == "expected_signal") {
31497bff204Sjmmv             return test_case_result("failed", -1, "Test case exited cleanly "
31597bff204Sjmmv                 "but was expected to receive a signal");
31697bff204Sjmmv         } else if (tcr.state() == "failed") {
31797bff204Sjmmv             if (s.exitstatus() == EXIT_SUCCESS)
31897bff204Sjmmv                 return test_case_result("failed", -1, "Test case "
31997bff204Sjmmv                     "exited successfully but reported failure");
32097bff204Sjmmv             else
32197bff204Sjmmv                 return tcr;
32297bff204Sjmmv         } else if (tcr.state() == "passed") {
32397bff204Sjmmv             if (s.exitstatus() == EXIT_SUCCESS)
32497bff204Sjmmv                 return tcr;
32597bff204Sjmmv             else
32697bff204Sjmmv                 return test_case_result("failed", -1, "Test case exited as "
32797bff204Sjmmv                     "passed but reported an error");
32897bff204Sjmmv         } else if (tcr.state() == "skipped") {
32997bff204Sjmmv             if (s.exitstatus() == EXIT_SUCCESS)
33097bff204Sjmmv                 return tcr;
33197bff204Sjmmv             else
33297bff204Sjmmv                 return test_case_result("failed", -1, "Test case exited as "
33397bff204Sjmmv                     "skipped but reported an error");
33497bff204Sjmmv         }
33597bff204Sjmmv     } else if (s.signaled()) {
33697bff204Sjmmv         test_case_result tcr;
33797bff204Sjmmv 
33897bff204Sjmmv         try {
33997bff204Sjmmv             tcr = read_test_case_result(resfile);
34097bff204Sjmmv         } catch (const std::runtime_error&) {
34197bff204Sjmmv             return test_case_result("failed", -1, "Test program received "
34297bff204Sjmmv                 "signal " + tools::text::to_string(s.termsig()) +
34397bff204Sjmmv                 (s.coredump() ? " (core dumped)" : ""));
34497bff204Sjmmv         }
34597bff204Sjmmv 
34697bff204Sjmmv         if (tcr.state() == "expected_death") {
34797bff204Sjmmv             return tcr;
34897bff204Sjmmv         } else if (tcr.state() == "expected_signal") {
34997bff204Sjmmv             if (tcr.value() == -1 || s.termsig() == tcr.value())
35097bff204Sjmmv                 return tcr;
35197bff204Sjmmv             else
35297bff204Sjmmv                 return test_case_result("failed", -1, "Test case was "
35397bff204Sjmmv                     "expected to exit due to a " + to_string(tcr.value()) +
35497bff204Sjmmv                     " signal but got " + to_string(s.termsig()));
35597bff204Sjmmv         } else {
35697bff204Sjmmv             return test_case_result("failed", -1, "Test program received "
35797bff204Sjmmv                 "signal " + tools::text::to_string(s.termsig()) +
35897bff204Sjmmv                 (s.coredump() ? " (core dumped)" : "") + " and created a "
35997bff204Sjmmv                 "bogus results file");
36097bff204Sjmmv         }
36197bff204Sjmmv     }
36297bff204Sjmmv     std::abort();
36397bff204Sjmmv     return test_case_result();
36497bff204Sjmmv }
36597bff204Sjmmv 
36697bff204Sjmmv int
run_test_program(const tools::fs::path & tp,const std::string tc,tools::test_program::atf_tps_writer & w,const vars_map & config)36797bff204Sjmmv atf_run::run_test_program(const tools::fs::path& tp,
368e4942545Sgson                           const std::string tc,
36997bff204Sjmmv                           tools::test_program::atf_tps_writer& w,
37097bff204Sjmmv                           const vars_map& config)
37197bff204Sjmmv {
37297bff204Sjmmv     int errcode = EXIT_SUCCESS;
37397bff204Sjmmv 
37497bff204Sjmmv     tools::test_program::metadata md;
37597bff204Sjmmv     try {
37697bff204Sjmmv         md = tools::test_program::get_metadata(tp, config);
37797bff204Sjmmv     } catch (const tools::parser::format_error& e) {
37897bff204Sjmmv         w.start_tp(tp.str(), 0);
37997bff204Sjmmv         w.end_tp("Invalid format for test case list: " + std::string(e.what()));
38097bff204Sjmmv         return EXIT_FAILURE;
38197bff204Sjmmv     } catch (const tools::parser::parse_errors& e) {
38297bff204Sjmmv         const std::string reason = tools::text::join(e, "; ");
38397bff204Sjmmv         w.start_tp(tp.str(), 0);
38497bff204Sjmmv         w.end_tp("Invalid format for test case list: " + reason);
38597bff204Sjmmv         return EXIT_FAILURE;
38697bff204Sjmmv     }
38797bff204Sjmmv 
38897bff204Sjmmv     tools::fs::temp_dir resdir(
38997bff204Sjmmv         tools::fs::path(tools::config::get("atf_workdir")) / "atf-run.XXXXXX");
39097bff204Sjmmv 
391*00339694Sgson     size_t nseltcs;
392*00339694Sgson     if (tc.empty()) {
393*00339694Sgson         nseltcs = md.test_cases.size();
394*00339694Sgson     } else {
395*00339694Sgson         nseltcs = 0;
396*00339694Sgson         for (std::map< std::string, vars_map >::const_iterator iter
397*00339694Sgson              = md.test_cases.begin(); iter != md.test_cases.end(); iter++) {
398*00339694Sgson             const std::string& tcname = (*iter).first;
399*00339694Sgson             if (tcname == tc)
400*00339694Sgson                 nseltcs++;
401*00339694Sgson         }
402*00339694Sgson         if (nseltcs == 0)
403*00339694Sgson             throw std::runtime_error("No such test case");
404*00339694Sgson     }
405*00339694Sgson 
406*00339694Sgson     w.start_tp(tp.str(), nseltcs);
40797bff204Sjmmv     if (md.test_cases.empty()) {
40897bff204Sjmmv         w.end_tp("Bogus test program: reported 0 test cases");
40997bff204Sjmmv         errcode = EXIT_FAILURE;
41097bff204Sjmmv     } else {
41197bff204Sjmmv         for (std::map< std::string, vars_map >::const_iterator iter
41297bff204Sjmmv              = md.test_cases.begin(); iter != md.test_cases.end(); iter++) {
41397bff204Sjmmv             const std::string& tcname = (*iter).first;
41497bff204Sjmmv             const vars_map& tcmd = (*iter).second;
41597bff204Sjmmv 
416e4942545Sgson             if (! tc.empty() && tcname != tc)
417e4942545Sgson                 continue;
418e4942545Sgson 
41997bff204Sjmmv             w.start_tc(tcname);
42097bff204Sjmmv 
42197bff204Sjmmv             try {
42297bff204Sjmmv                 const std::string& reqfail = tools::check_requirements(
42397bff204Sjmmv                     tcmd, config);
42497bff204Sjmmv                 if (!reqfail.empty()) {
42597bff204Sjmmv                     w.end_tc("skipped", reqfail);
42697bff204Sjmmv                     continue;
42797bff204Sjmmv                 }
42897bff204Sjmmv             } catch (const std::runtime_error& e) {
42997bff204Sjmmv                 w.end_tc("failed", e.what());
43097bff204Sjmmv                 errcode = EXIT_FAILURE;
43197bff204Sjmmv                 continue;
43297bff204Sjmmv             }
43397bff204Sjmmv 
43497bff204Sjmmv             const std::pair< int, int > user = tools::get_required_user(
43597bff204Sjmmv                 tcmd, config);
43697bff204Sjmmv 
43797bff204Sjmmv             tools::fs::path resfile = resdir.get_path() / "tcr";
43897bff204Sjmmv             assert(!tools::fs::exists(resfile));
43997bff204Sjmmv             try {
44097bff204Sjmmv                 const bool has_cleanup = tools::text::to_bool(
44197bff204Sjmmv                     (*tcmd.find("has.cleanup")).second);
44297bff204Sjmmv 
44397bff204Sjmmv                 tools::fs::temp_dir workdir(tools::fs::path(tools::config::get(
44497bff204Sjmmv                     "atf_workdir")) / "atf-run.XXXXXX");
44597bff204Sjmmv                 if (user.first != -1 && user.second != -1) {
44697bff204Sjmmv                     if (::chown(workdir.get_path().c_str(), user.first,
44797bff204Sjmmv                                 user.second) == -1) {
44897bff204Sjmmv                         throw tools::system_error("chown(" +
44997bff204Sjmmv                             workdir.get_path().str() + ")", "chown(2) failed",
45097bff204Sjmmv                             errno);
45197bff204Sjmmv                     }
45297bff204Sjmmv                     resfile = workdir.get_path() / "tcr";
45397bff204Sjmmv                 }
45497bff204Sjmmv 
45597bff204Sjmmv                 std::pair< std::string, const tools::process::status > s =
45697bff204Sjmmv                     tools::test_program::run_test_case(
45797bff204Sjmmv                         tp, tcname, "body", tcmd, config,
45897bff204Sjmmv                         resfile, workdir.get_path(), w);
45997bff204Sjmmv                 if (s.second.signaled() && s.second.coredump())
46097bff204Sjmmv                     dump_stacktrace(tp, s.second, workdir.get_path(), w);
46197bff204Sjmmv                 if (has_cleanup)
46297bff204Sjmmv                     (void)tools::test_program::run_test_case(
46397bff204Sjmmv                         tp, tcname, "cleanup", tcmd,
46497bff204Sjmmv                         config, resfile, workdir.get_path(), w);
46597bff204Sjmmv 
46697bff204Sjmmv                 // TODO: Force deletion of workdir.
46797bff204Sjmmv 
46897bff204Sjmmv                 tools::test_program::test_case_result tcr =
46997bff204Sjmmv                     get_test_case_result(s.first, s.second, resfile);
47097bff204Sjmmv 
47197bff204Sjmmv                 w.end_tc(tcr.state(), tcr.reason());
47297bff204Sjmmv                 if (tcr.state() == "failed")
47397bff204Sjmmv                     errcode = EXIT_FAILURE;
47497bff204Sjmmv             } catch (...) {
47597bff204Sjmmv                 if (tools::fs::exists(resfile))
47697bff204Sjmmv                     tools::fs::remove(resfile);
47797bff204Sjmmv                 throw;
47897bff204Sjmmv             }
47997bff204Sjmmv             if (tools::fs::exists(resfile))
48097bff204Sjmmv                 tools::fs::remove(resfile);
48197bff204Sjmmv 
48297bff204Sjmmv         }
48397bff204Sjmmv         w.end_tp("");
48497bff204Sjmmv     }
48597bff204Sjmmv 
48697bff204Sjmmv     return errcode;
48797bff204Sjmmv }
48897bff204Sjmmv 
489e4942545Sgson static void
colon_split(const std::string & s,std::string & tp,std::string & tc)490e4942545Sgson colon_split(const std::string &s, std::string &tp, std::string &tc)
491e4942545Sgson {
492e4942545Sgson     size_t colon_pos = s.rfind(':');
493e4942545Sgson     if (colon_pos != std::string::npos && colon_pos < s.size() - 1) {
494e4942545Sgson         tp = s.substr(0, colon_pos);
495e4942545Sgson         tc = s.substr(colon_pos + 1);
496e4942545Sgson     } else {
497e4942545Sgson         tp = s;
498e4942545Sgson         tc = "";
499e4942545Sgson     }
500e4942545Sgson }
501e4942545Sgson 
50297bff204Sjmmv size_t
count_tps(std::vector<std::string> tps) const50397bff204Sjmmv atf_run::count_tps(std::vector< std::string > tps)
50497bff204Sjmmv     const
50597bff204Sjmmv {
50697bff204Sjmmv     size_t ntps = 0;
50797bff204Sjmmv 
50897bff204Sjmmv     for (std::vector< std::string >::const_iterator iter = tps.begin();
50997bff204Sjmmv          iter != tps.end(); iter++) {
510e4942545Sgson         std::string tpname, tcname;
511e4942545Sgson         colon_split(*iter, tpname, tcname);
512e4942545Sgson         tools::fs::path tp(tpname);
51397bff204Sjmmv         tools::fs::file_info fi(tp);
51497bff204Sjmmv 
51597bff204Sjmmv         if (fi.get_type() == tools::fs::file_info::dir_type) {
51697bff204Sjmmv             tools::atffile af = tools::read_atffile(tp / "Atffile");
51797bff204Sjmmv             std::vector< std::string > aux = af.tps();
51897bff204Sjmmv             for (std::vector< std::string >::iterator i2 = aux.begin();
51997bff204Sjmmv                  i2 != aux.end(); i2++)
52097bff204Sjmmv                 *i2 = (tp / *i2).str();
52197bff204Sjmmv             ntps += count_tps(aux);
52297bff204Sjmmv         } else
52397bff204Sjmmv             ntps++;
52497bff204Sjmmv     }
52597bff204Sjmmv 
52697bff204Sjmmv     return ntps;
52797bff204Sjmmv }
52897bff204Sjmmv 
52997bff204Sjmmv static
53097bff204Sjmmv void
call_hook(const std::string & tool,const std::string & hook)53197bff204Sjmmv call_hook(const std::string& tool, const std::string& hook)
53297bff204Sjmmv {
53397bff204Sjmmv     const tools::fs::path sh(tools::config::get("atf_shell"));
53497bff204Sjmmv     const tools::fs::path hooks =
53597bff204Sjmmv         tools::fs::path(tools::config::get("atf_pkgdatadir")) / (tool + ".hooks");
53697bff204Sjmmv 
53797bff204Sjmmv     const tools::process::status s =
53897bff204Sjmmv         tools::process::exec(sh,
53997bff204Sjmmv                            tools::process::argv_array(sh.c_str(), hooks.c_str(),
54097bff204Sjmmv                                                     hook.c_str(), NULL),
54197bff204Sjmmv                            tools::process::stream_inherit(),
54297bff204Sjmmv                            tools::process::stream_inherit());
54397bff204Sjmmv 
54497bff204Sjmmv 
54597bff204Sjmmv     if (!s.exited() || s.exitstatus() != EXIT_SUCCESS)
54697bff204Sjmmv         throw std::runtime_error("Failed to run the '" + hook + "' hook "
54797bff204Sjmmv                                  "for '" + tool + "'");
54897bff204Sjmmv }
54997bff204Sjmmv 
55097bff204Sjmmv int
main(void)55197bff204Sjmmv atf_run::main(void)
55297bff204Sjmmv {
55397bff204Sjmmv     tools::atffile af = tools::read_atffile(tools::fs::path("Atffile"));
55497bff204Sjmmv 
55597bff204Sjmmv     std::vector< std::string > tps;
55697bff204Sjmmv     tps = af.tps();
55797bff204Sjmmv     if (m_argc >= 1) {
55897bff204Sjmmv         // TODO: Ensure that the given test names are listed in the
55997bff204Sjmmv         // Atffile.  Take into account that the file can be using globs.
56097bff204Sjmmv         tps.clear();
56197bff204Sjmmv         for (int i = 0; i < m_argc; i++)
56297bff204Sjmmv             tps.push_back(m_argv[i]);
56397bff204Sjmmv     }
56497bff204Sjmmv 
56597bff204Sjmmv     // Read configuration data for this test suite.
56697bff204Sjmmv     vars_map test_suite_vars;
56797bff204Sjmmv     {
56897bff204Sjmmv         vars_map::const_iterator iter = af.props().find("test-suite");
56997bff204Sjmmv         assert(iter != af.props().end());
57097bff204Sjmmv         test_suite_vars = tools::config_file::read_config_files((*iter).second);
57197bff204Sjmmv     }
57297bff204Sjmmv 
57397bff204Sjmmv     tools::test_program::atf_tps_writer w(std::cout);
57497bff204Sjmmv     call_hook("atf-run", "info_start_hook");
57597bff204Sjmmv     w.ntps(count_tps(tps));
57697bff204Sjmmv 
57797bff204Sjmmv     bool ok = true;
57897bff204Sjmmv     for (std::vector< std::string >::const_iterator iter = tps.begin();
57997bff204Sjmmv          iter != tps.end(); iter++) {
580e4942545Sgson         std::string tp, tc;
581e4942545Sgson         colon_split(*iter, tp, tc);
582e4942545Sgson         const bool result = run_test(tools::fs::path(tp), tc, w,
58397bff204Sjmmv             tools::config_file::merge_configs(af.conf(), test_suite_vars));
58497bff204Sjmmv         ok &= (result == EXIT_SUCCESS);
58597bff204Sjmmv     }
58697bff204Sjmmv 
58797bff204Sjmmv     call_hook("atf-run", "info_end_hook");
58897bff204Sjmmv 
58997bff204Sjmmv     return ok ? EXIT_SUCCESS : EXIT_FAILURE;
59097bff204Sjmmv }
59197bff204Sjmmv 
59297bff204Sjmmv int
main(int argc,char * const * argv)59397bff204Sjmmv main(int argc, char* const* argv)
59497bff204Sjmmv {
59597bff204Sjmmv     return atf_run().run(argc, argv);
59697bff204Sjmmv }
597