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.7 gson size_t nseltcs; 392 1.7 gson if (tc.empty()) { 393 1.7 gson nseltcs = md.test_cases.size(); 394 1.7 gson } else { 395 1.7 gson nseltcs = 0; 396 1.7 gson for (std::map< std::string, vars_map >::const_iterator iter 397 1.7 gson = md.test_cases.begin(); iter != md.test_cases.end(); iter++) { 398 1.7 gson const std::string& tcname = (*iter).first; 399 1.7 gson if (tcname == tc) 400 1.7 gson nseltcs++; 401 1.7 gson } 402 1.7 gson if (nseltcs == 0) 403 1.7 gson throw std::runtime_error("No such test case"); 404 1.7 gson } 405 1.7 gson 406 1.7 gson w.start_tp(tp.str(), nseltcs); 407 1.3 jmmv if (md.test_cases.empty()) { 408 1.3 jmmv w.end_tp("Bogus test program: reported 0 test cases"); 409 1.1 jmmv errcode = EXIT_FAILURE; 410 1.1 jmmv } else { 411 1.3 jmmv for (std::map< std::string, vars_map >::const_iterator iter 412 1.3 jmmv = md.test_cases.begin(); iter != md.test_cases.end(); iter++) { 413 1.3 jmmv const std::string& tcname = (*iter).first; 414 1.3 jmmv const vars_map& tcmd = (*iter).second; 415 1.3 jmmv 416 1.6 gson if (! tc.empty() && tcname != tc) 417 1.6 gson continue; 418 1.6 gson 419 1.3 jmmv w.start_tc(tcname); 420 1.3 jmmv 421 1.3 jmmv try { 422 1.3 jmmv const std::string& reqfail = tools::check_requirements( 423 1.3 jmmv tcmd, config); 424 1.3 jmmv if (!reqfail.empty()) { 425 1.3 jmmv w.end_tc("skipped", reqfail); 426 1.3 jmmv continue; 427 1.3 jmmv } 428 1.3 jmmv } catch (const std::runtime_error& e) { 429 1.3 jmmv w.end_tc("failed", e.what()); 430 1.3 jmmv errcode = EXIT_FAILURE; 431 1.3 jmmv continue; 432 1.3 jmmv } 433 1.3 jmmv 434 1.3 jmmv const std::pair< int, int > user = tools::get_required_user( 435 1.3 jmmv tcmd, config); 436 1.3 jmmv 437 1.3 jmmv tools::fs::path resfile = resdir.get_path() / "tcr"; 438 1.3 jmmv assert(!tools::fs::exists(resfile)); 439 1.3 jmmv try { 440 1.3 jmmv const bool has_cleanup = tools::text::to_bool( 441 1.3 jmmv (*tcmd.find("has.cleanup")).second); 442 1.3 jmmv 443 1.3 jmmv tools::fs::temp_dir workdir(tools::fs::path(tools::config::get( 444 1.3 jmmv "atf_workdir")) / "atf-run.XXXXXX"); 445 1.3 jmmv if (user.first != -1 && user.second != -1) { 446 1.3 jmmv if (::chown(workdir.get_path().c_str(), user.first, 447 1.3 jmmv user.second) == -1) { 448 1.3 jmmv throw tools::system_error("chown(" + 449 1.3 jmmv workdir.get_path().str() + ")", "chown(2) failed", 450 1.3 jmmv errno); 451 1.3 jmmv } 452 1.3 jmmv resfile = workdir.get_path() / "tcr"; 453 1.3 jmmv } 454 1.3 jmmv 455 1.3 jmmv std::pair< std::string, const tools::process::status > s = 456 1.3 jmmv tools::test_program::run_test_case( 457 1.3 jmmv tp, tcname, "body", tcmd, config, 458 1.3 jmmv resfile, workdir.get_path(), w); 459 1.3 jmmv if (s.second.signaled() && s.second.coredump()) 460 1.3 jmmv dump_stacktrace(tp, s.second, workdir.get_path(), w); 461 1.3 jmmv if (has_cleanup) 462 1.3 jmmv (void)tools::test_program::run_test_case( 463 1.3 jmmv tp, tcname, "cleanup", tcmd, 464 1.3 jmmv config, resfile, workdir.get_path(), w); 465 1.3 jmmv 466 1.3 jmmv // TODO: Force deletion of workdir. 467 1.3 jmmv 468 1.3 jmmv tools::test_program::test_case_result tcr = 469 1.3 jmmv get_test_case_result(s.first, s.second, resfile); 470 1.3 jmmv 471 1.3 jmmv w.end_tc(tcr.state(), tcr.reason()); 472 1.3 jmmv if (tcr.state() == "failed") 473 1.3 jmmv errcode = EXIT_FAILURE; 474 1.3 jmmv } catch (...) { 475 1.3 jmmv if (tools::fs::exists(resfile)) 476 1.3 jmmv tools::fs::remove(resfile); 477 1.3 jmmv throw; 478 1.3 jmmv } 479 1.3 jmmv if (tools::fs::exists(resfile)) 480 1.3 jmmv tools::fs::remove(resfile); 481 1.3 jmmv 482 1.3 jmmv } 483 1.3 jmmv w.end_tp(""); 484 1.1 jmmv } 485 1.1 jmmv 486 1.1 jmmv return errcode; 487 1.1 jmmv } 488 1.1 jmmv 489 1.6 gson static void 490 1.6 gson colon_split(const std::string &s, std::string &tp, std::string &tc) 491 1.6 gson { 492 1.6 gson size_t colon_pos = s.rfind(':'); 493 1.6 gson if (colon_pos != std::string::npos && colon_pos < s.size() - 1) { 494 1.6 gson tp = s.substr(0, colon_pos); 495 1.6 gson tc = s.substr(colon_pos + 1); 496 1.6 gson } else { 497 1.6 gson tp = s; 498 1.6 gson tc = ""; 499 1.6 gson } 500 1.6 gson } 501 1.6 gson 502 1.1 jmmv size_t 503 1.1 jmmv atf_run::count_tps(std::vector< std::string > tps) 504 1.1 jmmv const 505 1.1 jmmv { 506 1.1 jmmv size_t ntps = 0; 507 1.1 jmmv 508 1.1 jmmv for (std::vector< std::string >::const_iterator iter = tps.begin(); 509 1.1 jmmv iter != tps.end(); iter++) { 510 1.6 gson std::string tpname, tcname; 511 1.6 gson colon_split(*iter, tpname, tcname); 512 1.6 gson tools::fs::path tp(tpname); 513 1.3 jmmv tools::fs::file_info fi(tp); 514 1.1 jmmv 515 1.3 jmmv if (fi.get_type() == tools::fs::file_info::dir_type) { 516 1.3 jmmv tools::atffile af = tools::read_atffile(tp / "Atffile"); 517 1.1 jmmv std::vector< std::string > aux = af.tps(); 518 1.1 jmmv for (std::vector< std::string >::iterator i2 = aux.begin(); 519 1.1 jmmv i2 != aux.end(); i2++) 520 1.1 jmmv *i2 = (tp / *i2).str(); 521 1.1 jmmv ntps += count_tps(aux); 522 1.1 jmmv } else 523 1.1 jmmv ntps++; 524 1.1 jmmv } 525 1.1 jmmv 526 1.1 jmmv return ntps; 527 1.1 jmmv } 528 1.1 jmmv 529 1.3 jmmv static 530 1.1 jmmv void 531 1.3 jmmv call_hook(const std::string& tool, const std::string& hook) 532 1.1 jmmv { 533 1.3 jmmv const tools::fs::path sh(tools::config::get("atf_shell")); 534 1.3 jmmv const tools::fs::path hooks = 535 1.3 jmmv tools::fs::path(tools::config::get("atf_pkgdatadir")) / (tool + ".hooks"); 536 1.3 jmmv 537 1.3 jmmv const tools::process::status s = 538 1.3 jmmv tools::process::exec(sh, 539 1.3 jmmv tools::process::argv_array(sh.c_str(), hooks.c_str(), 540 1.3 jmmv hook.c_str(), NULL), 541 1.3 jmmv tools::process::stream_inherit(), 542 1.3 jmmv tools::process::stream_inherit()); 543 1.1 jmmv 544 1.1 jmmv 545 1.3 jmmv if (!s.exited() || s.exitstatus() != EXIT_SUCCESS) 546 1.1 jmmv throw std::runtime_error("Failed to run the '" + hook + "' hook " 547 1.3 jmmv "for '" + tool + "'"); 548 1.1 jmmv } 549 1.1 jmmv 550 1.1 jmmv int 551 1.1 jmmv atf_run::main(void) 552 1.1 jmmv { 553 1.3 jmmv tools::atffile af = tools::read_atffile(tools::fs::path("Atffile")); 554 1.1 jmmv 555 1.1 jmmv std::vector< std::string > tps; 556 1.1 jmmv tps = af.tps(); 557 1.1 jmmv if (m_argc >= 1) { 558 1.1 jmmv // TODO: Ensure that the given test names are listed in the 559 1.1 jmmv // Atffile. Take into account that the file can be using globs. 560 1.1 jmmv tps.clear(); 561 1.1 jmmv for (int i = 0; i < m_argc; i++) 562 1.1 jmmv tps.push_back(m_argv[i]); 563 1.1 jmmv } 564 1.1 jmmv 565 1.1 jmmv // Read configuration data for this test suite. 566 1.3 jmmv vars_map test_suite_vars; 567 1.1 jmmv { 568 1.3 jmmv vars_map::const_iterator iter = af.props().find("test-suite"); 569 1.3 jmmv assert(iter != af.props().end()); 570 1.3 jmmv test_suite_vars = tools::config_file::read_config_files((*iter).second); 571 1.1 jmmv } 572 1.1 jmmv 573 1.3 jmmv tools::test_program::atf_tps_writer w(std::cout); 574 1.1 jmmv call_hook("atf-run", "info_start_hook"); 575 1.1 jmmv w.ntps(count_tps(tps)); 576 1.1 jmmv 577 1.1 jmmv bool ok = true; 578 1.1 jmmv for (std::vector< std::string >::const_iterator iter = tps.begin(); 579 1.3 jmmv iter != tps.end(); iter++) { 580 1.6 gson std::string tp, tc; 581 1.6 gson colon_split(*iter, tp, tc); 582 1.6 gson const bool result = run_test(tools::fs::path(tp), tc, w, 583 1.3 jmmv tools::config_file::merge_configs(af.conf(), test_suite_vars)); 584 1.3 jmmv ok &= (result == EXIT_SUCCESS); 585 1.3 jmmv } 586 1.1 jmmv 587 1.1 jmmv call_hook("atf-run", "info_end_hook"); 588 1.1 jmmv 589 1.1 jmmv return ok ? EXIT_SUCCESS : EXIT_FAILURE; 590 1.1 jmmv } 591 1.1 jmmv 592 1.1 jmmv int 593 1.1 jmmv main(int argc, char* const* argv) 594 1.1 jmmv { 595 1.1 jmmv return atf_run().run(argc, argv); 596 1.1 jmmv } 597