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 <unistd.h> 33*b0d29bc4SBrooks Davis } 34*b0d29bc4SBrooks Davis 35*b0d29bc4SBrooks Davis #include <cstdio> 36*b0d29bc4SBrooks Davis #include <cstdlib> 37*b0d29bc4SBrooks Davis #include <fstream> 38*b0d29bc4SBrooks Davis #include <memory> 39*b0d29bc4SBrooks Davis #include <stdexcept> 40*b0d29bc4SBrooks Davis 41*b0d29bc4SBrooks Davis #include "engine/config.hpp" 42*b0d29bc4SBrooks Davis #include "engine/exceptions.hpp" 43*b0d29bc4SBrooks Davis #include "engine/requirements.hpp" 44*b0d29bc4SBrooks Davis #include "model/context.hpp" 45*b0d29bc4SBrooks Davis #include "model/metadata.hpp" 46*b0d29bc4SBrooks Davis #include "model/test_case.hpp" 47*b0d29bc4SBrooks Davis #include "model/test_program.hpp" 48*b0d29bc4SBrooks Davis #include "model/test_result.hpp" 49*b0d29bc4SBrooks Davis #include "utils/config/tree.ipp" 50*b0d29bc4SBrooks Davis #include "utils/datetime.hpp" 51*b0d29bc4SBrooks Davis #include "utils/defs.hpp" 52*b0d29bc4SBrooks Davis #include "utils/env.hpp" 53*b0d29bc4SBrooks Davis #include "utils/format/macros.hpp" 54*b0d29bc4SBrooks Davis #include "utils/fs/directory.hpp" 55*b0d29bc4SBrooks Davis #include "utils/fs/exceptions.hpp" 56*b0d29bc4SBrooks Davis #include "utils/fs/operations.hpp" 57*b0d29bc4SBrooks Davis #include "utils/fs/path.hpp" 58*b0d29bc4SBrooks Davis #include "utils/logging/macros.hpp" 59*b0d29bc4SBrooks Davis #include "utils/noncopyable.hpp" 60*b0d29bc4SBrooks Davis #include "utils/optional.ipp" 61*b0d29bc4SBrooks Davis #include "utils/passwd.hpp" 62*b0d29bc4SBrooks Davis #include "utils/process/executor.ipp" 63*b0d29bc4SBrooks Davis #include "utils/process/status.hpp" 64*b0d29bc4SBrooks Davis #include "utils/sanity.hpp" 65*b0d29bc4SBrooks Davis #include "utils/stacktrace.hpp" 66*b0d29bc4SBrooks Davis #include "utils/stream.hpp" 67*b0d29bc4SBrooks Davis #include "utils/text/operations.ipp" 68*b0d29bc4SBrooks Davis 69*b0d29bc4SBrooks Davis namespace config = utils::config; 70*b0d29bc4SBrooks Davis namespace datetime = utils::datetime; 71*b0d29bc4SBrooks Davis namespace executor = utils::process::executor; 72*b0d29bc4SBrooks Davis namespace fs = utils::fs; 73*b0d29bc4SBrooks Davis namespace logging = utils::logging; 74*b0d29bc4SBrooks Davis namespace passwd = utils::passwd; 75*b0d29bc4SBrooks Davis namespace process = utils::process; 76*b0d29bc4SBrooks Davis namespace scheduler = engine::scheduler; 77*b0d29bc4SBrooks Davis namespace text = utils::text; 78*b0d29bc4SBrooks Davis 79*b0d29bc4SBrooks Davis using utils::none; 80*b0d29bc4SBrooks Davis using utils::optional; 81*b0d29bc4SBrooks Davis 82*b0d29bc4SBrooks Davis 83*b0d29bc4SBrooks Davis /// Timeout for the test case cleanup operation. 84*b0d29bc4SBrooks Davis /// 85*b0d29bc4SBrooks Davis /// TODO(jmmv): This is here only for testing purposes. Maybe we should expose 86*b0d29bc4SBrooks Davis /// this setting as part of the user_config. 87*b0d29bc4SBrooks Davis datetime::delta scheduler::cleanup_timeout(60, 0); 88*b0d29bc4SBrooks Davis 89*b0d29bc4SBrooks Davis 90*b0d29bc4SBrooks Davis /// Timeout for the test case listing operation. 91*b0d29bc4SBrooks Davis /// 92*b0d29bc4SBrooks Davis /// TODO(jmmv): This is here only for testing purposes. Maybe we should expose 93*b0d29bc4SBrooks Davis /// this setting as part of the user_config. 94*b0d29bc4SBrooks Davis datetime::delta scheduler::list_timeout(300, 0); 95*b0d29bc4SBrooks Davis 96*b0d29bc4SBrooks Davis 97*b0d29bc4SBrooks Davis namespace { 98*b0d29bc4SBrooks Davis 99*b0d29bc4SBrooks Davis 100*b0d29bc4SBrooks Davis /// Magic exit status to indicate that the test case was probably skipped. 101*b0d29bc4SBrooks Davis /// 102*b0d29bc4SBrooks Davis /// The test case was only skipped if and only if we return this exit code and 103*b0d29bc4SBrooks Davis /// we find the skipped_cookie file on disk. 104*b0d29bc4SBrooks Davis static const int exit_skipped = 84; 105*b0d29bc4SBrooks Davis 106*b0d29bc4SBrooks Davis 107*b0d29bc4SBrooks Davis /// Text file containing the skip reason for the test case. 108*b0d29bc4SBrooks Davis /// 109*b0d29bc4SBrooks Davis /// This will only be present within unique_work_directory if the test case 110*b0d29bc4SBrooks Davis /// exited with the exit_skipped code. However, there is no guarantee that the 111*b0d29bc4SBrooks Davis /// file is there (say if the test really decided to exit with code exit_skipped 112*b0d29bc4SBrooks Davis /// on its own). 113*b0d29bc4SBrooks Davis static const char* skipped_cookie = "skipped.txt"; 114*b0d29bc4SBrooks Davis 115*b0d29bc4SBrooks Davis 116*b0d29bc4SBrooks Davis /// Mapping of interface names to interface definitions. 117*b0d29bc4SBrooks Davis typedef std::map< std::string, std::shared_ptr< scheduler::interface > > 118*b0d29bc4SBrooks Davis interfaces_map; 119*b0d29bc4SBrooks Davis 120*b0d29bc4SBrooks Davis 121*b0d29bc4SBrooks Davis /// Mapping of interface names to interface definitions. 122*b0d29bc4SBrooks Davis /// 123*b0d29bc4SBrooks Davis /// Use register_interface() to add an entry to this global table. 124*b0d29bc4SBrooks Davis static interfaces_map interfaces; 125*b0d29bc4SBrooks Davis 126*b0d29bc4SBrooks Davis 127*b0d29bc4SBrooks Davis /// Scans the contents of a directory and appends the file listing to a file. 128*b0d29bc4SBrooks Davis /// 129*b0d29bc4SBrooks Davis /// \param dir_path The directory to scan. 130*b0d29bc4SBrooks Davis /// \param output_file The file to which to append the listing. 131*b0d29bc4SBrooks Davis /// 132*b0d29bc4SBrooks Davis /// \throw engine::error If there are problems listing the files. 133*b0d29bc4SBrooks Davis static void 134*b0d29bc4SBrooks Davis append_files_listing(const fs::path& dir_path, const fs::path& output_file) 135*b0d29bc4SBrooks Davis { 136*b0d29bc4SBrooks Davis std::ofstream output(output_file.c_str(), std::ios::app); 137*b0d29bc4SBrooks Davis if (!output) 138*b0d29bc4SBrooks Davis throw engine::error(F("Failed to open output file %s for append") 139*b0d29bc4SBrooks Davis % output_file); 140*b0d29bc4SBrooks Davis try { 141*b0d29bc4SBrooks Davis std::set < std::string > names; 142*b0d29bc4SBrooks Davis 143*b0d29bc4SBrooks Davis const fs::directory dir(dir_path); 144*b0d29bc4SBrooks Davis for (fs::directory::const_iterator iter = dir.begin(); 145*b0d29bc4SBrooks Davis iter != dir.end(); ++iter) { 146*b0d29bc4SBrooks Davis if (iter->name != "." && iter->name != "..") 147*b0d29bc4SBrooks Davis names.insert(iter->name); 148*b0d29bc4SBrooks Davis } 149*b0d29bc4SBrooks Davis 150*b0d29bc4SBrooks Davis if (!names.empty()) { 151*b0d29bc4SBrooks Davis output << "Files left in work directory after failure: " 152*b0d29bc4SBrooks Davis << text::join(names, ", ") << '\n'; 153*b0d29bc4SBrooks Davis } 154*b0d29bc4SBrooks Davis } catch (const fs::error& e) { 155*b0d29bc4SBrooks Davis throw engine::error(F("Cannot append files listing to %s: %s") 156*b0d29bc4SBrooks Davis % output_file % e.what()); 157*b0d29bc4SBrooks Davis } 158*b0d29bc4SBrooks Davis } 159*b0d29bc4SBrooks Davis 160*b0d29bc4SBrooks Davis 161*b0d29bc4SBrooks Davis /// Maintenance data held while a test is being executed. 162*b0d29bc4SBrooks Davis /// 163*b0d29bc4SBrooks Davis /// This data structure exists from the moment when a test is executed via 164*b0d29bc4SBrooks Davis /// scheduler::spawn_test() or scheduler::impl::spawn_cleanup() to when it is 165*b0d29bc4SBrooks Davis /// cleaned up with result_handle::cleanup(). 166*b0d29bc4SBrooks Davis /// 167*b0d29bc4SBrooks Davis /// This is a base data type intended to be extended for the test and cleanup 168*b0d29bc4SBrooks Davis /// cases so that each contains only the relevant data. 169*b0d29bc4SBrooks Davis struct exec_data : utils::noncopyable { 170*b0d29bc4SBrooks Davis /// Test program data for this test case. 171*b0d29bc4SBrooks Davis const model::test_program_ptr test_program; 172*b0d29bc4SBrooks Davis 173*b0d29bc4SBrooks Davis /// Name of the test case. 174*b0d29bc4SBrooks Davis const std::string test_case_name; 175*b0d29bc4SBrooks Davis 176*b0d29bc4SBrooks Davis /// Constructor. 177*b0d29bc4SBrooks Davis /// 178*b0d29bc4SBrooks Davis /// \param test_program_ Test program data for this test case. 179*b0d29bc4SBrooks Davis /// \param test_case_name_ Name of the test case. 180*b0d29bc4SBrooks Davis exec_data(const model::test_program_ptr test_program_, 181*b0d29bc4SBrooks Davis const std::string& test_case_name_) : 182*b0d29bc4SBrooks Davis test_program(test_program_), test_case_name(test_case_name_) 183*b0d29bc4SBrooks Davis { 184*b0d29bc4SBrooks Davis } 185*b0d29bc4SBrooks Davis 186*b0d29bc4SBrooks Davis /// Destructor. 187*b0d29bc4SBrooks Davis virtual ~exec_data(void) 188*b0d29bc4SBrooks Davis { 189*b0d29bc4SBrooks Davis } 190*b0d29bc4SBrooks Davis }; 191*b0d29bc4SBrooks Davis 192*b0d29bc4SBrooks Davis 193*b0d29bc4SBrooks Davis /// Maintenance data held while a test is being executed. 194*b0d29bc4SBrooks Davis struct test_exec_data : public exec_data { 195*b0d29bc4SBrooks Davis /// Test program-specific execution interface. 196*b0d29bc4SBrooks Davis const std::shared_ptr< scheduler::interface > interface; 197*b0d29bc4SBrooks Davis 198*b0d29bc4SBrooks Davis /// User configuration passed to the execution of the test. We need this 199*b0d29bc4SBrooks Davis /// here to recover it later when chaining the execution of a cleanup 200*b0d29bc4SBrooks Davis /// routine (if any). 201*b0d29bc4SBrooks Davis const config::tree user_config; 202*b0d29bc4SBrooks Davis 203*b0d29bc4SBrooks Davis /// Whether this test case still needs to have its cleanup routine executed. 204*b0d29bc4SBrooks Davis /// 205*b0d29bc4SBrooks Davis /// This is set externally when the cleanup routine is actually invoked to 206*b0d29bc4SBrooks Davis /// denote that no further attempts shall be made at cleaning this up. 207*b0d29bc4SBrooks Davis bool needs_cleanup; 208*b0d29bc4SBrooks Davis 209*b0d29bc4SBrooks Davis /// The exit_handle for this test once it has completed. 210*b0d29bc4SBrooks Davis /// 211*b0d29bc4SBrooks Davis /// This is set externally when the test case has finished, as we need this 212*b0d29bc4SBrooks Davis /// information to invoke the followup cleanup routine in the right context, 213*b0d29bc4SBrooks Davis /// as indicated by needs_cleanup. 214*b0d29bc4SBrooks Davis optional< executor::exit_handle > exit_handle; 215*b0d29bc4SBrooks Davis 216*b0d29bc4SBrooks Davis /// Constructor. 217*b0d29bc4SBrooks Davis /// 218*b0d29bc4SBrooks Davis /// \param test_program_ Test program data for this test case. 219*b0d29bc4SBrooks Davis /// \param test_case_name_ Name of the test case. 220*b0d29bc4SBrooks Davis /// \param interface_ Test program-specific execution interface. 221*b0d29bc4SBrooks Davis /// \param user_config_ User configuration passed to the test. 222*b0d29bc4SBrooks Davis test_exec_data(const model::test_program_ptr test_program_, 223*b0d29bc4SBrooks Davis const std::string& test_case_name_, 224*b0d29bc4SBrooks Davis const std::shared_ptr< scheduler::interface > interface_, 225*b0d29bc4SBrooks Davis const config::tree& user_config_) : 226*b0d29bc4SBrooks Davis exec_data(test_program_, test_case_name_), 227*b0d29bc4SBrooks Davis interface(interface_), user_config(user_config_) 228*b0d29bc4SBrooks Davis { 229*b0d29bc4SBrooks Davis const model::test_case& test_case = test_program->find(test_case_name); 230*b0d29bc4SBrooks Davis needs_cleanup = test_case.get_metadata().has_cleanup(); 231*b0d29bc4SBrooks Davis } 232*b0d29bc4SBrooks Davis }; 233*b0d29bc4SBrooks Davis 234*b0d29bc4SBrooks Davis 235*b0d29bc4SBrooks Davis /// Maintenance data held while a test cleanup routine is being executed. 236*b0d29bc4SBrooks Davis /// 237*b0d29bc4SBrooks Davis /// Instances of this object are related to a previous test_exec_data, as 238*b0d29bc4SBrooks Davis /// cleanup routines can only exist once the test has been run. 239*b0d29bc4SBrooks Davis struct cleanup_exec_data : public exec_data { 240*b0d29bc4SBrooks Davis /// The exit handle of the test. This is necessary so that we can return 241*b0d29bc4SBrooks Davis /// the correct exit_handle to the user of the scheduler. 242*b0d29bc4SBrooks Davis executor::exit_handle body_exit_handle; 243*b0d29bc4SBrooks Davis 244*b0d29bc4SBrooks Davis /// The final result of the test's body. This is necessary to compute the 245*b0d29bc4SBrooks Davis /// right return value for a test with a cleanup routine: the body result is 246*b0d29bc4SBrooks Davis /// respected if it is a "bad" result; else the result of the cleanup 247*b0d29bc4SBrooks Davis /// routine is used if it has failed. 248*b0d29bc4SBrooks Davis model::test_result body_result; 249*b0d29bc4SBrooks Davis 250*b0d29bc4SBrooks Davis /// Constructor. 251*b0d29bc4SBrooks Davis /// 252*b0d29bc4SBrooks Davis /// \param test_program_ Test program data for this test case. 253*b0d29bc4SBrooks Davis /// \param test_case_name_ Name of the test case. 254*b0d29bc4SBrooks Davis /// \param body_exit_handle_ If not none, exit handle of the body 255*b0d29bc4SBrooks Davis /// corresponding to the cleanup routine represented by this exec_data. 256*b0d29bc4SBrooks Davis /// \param body_result_ If not none, result of the body corresponding to the 257*b0d29bc4SBrooks Davis /// cleanup routine represented by this exec_data. 258*b0d29bc4SBrooks Davis cleanup_exec_data(const model::test_program_ptr test_program_, 259*b0d29bc4SBrooks Davis const std::string& test_case_name_, 260*b0d29bc4SBrooks Davis const executor::exit_handle& body_exit_handle_, 261*b0d29bc4SBrooks Davis const model::test_result& body_result_) : 262*b0d29bc4SBrooks Davis exec_data(test_program_, test_case_name_), 263*b0d29bc4SBrooks Davis body_exit_handle(body_exit_handle_), body_result(body_result_) 264*b0d29bc4SBrooks Davis { 265*b0d29bc4SBrooks Davis } 266*b0d29bc4SBrooks Davis }; 267*b0d29bc4SBrooks Davis 268*b0d29bc4SBrooks Davis 269*b0d29bc4SBrooks Davis /// Shared pointer to exec_data. 270*b0d29bc4SBrooks Davis /// 271*b0d29bc4SBrooks Davis /// We require this because we want exec_data to not be copyable, and thus we 272*b0d29bc4SBrooks Davis /// cannot just store it in the map without move constructors. 273*b0d29bc4SBrooks Davis typedef std::shared_ptr< exec_data > exec_data_ptr; 274*b0d29bc4SBrooks Davis 275*b0d29bc4SBrooks Davis 276*b0d29bc4SBrooks Davis /// Mapping of active PIDs to their maintenance data. 277*b0d29bc4SBrooks Davis typedef std::map< int, exec_data_ptr > exec_data_map; 278*b0d29bc4SBrooks Davis 279*b0d29bc4SBrooks Davis 280*b0d29bc4SBrooks Davis /// Enforces a test program to hold an absolute path. 281*b0d29bc4SBrooks Davis /// 282*b0d29bc4SBrooks Davis /// TODO(jmmv): This function (which is a pretty ugly hack) exists because we 283*b0d29bc4SBrooks Davis /// want the interface hooks to receive a test_program as their argument. 284*b0d29bc4SBrooks Davis /// However, those hooks run after the test program has been isolated, which 285*b0d29bc4SBrooks Davis /// means that the current directory has changed since when the test_program 286*b0d29bc4SBrooks Davis /// objects were created. This causes the absolute_path() method of 287*b0d29bc4SBrooks Davis /// test_program to return bogus values if the internal representation of their 288*b0d29bc4SBrooks Davis /// path is relative. We should fix somehow: maybe making the fs module grab 289*b0d29bc4SBrooks Davis /// its "current_path" view at program startup time; or maybe by grabbing the 290*b0d29bc4SBrooks Davis /// current path at test_program creation time; or maybe something else. 291*b0d29bc4SBrooks Davis /// 292*b0d29bc4SBrooks Davis /// \param program The test program to modify. 293*b0d29bc4SBrooks Davis /// 294*b0d29bc4SBrooks Davis /// \return A new test program whose internal paths are absolute. 295*b0d29bc4SBrooks Davis static model::test_program 296*b0d29bc4SBrooks Davis force_absolute_paths(const model::test_program program) 297*b0d29bc4SBrooks Davis { 298*b0d29bc4SBrooks Davis const std::string& relative = program.relative_path().str(); 299*b0d29bc4SBrooks Davis const std::string absolute = program.absolute_path().str(); 300*b0d29bc4SBrooks Davis 301*b0d29bc4SBrooks Davis const std::string root = absolute.substr( 302*b0d29bc4SBrooks Davis 0, absolute.length() - relative.length()); 303*b0d29bc4SBrooks Davis 304*b0d29bc4SBrooks Davis return model::test_program( 305*b0d29bc4SBrooks Davis program.interface_name(), 306*b0d29bc4SBrooks Davis program.relative_path(), fs::path(root), 307*b0d29bc4SBrooks Davis program.test_suite_name(), 308*b0d29bc4SBrooks Davis program.get_metadata(), program.test_cases()); 309*b0d29bc4SBrooks Davis } 310*b0d29bc4SBrooks Davis 311*b0d29bc4SBrooks Davis 312*b0d29bc4SBrooks Davis /// Functor to list the test cases of a test program. 313*b0d29bc4SBrooks Davis class list_test_cases { 314*b0d29bc4SBrooks Davis /// Interface of the test program to execute. 315*b0d29bc4SBrooks Davis std::shared_ptr< scheduler::interface > _interface; 316*b0d29bc4SBrooks Davis 317*b0d29bc4SBrooks Davis /// Test program to execute. 318*b0d29bc4SBrooks Davis const model::test_program _test_program; 319*b0d29bc4SBrooks Davis 320*b0d29bc4SBrooks Davis /// User-provided configuration variables. 321*b0d29bc4SBrooks Davis const config::tree& _user_config; 322*b0d29bc4SBrooks Davis 323*b0d29bc4SBrooks Davis public: 324*b0d29bc4SBrooks Davis /// Constructor. 325*b0d29bc4SBrooks Davis /// 326*b0d29bc4SBrooks Davis /// \param interface Interface of the test program to execute. 327*b0d29bc4SBrooks Davis /// \param test_program Test program to execute. 328*b0d29bc4SBrooks Davis /// \param user_config User-provided configuration variables. 329*b0d29bc4SBrooks Davis list_test_cases( 330*b0d29bc4SBrooks Davis const std::shared_ptr< scheduler::interface > interface, 331*b0d29bc4SBrooks Davis const model::test_program* test_program, 332*b0d29bc4SBrooks Davis const config::tree& user_config) : 333*b0d29bc4SBrooks Davis _interface(interface), 334*b0d29bc4SBrooks Davis _test_program(force_absolute_paths(*test_program)), 335*b0d29bc4SBrooks Davis _user_config(user_config) 336*b0d29bc4SBrooks Davis { 337*b0d29bc4SBrooks Davis } 338*b0d29bc4SBrooks Davis 339*b0d29bc4SBrooks Davis /// Body of the subprocess. 340*b0d29bc4SBrooks Davis void 341*b0d29bc4SBrooks Davis operator()(const fs::path& /* control_directory */) 342*b0d29bc4SBrooks Davis { 343*b0d29bc4SBrooks Davis const config::properties_map vars = scheduler::generate_config( 344*b0d29bc4SBrooks Davis _user_config, _test_program.test_suite_name()); 345*b0d29bc4SBrooks Davis _interface->exec_list(_test_program, vars); 346*b0d29bc4SBrooks Davis } 347*b0d29bc4SBrooks Davis }; 348*b0d29bc4SBrooks Davis 349*b0d29bc4SBrooks Davis 350*b0d29bc4SBrooks Davis /// Functor to execute a test program in a child process. 351*b0d29bc4SBrooks Davis class run_test_program { 352*b0d29bc4SBrooks Davis /// Interface of the test program to execute. 353*b0d29bc4SBrooks Davis std::shared_ptr< scheduler::interface > _interface; 354*b0d29bc4SBrooks Davis 355*b0d29bc4SBrooks Davis /// Test program to execute. 356*b0d29bc4SBrooks Davis const model::test_program _test_program; 357*b0d29bc4SBrooks Davis 358*b0d29bc4SBrooks Davis /// Name of the test case to execute. 359*b0d29bc4SBrooks Davis const std::string& _test_case_name; 360*b0d29bc4SBrooks Davis 361*b0d29bc4SBrooks Davis /// User-provided configuration variables. 362*b0d29bc4SBrooks Davis const config::tree& _user_config; 363*b0d29bc4SBrooks Davis 364*b0d29bc4SBrooks Davis /// Verifies if the test case needs to be skipped or not. 365*b0d29bc4SBrooks Davis /// 366*b0d29bc4SBrooks Davis /// We could very well run this on the scheduler parent process before 367*b0d29bc4SBrooks Davis /// issuing the fork. However, doing this here in the child process is 368*b0d29bc4SBrooks Davis /// better for two reasons: first, it allows us to continue using the simple 369*b0d29bc4SBrooks Davis /// spawn/wait abstraction of the scheduler; and, second, we parallelize the 370*b0d29bc4SBrooks Davis /// requirements checks among tests. 371*b0d29bc4SBrooks Davis /// 372*b0d29bc4SBrooks Davis /// \post If the test's preconditions are not met, the caller process is 373*b0d29bc4SBrooks Davis /// terminated with a special exit code and a "skipped cookie" is written to 374*b0d29bc4SBrooks Davis /// the disk with the reason for the failure. 375*b0d29bc4SBrooks Davis /// 376*b0d29bc4SBrooks Davis /// \param skipped_cookie_path File to create with the skip reason details 377*b0d29bc4SBrooks Davis /// if this test is skipped. 378*b0d29bc4SBrooks Davis void 379*b0d29bc4SBrooks Davis do_requirements_check(const fs::path& skipped_cookie_path) 380*b0d29bc4SBrooks Davis { 381*b0d29bc4SBrooks Davis const model::test_case& test_case = _test_program.find( 382*b0d29bc4SBrooks Davis _test_case_name); 383*b0d29bc4SBrooks Davis 384*b0d29bc4SBrooks Davis const std::string skip_reason = engine::check_reqs( 385*b0d29bc4SBrooks Davis test_case.get_metadata(), _user_config, 386*b0d29bc4SBrooks Davis _test_program.test_suite_name(), 387*b0d29bc4SBrooks Davis fs::current_path()); 388*b0d29bc4SBrooks Davis if (skip_reason.empty()) 389*b0d29bc4SBrooks Davis return; 390*b0d29bc4SBrooks Davis 391*b0d29bc4SBrooks Davis std::ofstream output(skipped_cookie_path.c_str()); 392*b0d29bc4SBrooks Davis if (!output) { 393*b0d29bc4SBrooks Davis std::perror((F("Failed to open %s for write") % 394*b0d29bc4SBrooks Davis skipped_cookie_path).str().c_str()); 395*b0d29bc4SBrooks Davis std::abort(); 396*b0d29bc4SBrooks Davis } 397*b0d29bc4SBrooks Davis output << skip_reason; 398*b0d29bc4SBrooks Davis output.close(); 399*b0d29bc4SBrooks Davis 400*b0d29bc4SBrooks Davis // Abruptly terminate the process. We don't want to run any destructors 401*b0d29bc4SBrooks Davis // inherited from the parent process by mistake, which could, for 402*b0d29bc4SBrooks Davis // example, delete our own control files! 403*b0d29bc4SBrooks Davis ::_exit(exit_skipped); 404*b0d29bc4SBrooks Davis } 405*b0d29bc4SBrooks Davis 406*b0d29bc4SBrooks Davis public: 407*b0d29bc4SBrooks Davis /// Constructor. 408*b0d29bc4SBrooks Davis /// 409*b0d29bc4SBrooks Davis /// \param interface Interface of the test program to execute. 410*b0d29bc4SBrooks Davis /// \param test_program Test program to execute. 411*b0d29bc4SBrooks Davis /// \param test_case_name Name of the test case to execute. 412*b0d29bc4SBrooks Davis /// \param user_config User-provided configuration variables. 413*b0d29bc4SBrooks Davis run_test_program( 414*b0d29bc4SBrooks Davis const std::shared_ptr< scheduler::interface > interface, 415*b0d29bc4SBrooks Davis const model::test_program_ptr test_program, 416*b0d29bc4SBrooks Davis const std::string& test_case_name, 417*b0d29bc4SBrooks Davis const config::tree& user_config) : 418*b0d29bc4SBrooks Davis _interface(interface), 419*b0d29bc4SBrooks Davis _test_program(force_absolute_paths(*test_program)), 420*b0d29bc4SBrooks Davis _test_case_name(test_case_name), 421*b0d29bc4SBrooks Davis _user_config(user_config) 422*b0d29bc4SBrooks Davis { 423*b0d29bc4SBrooks Davis } 424*b0d29bc4SBrooks Davis 425*b0d29bc4SBrooks Davis /// Body of the subprocess. 426*b0d29bc4SBrooks Davis /// 427*b0d29bc4SBrooks Davis /// \param control_directory The testcase directory where files will be 428*b0d29bc4SBrooks Davis /// read from. 429*b0d29bc4SBrooks Davis void 430*b0d29bc4SBrooks Davis operator()(const fs::path& control_directory) 431*b0d29bc4SBrooks Davis { 432*b0d29bc4SBrooks Davis const model::test_case& test_case = _test_program.find( 433*b0d29bc4SBrooks Davis _test_case_name); 434*b0d29bc4SBrooks Davis if (test_case.fake_result()) 435*b0d29bc4SBrooks Davis ::_exit(EXIT_SUCCESS); 436*b0d29bc4SBrooks Davis 437*b0d29bc4SBrooks Davis do_requirements_check(control_directory / skipped_cookie); 438*b0d29bc4SBrooks Davis 439*b0d29bc4SBrooks Davis const config::properties_map vars = scheduler::generate_config( 440*b0d29bc4SBrooks Davis _user_config, _test_program.test_suite_name()); 441*b0d29bc4SBrooks Davis _interface->exec_test(_test_program, _test_case_name, vars, 442*b0d29bc4SBrooks Davis control_directory); 443*b0d29bc4SBrooks Davis } 444*b0d29bc4SBrooks Davis }; 445*b0d29bc4SBrooks Davis 446*b0d29bc4SBrooks Davis 447*b0d29bc4SBrooks Davis /// Functor to execute a test program in a child process. 448*b0d29bc4SBrooks Davis class run_test_cleanup { 449*b0d29bc4SBrooks Davis /// Interface of the test program to execute. 450*b0d29bc4SBrooks Davis std::shared_ptr< scheduler::interface > _interface; 451*b0d29bc4SBrooks Davis 452*b0d29bc4SBrooks Davis /// Test program to execute. 453*b0d29bc4SBrooks Davis const model::test_program _test_program; 454*b0d29bc4SBrooks Davis 455*b0d29bc4SBrooks Davis /// Name of the test case to execute. 456*b0d29bc4SBrooks Davis const std::string& _test_case_name; 457*b0d29bc4SBrooks Davis 458*b0d29bc4SBrooks Davis /// User-provided configuration variables. 459*b0d29bc4SBrooks Davis const config::tree& _user_config; 460*b0d29bc4SBrooks Davis 461*b0d29bc4SBrooks Davis public: 462*b0d29bc4SBrooks Davis /// Constructor. 463*b0d29bc4SBrooks Davis /// 464*b0d29bc4SBrooks Davis /// \param interface Interface of the test program to execute. 465*b0d29bc4SBrooks Davis /// \param test_program Test program to execute. 466*b0d29bc4SBrooks Davis /// \param test_case_name Name of the test case to execute. 467*b0d29bc4SBrooks Davis /// \param user_config User-provided configuration variables. 468*b0d29bc4SBrooks Davis run_test_cleanup( 469*b0d29bc4SBrooks Davis const std::shared_ptr< scheduler::interface > interface, 470*b0d29bc4SBrooks Davis const model::test_program_ptr test_program, 471*b0d29bc4SBrooks Davis const std::string& test_case_name, 472*b0d29bc4SBrooks Davis const config::tree& user_config) : 473*b0d29bc4SBrooks Davis _interface(interface), 474*b0d29bc4SBrooks Davis _test_program(force_absolute_paths(*test_program)), 475*b0d29bc4SBrooks Davis _test_case_name(test_case_name), 476*b0d29bc4SBrooks Davis _user_config(user_config) 477*b0d29bc4SBrooks Davis { 478*b0d29bc4SBrooks Davis } 479*b0d29bc4SBrooks Davis 480*b0d29bc4SBrooks Davis /// Body of the subprocess. 481*b0d29bc4SBrooks Davis /// 482*b0d29bc4SBrooks Davis /// \param control_directory The testcase directory where cleanup will be 483*b0d29bc4SBrooks Davis /// run from. 484*b0d29bc4SBrooks Davis void 485*b0d29bc4SBrooks Davis operator()(const fs::path& control_directory) 486*b0d29bc4SBrooks Davis { 487*b0d29bc4SBrooks Davis const config::properties_map vars = scheduler::generate_config( 488*b0d29bc4SBrooks Davis _user_config, _test_program.test_suite_name()); 489*b0d29bc4SBrooks Davis _interface->exec_cleanup(_test_program, _test_case_name, vars, 490*b0d29bc4SBrooks Davis control_directory); 491*b0d29bc4SBrooks Davis } 492*b0d29bc4SBrooks Davis }; 493*b0d29bc4SBrooks Davis 494*b0d29bc4SBrooks Davis 495*b0d29bc4SBrooks Davis /// Obtains the right scheduler interface for a given test program. 496*b0d29bc4SBrooks Davis /// 497*b0d29bc4SBrooks Davis /// \param name The name of the interface of the test program. 498*b0d29bc4SBrooks Davis /// 499*b0d29bc4SBrooks Davis /// \return An scheduler interface. 500*b0d29bc4SBrooks Davis std::shared_ptr< scheduler::interface > 501*b0d29bc4SBrooks Davis find_interface(const std::string& name) 502*b0d29bc4SBrooks Davis { 503*b0d29bc4SBrooks Davis const interfaces_map::const_iterator iter = interfaces.find(name); 504*b0d29bc4SBrooks Davis PRE(interfaces.find(name) != interfaces.end()); 505*b0d29bc4SBrooks Davis return (*iter).second; 506*b0d29bc4SBrooks Davis } 507*b0d29bc4SBrooks Davis 508*b0d29bc4SBrooks Davis 509*b0d29bc4SBrooks Davis } // anonymous namespace 510*b0d29bc4SBrooks Davis 511*b0d29bc4SBrooks Davis 512*b0d29bc4SBrooks Davis void 513*b0d29bc4SBrooks Davis scheduler::interface::exec_cleanup( 514*b0d29bc4SBrooks Davis const model::test_program& /* test_program */, 515*b0d29bc4SBrooks Davis const std::string& /* test_case_name */, 516*b0d29bc4SBrooks Davis const config::properties_map& /* vars */, 517*b0d29bc4SBrooks Davis const utils::fs::path& /* control_directory */) const 518*b0d29bc4SBrooks Davis { 519*b0d29bc4SBrooks Davis // Most test interfaces do not support standalone cleanup routines so 520*b0d29bc4SBrooks Davis // provide a default implementation that does nothing. 521*b0d29bc4SBrooks Davis UNREACHABLE_MSG("exec_cleanup not implemented for an interface that " 522*b0d29bc4SBrooks Davis "supports standalone cleanup routines"); 523*b0d29bc4SBrooks Davis } 524*b0d29bc4SBrooks Davis 525*b0d29bc4SBrooks Davis 526*b0d29bc4SBrooks Davis /// Internal implementation of a lazy_test_program. 527*b0d29bc4SBrooks Davis struct engine::scheduler::lazy_test_program::impl : utils::noncopyable { 528*b0d29bc4SBrooks Davis /// Whether the test cases list has been yet loaded or not. 529*b0d29bc4SBrooks Davis bool _loaded; 530*b0d29bc4SBrooks Davis 531*b0d29bc4SBrooks Davis /// User configuration to pass to the test program list operation. 532*b0d29bc4SBrooks Davis config::tree _user_config; 533*b0d29bc4SBrooks Davis 534*b0d29bc4SBrooks Davis /// Scheduler context to use to load test cases. 535*b0d29bc4SBrooks Davis scheduler::scheduler_handle& _scheduler_handle; 536*b0d29bc4SBrooks Davis 537*b0d29bc4SBrooks Davis /// Constructor. 538*b0d29bc4SBrooks Davis /// 539*b0d29bc4SBrooks Davis /// \param user_config_ User configuration to pass to the test program list 540*b0d29bc4SBrooks Davis /// operation. 541*b0d29bc4SBrooks Davis /// \param scheduler_handle_ Scheduler context to use when loading test 542*b0d29bc4SBrooks Davis /// cases. 543*b0d29bc4SBrooks Davis impl(const config::tree& user_config_, 544*b0d29bc4SBrooks Davis scheduler::scheduler_handle& scheduler_handle_) : 545*b0d29bc4SBrooks Davis _loaded(false), _user_config(user_config_), 546*b0d29bc4SBrooks Davis _scheduler_handle(scheduler_handle_) 547*b0d29bc4SBrooks Davis { 548*b0d29bc4SBrooks Davis } 549*b0d29bc4SBrooks Davis }; 550*b0d29bc4SBrooks Davis 551*b0d29bc4SBrooks Davis 552*b0d29bc4SBrooks Davis /// Constructs a new test program. 553*b0d29bc4SBrooks Davis /// 554*b0d29bc4SBrooks Davis /// \param interface_name_ Name of the test program interface. 555*b0d29bc4SBrooks Davis /// \param binary_ The name of the test program binary relative to root_. 556*b0d29bc4SBrooks Davis /// \param root_ The root of the test suite containing the test program. 557*b0d29bc4SBrooks Davis /// \param test_suite_name_ The name of the test suite this program belongs to. 558*b0d29bc4SBrooks Davis /// \param md_ Metadata of the test program. 559*b0d29bc4SBrooks Davis /// \param user_config_ User configuration to pass to the scheduler. 560*b0d29bc4SBrooks Davis /// \param scheduler_handle_ Scheduler context to use to load test cases. 561*b0d29bc4SBrooks Davis scheduler::lazy_test_program::lazy_test_program( 562*b0d29bc4SBrooks Davis const std::string& interface_name_, 563*b0d29bc4SBrooks Davis const fs::path& binary_, 564*b0d29bc4SBrooks Davis const fs::path& root_, 565*b0d29bc4SBrooks Davis const std::string& test_suite_name_, 566*b0d29bc4SBrooks Davis const model::metadata& md_, 567*b0d29bc4SBrooks Davis const config::tree& user_config_, 568*b0d29bc4SBrooks Davis scheduler::scheduler_handle& scheduler_handle_) : 569*b0d29bc4SBrooks Davis test_program(interface_name_, binary_, root_, test_suite_name_, md_, 570*b0d29bc4SBrooks Davis model::test_cases_map()), 571*b0d29bc4SBrooks Davis _pimpl(new impl(user_config_, scheduler_handle_)) 572*b0d29bc4SBrooks Davis { 573*b0d29bc4SBrooks Davis } 574*b0d29bc4SBrooks Davis 575*b0d29bc4SBrooks Davis 576*b0d29bc4SBrooks Davis /// Gets or loads the list of test cases from the test program. 577*b0d29bc4SBrooks Davis /// 578*b0d29bc4SBrooks Davis /// \return The list of test cases provided by the test program. 579*b0d29bc4SBrooks Davis const model::test_cases_map& 580*b0d29bc4SBrooks Davis scheduler::lazy_test_program::test_cases(void) const 581*b0d29bc4SBrooks Davis { 582*b0d29bc4SBrooks Davis _pimpl->_scheduler_handle.check_interrupt(); 583*b0d29bc4SBrooks Davis 584*b0d29bc4SBrooks Davis if (!_pimpl->_loaded) { 585*b0d29bc4SBrooks Davis const model::test_cases_map tcs = _pimpl->_scheduler_handle.list_tests( 586*b0d29bc4SBrooks Davis this, _pimpl->_user_config); 587*b0d29bc4SBrooks Davis 588*b0d29bc4SBrooks Davis // Due to the restrictions on when set_test_cases() may be called (as a 589*b0d29bc4SBrooks Davis // way to lazily initialize the test cases list before it is ever 590*b0d29bc4SBrooks Davis // returned), this cast is valid. 591*b0d29bc4SBrooks Davis const_cast< scheduler::lazy_test_program* >(this)->set_test_cases(tcs); 592*b0d29bc4SBrooks Davis 593*b0d29bc4SBrooks Davis _pimpl->_loaded = true; 594*b0d29bc4SBrooks Davis 595*b0d29bc4SBrooks Davis _pimpl->_scheduler_handle.check_interrupt(); 596*b0d29bc4SBrooks Davis } 597*b0d29bc4SBrooks Davis 598*b0d29bc4SBrooks Davis INV(_pimpl->_loaded); 599*b0d29bc4SBrooks Davis return test_program::test_cases(); 600*b0d29bc4SBrooks Davis } 601*b0d29bc4SBrooks Davis 602*b0d29bc4SBrooks Davis 603*b0d29bc4SBrooks Davis /// Internal implementation for the result_handle class. 604*b0d29bc4SBrooks Davis struct engine::scheduler::result_handle::bimpl : utils::noncopyable { 605*b0d29bc4SBrooks Davis /// Generic executor exit handle for this result handle. 606*b0d29bc4SBrooks Davis executor::exit_handle generic; 607*b0d29bc4SBrooks Davis 608*b0d29bc4SBrooks Davis /// Mutable pointer to the corresponding scheduler state. 609*b0d29bc4SBrooks Davis /// 610*b0d29bc4SBrooks Davis /// This object references a member of the scheduler_handle that yielded 611*b0d29bc4SBrooks Davis /// this result_handle instance. We need this direct access to clean up 612*b0d29bc4SBrooks Davis /// after ourselves when the result is destroyed. 613*b0d29bc4SBrooks Davis exec_data_map& all_exec_data; 614*b0d29bc4SBrooks Davis 615*b0d29bc4SBrooks Davis /// Constructor. 616*b0d29bc4SBrooks Davis /// 617*b0d29bc4SBrooks Davis /// \param generic_ Generic executor exit handle for this result handle. 618*b0d29bc4SBrooks Davis /// \param [in,out] all_exec_data_ Global object keeping track of all active 619*b0d29bc4SBrooks Davis /// executions for an scheduler. This is a pointer to a member of the 620*b0d29bc4SBrooks Davis /// scheduler_handle object. 621*b0d29bc4SBrooks Davis bimpl(const executor::exit_handle generic_, exec_data_map& all_exec_data_) : 622*b0d29bc4SBrooks Davis generic(generic_), all_exec_data(all_exec_data_) 623*b0d29bc4SBrooks Davis { 624*b0d29bc4SBrooks Davis } 625*b0d29bc4SBrooks Davis 626*b0d29bc4SBrooks Davis /// Destructor. 627*b0d29bc4SBrooks Davis ~bimpl(void) 628*b0d29bc4SBrooks Davis { 629*b0d29bc4SBrooks Davis LD(F("Removing %s from all_exec_data") % generic.original_pid()); 630*b0d29bc4SBrooks Davis all_exec_data.erase(generic.original_pid()); 631*b0d29bc4SBrooks Davis } 632*b0d29bc4SBrooks Davis }; 633*b0d29bc4SBrooks Davis 634*b0d29bc4SBrooks Davis 635*b0d29bc4SBrooks Davis /// Constructor. 636*b0d29bc4SBrooks Davis /// 637*b0d29bc4SBrooks Davis /// \param pbimpl Constructed internal implementation. 638*b0d29bc4SBrooks Davis scheduler::result_handle::result_handle(std::shared_ptr< bimpl > pbimpl) : 639*b0d29bc4SBrooks Davis _pbimpl(pbimpl) 640*b0d29bc4SBrooks Davis { 641*b0d29bc4SBrooks Davis } 642*b0d29bc4SBrooks Davis 643*b0d29bc4SBrooks Davis 644*b0d29bc4SBrooks Davis /// Destructor. 645*b0d29bc4SBrooks Davis scheduler::result_handle::~result_handle(void) 646*b0d29bc4SBrooks Davis { 647*b0d29bc4SBrooks Davis } 648*b0d29bc4SBrooks Davis 649*b0d29bc4SBrooks Davis 650*b0d29bc4SBrooks Davis /// Cleans up the test case results. 651*b0d29bc4SBrooks Davis /// 652*b0d29bc4SBrooks Davis /// This function should be called explicitly as it provides the means to 653*b0d29bc4SBrooks Davis /// control any exceptions raised during cleanup. Do not rely on the destructor 654*b0d29bc4SBrooks Davis /// to clean things up. 655*b0d29bc4SBrooks Davis /// 656*b0d29bc4SBrooks Davis /// \throw engine::error If the cleanup fails, especially due to the inability 657*b0d29bc4SBrooks Davis /// to remove the work directory. 658*b0d29bc4SBrooks Davis void 659*b0d29bc4SBrooks Davis scheduler::result_handle::cleanup(void) 660*b0d29bc4SBrooks Davis { 661*b0d29bc4SBrooks Davis _pbimpl->generic.cleanup(); 662*b0d29bc4SBrooks Davis } 663*b0d29bc4SBrooks Davis 664*b0d29bc4SBrooks Davis 665*b0d29bc4SBrooks Davis /// Returns the original PID corresponding to this result. 666*b0d29bc4SBrooks Davis /// 667*b0d29bc4SBrooks Davis /// \return An exec_handle. 668*b0d29bc4SBrooks Davis int 669*b0d29bc4SBrooks Davis scheduler::result_handle::original_pid(void) const 670*b0d29bc4SBrooks Davis { 671*b0d29bc4SBrooks Davis return _pbimpl->generic.original_pid(); 672*b0d29bc4SBrooks Davis } 673*b0d29bc4SBrooks Davis 674*b0d29bc4SBrooks Davis 675*b0d29bc4SBrooks Davis /// Returns the timestamp of when spawn_test was called. 676*b0d29bc4SBrooks Davis /// 677*b0d29bc4SBrooks Davis /// \return A timestamp. 678*b0d29bc4SBrooks Davis const datetime::timestamp& 679*b0d29bc4SBrooks Davis scheduler::result_handle::start_time(void) const 680*b0d29bc4SBrooks Davis { 681*b0d29bc4SBrooks Davis return _pbimpl->generic.start_time(); 682*b0d29bc4SBrooks Davis } 683*b0d29bc4SBrooks Davis 684*b0d29bc4SBrooks Davis 685*b0d29bc4SBrooks Davis /// Returns the timestamp of when wait_any_test returned this object. 686*b0d29bc4SBrooks Davis /// 687*b0d29bc4SBrooks Davis /// \return A timestamp. 688*b0d29bc4SBrooks Davis const datetime::timestamp& 689*b0d29bc4SBrooks Davis scheduler::result_handle::end_time(void) const 690*b0d29bc4SBrooks Davis { 691*b0d29bc4SBrooks Davis return _pbimpl->generic.end_time(); 692*b0d29bc4SBrooks Davis } 693*b0d29bc4SBrooks Davis 694*b0d29bc4SBrooks Davis 695*b0d29bc4SBrooks Davis /// Returns the path to the test-specific work directory. 696*b0d29bc4SBrooks Davis /// 697*b0d29bc4SBrooks Davis /// This is guaranteed to be clear of files created by the scheduler. 698*b0d29bc4SBrooks Davis /// 699*b0d29bc4SBrooks Davis /// \return The path to a directory that exists until cleanup() is called. 700*b0d29bc4SBrooks Davis fs::path 701*b0d29bc4SBrooks Davis scheduler::result_handle::work_directory(void) const 702*b0d29bc4SBrooks Davis { 703*b0d29bc4SBrooks Davis return _pbimpl->generic.work_directory(); 704*b0d29bc4SBrooks Davis } 705*b0d29bc4SBrooks Davis 706*b0d29bc4SBrooks Davis 707*b0d29bc4SBrooks Davis /// Returns the path to the test's stdout file. 708*b0d29bc4SBrooks Davis /// 709*b0d29bc4SBrooks Davis /// \return The path to a file that exists until cleanup() is called. 710*b0d29bc4SBrooks Davis const fs::path& 711*b0d29bc4SBrooks Davis scheduler::result_handle::stdout_file(void) const 712*b0d29bc4SBrooks Davis { 713*b0d29bc4SBrooks Davis return _pbimpl->generic.stdout_file(); 714*b0d29bc4SBrooks Davis } 715*b0d29bc4SBrooks Davis 716*b0d29bc4SBrooks Davis 717*b0d29bc4SBrooks Davis /// Returns the path to the test's stderr file. 718*b0d29bc4SBrooks Davis /// 719*b0d29bc4SBrooks Davis /// \return The path to a file that exists until cleanup() is called. 720*b0d29bc4SBrooks Davis const fs::path& 721*b0d29bc4SBrooks Davis scheduler::result_handle::stderr_file(void) const 722*b0d29bc4SBrooks Davis { 723*b0d29bc4SBrooks Davis return _pbimpl->generic.stderr_file(); 724*b0d29bc4SBrooks Davis } 725*b0d29bc4SBrooks Davis 726*b0d29bc4SBrooks Davis 727*b0d29bc4SBrooks Davis /// Internal implementation for the test_result_handle class. 728*b0d29bc4SBrooks Davis struct engine::scheduler::test_result_handle::impl : utils::noncopyable { 729*b0d29bc4SBrooks Davis /// Test program data for this test case. 730*b0d29bc4SBrooks Davis model::test_program_ptr test_program; 731*b0d29bc4SBrooks Davis 732*b0d29bc4SBrooks Davis /// Name of the test case. 733*b0d29bc4SBrooks Davis std::string test_case_name; 734*b0d29bc4SBrooks Davis 735*b0d29bc4SBrooks Davis /// The actual result of the test execution. 736*b0d29bc4SBrooks Davis const model::test_result test_result; 737*b0d29bc4SBrooks Davis 738*b0d29bc4SBrooks Davis /// Constructor. 739*b0d29bc4SBrooks Davis /// 740*b0d29bc4SBrooks Davis /// \param test_program_ Test program data for this test case. 741*b0d29bc4SBrooks Davis /// \param test_case_name_ Name of the test case. 742*b0d29bc4SBrooks Davis /// \param test_result_ The actual result of the test execution. 743*b0d29bc4SBrooks Davis impl(const model::test_program_ptr test_program_, 744*b0d29bc4SBrooks Davis const std::string& test_case_name_, 745*b0d29bc4SBrooks Davis const model::test_result& test_result_) : 746*b0d29bc4SBrooks Davis test_program(test_program_), 747*b0d29bc4SBrooks Davis test_case_name(test_case_name_), 748*b0d29bc4SBrooks Davis test_result(test_result_) 749*b0d29bc4SBrooks Davis { 750*b0d29bc4SBrooks Davis } 751*b0d29bc4SBrooks Davis }; 752*b0d29bc4SBrooks Davis 753*b0d29bc4SBrooks Davis 754*b0d29bc4SBrooks Davis /// Constructor. 755*b0d29bc4SBrooks Davis /// 756*b0d29bc4SBrooks Davis /// \param pbimpl Constructed internal implementation for the base object. 757*b0d29bc4SBrooks Davis /// \param pimpl Constructed internal implementation. 758*b0d29bc4SBrooks Davis scheduler::test_result_handle::test_result_handle( 759*b0d29bc4SBrooks Davis std::shared_ptr< bimpl > pbimpl, std::shared_ptr< impl > pimpl) : 760*b0d29bc4SBrooks Davis result_handle(pbimpl), _pimpl(pimpl) 761*b0d29bc4SBrooks Davis { 762*b0d29bc4SBrooks Davis } 763*b0d29bc4SBrooks Davis 764*b0d29bc4SBrooks Davis 765*b0d29bc4SBrooks Davis /// Destructor. 766*b0d29bc4SBrooks Davis scheduler::test_result_handle::~test_result_handle(void) 767*b0d29bc4SBrooks Davis { 768*b0d29bc4SBrooks Davis } 769*b0d29bc4SBrooks Davis 770*b0d29bc4SBrooks Davis 771*b0d29bc4SBrooks Davis /// Returns the test program that yielded this result. 772*b0d29bc4SBrooks Davis /// 773*b0d29bc4SBrooks Davis /// \return A test program. 774*b0d29bc4SBrooks Davis const model::test_program_ptr 775*b0d29bc4SBrooks Davis scheduler::test_result_handle::test_program(void) const 776*b0d29bc4SBrooks Davis { 777*b0d29bc4SBrooks Davis return _pimpl->test_program; 778*b0d29bc4SBrooks Davis } 779*b0d29bc4SBrooks Davis 780*b0d29bc4SBrooks Davis 781*b0d29bc4SBrooks Davis /// Returns the name of the test case that yielded this result. 782*b0d29bc4SBrooks Davis /// 783*b0d29bc4SBrooks Davis /// \return A test case name 784*b0d29bc4SBrooks Davis const std::string& 785*b0d29bc4SBrooks Davis scheduler::test_result_handle::test_case_name(void) const 786*b0d29bc4SBrooks Davis { 787*b0d29bc4SBrooks Davis return _pimpl->test_case_name; 788*b0d29bc4SBrooks Davis } 789*b0d29bc4SBrooks Davis 790*b0d29bc4SBrooks Davis 791*b0d29bc4SBrooks Davis /// Returns the actual result of the test execution. 792*b0d29bc4SBrooks Davis /// 793*b0d29bc4SBrooks Davis /// \return A test result. 794*b0d29bc4SBrooks Davis const model::test_result& 795*b0d29bc4SBrooks Davis scheduler::test_result_handle::test_result(void) const 796*b0d29bc4SBrooks Davis { 797*b0d29bc4SBrooks Davis return _pimpl->test_result; 798*b0d29bc4SBrooks Davis } 799*b0d29bc4SBrooks Davis 800*b0d29bc4SBrooks Davis 801*b0d29bc4SBrooks Davis /// Internal implementation for the scheduler_handle. 802*b0d29bc4SBrooks Davis struct engine::scheduler::scheduler_handle::impl : utils::noncopyable { 803*b0d29bc4SBrooks Davis /// Generic executor instance encapsulated by this one. 804*b0d29bc4SBrooks Davis executor::executor_handle generic; 805*b0d29bc4SBrooks Davis 806*b0d29bc4SBrooks Davis /// Mapping of exec handles to the data required at run time. 807*b0d29bc4SBrooks Davis exec_data_map all_exec_data; 808*b0d29bc4SBrooks Davis 809*b0d29bc4SBrooks Davis /// Collection of test_exec_data objects. 810*b0d29bc4SBrooks Davis typedef std::vector< const test_exec_data* > test_exec_data_vector; 811*b0d29bc4SBrooks Davis 812*b0d29bc4SBrooks Davis /// Constructor. 813*b0d29bc4SBrooks Davis impl(void) : generic(executor::setup()) 814*b0d29bc4SBrooks Davis { 815*b0d29bc4SBrooks Davis } 816*b0d29bc4SBrooks Davis 817*b0d29bc4SBrooks Davis /// Destructor. 818*b0d29bc4SBrooks Davis /// 819*b0d29bc4SBrooks Davis /// This runs any pending cleanup routines, which should only happen if the 820*b0d29bc4SBrooks Davis /// scheduler is abruptly terminated (aka if a signal is received). 821*b0d29bc4SBrooks Davis ~impl(void) 822*b0d29bc4SBrooks Davis { 823*b0d29bc4SBrooks Davis const test_exec_data_vector tests_data = tests_needing_cleanup(); 824*b0d29bc4SBrooks Davis 825*b0d29bc4SBrooks Davis for (test_exec_data_vector::const_iterator iter = tests_data.begin(); 826*b0d29bc4SBrooks Davis iter != tests_data.end(); ++iter) { 827*b0d29bc4SBrooks Davis const test_exec_data* test_data = *iter; 828*b0d29bc4SBrooks Davis 829*b0d29bc4SBrooks Davis try { 830*b0d29bc4SBrooks Davis sync_cleanup(test_data); 831*b0d29bc4SBrooks Davis } catch (const std::runtime_error& e) { 832*b0d29bc4SBrooks Davis LW(F("Failed to run cleanup routine for %s:%s on abrupt " 833*b0d29bc4SBrooks Davis "termination") 834*b0d29bc4SBrooks Davis % test_data->test_program->relative_path() 835*b0d29bc4SBrooks Davis % test_data->test_case_name); 836*b0d29bc4SBrooks Davis } 837*b0d29bc4SBrooks Davis } 838*b0d29bc4SBrooks Davis } 839*b0d29bc4SBrooks Davis 840*b0d29bc4SBrooks Davis /// Finds any pending exec_datas that correspond to tests needing cleanup. 841*b0d29bc4SBrooks Davis /// 842*b0d29bc4SBrooks Davis /// \return The collection of test_exec_data objects that have their 843*b0d29bc4SBrooks Davis /// needs_cleanup property set to true. 844*b0d29bc4SBrooks Davis test_exec_data_vector 845*b0d29bc4SBrooks Davis tests_needing_cleanup(void) 846*b0d29bc4SBrooks Davis { 847*b0d29bc4SBrooks Davis test_exec_data_vector tests_data; 848*b0d29bc4SBrooks Davis 849*b0d29bc4SBrooks Davis for (exec_data_map::const_iterator iter = all_exec_data.begin(); 850*b0d29bc4SBrooks Davis iter != all_exec_data.end(); ++iter) { 851*b0d29bc4SBrooks Davis const exec_data_ptr data = (*iter).second; 852*b0d29bc4SBrooks Davis 853*b0d29bc4SBrooks Davis try { 854*b0d29bc4SBrooks Davis test_exec_data* test_data = &dynamic_cast< test_exec_data& >( 855*b0d29bc4SBrooks Davis *data.get()); 856*b0d29bc4SBrooks Davis if (test_data->needs_cleanup) { 857*b0d29bc4SBrooks Davis tests_data.push_back(test_data); 858*b0d29bc4SBrooks Davis test_data->needs_cleanup = false; 859*b0d29bc4SBrooks Davis } 860*b0d29bc4SBrooks Davis } catch (const std::bad_cast& e) { 861*b0d29bc4SBrooks Davis // Do nothing for cleanup_exec_data objects. 862*b0d29bc4SBrooks Davis } 863*b0d29bc4SBrooks Davis } 864*b0d29bc4SBrooks Davis 865*b0d29bc4SBrooks Davis return tests_data; 866*b0d29bc4SBrooks Davis } 867*b0d29bc4SBrooks Davis 868*b0d29bc4SBrooks Davis /// Cleans up a single test case synchronously. 869*b0d29bc4SBrooks Davis /// 870*b0d29bc4SBrooks Davis /// \param test_data The data of the previously executed test case to be 871*b0d29bc4SBrooks Davis /// cleaned up. 872*b0d29bc4SBrooks Davis void 873*b0d29bc4SBrooks Davis sync_cleanup(const test_exec_data* test_data) 874*b0d29bc4SBrooks Davis { 875*b0d29bc4SBrooks Davis // The message in this result should never be seen by the user, but use 876*b0d29bc4SBrooks Davis // something reasonable just in case it leaks and we need to pinpoint 877*b0d29bc4SBrooks Davis // the call site. 878*b0d29bc4SBrooks Davis model::test_result result(model::test_result_broken, 879*b0d29bc4SBrooks Davis "Test case died abruptly"); 880*b0d29bc4SBrooks Davis 881*b0d29bc4SBrooks Davis const executor::exec_handle cleanup_handle = spawn_cleanup( 882*b0d29bc4SBrooks Davis test_data->test_program, test_data->test_case_name, 883*b0d29bc4SBrooks Davis test_data->user_config, test_data->exit_handle.get(), 884*b0d29bc4SBrooks Davis result); 885*b0d29bc4SBrooks Davis generic.wait(cleanup_handle); 886*b0d29bc4SBrooks Davis } 887*b0d29bc4SBrooks Davis 888*b0d29bc4SBrooks Davis /// Forks and executes a test case cleanup routine asynchronously. 889*b0d29bc4SBrooks Davis /// 890*b0d29bc4SBrooks Davis /// \param test_program The container test program. 891*b0d29bc4SBrooks Davis /// \param test_case_name The name of the test case to run. 892*b0d29bc4SBrooks Davis /// \param user_config User-provided configuration variables. 893*b0d29bc4SBrooks Davis /// \param body_handle The exit handle of the test case's corresponding 894*b0d29bc4SBrooks Davis /// body. The cleanup will be executed in the same context. 895*b0d29bc4SBrooks Davis /// \param body_result The result of the test case's corresponding body. 896*b0d29bc4SBrooks Davis /// 897*b0d29bc4SBrooks Davis /// \return A handle for the background operation. Used to match the result 898*b0d29bc4SBrooks Davis /// of the execution returned by wait_any() with this invocation. 899*b0d29bc4SBrooks Davis executor::exec_handle 900*b0d29bc4SBrooks Davis spawn_cleanup(const model::test_program_ptr test_program, 901*b0d29bc4SBrooks Davis const std::string& test_case_name, 902*b0d29bc4SBrooks Davis const config::tree& user_config, 903*b0d29bc4SBrooks Davis const executor::exit_handle& body_handle, 904*b0d29bc4SBrooks Davis const model::test_result& body_result) 905*b0d29bc4SBrooks Davis { 906*b0d29bc4SBrooks Davis generic.check_interrupt(); 907*b0d29bc4SBrooks Davis 908*b0d29bc4SBrooks Davis const std::shared_ptr< scheduler::interface > interface = 909*b0d29bc4SBrooks Davis find_interface(test_program->interface_name()); 910*b0d29bc4SBrooks Davis 911*b0d29bc4SBrooks Davis LI(F("Spawning %s:%s (cleanup)") % test_program->absolute_path() % 912*b0d29bc4SBrooks Davis test_case_name); 913*b0d29bc4SBrooks Davis 914*b0d29bc4SBrooks Davis const executor::exec_handle handle = generic.spawn_followup( 915*b0d29bc4SBrooks Davis run_test_cleanup(interface, test_program, test_case_name, 916*b0d29bc4SBrooks Davis user_config), 917*b0d29bc4SBrooks Davis body_handle, cleanup_timeout); 918*b0d29bc4SBrooks Davis 919*b0d29bc4SBrooks Davis const exec_data_ptr data(new cleanup_exec_data( 920*b0d29bc4SBrooks Davis test_program, test_case_name, body_handle, body_result)); 921*b0d29bc4SBrooks Davis LD(F("Inserting %s into all_exec_data (cleanup)") % handle.pid()); 922*b0d29bc4SBrooks Davis INV_MSG(all_exec_data.find(handle.pid()) == all_exec_data.end(), 923*b0d29bc4SBrooks Davis F("PID %s already in all_exec_data; not properly cleaned " 924*b0d29bc4SBrooks Davis "up or reused too fast") % handle.pid());; 925*b0d29bc4SBrooks Davis all_exec_data.insert(exec_data_map::value_type(handle.pid(), data)); 926*b0d29bc4SBrooks Davis 927*b0d29bc4SBrooks Davis return handle; 928*b0d29bc4SBrooks Davis } 929*b0d29bc4SBrooks Davis }; 930*b0d29bc4SBrooks Davis 931*b0d29bc4SBrooks Davis 932*b0d29bc4SBrooks Davis /// Constructor. 933*b0d29bc4SBrooks Davis scheduler::scheduler_handle::scheduler_handle(void) : _pimpl(new impl()) 934*b0d29bc4SBrooks Davis { 935*b0d29bc4SBrooks Davis } 936*b0d29bc4SBrooks Davis 937*b0d29bc4SBrooks Davis 938*b0d29bc4SBrooks Davis /// Destructor. 939*b0d29bc4SBrooks Davis scheduler::scheduler_handle::~scheduler_handle(void) 940*b0d29bc4SBrooks Davis { 941*b0d29bc4SBrooks Davis } 942*b0d29bc4SBrooks Davis 943*b0d29bc4SBrooks Davis 944*b0d29bc4SBrooks Davis /// Queries the path to the root of the work directory for all tests. 945*b0d29bc4SBrooks Davis /// 946*b0d29bc4SBrooks Davis /// \return A path. 947*b0d29bc4SBrooks Davis const fs::path& 948*b0d29bc4SBrooks Davis scheduler::scheduler_handle::root_work_directory(void) const 949*b0d29bc4SBrooks Davis { 950*b0d29bc4SBrooks Davis return _pimpl->generic.root_work_directory(); 951*b0d29bc4SBrooks Davis } 952*b0d29bc4SBrooks Davis 953*b0d29bc4SBrooks Davis 954*b0d29bc4SBrooks Davis /// Cleans up the scheduler state. 955*b0d29bc4SBrooks Davis /// 956*b0d29bc4SBrooks Davis /// This function should be called explicitly as it provides the means to 957*b0d29bc4SBrooks Davis /// control any exceptions raised during cleanup. Do not rely on the destructor 958*b0d29bc4SBrooks Davis /// to clean things up. 959*b0d29bc4SBrooks Davis /// 960*b0d29bc4SBrooks Davis /// \throw engine::error If there are problems cleaning up the scheduler. 961*b0d29bc4SBrooks Davis void 962*b0d29bc4SBrooks Davis scheduler::scheduler_handle::cleanup(void) 963*b0d29bc4SBrooks Davis { 964*b0d29bc4SBrooks Davis _pimpl->generic.cleanup(); 965*b0d29bc4SBrooks Davis } 966*b0d29bc4SBrooks Davis 967*b0d29bc4SBrooks Davis 968*b0d29bc4SBrooks Davis /// Checks if the given interface name is valid. 969*b0d29bc4SBrooks Davis /// 970*b0d29bc4SBrooks Davis /// \param name The name of the interface to validate. 971*b0d29bc4SBrooks Davis /// 972*b0d29bc4SBrooks Davis /// \throw engine::error If the given interface is not supported. 973*b0d29bc4SBrooks Davis void 974*b0d29bc4SBrooks Davis scheduler::ensure_valid_interface(const std::string& name) 975*b0d29bc4SBrooks Davis { 976*b0d29bc4SBrooks Davis if (interfaces.find(name) == interfaces.end()) 977*b0d29bc4SBrooks Davis throw engine::error(F("Unsupported test interface '%s'") % name); 978*b0d29bc4SBrooks Davis } 979*b0d29bc4SBrooks Davis 980*b0d29bc4SBrooks Davis 981*b0d29bc4SBrooks Davis /// Registers a new interface. 982*b0d29bc4SBrooks Davis /// 983*b0d29bc4SBrooks Davis /// \param name The name of the interface. Must not have yet been registered. 984*b0d29bc4SBrooks Davis /// \param spec Interface specification. 985*b0d29bc4SBrooks Davis void 986*b0d29bc4SBrooks Davis scheduler::register_interface(const std::string& name, 987*b0d29bc4SBrooks Davis const std::shared_ptr< interface > spec) 988*b0d29bc4SBrooks Davis { 989*b0d29bc4SBrooks Davis PRE(interfaces.find(name) == interfaces.end()); 990*b0d29bc4SBrooks Davis interfaces.insert(interfaces_map::value_type(name, spec)); 991*b0d29bc4SBrooks Davis } 992*b0d29bc4SBrooks Davis 993*b0d29bc4SBrooks Davis 994*b0d29bc4SBrooks Davis /// Returns the names of all registered interfaces. 995*b0d29bc4SBrooks Davis /// 996*b0d29bc4SBrooks Davis /// \return A collection of interface names. 997*b0d29bc4SBrooks Davis std::set< std::string > 998*b0d29bc4SBrooks Davis scheduler::registered_interface_names(void) 999*b0d29bc4SBrooks Davis { 1000*b0d29bc4SBrooks Davis std::set< std::string > names; 1001*b0d29bc4SBrooks Davis for (interfaces_map::const_iterator iter = interfaces.begin(); 1002*b0d29bc4SBrooks Davis iter != interfaces.end(); ++iter) { 1003*b0d29bc4SBrooks Davis names.insert((*iter).first); 1004*b0d29bc4SBrooks Davis } 1005*b0d29bc4SBrooks Davis return names; 1006*b0d29bc4SBrooks Davis } 1007*b0d29bc4SBrooks Davis 1008*b0d29bc4SBrooks Davis 1009*b0d29bc4SBrooks Davis /// Initializes the scheduler. 1010*b0d29bc4SBrooks Davis /// 1011*b0d29bc4SBrooks Davis /// \pre This function can only be called if there is no other scheduler_handle 1012*b0d29bc4SBrooks Davis /// object alive. 1013*b0d29bc4SBrooks Davis /// 1014*b0d29bc4SBrooks Davis /// \return A handle to the operations of the scheduler. 1015*b0d29bc4SBrooks Davis scheduler::scheduler_handle 1016*b0d29bc4SBrooks Davis scheduler::setup(void) 1017*b0d29bc4SBrooks Davis { 1018*b0d29bc4SBrooks Davis return scheduler_handle(); 1019*b0d29bc4SBrooks Davis } 1020*b0d29bc4SBrooks Davis 1021*b0d29bc4SBrooks Davis 1022*b0d29bc4SBrooks Davis /// Retrieves the list of test cases from a test program. 1023*b0d29bc4SBrooks Davis /// 1024*b0d29bc4SBrooks Davis /// This operation is currently synchronous. 1025*b0d29bc4SBrooks Davis /// 1026*b0d29bc4SBrooks Davis /// This operation should never throw. Any errors during the processing of the 1027*b0d29bc4SBrooks Davis /// test case list are subsumed into a single test case in the return value that 1028*b0d29bc4SBrooks Davis /// represents the failed retrieval. 1029*b0d29bc4SBrooks Davis /// 1030*b0d29bc4SBrooks Davis /// \param test_program The test program from which to obtain the list of test 1031*b0d29bc4SBrooks Davis /// cases. 1032*b0d29bc4SBrooks Davis /// \param user_config User-provided configuration variables. 1033*b0d29bc4SBrooks Davis /// 1034*b0d29bc4SBrooks Davis /// \return The list of test cases. 1035*b0d29bc4SBrooks Davis model::test_cases_map 1036*b0d29bc4SBrooks Davis scheduler::scheduler_handle::list_tests( 1037*b0d29bc4SBrooks Davis const model::test_program* test_program, 1038*b0d29bc4SBrooks Davis const config::tree& user_config) 1039*b0d29bc4SBrooks Davis { 1040*b0d29bc4SBrooks Davis _pimpl->generic.check_interrupt(); 1041*b0d29bc4SBrooks Davis 1042*b0d29bc4SBrooks Davis const std::shared_ptr< scheduler::interface > interface = find_interface( 1043*b0d29bc4SBrooks Davis test_program->interface_name()); 1044*b0d29bc4SBrooks Davis 1045*b0d29bc4SBrooks Davis try { 1046*b0d29bc4SBrooks Davis const executor::exec_handle exec_handle = _pimpl->generic.spawn( 1047*b0d29bc4SBrooks Davis list_test_cases(interface, test_program, user_config), 1048*b0d29bc4SBrooks Davis list_timeout, none); 1049*b0d29bc4SBrooks Davis executor::exit_handle exit_handle = _pimpl->generic.wait(exec_handle); 1050*b0d29bc4SBrooks Davis 1051*b0d29bc4SBrooks Davis const model::test_cases_map test_cases = interface->parse_list( 1052*b0d29bc4SBrooks Davis exit_handle.status(), 1053*b0d29bc4SBrooks Davis exit_handle.stdout_file(), 1054*b0d29bc4SBrooks Davis exit_handle.stderr_file()); 1055*b0d29bc4SBrooks Davis 1056*b0d29bc4SBrooks Davis exit_handle.cleanup(); 1057*b0d29bc4SBrooks Davis 1058*b0d29bc4SBrooks Davis if (test_cases.empty()) 1059*b0d29bc4SBrooks Davis throw std::runtime_error("Empty test cases list"); 1060*b0d29bc4SBrooks Davis 1061*b0d29bc4SBrooks Davis return test_cases; 1062*b0d29bc4SBrooks Davis } catch (const std::runtime_error& e) { 1063*b0d29bc4SBrooks Davis // TODO(jmmv): This is a very ugly workaround for the fact that we 1064*b0d29bc4SBrooks Davis // cannot report failures at the test-program level. 1065*b0d29bc4SBrooks Davis LW(F("Failed to load test cases list: %s") % e.what()); 1066*b0d29bc4SBrooks Davis model::test_cases_map fake_test_cases; 1067*b0d29bc4SBrooks Davis fake_test_cases.insert(model::test_cases_map::value_type( 1068*b0d29bc4SBrooks Davis "__test_cases_list__", 1069*b0d29bc4SBrooks Davis model::test_case( 1070*b0d29bc4SBrooks Davis "__test_cases_list__", 1071*b0d29bc4SBrooks Davis "Represents the correct processing of the test cases list", 1072*b0d29bc4SBrooks Davis model::test_result(model::test_result_broken, e.what())))); 1073*b0d29bc4SBrooks Davis return fake_test_cases; 1074*b0d29bc4SBrooks Davis } 1075*b0d29bc4SBrooks Davis } 1076*b0d29bc4SBrooks Davis 1077*b0d29bc4SBrooks Davis 1078*b0d29bc4SBrooks Davis /// Forks and executes a test case asynchronously. 1079*b0d29bc4SBrooks Davis /// 1080*b0d29bc4SBrooks Davis /// Note that the caller needn't know if the test has a cleanup routine or not. 1081*b0d29bc4SBrooks Davis /// If there indeed is a cleanup routine, we trigger it at wait_any() time. 1082*b0d29bc4SBrooks Davis /// 1083*b0d29bc4SBrooks Davis /// \param test_program The container test program. 1084*b0d29bc4SBrooks Davis /// \param test_case_name The name of the test case to run. 1085*b0d29bc4SBrooks Davis /// \param user_config User-provided configuration variables. 1086*b0d29bc4SBrooks Davis /// 1087*b0d29bc4SBrooks Davis /// \return A handle for the background operation. Used to match the result of 1088*b0d29bc4SBrooks Davis /// the execution returned by wait_any() with this invocation. 1089*b0d29bc4SBrooks Davis scheduler::exec_handle 1090*b0d29bc4SBrooks Davis scheduler::scheduler_handle::spawn_test( 1091*b0d29bc4SBrooks Davis const model::test_program_ptr test_program, 1092*b0d29bc4SBrooks Davis const std::string& test_case_name, 1093*b0d29bc4SBrooks Davis const config::tree& user_config) 1094*b0d29bc4SBrooks Davis { 1095*b0d29bc4SBrooks Davis _pimpl->generic.check_interrupt(); 1096*b0d29bc4SBrooks Davis 1097*b0d29bc4SBrooks Davis const std::shared_ptr< scheduler::interface > interface = find_interface( 1098*b0d29bc4SBrooks Davis test_program->interface_name()); 1099*b0d29bc4SBrooks Davis 1100*b0d29bc4SBrooks Davis LI(F("Spawning %s:%s") % test_program->absolute_path() % test_case_name); 1101*b0d29bc4SBrooks Davis 1102*b0d29bc4SBrooks Davis const model::test_case& test_case = test_program->find(test_case_name); 1103*b0d29bc4SBrooks Davis 1104*b0d29bc4SBrooks Davis optional< passwd::user > unprivileged_user; 1105*b0d29bc4SBrooks Davis if (user_config.is_set("unprivileged_user") && 1106*b0d29bc4SBrooks Davis test_case.get_metadata().required_user() == "unprivileged") { 1107*b0d29bc4SBrooks Davis unprivileged_user = user_config.lookup< engine::user_node >( 1108*b0d29bc4SBrooks Davis "unprivileged_user"); 1109*b0d29bc4SBrooks Davis } 1110*b0d29bc4SBrooks Davis 1111*b0d29bc4SBrooks Davis const executor::exec_handle handle = _pimpl->generic.spawn( 1112*b0d29bc4SBrooks Davis run_test_program(interface, test_program, test_case_name, 1113*b0d29bc4SBrooks Davis user_config), 1114*b0d29bc4SBrooks Davis test_case.get_metadata().timeout(), 1115*b0d29bc4SBrooks Davis unprivileged_user); 1116*b0d29bc4SBrooks Davis 1117*b0d29bc4SBrooks Davis const exec_data_ptr data(new test_exec_data( 1118*b0d29bc4SBrooks Davis test_program, test_case_name, interface, user_config)); 1119*b0d29bc4SBrooks Davis LD(F("Inserting %s into all_exec_data") % handle.pid()); 1120*b0d29bc4SBrooks Davis INV_MSG( 1121*b0d29bc4SBrooks Davis _pimpl->all_exec_data.find(handle.pid()) == _pimpl->all_exec_data.end(), 1122*b0d29bc4SBrooks Davis F("PID %s already in all_exec_data; not cleaned up or reused too fast") 1123*b0d29bc4SBrooks Davis % handle.pid());; 1124*b0d29bc4SBrooks Davis _pimpl->all_exec_data.insert(exec_data_map::value_type(handle.pid(), data)); 1125*b0d29bc4SBrooks Davis 1126*b0d29bc4SBrooks Davis return handle.pid(); 1127*b0d29bc4SBrooks Davis } 1128*b0d29bc4SBrooks Davis 1129*b0d29bc4SBrooks Davis 1130*b0d29bc4SBrooks Davis /// Waits for completion of any forked test case. 1131*b0d29bc4SBrooks Davis /// 1132*b0d29bc4SBrooks Davis /// Note that if the terminated test case has a cleanup routine, this function 1133*b0d29bc4SBrooks Davis /// is the one in charge of spawning the cleanup routine asynchronously. 1134*b0d29bc4SBrooks Davis /// 1135*b0d29bc4SBrooks Davis /// \return The result of the execution of a subprocess. This is a dynamically 1136*b0d29bc4SBrooks Davis /// allocated object because the scheduler can spawn subprocesses of various 1137*b0d29bc4SBrooks Davis /// types and, at wait time, we don't know upfront what we are going to get. 1138*b0d29bc4SBrooks Davis scheduler::result_handle_ptr 1139*b0d29bc4SBrooks Davis scheduler::scheduler_handle::wait_any(void) 1140*b0d29bc4SBrooks Davis { 1141*b0d29bc4SBrooks Davis _pimpl->generic.check_interrupt(); 1142*b0d29bc4SBrooks Davis 1143*b0d29bc4SBrooks Davis executor::exit_handle handle = _pimpl->generic.wait_any(); 1144*b0d29bc4SBrooks Davis 1145*b0d29bc4SBrooks Davis const exec_data_map::iterator iter = _pimpl->all_exec_data.find( 1146*b0d29bc4SBrooks Davis handle.original_pid()); 1147*b0d29bc4SBrooks Davis exec_data_ptr data = (*iter).second; 1148*b0d29bc4SBrooks Davis 1149*b0d29bc4SBrooks Davis utils::dump_stacktrace_if_available(data->test_program->absolute_path(), 1150*b0d29bc4SBrooks Davis _pimpl->generic, handle); 1151*b0d29bc4SBrooks Davis 1152*b0d29bc4SBrooks Davis optional< model::test_result > result; 1153*b0d29bc4SBrooks Davis try { 1154*b0d29bc4SBrooks Davis test_exec_data* test_data = &dynamic_cast< test_exec_data& >( 1155*b0d29bc4SBrooks Davis *data.get()); 1156*b0d29bc4SBrooks Davis LD(F("Got %s from all_exec_data") % handle.original_pid()); 1157*b0d29bc4SBrooks Davis 1158*b0d29bc4SBrooks Davis test_data->exit_handle = handle; 1159*b0d29bc4SBrooks Davis 1160*b0d29bc4SBrooks Davis const model::test_case& test_case = test_data->test_program->find( 1161*b0d29bc4SBrooks Davis test_data->test_case_name); 1162*b0d29bc4SBrooks Davis 1163*b0d29bc4SBrooks Davis result = test_case.fake_result(); 1164*b0d29bc4SBrooks Davis 1165*b0d29bc4SBrooks Davis if (!result && handle.status() && handle.status().get().exited() && 1166*b0d29bc4SBrooks Davis handle.status().get().exitstatus() == exit_skipped) { 1167*b0d29bc4SBrooks Davis // If the test's process terminated with our magic "exit_skipped" 1168*b0d29bc4SBrooks Davis // status, there are two cases to handle. The first is the case 1169*b0d29bc4SBrooks Davis // where the "skipped cookie" exists, in which case we never got to 1170*b0d29bc4SBrooks Davis // actually invoke the test program; if that's the case, handle it 1171*b0d29bc4SBrooks Davis // here. The second case is where the test case actually decided to 1172*b0d29bc4SBrooks Davis // exit with the "exit_skipped" status; in that case, just fall back 1173*b0d29bc4SBrooks Davis // to the regular status handling. 1174*b0d29bc4SBrooks Davis const fs::path skipped_cookie_path = handle.control_directory() / 1175*b0d29bc4SBrooks Davis skipped_cookie; 1176*b0d29bc4SBrooks Davis std::ifstream input(skipped_cookie_path.c_str()); 1177*b0d29bc4SBrooks Davis if (input) { 1178*b0d29bc4SBrooks Davis result = model::test_result(model::test_result_skipped, 1179*b0d29bc4SBrooks Davis utils::read_stream(input)); 1180*b0d29bc4SBrooks Davis input.close(); 1181*b0d29bc4SBrooks Davis 1182*b0d29bc4SBrooks Davis // If we determined that the test needs to be skipped, we do not 1183*b0d29bc4SBrooks Davis // want to run the cleanup routine because doing so could result 1184*b0d29bc4SBrooks Davis // in errors. However, we still want to run the cleanup routine 1185*b0d29bc4SBrooks Davis // if the test's body reports a skip (because actions could have 1186*b0d29bc4SBrooks Davis // already been taken). 1187*b0d29bc4SBrooks Davis test_data->needs_cleanup = false; 1188*b0d29bc4SBrooks Davis } 1189*b0d29bc4SBrooks Davis } 1190*b0d29bc4SBrooks Davis if (!result) { 1191*b0d29bc4SBrooks Davis result = test_data->interface->compute_result( 1192*b0d29bc4SBrooks Davis handle.status(), 1193*b0d29bc4SBrooks Davis handle.control_directory(), 1194*b0d29bc4SBrooks Davis handle.stdout_file(), 1195*b0d29bc4SBrooks Davis handle.stderr_file()); 1196*b0d29bc4SBrooks Davis } 1197*b0d29bc4SBrooks Davis INV(result); 1198*b0d29bc4SBrooks Davis 1199*b0d29bc4SBrooks Davis if (!result.get().good()) { 1200*b0d29bc4SBrooks Davis append_files_listing(handle.work_directory(), 1201*b0d29bc4SBrooks Davis handle.stderr_file()); 1202*b0d29bc4SBrooks Davis } 1203*b0d29bc4SBrooks Davis 1204*b0d29bc4SBrooks Davis if (test_data->needs_cleanup) { 1205*b0d29bc4SBrooks Davis INV(test_case.get_metadata().has_cleanup()); 1206*b0d29bc4SBrooks Davis // The test body has completed and we have processed it. If there 1207*b0d29bc4SBrooks Davis // is a cleanup routine, trigger it now and wait for any other test 1208*b0d29bc4SBrooks Davis // completion. The caller never knows about cleanup routines. 1209*b0d29bc4SBrooks Davis _pimpl->spawn_cleanup(test_data->test_program, 1210*b0d29bc4SBrooks Davis test_data->test_case_name, 1211*b0d29bc4SBrooks Davis test_data->user_config, handle, result.get()); 1212*b0d29bc4SBrooks Davis test_data->needs_cleanup = false; 1213*b0d29bc4SBrooks Davis 1214*b0d29bc4SBrooks Davis // TODO(jmmv): Chaining this call is ugly. We'd be better off by 1215*b0d29bc4SBrooks Davis // looping over terminated processes until we got a result suitable 1216*b0d29bc4SBrooks Davis // for user consumption. For the time being this is good enough and 1217*b0d29bc4SBrooks Davis // not a problem because the call chain won't get big: the majority 1218*b0d29bc4SBrooks Davis // of test cases do not have cleanup routines. 1219*b0d29bc4SBrooks Davis return wait_any(); 1220*b0d29bc4SBrooks Davis } 1221*b0d29bc4SBrooks Davis } catch (const std::bad_cast& e) { 1222*b0d29bc4SBrooks Davis const cleanup_exec_data* cleanup_data = 1223*b0d29bc4SBrooks Davis &dynamic_cast< const cleanup_exec_data& >(*data.get()); 1224*b0d29bc4SBrooks Davis LD(F("Got %s from all_exec_data (cleanup)") % handle.original_pid()); 1225*b0d29bc4SBrooks Davis 1226*b0d29bc4SBrooks Davis // Handle the completion of cleanup subprocesses internally: the caller 1227*b0d29bc4SBrooks Davis // is not aware that these exist so, when we return, we must return the 1228*b0d29bc4SBrooks Davis // data for the original test that triggered this routine. For example, 1229*b0d29bc4SBrooks Davis // because the caller wants to see the exact same exec_handle that was 1230*b0d29bc4SBrooks Davis // returned by spawn_test. 1231*b0d29bc4SBrooks Davis 1232*b0d29bc4SBrooks Davis const model::test_result& body_result = cleanup_data->body_result; 1233*b0d29bc4SBrooks Davis if (body_result.good()) { 1234*b0d29bc4SBrooks Davis if (!handle.status()) { 1235*b0d29bc4SBrooks Davis result = model::test_result(model::test_result_broken, 1236*b0d29bc4SBrooks Davis "Test case cleanup timed out"); 1237*b0d29bc4SBrooks Davis } else { 1238*b0d29bc4SBrooks Davis if (!handle.status().get().exited() || 1239*b0d29bc4SBrooks Davis handle.status().get().exitstatus() != EXIT_SUCCESS) { 1240*b0d29bc4SBrooks Davis result = model::test_result( 1241*b0d29bc4SBrooks Davis model::test_result_broken, 1242*b0d29bc4SBrooks Davis "Test case cleanup did not terminate successfully"); 1243*b0d29bc4SBrooks Davis } else { 1244*b0d29bc4SBrooks Davis result = body_result; 1245*b0d29bc4SBrooks Davis } 1246*b0d29bc4SBrooks Davis } 1247*b0d29bc4SBrooks Davis } else { 1248*b0d29bc4SBrooks Davis result = body_result; 1249*b0d29bc4SBrooks Davis } 1250*b0d29bc4SBrooks Davis 1251*b0d29bc4SBrooks Davis // Untrack the cleanup process. This must be done explicitly because we 1252*b0d29bc4SBrooks Davis // do not create a result_handle object for the cleanup, and that is the 1253*b0d29bc4SBrooks Davis // one in charge of doing so in the regular (non-cleanup) case. 1254*b0d29bc4SBrooks Davis LD(F("Removing %s from all_exec_data (cleanup) in favor of %s") 1255*b0d29bc4SBrooks Davis % handle.original_pid() 1256*b0d29bc4SBrooks Davis % cleanup_data->body_exit_handle.original_pid()); 1257*b0d29bc4SBrooks Davis _pimpl->all_exec_data.erase(handle.original_pid()); 1258*b0d29bc4SBrooks Davis 1259*b0d29bc4SBrooks Davis handle = cleanup_data->body_exit_handle; 1260*b0d29bc4SBrooks Davis } 1261*b0d29bc4SBrooks Davis INV(result); 1262*b0d29bc4SBrooks Davis 1263*b0d29bc4SBrooks Davis std::shared_ptr< result_handle::bimpl > result_handle_bimpl( 1264*b0d29bc4SBrooks Davis new result_handle::bimpl(handle, _pimpl->all_exec_data)); 1265*b0d29bc4SBrooks Davis std::shared_ptr< test_result_handle::impl > test_result_handle_impl( 1266*b0d29bc4SBrooks Davis new test_result_handle::impl( 1267*b0d29bc4SBrooks Davis data->test_program, data->test_case_name, result.get())); 1268*b0d29bc4SBrooks Davis return result_handle_ptr(new test_result_handle(result_handle_bimpl, 1269*b0d29bc4SBrooks Davis test_result_handle_impl)); 1270*b0d29bc4SBrooks Davis } 1271*b0d29bc4SBrooks Davis 1272*b0d29bc4SBrooks Davis 1273*b0d29bc4SBrooks Davis /// Forks and executes a test case synchronously for debugging. 1274*b0d29bc4SBrooks Davis /// 1275*b0d29bc4SBrooks Davis /// \pre No other processes should be in execution by the scheduler. 1276*b0d29bc4SBrooks Davis /// 1277*b0d29bc4SBrooks Davis /// \param test_program The container test program. 1278*b0d29bc4SBrooks Davis /// \param test_case_name The name of the test case to run. 1279*b0d29bc4SBrooks Davis /// \param user_config User-provided configuration variables. 1280*b0d29bc4SBrooks Davis /// \param stdout_target File to which to write the stdout of the test case. 1281*b0d29bc4SBrooks Davis /// \param stderr_target File to which to write the stderr of the test case. 1282*b0d29bc4SBrooks Davis /// 1283*b0d29bc4SBrooks Davis /// \return The result of the execution of the test. 1284*b0d29bc4SBrooks Davis scheduler::result_handle_ptr 1285*b0d29bc4SBrooks Davis scheduler::scheduler_handle::debug_test( 1286*b0d29bc4SBrooks Davis const model::test_program_ptr test_program, 1287*b0d29bc4SBrooks Davis const std::string& test_case_name, 1288*b0d29bc4SBrooks Davis const config::tree& user_config, 1289*b0d29bc4SBrooks Davis const fs::path& stdout_target, 1290*b0d29bc4SBrooks Davis const fs::path& stderr_target) 1291*b0d29bc4SBrooks Davis { 1292*b0d29bc4SBrooks Davis const exec_handle exec_handle = spawn_test( 1293*b0d29bc4SBrooks Davis test_program, test_case_name, user_config); 1294*b0d29bc4SBrooks Davis result_handle_ptr result_handle = wait_any(); 1295*b0d29bc4SBrooks Davis 1296*b0d29bc4SBrooks Davis // TODO(jmmv): We need to do this while the subprocess is alive. This is 1297*b0d29bc4SBrooks Davis // important for debugging purposes, as we should see the contents of stdout 1298*b0d29bc4SBrooks Davis // or stderr as they come in. 1299*b0d29bc4SBrooks Davis // 1300*b0d29bc4SBrooks Davis // Unfortunately, we cannot do so. We cannot just read and block from a 1301*b0d29bc4SBrooks Davis // file, waiting for further output to appear... as this only works on pipes 1302*b0d29bc4SBrooks Davis // or sockets. We need a better interface for this whole thing. 1303*b0d29bc4SBrooks Davis { 1304*b0d29bc4SBrooks Davis std::auto_ptr< std::ostream > output = utils::open_ostream( 1305*b0d29bc4SBrooks Davis stdout_target); 1306*b0d29bc4SBrooks Davis *output << utils::read_file(result_handle->stdout_file()); 1307*b0d29bc4SBrooks Davis } 1308*b0d29bc4SBrooks Davis { 1309*b0d29bc4SBrooks Davis std::auto_ptr< std::ostream > output = utils::open_ostream( 1310*b0d29bc4SBrooks Davis stderr_target); 1311*b0d29bc4SBrooks Davis *output << utils::read_file(result_handle->stderr_file()); 1312*b0d29bc4SBrooks Davis } 1313*b0d29bc4SBrooks Davis 1314*b0d29bc4SBrooks Davis INV(result_handle->original_pid() == exec_handle); 1315*b0d29bc4SBrooks Davis return result_handle; 1316*b0d29bc4SBrooks Davis } 1317*b0d29bc4SBrooks Davis 1318*b0d29bc4SBrooks Davis 1319*b0d29bc4SBrooks Davis /// Checks if an interrupt has fired. 1320*b0d29bc4SBrooks Davis /// 1321*b0d29bc4SBrooks Davis /// Calls to this function should be sprinkled in strategic places through the 1322*b0d29bc4SBrooks Davis /// code protected by an interrupts_handler object. 1323*b0d29bc4SBrooks Davis /// 1324*b0d29bc4SBrooks Davis /// This is just a wrapper over signals::check_interrupt() to avoid leaking this 1325*b0d29bc4SBrooks Davis /// dependency to the caller. 1326*b0d29bc4SBrooks Davis /// 1327*b0d29bc4SBrooks Davis /// \throw signals::interrupted_error If there has been an interrupt. 1328*b0d29bc4SBrooks Davis void 1329*b0d29bc4SBrooks Davis scheduler::scheduler_handle::check_interrupt(void) const 1330*b0d29bc4SBrooks Davis { 1331*b0d29bc4SBrooks Davis _pimpl->generic.check_interrupt(); 1332*b0d29bc4SBrooks Davis } 1333*b0d29bc4SBrooks Davis 1334*b0d29bc4SBrooks Davis 1335*b0d29bc4SBrooks Davis /// Queries the current execution context. 1336*b0d29bc4SBrooks Davis /// 1337*b0d29bc4SBrooks Davis /// \return The queried context. 1338*b0d29bc4SBrooks Davis model::context 1339*b0d29bc4SBrooks Davis scheduler::current_context(void) 1340*b0d29bc4SBrooks Davis { 1341*b0d29bc4SBrooks Davis return model::context(fs::current_path(), utils::getallenv()); 1342*b0d29bc4SBrooks Davis } 1343*b0d29bc4SBrooks Davis 1344*b0d29bc4SBrooks Davis 1345*b0d29bc4SBrooks Davis /// Generates the set of configuration variables for a test program. 1346*b0d29bc4SBrooks Davis /// 1347*b0d29bc4SBrooks Davis /// \param user_config The configuration variables provided by the user. 1348*b0d29bc4SBrooks Davis /// \param test_suite The name of the test suite. 1349*b0d29bc4SBrooks Davis /// 1350*b0d29bc4SBrooks Davis /// \return The mapping of configuration variables for the test program. 1351*b0d29bc4SBrooks Davis config::properties_map 1352*b0d29bc4SBrooks Davis scheduler::generate_config(const config::tree& user_config, 1353*b0d29bc4SBrooks Davis const std::string& test_suite) 1354*b0d29bc4SBrooks Davis { 1355*b0d29bc4SBrooks Davis config::properties_map props; 1356*b0d29bc4SBrooks Davis 1357*b0d29bc4SBrooks Davis try { 1358*b0d29bc4SBrooks Davis props = user_config.all_properties(F("test_suites.%s") % test_suite, 1359*b0d29bc4SBrooks Davis true); 1360*b0d29bc4SBrooks Davis } catch (const config::unknown_key_error& unused_error) { 1361*b0d29bc4SBrooks Davis // Ignore: not all test suites have entries in the configuration. 1362*b0d29bc4SBrooks Davis } 1363*b0d29bc4SBrooks Davis 1364*b0d29bc4SBrooks Davis // TODO(jmmv): This is a hack that exists for the ATF interface only, so it 1365*b0d29bc4SBrooks Davis // should be moved there. 1366*b0d29bc4SBrooks Davis if (user_config.is_set("unprivileged_user")) { 1367*b0d29bc4SBrooks Davis const passwd::user& user = 1368*b0d29bc4SBrooks Davis user_config.lookup< engine::user_node >("unprivileged_user"); 1369*b0d29bc4SBrooks Davis props["unprivileged-user"] = user.name; 1370*b0d29bc4SBrooks Davis } 1371*b0d29bc4SBrooks Davis 1372*b0d29bc4SBrooks Davis return props; 1373*b0d29bc4SBrooks Davis } 1374