1*b0d29bc4SBrooks Davis // Copyright 2014 The Kyua Authors. 2*b0d29bc4SBrooks Davis // All rights reserved. 3*b0d29bc4SBrooks Davis // 4*b0d29bc4SBrooks Davis // Redistribution and use in source and binary forms, with or without 5*b0d29bc4SBrooks Davis // modification, are permitted provided that the following conditions are 6*b0d29bc4SBrooks Davis // met: 7*b0d29bc4SBrooks Davis // 8*b0d29bc4SBrooks Davis // * Redistributions of source code must retain the above copyright 9*b0d29bc4SBrooks Davis // notice, this list of conditions and the following disclaimer. 10*b0d29bc4SBrooks Davis // * Redistributions in binary form must reproduce the above copyright 11*b0d29bc4SBrooks Davis // notice, this list of conditions and the following disclaimer in the 12*b0d29bc4SBrooks Davis // documentation and/or other materials provided with the distribution. 13*b0d29bc4SBrooks Davis // * Neither the name of Google Inc. nor the names of its contributors 14*b0d29bc4SBrooks Davis // may be used to endorse or promote products derived from this software 15*b0d29bc4SBrooks Davis // without specific prior written permission. 16*b0d29bc4SBrooks Davis // 17*b0d29bc4SBrooks Davis // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18*b0d29bc4SBrooks Davis // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19*b0d29bc4SBrooks Davis // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20*b0d29bc4SBrooks Davis // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21*b0d29bc4SBrooks Davis // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22*b0d29bc4SBrooks Davis // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23*b0d29bc4SBrooks Davis // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24*b0d29bc4SBrooks Davis // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25*b0d29bc4SBrooks Davis // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26*b0d29bc4SBrooks Davis // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27*b0d29bc4SBrooks Davis // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28*b0d29bc4SBrooks Davis 29*b0d29bc4SBrooks Davis #include "engine/scheduler.hpp" 30*b0d29bc4SBrooks Davis 31*b0d29bc4SBrooks Davis extern "C" { 32*b0d29bc4SBrooks Davis #include <sys/types.h> 33*b0d29bc4SBrooks Davis 34*b0d29bc4SBrooks Davis #include <signal.h> 35*b0d29bc4SBrooks Davis #include <unistd.h> 36*b0d29bc4SBrooks Davis } 37*b0d29bc4SBrooks Davis 38*b0d29bc4SBrooks Davis #include <cstdlib> 39*b0d29bc4SBrooks Davis #include <fstream> 40*b0d29bc4SBrooks Davis #include <iostream> 41*b0d29bc4SBrooks Davis #include <string> 42*b0d29bc4SBrooks Davis 43*b0d29bc4SBrooks Davis #include <atf-c++.hpp> 44*b0d29bc4SBrooks Davis 45*b0d29bc4SBrooks Davis #include "engine/config.hpp" 46*b0d29bc4SBrooks Davis #include "engine/exceptions.hpp" 47*b0d29bc4SBrooks Davis #include "model/context.hpp" 48*b0d29bc4SBrooks Davis #include "model/metadata.hpp" 49*b0d29bc4SBrooks Davis #include "model/test_case.hpp" 50*b0d29bc4SBrooks Davis #include "model/test_program.hpp" 51*b0d29bc4SBrooks Davis #include "model/test_result.hpp" 52*b0d29bc4SBrooks Davis #include "utils/config/tree.ipp" 53*b0d29bc4SBrooks Davis #include "utils/datetime.hpp" 54*b0d29bc4SBrooks Davis #include "utils/defs.hpp" 55*b0d29bc4SBrooks Davis #include "utils/env.hpp" 56*b0d29bc4SBrooks Davis #include "utils/format/containers.ipp" 57*b0d29bc4SBrooks Davis #include "utils/format/macros.hpp" 58*b0d29bc4SBrooks Davis #include "utils/fs/operations.hpp" 59*b0d29bc4SBrooks Davis #include "utils/fs/path.hpp" 60*b0d29bc4SBrooks Davis #include "utils/optional.ipp" 61*b0d29bc4SBrooks Davis #include "utils/passwd.hpp" 62*b0d29bc4SBrooks Davis #include "utils/process/status.hpp" 63*b0d29bc4SBrooks Davis #include "utils/sanity.hpp" 64*b0d29bc4SBrooks Davis #include "utils/stacktrace.hpp" 65*b0d29bc4SBrooks Davis #include "utils/stream.hpp" 66*b0d29bc4SBrooks Davis #include "utils/test_utils.ipp" 67*b0d29bc4SBrooks Davis #include "utils/text/exceptions.hpp" 68*b0d29bc4SBrooks Davis #include "utils/text/operations.ipp" 69*b0d29bc4SBrooks Davis 70*b0d29bc4SBrooks Davis namespace config = utils::config; 71*b0d29bc4SBrooks Davis namespace datetime = utils::datetime; 72*b0d29bc4SBrooks Davis namespace fs = utils::fs; 73*b0d29bc4SBrooks Davis namespace passwd = utils::passwd; 74*b0d29bc4SBrooks Davis namespace process = utils::process; 75*b0d29bc4SBrooks Davis namespace scheduler = engine::scheduler; 76*b0d29bc4SBrooks Davis namespace text = utils::text; 77*b0d29bc4SBrooks Davis 78*b0d29bc4SBrooks Davis using utils::none; 79*b0d29bc4SBrooks Davis using utils::optional; 80*b0d29bc4SBrooks Davis 81*b0d29bc4SBrooks Davis 82*b0d29bc4SBrooks Davis namespace { 83*b0d29bc4SBrooks Davis 84*b0d29bc4SBrooks Davis 85*b0d29bc4SBrooks Davis /// Checks if a string starts with a prefix. 86*b0d29bc4SBrooks Davis /// 87*b0d29bc4SBrooks Davis /// \param str The string to be tested. 88*b0d29bc4SBrooks Davis /// \param prefix The prefix to look for. 89*b0d29bc4SBrooks Davis /// 90*b0d29bc4SBrooks Davis /// \return True if the string is prefixed as specified. 91*b0d29bc4SBrooks Davis static bool 92*b0d29bc4SBrooks Davis starts_with(const std::string& str, const std::string& prefix) 93*b0d29bc4SBrooks Davis { 94*b0d29bc4SBrooks Davis return (str.length() >= prefix.length() && 95*b0d29bc4SBrooks Davis str.substr(0, prefix.length()) == prefix); 96*b0d29bc4SBrooks Davis } 97*b0d29bc4SBrooks Davis 98*b0d29bc4SBrooks Davis 99*b0d29bc4SBrooks Davis /// Strips a prefix from a string and converts the rest to an integer. 100*b0d29bc4SBrooks Davis /// 101*b0d29bc4SBrooks Davis /// \param str The string to be tested. 102*b0d29bc4SBrooks Davis /// \param prefix The prefix to strip from the string. 103*b0d29bc4SBrooks Davis /// 104*b0d29bc4SBrooks Davis /// \return The part of the string after the prefix converted to an integer. 105*b0d29bc4SBrooks Davis static int 106*b0d29bc4SBrooks Davis suffix_to_int(const std::string& str, const std::string& prefix) 107*b0d29bc4SBrooks Davis { 108*b0d29bc4SBrooks Davis PRE(starts_with(str, prefix)); 109*b0d29bc4SBrooks Davis try { 110*b0d29bc4SBrooks Davis return text::to_type< int >(str.substr(prefix.length())); 111*b0d29bc4SBrooks Davis } catch (const text::value_error& error) { 112*b0d29bc4SBrooks Davis std::cerr << F("Failed: %s\n") % error.what(); 113*b0d29bc4SBrooks Davis std::abort(); 114*b0d29bc4SBrooks Davis } 115*b0d29bc4SBrooks Davis } 116*b0d29bc4SBrooks Davis 117*b0d29bc4SBrooks Davis 118*b0d29bc4SBrooks Davis /// Mock interface definition for testing. 119*b0d29bc4SBrooks Davis /// 120*b0d29bc4SBrooks Davis /// This scheduler interface does not execute external binaries. It is designed 121*b0d29bc4SBrooks Davis /// to simulate the scheduler of various programs with different exit statuses. 122*b0d29bc4SBrooks Davis class mock_interface : public scheduler::interface { 123*b0d29bc4SBrooks Davis /// Executes the subprocess simulating an exec. 124*b0d29bc4SBrooks Davis /// 125*b0d29bc4SBrooks Davis /// This is just a simple wrapper over _exit(2) because we cannot use 126*b0d29bc4SBrooks Davis /// std::exit on exit from this mock interface. The reason is that we do 127*b0d29bc4SBrooks Davis /// not want to invoke any destructors as otherwise we'd clear up the global 128*b0d29bc4SBrooks Davis /// scheduler state by mistake. This wouldn't be a major problem if it 129*b0d29bc4SBrooks Davis /// wasn't because doing so deletes on-disk files and we want to leave them 130*b0d29bc4SBrooks Davis /// in place so that the parent process can test for them! 131*b0d29bc4SBrooks Davis /// 132*b0d29bc4SBrooks Davis /// \param exit_code Exit code. 133*b0d29bc4SBrooks Davis void 134*b0d29bc4SBrooks Davis do_exit(const int exit_code) const UTILS_NORETURN 135*b0d29bc4SBrooks Davis { 136*b0d29bc4SBrooks Davis std::cout.flush(); 137*b0d29bc4SBrooks Davis std::cerr.flush(); 138*b0d29bc4SBrooks Davis ::_exit(exit_code); 139*b0d29bc4SBrooks Davis } 140*b0d29bc4SBrooks Davis 141*b0d29bc4SBrooks Davis /// Executes a test case that creates various files and then fails. 142*b0d29bc4SBrooks Davis void 143*b0d29bc4SBrooks Davis exec_create_files_and_fail(void) const UTILS_NORETURN 144*b0d29bc4SBrooks Davis { 145*b0d29bc4SBrooks Davis std::cerr << "This should not be clobbered\n"; 146*b0d29bc4SBrooks Davis atf::utils::create_file("first file", ""); 147*b0d29bc4SBrooks Davis atf::utils::create_file("second-file", ""); 148*b0d29bc4SBrooks Davis fs::mkdir_p(fs::path("dir1/dir2"), 0755); 149*b0d29bc4SBrooks Davis ::kill(::getpid(), SIGTERM); 150*b0d29bc4SBrooks Davis std::abort(); 151*b0d29bc4SBrooks Davis } 152*b0d29bc4SBrooks Davis 153*b0d29bc4SBrooks Davis /// Executes a test case that deletes all files in the current directory. 154*b0d29bc4SBrooks Davis /// 155*b0d29bc4SBrooks Davis /// This is intended to validate that the test runs in an empty directory, 156*b0d29bc4SBrooks Davis /// separate from any control files that the scheduler may have created. 157*b0d29bc4SBrooks Davis void 158*b0d29bc4SBrooks Davis exec_delete_all(void) const UTILS_NORETURN 159*b0d29bc4SBrooks Davis { 160*b0d29bc4SBrooks Davis const int exit_code = ::system("rm *") == -1 161*b0d29bc4SBrooks Davis ? EXIT_FAILURE : EXIT_SUCCESS; 162*b0d29bc4SBrooks Davis 163*b0d29bc4SBrooks Davis // Recreate our own cookie. 164*b0d29bc4SBrooks Davis atf::utils::create_file("exec_test_was_called", ""); 165*b0d29bc4SBrooks Davis 166*b0d29bc4SBrooks Davis do_exit(exit_code); 167*b0d29bc4SBrooks Davis } 168*b0d29bc4SBrooks Davis 169*b0d29bc4SBrooks Davis /// Executes a test case that returns a specific exit code. 170*b0d29bc4SBrooks Davis /// 171*b0d29bc4SBrooks Davis /// \param exit_code Exit status to terminate the program with. 172*b0d29bc4SBrooks Davis void 173*b0d29bc4SBrooks Davis exec_exit(const int exit_code) const UTILS_NORETURN 174*b0d29bc4SBrooks Davis { 175*b0d29bc4SBrooks Davis do_exit(exit_code); 176*b0d29bc4SBrooks Davis } 177*b0d29bc4SBrooks Davis 178*b0d29bc4SBrooks Davis /// Executes a test case that just fails. 179*b0d29bc4SBrooks Davis void 180*b0d29bc4SBrooks Davis exec_fail(void) const UTILS_NORETURN 181*b0d29bc4SBrooks Davis { 182*b0d29bc4SBrooks Davis std::cerr << "This should not be clobbered\n"; 183*b0d29bc4SBrooks Davis ::kill(::getpid(), SIGTERM); 184*b0d29bc4SBrooks Davis std::abort(); 185*b0d29bc4SBrooks Davis } 186*b0d29bc4SBrooks Davis 187*b0d29bc4SBrooks Davis /// Executes a test case that prints all input parameters to the functor. 188*b0d29bc4SBrooks Davis /// 189*b0d29bc4SBrooks Davis /// \param test_program The test program to execute. 190*b0d29bc4SBrooks Davis /// \param test_case_name Name of the test case to invoke, which must be a 191*b0d29bc4SBrooks Davis /// number. 192*b0d29bc4SBrooks Davis /// \param vars User-provided variables to pass to the test program. 193*b0d29bc4SBrooks Davis void 194*b0d29bc4SBrooks Davis exec_print_params(const model::test_program& test_program, 195*b0d29bc4SBrooks Davis const std::string& test_case_name, 196*b0d29bc4SBrooks Davis const config::properties_map& vars) const 197*b0d29bc4SBrooks Davis UTILS_NORETURN 198*b0d29bc4SBrooks Davis { 199*b0d29bc4SBrooks Davis std::cout << F("Test program: %s\n") % test_program.relative_path(); 200*b0d29bc4SBrooks Davis std::cout << F("Test case: %s\n") % test_case_name; 201*b0d29bc4SBrooks Davis for (config::properties_map::const_iterator iter = vars.begin(); 202*b0d29bc4SBrooks Davis iter != vars.end(); ++iter) { 203*b0d29bc4SBrooks Davis std::cout << F("%s=%s\n") % (*iter).first % (*iter).second; 204*b0d29bc4SBrooks Davis } 205*b0d29bc4SBrooks Davis 206*b0d29bc4SBrooks Davis std::cerr << F("stderr: %s\n") % test_case_name; 207*b0d29bc4SBrooks Davis 208*b0d29bc4SBrooks Davis do_exit(EXIT_SUCCESS); 209*b0d29bc4SBrooks Davis } 210*b0d29bc4SBrooks Davis 211*b0d29bc4SBrooks Davis public: 212*b0d29bc4SBrooks Davis /// Executes a test program's list operation. 213*b0d29bc4SBrooks Davis /// 214*b0d29bc4SBrooks Davis /// This method is intended to be called within a subprocess and is expected 215*b0d29bc4SBrooks Davis /// to terminate execution either by exec(2)ing the test program or by 216*b0d29bc4SBrooks Davis /// exiting with a failure. 217*b0d29bc4SBrooks Davis /// 218*b0d29bc4SBrooks Davis /// \param test_program The test program to execute. 219*b0d29bc4SBrooks Davis /// \param vars User-provided variables to pass to the test program. 220*b0d29bc4SBrooks Davis void 221*b0d29bc4SBrooks Davis exec_list(const model::test_program& test_program, 222*b0d29bc4SBrooks Davis const config::properties_map& vars) 223*b0d29bc4SBrooks Davis const UTILS_NORETURN 224*b0d29bc4SBrooks Davis { 225*b0d29bc4SBrooks Davis const std::string name = test_program.absolute_path().leaf_name(); 226*b0d29bc4SBrooks Davis 227*b0d29bc4SBrooks Davis std::cerr << name; 228*b0d29bc4SBrooks Davis std::cerr.flush(); 229*b0d29bc4SBrooks Davis if (name == "check_i_exist") { 230*b0d29bc4SBrooks Davis if (fs::exists(test_program.absolute_path())) { 231*b0d29bc4SBrooks Davis std::cout << "found\n"; 232*b0d29bc4SBrooks Davis do_exit(EXIT_SUCCESS); 233*b0d29bc4SBrooks Davis } else { 234*b0d29bc4SBrooks Davis std::cout << "not_found\n"; 235*b0d29bc4SBrooks Davis do_exit(EXIT_FAILURE); 236*b0d29bc4SBrooks Davis } 237*b0d29bc4SBrooks Davis } else if (name == "empty") { 238*b0d29bc4SBrooks Davis do_exit(EXIT_SUCCESS); 239*b0d29bc4SBrooks Davis } else if (name == "misbehave") { 240*b0d29bc4SBrooks Davis utils::abort_without_coredump(); 241*b0d29bc4SBrooks Davis } else if (name == "timeout") { 242*b0d29bc4SBrooks Davis std::cout << "sleeping\n"; 243*b0d29bc4SBrooks Davis std::cout.flush(); 244*b0d29bc4SBrooks Davis ::sleep(100); 245*b0d29bc4SBrooks Davis utils::abort_without_coredump(); 246*b0d29bc4SBrooks Davis } else if (name == "vars") { 247*b0d29bc4SBrooks Davis for (config::properties_map::const_iterator iter = vars.begin(); 248*b0d29bc4SBrooks Davis iter != vars.end(); ++iter) { 249*b0d29bc4SBrooks Davis std::cout << F("%s_%s\n") % (*iter).first % (*iter).second; 250*b0d29bc4SBrooks Davis } 251*b0d29bc4SBrooks Davis do_exit(15); 252*b0d29bc4SBrooks Davis } else { 253*b0d29bc4SBrooks Davis std::abort(); 254*b0d29bc4SBrooks Davis } 255*b0d29bc4SBrooks Davis } 256*b0d29bc4SBrooks Davis 257*b0d29bc4SBrooks Davis /// Computes the test cases list of a test program. 258*b0d29bc4SBrooks Davis /// 259*b0d29bc4SBrooks Davis /// \param status The termination status of the subprocess used to execute 260*b0d29bc4SBrooks Davis /// the exec_test() method or none if the test timed out. 261*b0d29bc4SBrooks Davis /// \param stdout_path Path to the file containing the stdout of the test. 262*b0d29bc4SBrooks Davis /// \param stderr_path Path to the file containing the stderr of the test. 263*b0d29bc4SBrooks Davis /// 264*b0d29bc4SBrooks Davis /// \return A list of test cases. 265*b0d29bc4SBrooks Davis model::test_cases_map 266*b0d29bc4SBrooks Davis parse_list(const optional< process::status >& status, 267*b0d29bc4SBrooks Davis const fs::path& stdout_path, 268*b0d29bc4SBrooks Davis const fs::path& stderr_path) const 269*b0d29bc4SBrooks Davis { 270*b0d29bc4SBrooks Davis const std::string name = utils::read_file(stderr_path); 271*b0d29bc4SBrooks Davis if (name == "check_i_exist") { 272*b0d29bc4SBrooks Davis ATF_REQUIRE(status.get().exited()); 273*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(EXIT_SUCCESS, status.get().exitstatus()); 274*b0d29bc4SBrooks Davis } else if (name == "empty") { 275*b0d29bc4SBrooks Davis ATF_REQUIRE(status.get().exited()); 276*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(EXIT_SUCCESS, status.get().exitstatus()); 277*b0d29bc4SBrooks Davis } else if (name == "misbehave") { 278*b0d29bc4SBrooks Davis throw std::runtime_error("misbehaved in parse_list"); 279*b0d29bc4SBrooks Davis } else if (name == "timeout") { 280*b0d29bc4SBrooks Davis ATF_REQUIRE(!status); 281*b0d29bc4SBrooks Davis } else if (name == "vars") { 282*b0d29bc4SBrooks Davis ATF_REQUIRE(status.get().exited()); 283*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(15, status.get().exitstatus()); 284*b0d29bc4SBrooks Davis } else { 285*b0d29bc4SBrooks Davis ATF_FAIL("Invalid stderr contents; got " + name); 286*b0d29bc4SBrooks Davis } 287*b0d29bc4SBrooks Davis 288*b0d29bc4SBrooks Davis model::test_cases_map_builder test_cases_builder; 289*b0d29bc4SBrooks Davis 290*b0d29bc4SBrooks Davis std::ifstream input(stdout_path.c_str()); 291*b0d29bc4SBrooks Davis ATF_REQUIRE(input); 292*b0d29bc4SBrooks Davis std::string line; 293*b0d29bc4SBrooks Davis while (std::getline(input, line).good()) { 294*b0d29bc4SBrooks Davis test_cases_builder.add(line); 295*b0d29bc4SBrooks Davis } 296*b0d29bc4SBrooks Davis 297*b0d29bc4SBrooks Davis return test_cases_builder.build(); 298*b0d29bc4SBrooks Davis } 299*b0d29bc4SBrooks Davis 300*b0d29bc4SBrooks Davis /// Executes a test case of the test program. 301*b0d29bc4SBrooks Davis /// 302*b0d29bc4SBrooks Davis /// This method is intended to be called within a subprocess and is expected 303*b0d29bc4SBrooks Davis /// to terminate execution either by exec(2)ing the test program or by 304*b0d29bc4SBrooks Davis /// exiting with a failure. 305*b0d29bc4SBrooks Davis /// 306*b0d29bc4SBrooks Davis /// \param test_program The test program to execute. 307*b0d29bc4SBrooks Davis /// \param test_case_name Name of the test case to invoke. 308*b0d29bc4SBrooks Davis /// \param vars User-provided variables to pass to the test program. 309*b0d29bc4SBrooks Davis /// \param control_directory Directory where the interface may place control 310*b0d29bc4SBrooks Davis /// files. 311*b0d29bc4SBrooks Davis void 312*b0d29bc4SBrooks Davis exec_test(const model::test_program& test_program, 313*b0d29bc4SBrooks Davis const std::string& test_case_name, 314*b0d29bc4SBrooks Davis const config::properties_map& vars, 315*b0d29bc4SBrooks Davis const fs::path& control_directory) const 316*b0d29bc4SBrooks Davis { 317*b0d29bc4SBrooks Davis const fs::path cookie = control_directory / "exec_test_was_called"; 318*b0d29bc4SBrooks Davis std::ofstream control_file(cookie.c_str()); 319*b0d29bc4SBrooks Davis if (!control_file) { 320*b0d29bc4SBrooks Davis std::cerr << "Failed to create " << cookie << '\n'; 321*b0d29bc4SBrooks Davis std::abort(); 322*b0d29bc4SBrooks Davis } 323*b0d29bc4SBrooks Davis control_file << test_case_name; 324*b0d29bc4SBrooks Davis control_file.close(); 325*b0d29bc4SBrooks Davis 326*b0d29bc4SBrooks Davis if (test_case_name == "check_i_exist") { 327*b0d29bc4SBrooks Davis do_exit(fs::exists(test_program.absolute_path()) ? 0 : 1); 328*b0d29bc4SBrooks Davis } else if (starts_with(test_case_name, "cleanup_timeout")) { 329*b0d29bc4SBrooks Davis exec_exit(EXIT_SUCCESS); 330*b0d29bc4SBrooks Davis } else if (starts_with(test_case_name, "create_files_and_fail")) { 331*b0d29bc4SBrooks Davis exec_create_files_and_fail(); 332*b0d29bc4SBrooks Davis } else if (test_case_name == "delete_all") { 333*b0d29bc4SBrooks Davis exec_delete_all(); 334*b0d29bc4SBrooks Davis } else if (starts_with(test_case_name, "exit ")) { 335*b0d29bc4SBrooks Davis exec_exit(suffix_to_int(test_case_name, "exit ")); 336*b0d29bc4SBrooks Davis } else if (starts_with(test_case_name, "fail")) { 337*b0d29bc4SBrooks Davis exec_fail(); 338*b0d29bc4SBrooks Davis } else if (starts_with(test_case_name, "fail_body_fail_cleanup")) { 339*b0d29bc4SBrooks Davis exec_fail(); 340*b0d29bc4SBrooks Davis } else if (starts_with(test_case_name, "fail_body_pass_cleanup")) { 341*b0d29bc4SBrooks Davis exec_fail(); 342*b0d29bc4SBrooks Davis } else if (starts_with(test_case_name, "pass_body_fail_cleanup")) { 343*b0d29bc4SBrooks Davis exec_exit(EXIT_SUCCESS); 344*b0d29bc4SBrooks Davis } else if (starts_with(test_case_name, "print_params")) { 345*b0d29bc4SBrooks Davis exec_print_params(test_program, test_case_name, vars); 346*b0d29bc4SBrooks Davis } else if (starts_with(test_case_name, "skip_body_pass_cleanup")) { 347*b0d29bc4SBrooks Davis exec_exit(EXIT_SUCCESS); 348*b0d29bc4SBrooks Davis } else { 349*b0d29bc4SBrooks Davis std::cerr << "Unknown test case " << test_case_name << '\n'; 350*b0d29bc4SBrooks Davis std::abort(); 351*b0d29bc4SBrooks Davis } 352*b0d29bc4SBrooks Davis } 353*b0d29bc4SBrooks Davis 354*b0d29bc4SBrooks Davis /// Executes a test cleanup routine of the test program. 355*b0d29bc4SBrooks Davis /// 356*b0d29bc4SBrooks Davis /// This method is intended to be called within a subprocess and is expected 357*b0d29bc4SBrooks Davis /// to terminate execution either by exec(2)ing the test program or by 358*b0d29bc4SBrooks Davis /// exiting with a failure. 359*b0d29bc4SBrooks Davis /// 360*b0d29bc4SBrooks Davis /// \param test_case_name Name of the test case to invoke. 361*b0d29bc4SBrooks Davis void 362*b0d29bc4SBrooks Davis exec_cleanup(const model::test_program& /* test_program */, 363*b0d29bc4SBrooks Davis const std::string& test_case_name, 364*b0d29bc4SBrooks Davis const config::properties_map& /* vars */, 365*b0d29bc4SBrooks Davis const fs::path& /* control_directory */) const 366*b0d29bc4SBrooks Davis { 367*b0d29bc4SBrooks Davis std::cout << "exec_cleanup was called\n"; 368*b0d29bc4SBrooks Davis std::cout.flush(); 369*b0d29bc4SBrooks Davis 370*b0d29bc4SBrooks Davis if (starts_with(test_case_name, "cleanup_timeout")) { 371*b0d29bc4SBrooks Davis ::sleep(100); 372*b0d29bc4SBrooks Davis std::abort(); 373*b0d29bc4SBrooks Davis } else if (starts_with(test_case_name, "fail_body_fail_cleanup")) { 374*b0d29bc4SBrooks Davis exec_fail(); 375*b0d29bc4SBrooks Davis } else if (starts_with(test_case_name, "fail_body_pass_cleanup")) { 376*b0d29bc4SBrooks Davis exec_exit(EXIT_SUCCESS); 377*b0d29bc4SBrooks Davis } else if (starts_with(test_case_name, "pass_body_fail_cleanup")) { 378*b0d29bc4SBrooks Davis exec_fail(); 379*b0d29bc4SBrooks Davis } else if (starts_with(test_case_name, "skip_body_pass_cleanup")) { 380*b0d29bc4SBrooks Davis exec_exit(EXIT_SUCCESS); 381*b0d29bc4SBrooks Davis } else { 382*b0d29bc4SBrooks Davis std::cerr << "Should not have been called for a test without " 383*b0d29bc4SBrooks Davis "a cleanup routine" << '\n'; 384*b0d29bc4SBrooks Davis std::abort(); 385*b0d29bc4SBrooks Davis } 386*b0d29bc4SBrooks Davis } 387*b0d29bc4SBrooks Davis 388*b0d29bc4SBrooks Davis /// Computes the result of a test case based on its termination status. 389*b0d29bc4SBrooks Davis /// 390*b0d29bc4SBrooks Davis /// \param status The termination status of the subprocess used to execute 391*b0d29bc4SBrooks Davis /// the exec_test() method or none if the test timed out. 392*b0d29bc4SBrooks Davis /// \param control_directory Path to the directory where the interface may 393*b0d29bc4SBrooks Davis /// have placed control files. 394*b0d29bc4SBrooks Davis /// \param stdout_path Path to the file containing the stdout of the test. 395*b0d29bc4SBrooks Davis /// \param stderr_path Path to the file containing the stderr of the test. 396*b0d29bc4SBrooks Davis /// 397*b0d29bc4SBrooks Davis /// \return A test result. 398*b0d29bc4SBrooks Davis model::test_result 399*b0d29bc4SBrooks Davis compute_result(const optional< process::status >& status, 400*b0d29bc4SBrooks Davis const fs::path& control_directory, 401*b0d29bc4SBrooks Davis const fs::path& stdout_path, 402*b0d29bc4SBrooks Davis const fs::path& stderr_path) const 403*b0d29bc4SBrooks Davis { 404*b0d29bc4SBrooks Davis // Do not use any ATF_* macros here. Some of the tests below invoke 405*b0d29bc4SBrooks Davis // this code in a subprocess, and terminating such subprocess due to a 406*b0d29bc4SBrooks Davis // failed ATF_* macro yields mysterious failures that are incredibly 407*b0d29bc4SBrooks Davis // hard to debug. (Case in point: the signal_handling test is racy by 408*b0d29bc4SBrooks Davis // nature, and the test run by exec_test() above may not have created 409*b0d29bc4SBrooks Davis // the cookie we expect below. We don't want to "silently" exit if the 410*b0d29bc4SBrooks Davis // file is not there.) 411*b0d29bc4SBrooks Davis 412*b0d29bc4SBrooks Davis if (!status) { 413*b0d29bc4SBrooks Davis return model::test_result(model::test_result_broken, 414*b0d29bc4SBrooks Davis "Timed out"); 415*b0d29bc4SBrooks Davis } 416*b0d29bc4SBrooks Davis 417*b0d29bc4SBrooks Davis if (status.get().exited()) { 418*b0d29bc4SBrooks Davis // Only sanity-check the work directory-related parameters in case 419*b0d29bc4SBrooks Davis // of a clean exit. In all other cases, there is no guarantee that 420*b0d29bc4SBrooks Davis // these were ever created. 421*b0d29bc4SBrooks Davis const fs::path cookie = control_directory / "exec_test_was_called"; 422*b0d29bc4SBrooks Davis if (!atf::utils::file_exists(cookie.str())) { 423*b0d29bc4SBrooks Davis return model::test_result( 424*b0d29bc4SBrooks Davis model::test_result_broken, 425*b0d29bc4SBrooks Davis "compute_result's control_directory does not seem to point " 426*b0d29bc4SBrooks Davis "to the right location"); 427*b0d29bc4SBrooks Davis } 428*b0d29bc4SBrooks Davis const std::string test_case_name = utils::read_file(cookie); 429*b0d29bc4SBrooks Davis 430*b0d29bc4SBrooks Davis if (!atf::utils::file_exists(stdout_path.str())) { 431*b0d29bc4SBrooks Davis return model::test_result( 432*b0d29bc4SBrooks Davis model::test_result_broken, 433*b0d29bc4SBrooks Davis "compute_result's stdout_path does not exist"); 434*b0d29bc4SBrooks Davis } 435*b0d29bc4SBrooks Davis if (!atf::utils::file_exists(stderr_path.str())) { 436*b0d29bc4SBrooks Davis return model::test_result( 437*b0d29bc4SBrooks Davis model::test_result_broken, 438*b0d29bc4SBrooks Davis "compute_result's stderr_path does not exist"); 439*b0d29bc4SBrooks Davis } 440*b0d29bc4SBrooks Davis 441*b0d29bc4SBrooks Davis if (test_case_name == "skip_body_pass_cleanup") { 442*b0d29bc4SBrooks Davis return model::test_result( 443*b0d29bc4SBrooks Davis model::test_result_skipped, 444*b0d29bc4SBrooks Davis F("Exit %s") % status.get().exitstatus()); 445*b0d29bc4SBrooks Davis } else { 446*b0d29bc4SBrooks Davis return model::test_result( 447*b0d29bc4SBrooks Davis model::test_result_passed, 448*b0d29bc4SBrooks Davis F("Exit %s") % status.get().exitstatus()); 449*b0d29bc4SBrooks Davis } 450*b0d29bc4SBrooks Davis } else { 451*b0d29bc4SBrooks Davis return model::test_result( 452*b0d29bc4SBrooks Davis model::test_result_failed, 453*b0d29bc4SBrooks Davis F("Signal %s") % status.get().termsig()); 454*b0d29bc4SBrooks Davis } 455*b0d29bc4SBrooks Davis } 456*b0d29bc4SBrooks Davis }; 457*b0d29bc4SBrooks Davis 458*b0d29bc4SBrooks Davis 459*b0d29bc4SBrooks Davis } // anonymous namespace 460*b0d29bc4SBrooks Davis 461*b0d29bc4SBrooks Davis 462*b0d29bc4SBrooks Davis /// Runs list_tests on the scheduler and returns the results. 463*b0d29bc4SBrooks Davis /// 464*b0d29bc4SBrooks Davis /// \param test_name The name of the test supported by our exec_list function. 465*b0d29bc4SBrooks Davis /// \param user_config Optional user settings for the test. 466*b0d29bc4SBrooks Davis /// 467*b0d29bc4SBrooks Davis /// \return The loaded list of test cases. 468*b0d29bc4SBrooks Davis static model::test_cases_map 469*b0d29bc4SBrooks Davis check_integration_list(const char* test_name, const fs::path root, 470*b0d29bc4SBrooks Davis const config::tree& user_config = engine::empty_config()) 471*b0d29bc4SBrooks Davis { 472*b0d29bc4SBrooks Davis const model::test_program program = model::test_program_builder( 473*b0d29bc4SBrooks Davis "mock", fs::path(test_name), root, "the-suite") 474*b0d29bc4SBrooks Davis .build(); 475*b0d29bc4SBrooks Davis 476*b0d29bc4SBrooks Davis scheduler::scheduler_handle handle = scheduler::setup(); 477*b0d29bc4SBrooks Davis const model::test_cases_map test_cases = handle.list_tests(&program, 478*b0d29bc4SBrooks Davis user_config); 479*b0d29bc4SBrooks Davis handle.cleanup(); 480*b0d29bc4SBrooks Davis 481*b0d29bc4SBrooks Davis return test_cases; 482*b0d29bc4SBrooks Davis } 483*b0d29bc4SBrooks Davis 484*b0d29bc4SBrooks Davis 485*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__list_some); 486*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(integration__list_some) 487*b0d29bc4SBrooks Davis { 488*b0d29bc4SBrooks Davis config::tree user_config = engine::empty_config(); 489*b0d29bc4SBrooks Davis user_config.set_string("test_suites.the-suite.first", "test"); 490*b0d29bc4SBrooks Davis user_config.set_string("test_suites.the-suite.second", "TEST"); 491*b0d29bc4SBrooks Davis user_config.set_string("test_suites.abc.unused", "unused"); 492*b0d29bc4SBrooks Davis 493*b0d29bc4SBrooks Davis const model::test_cases_map test_cases = check_integration_list( 494*b0d29bc4SBrooks Davis "vars", fs::path("."), user_config); 495*b0d29bc4SBrooks Davis 496*b0d29bc4SBrooks Davis const model::test_cases_map exp_test_cases = model::test_cases_map_builder() 497*b0d29bc4SBrooks Davis .add("first_test").add("second_TEST").build(); 498*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(exp_test_cases, test_cases); 499*b0d29bc4SBrooks Davis } 500*b0d29bc4SBrooks Davis 501*b0d29bc4SBrooks Davis 502*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__list_check_paths); 503*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(integration__list_check_paths) 504*b0d29bc4SBrooks Davis { 505*b0d29bc4SBrooks Davis fs::mkdir_p(fs::path("dir1/dir2/dir3"), 0755); 506*b0d29bc4SBrooks Davis atf::utils::create_file("dir1/dir2/dir3/check_i_exist", ""); 507*b0d29bc4SBrooks Davis 508*b0d29bc4SBrooks Davis const model::test_cases_map test_cases = check_integration_list( 509*b0d29bc4SBrooks Davis "dir2/dir3/check_i_exist", fs::path("dir1")); 510*b0d29bc4SBrooks Davis 511*b0d29bc4SBrooks Davis const model::test_cases_map exp_test_cases = model::test_cases_map_builder() 512*b0d29bc4SBrooks Davis .add("found").build(); 513*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(exp_test_cases, test_cases); 514*b0d29bc4SBrooks Davis } 515*b0d29bc4SBrooks Davis 516*b0d29bc4SBrooks Davis 517*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__list_timeout); 518*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(integration__list_timeout) 519*b0d29bc4SBrooks Davis { 520*b0d29bc4SBrooks Davis scheduler::list_timeout = datetime::delta(1, 0); 521*b0d29bc4SBrooks Davis const model::test_cases_map test_cases = check_integration_list( 522*b0d29bc4SBrooks Davis "timeout", fs::path(".")); 523*b0d29bc4SBrooks Davis 524*b0d29bc4SBrooks Davis const model::test_cases_map exp_test_cases = model::test_cases_map_builder() 525*b0d29bc4SBrooks Davis .add("sleeping").build(); 526*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(exp_test_cases, test_cases); 527*b0d29bc4SBrooks Davis } 528*b0d29bc4SBrooks Davis 529*b0d29bc4SBrooks Davis 530*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__list_fail); 531*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(integration__list_fail) 532*b0d29bc4SBrooks Davis { 533*b0d29bc4SBrooks Davis const model::test_cases_map test_cases = check_integration_list( 534*b0d29bc4SBrooks Davis "misbehave", fs::path(".")); 535*b0d29bc4SBrooks Davis 536*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(1, test_cases.size()); 537*b0d29bc4SBrooks Davis const model::test_case& test_case = test_cases.begin()->second; 538*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ("__test_cases_list__", test_case.name()); 539*b0d29bc4SBrooks Davis ATF_REQUIRE(test_case.fake_result()); 540*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(model::test_result(model::test_result_broken, 541*b0d29bc4SBrooks Davis "misbehaved in parse_list"), 542*b0d29bc4SBrooks Davis test_case.fake_result().get()); 543*b0d29bc4SBrooks Davis } 544*b0d29bc4SBrooks Davis 545*b0d29bc4SBrooks Davis 546*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__list_empty); 547*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(integration__list_empty) 548*b0d29bc4SBrooks Davis { 549*b0d29bc4SBrooks Davis const model::test_cases_map test_cases = check_integration_list( 550*b0d29bc4SBrooks Davis "empty", fs::path(".")); 551*b0d29bc4SBrooks Davis 552*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(1, test_cases.size()); 553*b0d29bc4SBrooks Davis const model::test_case& test_case = test_cases.begin()->second; 554*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ("__test_cases_list__", test_case.name()); 555*b0d29bc4SBrooks Davis ATF_REQUIRE(test_case.fake_result()); 556*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(model::test_result(model::test_result_broken, 557*b0d29bc4SBrooks Davis "Empty test cases list"), 558*b0d29bc4SBrooks Davis test_case.fake_result().get()); 559*b0d29bc4SBrooks Davis } 560*b0d29bc4SBrooks Davis 561*b0d29bc4SBrooks Davis 562*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__run_one); 563*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(integration__run_one) 564*b0d29bc4SBrooks Davis { 565*b0d29bc4SBrooks Davis const model::test_program_ptr program = model::test_program_builder( 566*b0d29bc4SBrooks Davis "mock", fs::path("the-program"), fs::current_path(), "the-suite") 567*b0d29bc4SBrooks Davis .add_test_case("exit 41").build_ptr(); 568*b0d29bc4SBrooks Davis 569*b0d29bc4SBrooks Davis const config::tree user_config = engine::empty_config(); 570*b0d29bc4SBrooks Davis 571*b0d29bc4SBrooks Davis scheduler::scheduler_handle handle = scheduler::setup(); 572*b0d29bc4SBrooks Davis 573*b0d29bc4SBrooks Davis const scheduler::exec_handle exec_handle = handle.spawn_test( 574*b0d29bc4SBrooks Davis program, "exit 41", user_config); 575*b0d29bc4SBrooks Davis 576*b0d29bc4SBrooks Davis scheduler::result_handle_ptr result_handle = handle.wait_any(); 577*b0d29bc4SBrooks Davis const scheduler::test_result_handle* test_result_handle = 578*b0d29bc4SBrooks Davis dynamic_cast< const scheduler::test_result_handle* >( 579*b0d29bc4SBrooks Davis result_handle.get()); 580*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(exec_handle, result_handle->original_pid()); 581*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(model::test_result(model::test_result_passed, "Exit 41"), 582*b0d29bc4SBrooks Davis test_result_handle->test_result()); 583*b0d29bc4SBrooks Davis result_handle->cleanup(); 584*b0d29bc4SBrooks Davis result_handle.reset(); 585*b0d29bc4SBrooks Davis 586*b0d29bc4SBrooks Davis handle.cleanup(); 587*b0d29bc4SBrooks Davis } 588*b0d29bc4SBrooks Davis 589*b0d29bc4SBrooks Davis 590*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__run_many); 591*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(integration__run_many) 592*b0d29bc4SBrooks Davis { 593*b0d29bc4SBrooks Davis static const std::size_t num_test_programs = 30; 594*b0d29bc4SBrooks Davis 595*b0d29bc4SBrooks Davis const config::tree user_config = engine::empty_config(); 596*b0d29bc4SBrooks Davis 597*b0d29bc4SBrooks Davis scheduler::scheduler_handle handle = scheduler::setup(); 598*b0d29bc4SBrooks Davis 599*b0d29bc4SBrooks Davis // We mess around with the "current time" below, so make sure the tests do 600*b0d29bc4SBrooks Davis // not spuriously exceed their deadline by bumping it to a large number. 601*b0d29bc4SBrooks Davis const model::metadata infinite_timeout = model::metadata_builder() 602*b0d29bc4SBrooks Davis .set_timeout(datetime::delta(1000000L, 0)).build(); 603*b0d29bc4SBrooks Davis 604*b0d29bc4SBrooks Davis std::size_t total_tests = 0; 605*b0d29bc4SBrooks Davis std::map< scheduler::exec_handle, model::test_program_ptr > 606*b0d29bc4SBrooks Davis exp_test_programs; 607*b0d29bc4SBrooks Davis std::map< scheduler::exec_handle, std::string > exp_test_case_names; 608*b0d29bc4SBrooks Davis std::map< scheduler::exec_handle, datetime::timestamp > exp_start_times; 609*b0d29bc4SBrooks Davis std::map< scheduler::exec_handle, int > exp_exit_statuses; 610*b0d29bc4SBrooks Davis for (std::size_t i = 0; i < num_test_programs; ++i) { 611*b0d29bc4SBrooks Davis const std::string test_case_0 = F("exit %s") % (i * 3 + 0); 612*b0d29bc4SBrooks Davis const std::string test_case_1 = F("exit %s") % (i * 3 + 1); 613*b0d29bc4SBrooks Davis const std::string test_case_2 = F("exit %s") % (i * 3 + 2); 614*b0d29bc4SBrooks Davis 615*b0d29bc4SBrooks Davis const model::test_program_ptr program = model::test_program_builder( 616*b0d29bc4SBrooks Davis "mock", fs::path(F("program-%s") % i), 617*b0d29bc4SBrooks Davis fs::current_path(), "the-suite") 618*b0d29bc4SBrooks Davis .set_metadata(infinite_timeout) 619*b0d29bc4SBrooks Davis .add_test_case(test_case_0) 620*b0d29bc4SBrooks Davis .add_test_case(test_case_1) 621*b0d29bc4SBrooks Davis .add_test_case(test_case_2) 622*b0d29bc4SBrooks Davis .build_ptr(); 623*b0d29bc4SBrooks Davis 624*b0d29bc4SBrooks Davis const datetime::timestamp start_time = datetime::timestamp::from_values( 625*b0d29bc4SBrooks Davis 2014, 12, 8, 9, 40, 0, i); 626*b0d29bc4SBrooks Davis 627*b0d29bc4SBrooks Davis scheduler::exec_handle exec_handle; 628*b0d29bc4SBrooks Davis 629*b0d29bc4SBrooks Davis datetime::set_mock_now(start_time); 630*b0d29bc4SBrooks Davis exec_handle = handle.spawn_test(program, test_case_0, user_config); 631*b0d29bc4SBrooks Davis exp_test_programs.insert(std::make_pair(exec_handle, program)); 632*b0d29bc4SBrooks Davis exp_test_case_names.insert(std::make_pair(exec_handle, test_case_0)); 633*b0d29bc4SBrooks Davis exp_start_times.insert(std::make_pair(exec_handle, start_time)); 634*b0d29bc4SBrooks Davis exp_exit_statuses.insert(std::make_pair(exec_handle, i * 3)); 635*b0d29bc4SBrooks Davis ++total_tests; 636*b0d29bc4SBrooks Davis 637*b0d29bc4SBrooks Davis datetime::set_mock_now(start_time); 638*b0d29bc4SBrooks Davis exec_handle = handle.spawn_test(program, test_case_1, user_config); 639*b0d29bc4SBrooks Davis exp_test_programs.insert(std::make_pair(exec_handle, program)); 640*b0d29bc4SBrooks Davis exp_test_case_names.insert(std::make_pair(exec_handle, test_case_1)); 641*b0d29bc4SBrooks Davis exp_start_times.insert(std::make_pair(exec_handle, start_time)); 642*b0d29bc4SBrooks Davis exp_exit_statuses.insert(std::make_pair(exec_handle, i * 3 + 1)); 643*b0d29bc4SBrooks Davis ++total_tests; 644*b0d29bc4SBrooks Davis 645*b0d29bc4SBrooks Davis datetime::set_mock_now(start_time); 646*b0d29bc4SBrooks Davis exec_handle = handle.spawn_test(program, test_case_2, user_config); 647*b0d29bc4SBrooks Davis exp_test_programs.insert(std::make_pair(exec_handle, program)); 648*b0d29bc4SBrooks Davis exp_test_case_names.insert(std::make_pair(exec_handle, test_case_2)); 649*b0d29bc4SBrooks Davis exp_start_times.insert(std::make_pair(exec_handle, start_time)); 650*b0d29bc4SBrooks Davis exp_exit_statuses.insert(std::make_pair(exec_handle, i * 3 + 2)); 651*b0d29bc4SBrooks Davis ++total_tests; 652*b0d29bc4SBrooks Davis } 653*b0d29bc4SBrooks Davis 654*b0d29bc4SBrooks Davis for (std::size_t i = 0; i < total_tests; ++i) { 655*b0d29bc4SBrooks Davis const datetime::timestamp end_time = datetime::timestamp::from_values( 656*b0d29bc4SBrooks Davis 2014, 12, 8, 9, 50, 10, i); 657*b0d29bc4SBrooks Davis datetime::set_mock_now(end_time); 658*b0d29bc4SBrooks Davis scheduler::result_handle_ptr result_handle = handle.wait_any(); 659*b0d29bc4SBrooks Davis const scheduler::test_result_handle* test_result_handle = 660*b0d29bc4SBrooks Davis dynamic_cast< const scheduler::test_result_handle* >( 661*b0d29bc4SBrooks Davis result_handle.get()); 662*b0d29bc4SBrooks Davis 663*b0d29bc4SBrooks Davis const scheduler::exec_handle exec_handle = 664*b0d29bc4SBrooks Davis result_handle->original_pid(); 665*b0d29bc4SBrooks Davis 666*b0d29bc4SBrooks Davis const model::test_program_ptr test_program = exp_test_programs.find( 667*b0d29bc4SBrooks Davis exec_handle)->second; 668*b0d29bc4SBrooks Davis const std::string& test_case_name = exp_test_case_names.find( 669*b0d29bc4SBrooks Davis exec_handle)->second; 670*b0d29bc4SBrooks Davis const datetime::timestamp& start_time = exp_start_times.find( 671*b0d29bc4SBrooks Davis exec_handle)->second; 672*b0d29bc4SBrooks Davis const int exit_status = exp_exit_statuses.find(exec_handle)->second; 673*b0d29bc4SBrooks Davis 674*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(model::test_result(model::test_result_passed, 675*b0d29bc4SBrooks Davis F("Exit %s") % exit_status), 676*b0d29bc4SBrooks Davis test_result_handle->test_result()); 677*b0d29bc4SBrooks Davis 678*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(test_program, test_result_handle->test_program()); 679*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(test_case_name, test_result_handle->test_case_name()); 680*b0d29bc4SBrooks Davis 681*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(start_time, result_handle->start_time()); 682*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(end_time, result_handle->end_time()); 683*b0d29bc4SBrooks Davis 684*b0d29bc4SBrooks Davis result_handle->cleanup(); 685*b0d29bc4SBrooks Davis 686*b0d29bc4SBrooks Davis ATF_REQUIRE(!atf::utils::file_exists( 687*b0d29bc4SBrooks Davis result_handle->stdout_file().str())); 688*b0d29bc4SBrooks Davis ATF_REQUIRE(!atf::utils::file_exists( 689*b0d29bc4SBrooks Davis result_handle->stderr_file().str())); 690*b0d29bc4SBrooks Davis ATF_REQUIRE(!atf::utils::file_exists( 691*b0d29bc4SBrooks Davis result_handle->work_directory().str())); 692*b0d29bc4SBrooks Davis 693*b0d29bc4SBrooks Davis result_handle.reset(); 694*b0d29bc4SBrooks Davis } 695*b0d29bc4SBrooks Davis 696*b0d29bc4SBrooks Davis handle.cleanup(); 697*b0d29bc4SBrooks Davis } 698*b0d29bc4SBrooks Davis 699*b0d29bc4SBrooks Davis 700*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__run_check_paths); 701*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(integration__run_check_paths) 702*b0d29bc4SBrooks Davis { 703*b0d29bc4SBrooks Davis fs::mkdir_p(fs::path("dir1/dir2/dir3"), 0755); 704*b0d29bc4SBrooks Davis atf::utils::create_file("dir1/dir2/dir3/program", ""); 705*b0d29bc4SBrooks Davis 706*b0d29bc4SBrooks Davis const model::test_program_ptr program = model::test_program_builder( 707*b0d29bc4SBrooks Davis "mock", fs::path("dir2/dir3/program"), fs::path("dir1"), "the-suite") 708*b0d29bc4SBrooks Davis .add_test_case("check_i_exist").build_ptr(); 709*b0d29bc4SBrooks Davis 710*b0d29bc4SBrooks Davis scheduler::scheduler_handle handle = scheduler::setup(); 711*b0d29bc4SBrooks Davis 712*b0d29bc4SBrooks Davis (void)handle.spawn_test(program, "check_i_exist", engine::default_config()); 713*b0d29bc4SBrooks Davis scheduler::result_handle_ptr result_handle = handle.wait_any(); 714*b0d29bc4SBrooks Davis const scheduler::test_result_handle* test_result_handle = 715*b0d29bc4SBrooks Davis dynamic_cast< const scheduler::test_result_handle* >( 716*b0d29bc4SBrooks Davis result_handle.get()); 717*b0d29bc4SBrooks Davis 718*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(model::test_result(model::test_result_passed, "Exit 0"), 719*b0d29bc4SBrooks Davis test_result_handle->test_result()); 720*b0d29bc4SBrooks Davis 721*b0d29bc4SBrooks Davis result_handle->cleanup(); 722*b0d29bc4SBrooks Davis result_handle.reset(); 723*b0d29bc4SBrooks Davis 724*b0d29bc4SBrooks Davis handle.cleanup(); 725*b0d29bc4SBrooks Davis } 726*b0d29bc4SBrooks Davis 727*b0d29bc4SBrooks Davis 728*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__parameters_and_output); 729*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(integration__parameters_and_output) 730*b0d29bc4SBrooks Davis { 731*b0d29bc4SBrooks Davis const model::test_program_ptr program = model::test_program_builder( 732*b0d29bc4SBrooks Davis "mock", fs::path("the-program"), fs::current_path(), "the-suite") 733*b0d29bc4SBrooks Davis .add_test_case("print_params").build_ptr(); 734*b0d29bc4SBrooks Davis 735*b0d29bc4SBrooks Davis config::tree user_config = engine::empty_config(); 736*b0d29bc4SBrooks Davis user_config.set_string("test_suites.the-suite.one", "first variable"); 737*b0d29bc4SBrooks Davis user_config.set_string("test_suites.the-suite.two", "second variable"); 738*b0d29bc4SBrooks Davis 739*b0d29bc4SBrooks Davis scheduler::scheduler_handle handle = scheduler::setup(); 740*b0d29bc4SBrooks Davis 741*b0d29bc4SBrooks Davis const scheduler::exec_handle exec_handle = handle.spawn_test( 742*b0d29bc4SBrooks Davis program, "print_params", user_config); 743*b0d29bc4SBrooks Davis 744*b0d29bc4SBrooks Davis scheduler::result_handle_ptr result_handle = handle.wait_any(); 745*b0d29bc4SBrooks Davis const scheduler::test_result_handle* test_result_handle = 746*b0d29bc4SBrooks Davis dynamic_cast< const scheduler::test_result_handle* >( 747*b0d29bc4SBrooks Davis result_handle.get()); 748*b0d29bc4SBrooks Davis 749*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(exec_handle, result_handle->original_pid()); 750*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(program, test_result_handle->test_program()); 751*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ("print_params", test_result_handle->test_case_name()); 752*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(model::test_result(model::test_result_passed, "Exit 0"), 753*b0d29bc4SBrooks Davis test_result_handle->test_result()); 754*b0d29bc4SBrooks Davis 755*b0d29bc4SBrooks Davis const fs::path stdout_file = result_handle->stdout_file(); 756*b0d29bc4SBrooks Davis ATF_REQUIRE(atf::utils::compare_file( 757*b0d29bc4SBrooks Davis stdout_file.str(), 758*b0d29bc4SBrooks Davis "Test program: the-program\n" 759*b0d29bc4SBrooks Davis "Test case: print_params\n" 760*b0d29bc4SBrooks Davis "one=first variable\n" 761*b0d29bc4SBrooks Davis "two=second variable\n")); 762*b0d29bc4SBrooks Davis const fs::path stderr_file = result_handle->stderr_file(); 763*b0d29bc4SBrooks Davis ATF_REQUIRE(atf::utils::compare_file( 764*b0d29bc4SBrooks Davis stderr_file.str(), "stderr: print_params\n")); 765*b0d29bc4SBrooks Davis 766*b0d29bc4SBrooks Davis result_handle->cleanup(); 767*b0d29bc4SBrooks Davis ATF_REQUIRE(!fs::exists(stdout_file)); 768*b0d29bc4SBrooks Davis ATF_REQUIRE(!fs::exists(stderr_file)); 769*b0d29bc4SBrooks Davis result_handle.reset(); 770*b0d29bc4SBrooks Davis 771*b0d29bc4SBrooks Davis handle.cleanup(); 772*b0d29bc4SBrooks Davis } 773*b0d29bc4SBrooks Davis 774*b0d29bc4SBrooks Davis 775*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__fake_result); 776*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(integration__fake_result) 777*b0d29bc4SBrooks Davis { 778*b0d29bc4SBrooks Davis const model::test_result fake_result(model::test_result_skipped, 779*b0d29bc4SBrooks Davis "Some fake details"); 780*b0d29bc4SBrooks Davis 781*b0d29bc4SBrooks Davis model::test_cases_map test_cases; 782*b0d29bc4SBrooks Davis test_cases.insert(model::test_cases_map::value_type( 783*b0d29bc4SBrooks Davis "__fake__", model::test_case("__fake__", "ABC", fake_result))); 784*b0d29bc4SBrooks Davis 785*b0d29bc4SBrooks Davis const model::test_program_ptr program(new model::test_program( 786*b0d29bc4SBrooks Davis "mock", fs::path("the-program"), fs::current_path(), "the-suite", 787*b0d29bc4SBrooks Davis model::metadata_builder().build(), test_cases)); 788*b0d29bc4SBrooks Davis 789*b0d29bc4SBrooks Davis const config::tree user_config = engine::empty_config(); 790*b0d29bc4SBrooks Davis 791*b0d29bc4SBrooks Davis scheduler::scheduler_handle handle = scheduler::setup(); 792*b0d29bc4SBrooks Davis 793*b0d29bc4SBrooks Davis (void)handle.spawn_test(program, "__fake__", user_config); 794*b0d29bc4SBrooks Davis 795*b0d29bc4SBrooks Davis scheduler::result_handle_ptr result_handle = handle.wait_any(); 796*b0d29bc4SBrooks Davis const scheduler::test_result_handle* test_result_handle = 797*b0d29bc4SBrooks Davis dynamic_cast< const scheduler::test_result_handle* >( 798*b0d29bc4SBrooks Davis result_handle.get()); 799*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(fake_result, test_result_handle->test_result()); 800*b0d29bc4SBrooks Davis result_handle->cleanup(); 801*b0d29bc4SBrooks Davis result_handle.reset(); 802*b0d29bc4SBrooks Davis 803*b0d29bc4SBrooks Davis handle.cleanup(); 804*b0d29bc4SBrooks Davis } 805*b0d29bc4SBrooks Davis 806*b0d29bc4SBrooks Davis 807*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__cleanup__head_skips); 808*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(integration__cleanup__head_skips) 809*b0d29bc4SBrooks Davis { 810*b0d29bc4SBrooks Davis const model::test_program_ptr program = model::test_program_builder( 811*b0d29bc4SBrooks Davis "mock", fs::path("the-program"), fs::current_path(), "the-suite") 812*b0d29bc4SBrooks Davis .add_test_case("skip_me", 813*b0d29bc4SBrooks Davis model::metadata_builder() 814*b0d29bc4SBrooks Davis .add_required_config("variable-that-does-not-exist") 815*b0d29bc4SBrooks Davis .set_has_cleanup(true) 816*b0d29bc4SBrooks Davis .build()) 817*b0d29bc4SBrooks Davis .build_ptr(); 818*b0d29bc4SBrooks Davis 819*b0d29bc4SBrooks Davis const config::tree user_config = engine::empty_config(); 820*b0d29bc4SBrooks Davis 821*b0d29bc4SBrooks Davis scheduler::scheduler_handle handle = scheduler::setup(); 822*b0d29bc4SBrooks Davis 823*b0d29bc4SBrooks Davis (void)handle.spawn_test(program, "skip_me", user_config); 824*b0d29bc4SBrooks Davis 825*b0d29bc4SBrooks Davis scheduler::result_handle_ptr result_handle = handle.wait_any(); 826*b0d29bc4SBrooks Davis const scheduler::test_result_handle* test_result_handle = 827*b0d29bc4SBrooks Davis dynamic_cast< const scheduler::test_result_handle* >( 828*b0d29bc4SBrooks Davis result_handle.get()); 829*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(model::test_result( 830*b0d29bc4SBrooks Davis model::test_result_skipped, 831*b0d29bc4SBrooks Davis "Required configuration property " 832*b0d29bc4SBrooks Davis "'variable-that-does-not-exist' not defined"), 833*b0d29bc4SBrooks Davis test_result_handle->test_result()); 834*b0d29bc4SBrooks Davis ATF_REQUIRE(!atf::utils::grep_file("exec_cleanup was called", 835*b0d29bc4SBrooks Davis result_handle->stdout_file().str())); 836*b0d29bc4SBrooks Davis result_handle->cleanup(); 837*b0d29bc4SBrooks Davis result_handle.reset(); 838*b0d29bc4SBrooks Davis 839*b0d29bc4SBrooks Davis handle.cleanup(); 840*b0d29bc4SBrooks Davis } 841*b0d29bc4SBrooks Davis 842*b0d29bc4SBrooks Davis 843*b0d29bc4SBrooks Davis /// Runs a test to verify the behavior of cleanup routines. 844*b0d29bc4SBrooks Davis /// 845*b0d29bc4SBrooks Davis /// \param test_case The name of the test case to invoke. 846*b0d29bc4SBrooks Davis /// \param exp_result The expected test result of the execution. 847*b0d29bc4SBrooks Davis static void 848*b0d29bc4SBrooks Davis do_cleanup_test(const char* test_case, 849*b0d29bc4SBrooks Davis const model::test_result& exp_result) 850*b0d29bc4SBrooks Davis { 851*b0d29bc4SBrooks Davis const model::test_program_ptr program = model::test_program_builder( 852*b0d29bc4SBrooks Davis "mock", fs::path("the-program"), fs::current_path(), "the-suite") 853*b0d29bc4SBrooks Davis .add_test_case(test_case) 854*b0d29bc4SBrooks Davis .set_metadata(model::metadata_builder().set_has_cleanup(true).build()) 855*b0d29bc4SBrooks Davis .build_ptr(); 856*b0d29bc4SBrooks Davis 857*b0d29bc4SBrooks Davis const config::tree user_config = engine::empty_config(); 858*b0d29bc4SBrooks Davis 859*b0d29bc4SBrooks Davis scheduler::scheduler_handle handle = scheduler::setup(); 860*b0d29bc4SBrooks Davis 861*b0d29bc4SBrooks Davis (void)handle.spawn_test(program, test_case, user_config); 862*b0d29bc4SBrooks Davis 863*b0d29bc4SBrooks Davis scheduler::result_handle_ptr result_handle = handle.wait_any(); 864*b0d29bc4SBrooks Davis const scheduler::test_result_handle* test_result_handle = 865*b0d29bc4SBrooks Davis dynamic_cast< const scheduler::test_result_handle* >( 866*b0d29bc4SBrooks Davis result_handle.get()); 867*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(exp_result, test_result_handle->test_result()); 868*b0d29bc4SBrooks Davis ATF_REQUIRE(atf::utils::compare_file( 869*b0d29bc4SBrooks Davis result_handle->stdout_file().str(), 870*b0d29bc4SBrooks Davis "exec_cleanup was called\n")); 871*b0d29bc4SBrooks Davis result_handle->cleanup(); 872*b0d29bc4SBrooks Davis result_handle.reset(); 873*b0d29bc4SBrooks Davis 874*b0d29bc4SBrooks Davis handle.cleanup(); 875*b0d29bc4SBrooks Davis } 876*b0d29bc4SBrooks Davis 877*b0d29bc4SBrooks Davis 878*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__cleanup__body_skips); 879*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(integration__cleanup__body_skips) 880*b0d29bc4SBrooks Davis { 881*b0d29bc4SBrooks Davis do_cleanup_test( 882*b0d29bc4SBrooks Davis "skip_body_pass_cleanup", 883*b0d29bc4SBrooks Davis model::test_result(model::test_result_skipped, "Exit 0")); 884*b0d29bc4SBrooks Davis } 885*b0d29bc4SBrooks Davis 886*b0d29bc4SBrooks Davis 887*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__cleanup__body_bad__cleanup_ok); 888*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(integration__cleanup__body_bad__cleanup_ok) 889*b0d29bc4SBrooks Davis { 890*b0d29bc4SBrooks Davis do_cleanup_test( 891*b0d29bc4SBrooks Davis "fail_body_pass_cleanup", 892*b0d29bc4SBrooks Davis model::test_result(model::test_result_failed, "Signal 15")); 893*b0d29bc4SBrooks Davis } 894*b0d29bc4SBrooks Davis 895*b0d29bc4SBrooks Davis 896*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__cleanup__body_ok__cleanup_bad); 897*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(integration__cleanup__body_ok__cleanup_bad) 898*b0d29bc4SBrooks Davis { 899*b0d29bc4SBrooks Davis do_cleanup_test( 900*b0d29bc4SBrooks Davis "pass_body_fail_cleanup", 901*b0d29bc4SBrooks Davis model::test_result(model::test_result_broken, "Test case cleanup " 902*b0d29bc4SBrooks Davis "did not terminate successfully")); 903*b0d29bc4SBrooks Davis } 904*b0d29bc4SBrooks Davis 905*b0d29bc4SBrooks Davis 906*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__cleanup__body_bad__cleanup_bad); 907*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(integration__cleanup__body_bad__cleanup_bad) 908*b0d29bc4SBrooks Davis { 909*b0d29bc4SBrooks Davis do_cleanup_test( 910*b0d29bc4SBrooks Davis "fail_body_fail_cleanup", 911*b0d29bc4SBrooks Davis model::test_result(model::test_result_failed, "Signal 15")); 912*b0d29bc4SBrooks Davis } 913*b0d29bc4SBrooks Davis 914*b0d29bc4SBrooks Davis 915*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__cleanup__timeout); 916*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(integration__cleanup__timeout) 917*b0d29bc4SBrooks Davis { 918*b0d29bc4SBrooks Davis scheduler::cleanup_timeout = datetime::delta(1, 0); 919*b0d29bc4SBrooks Davis do_cleanup_test( 920*b0d29bc4SBrooks Davis "cleanup_timeout", 921*b0d29bc4SBrooks Davis model::test_result(model::test_result_broken, "Test case cleanup " 922*b0d29bc4SBrooks Davis "timed out")); 923*b0d29bc4SBrooks Davis } 924*b0d29bc4SBrooks Davis 925*b0d29bc4SBrooks Davis 926*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__check_requirements); 927*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(integration__check_requirements) 928*b0d29bc4SBrooks Davis { 929*b0d29bc4SBrooks Davis const model::test_program_ptr program = model::test_program_builder( 930*b0d29bc4SBrooks Davis "mock", fs::path("the-program"), fs::current_path(), "the-suite") 931*b0d29bc4SBrooks Davis .add_test_case("exit 12") 932*b0d29bc4SBrooks Davis .set_metadata(model::metadata_builder() 933*b0d29bc4SBrooks Davis .add_required_config("abcde").build()) 934*b0d29bc4SBrooks Davis .build_ptr(); 935*b0d29bc4SBrooks Davis 936*b0d29bc4SBrooks Davis const config::tree user_config = engine::empty_config(); 937*b0d29bc4SBrooks Davis 938*b0d29bc4SBrooks Davis scheduler::scheduler_handle handle = scheduler::setup(); 939*b0d29bc4SBrooks Davis 940*b0d29bc4SBrooks Davis (void)handle.spawn_test(program, "exit 12", user_config); 941*b0d29bc4SBrooks Davis 942*b0d29bc4SBrooks Davis scheduler::result_handle_ptr result_handle = handle.wait_any(); 943*b0d29bc4SBrooks Davis const scheduler::test_result_handle* test_result_handle = 944*b0d29bc4SBrooks Davis dynamic_cast< const scheduler::test_result_handle* >( 945*b0d29bc4SBrooks Davis result_handle.get()); 946*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(model::test_result( 947*b0d29bc4SBrooks Davis model::test_result_skipped, 948*b0d29bc4SBrooks Davis "Required configuration property 'abcde' not defined"), 949*b0d29bc4SBrooks Davis test_result_handle->test_result()); 950*b0d29bc4SBrooks Davis result_handle->cleanup(); 951*b0d29bc4SBrooks Davis result_handle.reset(); 952*b0d29bc4SBrooks Davis 953*b0d29bc4SBrooks Davis handle.cleanup(); 954*b0d29bc4SBrooks Davis } 955*b0d29bc4SBrooks Davis 956*b0d29bc4SBrooks Davis 957*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__stacktrace); 958*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(integration__stacktrace) 959*b0d29bc4SBrooks Davis { 960*b0d29bc4SBrooks Davis utils::prepare_coredump_test(this); 961*b0d29bc4SBrooks Davis 962*b0d29bc4SBrooks Davis const model::test_program_ptr program = model::test_program_builder( 963*b0d29bc4SBrooks Davis "mock", fs::path("the-program"), fs::current_path(), "the-suite") 964*b0d29bc4SBrooks Davis .add_test_case("unknown-dumps-core").build_ptr(); 965*b0d29bc4SBrooks Davis 966*b0d29bc4SBrooks Davis const config::tree user_config = engine::empty_config(); 967*b0d29bc4SBrooks Davis 968*b0d29bc4SBrooks Davis scheduler::scheduler_handle handle = scheduler::setup(); 969*b0d29bc4SBrooks Davis 970*b0d29bc4SBrooks Davis (void)handle.spawn_test(program, "unknown-dumps-core", user_config); 971*b0d29bc4SBrooks Davis 972*b0d29bc4SBrooks Davis scheduler::result_handle_ptr result_handle = handle.wait_any(); 973*b0d29bc4SBrooks Davis const scheduler::test_result_handle* test_result_handle = 974*b0d29bc4SBrooks Davis dynamic_cast< const scheduler::test_result_handle* >( 975*b0d29bc4SBrooks Davis result_handle.get()); 976*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(model::test_result(model::test_result_failed, 977*b0d29bc4SBrooks Davis F("Signal %s") % SIGABRT), 978*b0d29bc4SBrooks Davis test_result_handle->test_result()); 979*b0d29bc4SBrooks Davis ATF_REQUIRE(!atf::utils::grep_file("attempting to gather stack trace", 980*b0d29bc4SBrooks Davis result_handle->stdout_file().str())); 981*b0d29bc4SBrooks Davis ATF_REQUIRE( atf::utils::grep_file("attempting to gather stack trace", 982*b0d29bc4SBrooks Davis result_handle->stderr_file().str())); 983*b0d29bc4SBrooks Davis result_handle->cleanup(); 984*b0d29bc4SBrooks Davis result_handle.reset(); 985*b0d29bc4SBrooks Davis 986*b0d29bc4SBrooks Davis handle.cleanup(); 987*b0d29bc4SBrooks Davis } 988*b0d29bc4SBrooks Davis 989*b0d29bc4SBrooks Davis 990*b0d29bc4SBrooks Davis /// Runs a test to verify the dumping of the list of existing files on failure. 991*b0d29bc4SBrooks Davis /// 992*b0d29bc4SBrooks Davis /// \param test_case The name of the test case to invoke. 993*b0d29bc4SBrooks Davis /// \param exp_stderr Expected contents of stderr. 994*b0d29bc4SBrooks Davis static void 995*b0d29bc4SBrooks Davis do_check_list_files_on_failure(const char* test_case, const char* exp_stderr) 996*b0d29bc4SBrooks Davis { 997*b0d29bc4SBrooks Davis const model::test_program_ptr program = model::test_program_builder( 998*b0d29bc4SBrooks Davis "mock", fs::path("the-program"), fs::current_path(), "the-suite") 999*b0d29bc4SBrooks Davis .add_test_case(test_case).build_ptr(); 1000*b0d29bc4SBrooks Davis 1001*b0d29bc4SBrooks Davis const config::tree user_config = engine::empty_config(); 1002*b0d29bc4SBrooks Davis 1003*b0d29bc4SBrooks Davis scheduler::scheduler_handle handle = scheduler::setup(); 1004*b0d29bc4SBrooks Davis 1005*b0d29bc4SBrooks Davis (void)handle.spawn_test(program, test_case, user_config); 1006*b0d29bc4SBrooks Davis 1007*b0d29bc4SBrooks Davis scheduler::result_handle_ptr result_handle = handle.wait_any(); 1008*b0d29bc4SBrooks Davis atf::utils::cat_file(result_handle->stdout_file().str(), "child stdout: "); 1009*b0d29bc4SBrooks Davis ATF_REQUIRE(atf::utils::compare_file(result_handle->stdout_file().str(), 1010*b0d29bc4SBrooks Davis "")); 1011*b0d29bc4SBrooks Davis atf::utils::cat_file(result_handle->stderr_file().str(), "child stderr: "); 1012*b0d29bc4SBrooks Davis ATF_REQUIRE(atf::utils::compare_file(result_handle->stderr_file().str(), 1013*b0d29bc4SBrooks Davis exp_stderr)); 1014*b0d29bc4SBrooks Davis result_handle->cleanup(); 1015*b0d29bc4SBrooks Davis result_handle.reset(); 1016*b0d29bc4SBrooks Davis 1017*b0d29bc4SBrooks Davis handle.cleanup(); 1018*b0d29bc4SBrooks Davis } 1019*b0d29bc4SBrooks Davis 1020*b0d29bc4SBrooks Davis 1021*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__list_files_on_failure__none); 1022*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(integration__list_files_on_failure__none) 1023*b0d29bc4SBrooks Davis { 1024*b0d29bc4SBrooks Davis do_check_list_files_on_failure("fail", "This should not be clobbered\n"); 1025*b0d29bc4SBrooks Davis } 1026*b0d29bc4SBrooks Davis 1027*b0d29bc4SBrooks Davis 1028*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__list_files_on_failure__some); 1029*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(integration__list_files_on_failure__some) 1030*b0d29bc4SBrooks Davis { 1031*b0d29bc4SBrooks Davis do_check_list_files_on_failure( 1032*b0d29bc4SBrooks Davis "create_files_and_fail", 1033*b0d29bc4SBrooks Davis "This should not be clobbered\n" 1034*b0d29bc4SBrooks Davis "Files left in work directory after failure: " 1035*b0d29bc4SBrooks Davis "dir1, first file, second-file\n"); 1036*b0d29bc4SBrooks Davis } 1037*b0d29bc4SBrooks Davis 1038*b0d29bc4SBrooks Davis 1039*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__prevent_clobbering_control_files); 1040*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(integration__prevent_clobbering_control_files) 1041*b0d29bc4SBrooks Davis { 1042*b0d29bc4SBrooks Davis const model::test_program_ptr program = model::test_program_builder( 1043*b0d29bc4SBrooks Davis "mock", fs::path("the-program"), fs::current_path(), "the-suite") 1044*b0d29bc4SBrooks Davis .add_test_case("delete_all").build_ptr(); 1045*b0d29bc4SBrooks Davis 1046*b0d29bc4SBrooks Davis const config::tree user_config = engine::empty_config(); 1047*b0d29bc4SBrooks Davis 1048*b0d29bc4SBrooks Davis scheduler::scheduler_handle handle = scheduler::setup(); 1049*b0d29bc4SBrooks Davis 1050*b0d29bc4SBrooks Davis (void)handle.spawn_test(program, "delete_all", user_config); 1051*b0d29bc4SBrooks Davis 1052*b0d29bc4SBrooks Davis scheduler::result_handle_ptr result_handle = handle.wait_any(); 1053*b0d29bc4SBrooks Davis const scheduler::test_result_handle* test_result_handle = 1054*b0d29bc4SBrooks Davis dynamic_cast< const scheduler::test_result_handle* >( 1055*b0d29bc4SBrooks Davis result_handle.get()); 1056*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(model::test_result(model::test_result_passed, "Exit 0"), 1057*b0d29bc4SBrooks Davis test_result_handle->test_result()); 1058*b0d29bc4SBrooks Davis result_handle->cleanup(); 1059*b0d29bc4SBrooks Davis result_handle.reset(); 1060*b0d29bc4SBrooks Davis 1061*b0d29bc4SBrooks Davis handle.cleanup(); 1062*b0d29bc4SBrooks Davis } 1063*b0d29bc4SBrooks Davis 1064*b0d29bc4SBrooks Davis 1065*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(debug_test); 1066*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(debug_test) 1067*b0d29bc4SBrooks Davis { 1068*b0d29bc4SBrooks Davis const model::test_program_ptr program = model::test_program_builder( 1069*b0d29bc4SBrooks Davis "mock", fs::path("the-program"), fs::current_path(), "the-suite") 1070*b0d29bc4SBrooks Davis .add_test_case("print_params").build_ptr(); 1071*b0d29bc4SBrooks Davis 1072*b0d29bc4SBrooks Davis config::tree user_config = engine::empty_config(); 1073*b0d29bc4SBrooks Davis user_config.set_string("test_suites.the-suite.one", "first variable"); 1074*b0d29bc4SBrooks Davis user_config.set_string("test_suites.the-suite.two", "second variable"); 1075*b0d29bc4SBrooks Davis 1076*b0d29bc4SBrooks Davis scheduler::scheduler_handle handle = scheduler::setup(); 1077*b0d29bc4SBrooks Davis 1078*b0d29bc4SBrooks Davis const fs::path stdout_file("custom-stdout.txt"); 1079*b0d29bc4SBrooks Davis const fs::path stderr_file("custom-stderr.txt"); 1080*b0d29bc4SBrooks Davis 1081*b0d29bc4SBrooks Davis scheduler::result_handle_ptr result_handle = handle.debug_test( 1082*b0d29bc4SBrooks Davis program, "print_params", user_config, stdout_file, stderr_file); 1083*b0d29bc4SBrooks Davis const scheduler::test_result_handle* test_result_handle = 1084*b0d29bc4SBrooks Davis dynamic_cast< const scheduler::test_result_handle* >( 1085*b0d29bc4SBrooks Davis result_handle.get()); 1086*b0d29bc4SBrooks Davis 1087*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(program, test_result_handle->test_program()); 1088*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ("print_params", test_result_handle->test_case_name()); 1089*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(model::test_result(model::test_result_passed, "Exit 0"), 1090*b0d29bc4SBrooks Davis test_result_handle->test_result()); 1091*b0d29bc4SBrooks Davis 1092*b0d29bc4SBrooks Davis // The original output went to a file. It's only an artifact of 1093*b0d29bc4SBrooks Davis // debug_test() that we later get a copy in our own files. 1094*b0d29bc4SBrooks Davis ATF_REQUIRE(stdout_file != result_handle->stdout_file()); 1095*b0d29bc4SBrooks Davis ATF_REQUIRE(stderr_file != result_handle->stderr_file()); 1096*b0d29bc4SBrooks Davis 1097*b0d29bc4SBrooks Davis result_handle->cleanup(); 1098*b0d29bc4SBrooks Davis result_handle.reset(); 1099*b0d29bc4SBrooks Davis 1100*b0d29bc4SBrooks Davis handle.cleanup(); 1101*b0d29bc4SBrooks Davis 1102*b0d29bc4SBrooks Davis ATF_REQUIRE(atf::utils::compare_file( 1103*b0d29bc4SBrooks Davis stdout_file.str(), 1104*b0d29bc4SBrooks Davis "Test program: the-program\n" 1105*b0d29bc4SBrooks Davis "Test case: print_params\n" 1106*b0d29bc4SBrooks Davis "one=first variable\n" 1107*b0d29bc4SBrooks Davis "two=second variable\n")); 1108*b0d29bc4SBrooks Davis ATF_REQUIRE(atf::utils::compare_file( 1109*b0d29bc4SBrooks Davis stderr_file.str(), "stderr: print_params\n")); 1110*b0d29bc4SBrooks Davis } 1111*b0d29bc4SBrooks Davis 1112*b0d29bc4SBrooks Davis 1113*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(ensure_valid_interface); 1114*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(ensure_valid_interface) 1115*b0d29bc4SBrooks Davis { 1116*b0d29bc4SBrooks Davis scheduler::ensure_valid_interface("mock"); 1117*b0d29bc4SBrooks Davis 1118*b0d29bc4SBrooks Davis ATF_REQUIRE_THROW_RE(engine::error, "Unsupported test interface 'mock2'", 1119*b0d29bc4SBrooks Davis scheduler::ensure_valid_interface("mock2")); 1120*b0d29bc4SBrooks Davis scheduler::register_interface( 1121*b0d29bc4SBrooks Davis "mock2", std::shared_ptr< scheduler::interface >(new mock_interface())); 1122*b0d29bc4SBrooks Davis scheduler::ensure_valid_interface("mock2"); 1123*b0d29bc4SBrooks Davis 1124*b0d29bc4SBrooks Davis // Standard interfaces should not be present unless registered. 1125*b0d29bc4SBrooks Davis ATF_REQUIRE_THROW_RE(engine::error, "Unsupported test interface 'plain'", 1126*b0d29bc4SBrooks Davis scheduler::ensure_valid_interface("plain")); 1127*b0d29bc4SBrooks Davis } 1128*b0d29bc4SBrooks Davis 1129*b0d29bc4SBrooks Davis 1130*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(registered_interface_names); 1131*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(registered_interface_names) 1132*b0d29bc4SBrooks Davis { 1133*b0d29bc4SBrooks Davis std::set< std::string > exp_names; 1134*b0d29bc4SBrooks Davis 1135*b0d29bc4SBrooks Davis exp_names.insert("mock"); 1136*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(exp_names, scheduler::registered_interface_names()); 1137*b0d29bc4SBrooks Davis 1138*b0d29bc4SBrooks Davis scheduler::register_interface( 1139*b0d29bc4SBrooks Davis "mock2", std::shared_ptr< scheduler::interface >(new mock_interface())); 1140*b0d29bc4SBrooks Davis exp_names.insert("mock2"); 1141*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(exp_names, scheduler::registered_interface_names()); 1142*b0d29bc4SBrooks Davis } 1143*b0d29bc4SBrooks Davis 1144*b0d29bc4SBrooks Davis 1145*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(current_context); 1146*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(current_context) 1147*b0d29bc4SBrooks Davis { 1148*b0d29bc4SBrooks Davis const model::context context = scheduler::current_context(); 1149*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(fs::current_path(), context.cwd()); 1150*b0d29bc4SBrooks Davis ATF_REQUIRE(utils::getallenv() == context.env()); 1151*b0d29bc4SBrooks Davis } 1152*b0d29bc4SBrooks Davis 1153*b0d29bc4SBrooks Davis 1154*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(generate_config__empty); 1155*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(generate_config__empty) 1156*b0d29bc4SBrooks Davis { 1157*b0d29bc4SBrooks Davis const config::tree user_config = engine::empty_config(); 1158*b0d29bc4SBrooks Davis 1159*b0d29bc4SBrooks Davis const config::properties_map exp_props; 1160*b0d29bc4SBrooks Davis 1161*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(exp_props, 1162*b0d29bc4SBrooks Davis scheduler::generate_config(user_config, "missing")); 1163*b0d29bc4SBrooks Davis } 1164*b0d29bc4SBrooks Davis 1165*b0d29bc4SBrooks Davis 1166*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(generate_config__no_matches); 1167*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(generate_config__no_matches) 1168*b0d29bc4SBrooks Davis { 1169*b0d29bc4SBrooks Davis config::tree user_config = engine::empty_config(); 1170*b0d29bc4SBrooks Davis user_config.set_string("architecture", "foo"); 1171*b0d29bc4SBrooks Davis user_config.set_string("test_suites.one.var1", "value 1"); 1172*b0d29bc4SBrooks Davis 1173*b0d29bc4SBrooks Davis const config::properties_map exp_props; 1174*b0d29bc4SBrooks Davis 1175*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(exp_props, 1176*b0d29bc4SBrooks Davis scheduler::generate_config(user_config, "two")); 1177*b0d29bc4SBrooks Davis } 1178*b0d29bc4SBrooks Davis 1179*b0d29bc4SBrooks Davis 1180*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(generate_config__some_matches); 1181*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(generate_config__some_matches) 1182*b0d29bc4SBrooks Davis { 1183*b0d29bc4SBrooks Davis std::vector< passwd::user > mock_users; 1184*b0d29bc4SBrooks Davis mock_users.push_back(passwd::user("nobody", 1234, 5678)); 1185*b0d29bc4SBrooks Davis passwd::set_mock_users_for_testing(mock_users); 1186*b0d29bc4SBrooks Davis 1187*b0d29bc4SBrooks Davis config::tree user_config = engine::empty_config(); 1188*b0d29bc4SBrooks Davis user_config.set_string("architecture", "foo"); 1189*b0d29bc4SBrooks Davis user_config.set_string("unprivileged_user", "nobody"); 1190*b0d29bc4SBrooks Davis user_config.set_string("test_suites.one.var1", "value 1"); 1191*b0d29bc4SBrooks Davis user_config.set_string("test_suites.two.var2", "value 2"); 1192*b0d29bc4SBrooks Davis 1193*b0d29bc4SBrooks Davis config::properties_map exp_props; 1194*b0d29bc4SBrooks Davis exp_props["unprivileged-user"] = "nobody"; 1195*b0d29bc4SBrooks Davis exp_props["var1"] = "value 1"; 1196*b0d29bc4SBrooks Davis 1197*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(exp_props, 1198*b0d29bc4SBrooks Davis scheduler::generate_config(user_config, "one")); 1199*b0d29bc4SBrooks Davis } 1200*b0d29bc4SBrooks Davis 1201*b0d29bc4SBrooks Davis 1202*b0d29bc4SBrooks Davis ATF_INIT_TEST_CASES(tcs) 1203*b0d29bc4SBrooks Davis { 1204*b0d29bc4SBrooks Davis scheduler::register_interface( 1205*b0d29bc4SBrooks Davis "mock", std::shared_ptr< scheduler::interface >(new mock_interface())); 1206*b0d29bc4SBrooks Davis 1207*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__list_some); 1208*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__list_check_paths); 1209*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__list_timeout); 1210*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__list_fail); 1211*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__list_empty); 1212*b0d29bc4SBrooks Davis 1213*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__run_one); 1214*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__run_many); 1215*b0d29bc4SBrooks Davis 1216*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__run_check_paths); 1217*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__parameters_and_output); 1218*b0d29bc4SBrooks Davis 1219*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__fake_result); 1220*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__cleanup__head_skips); 1221*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__cleanup__body_skips); 1222*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__cleanup__body_ok__cleanup_bad); 1223*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__cleanup__body_bad__cleanup_ok); 1224*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__cleanup__body_bad__cleanup_bad); 1225*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__cleanup__timeout); 1226*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__check_requirements); 1227*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__stacktrace); 1228*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__list_files_on_failure__none); 1229*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__list_files_on_failure__some); 1230*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__prevent_clobbering_control_files); 1231*b0d29bc4SBrooks Davis 1232*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, debug_test); 1233*b0d29bc4SBrooks Davis 1234*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, ensure_valid_interface); 1235*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, registered_interface_names); 1236*b0d29bc4SBrooks Davis 1237*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, current_context); 1238*b0d29bc4SBrooks Davis 1239*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, generate_config__empty); 1240*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, generate_config__no_matches); 1241*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, generate_config__some_matches); 1242*b0d29bc4SBrooks Davis } 1243