1 // Copyright 2014 The Kyua Authors.
2 // All rights reserved.
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 // * Neither the name of Google Inc. nor the names of its contributors
14 // may be used to endorse or promote products derived from this software
15 // without specific prior written permission.
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 /// \file engine/scheduler.hpp
30 /// Multiprogrammed executor of test related operations.
32 /// The scheduler's public interface exposes test cases as "black boxes". The
33 /// handling of cleanup routines is completely hidden from the caller and
34 /// happens in two cases: first, once a test case completes; and, second, in the
35 /// case of abrupt termination due to the reception of a signal.
37 /// Hiding cleanup routines from the caller is an attempt to keep the logic of
38 /// execution and results handling in a single place. Otherwise, the various
39 /// drivers (say run_tests and debug_test) would need to replicate the handling
40 /// of this logic, which is tricky in itself (particularly due to signal
41 /// handling) and would lead to inconsistencies.
43 /// Handling cleanup routines in the manner described above is *incredibly
44 /// complicated* (insane, actually) as you will see from the code. The
45 /// complexity will bite us in the future (today is 2015-06-26). Switching to a
46 /// threads-based implementation would probably simplify the code flow
47 /// significantly and allow parallelization of the test case listings in a
48 /// reasonable manner, though it depends on whether we can get clean handling of
49 /// signals and on whether we could use C++11's std::thread. (Is this a to-do?
50 /// Maybe. Maybe not.)
52 /// See the documentation in utils/process/executor.hpp for details on
53 /// the expected workflow of these classes.
55 #if !defined(ENGINE_SCHEDULER_HPP)
56 #define ENGINE_SCHEDULER_HPP
58 #include "engine/scheduler_fwd.hpp"
64 #include "model/context_fwd.hpp"
65 #include "model/metadata_fwd.hpp"
66 #include "model/test_case_fwd.hpp"
67 #include "model/test_program.hpp"
68 #include "model/test_result_fwd.hpp"
69 #include "utils/config/tree_fwd.hpp"
70 #include "utils/datetime_fwd.hpp"
71 #include "utils/defs.hpp"
72 #include "utils/fs/path_fwd.hpp"
73 #include "utils/optional.hpp"
74 #include "utils/process/executor_fwd.hpp"
75 #include "utils/process/status_fwd.hpp"
81 /// Abstract interface of a test program scheduler interface.
83 /// This interface defines the test program-specific operations that need to be
84 /// invoked at different points during the execution of a given test case. The
85 /// scheduler internally instantiates one of these for every test case.
89 virtual ~interface() {}
91 /// Executes a test program's list operation.
93 /// This method is intended to be called within a subprocess and is expected
94 /// to terminate execution either by exec(2)ing the test program or by
95 /// exiting with a failure.
97 /// \param test_program The test program to execute.
98 /// \param vars User-provided variables to pass to the test program.
99 virtual void exec_list(const model::test_program& test_program,
100 const utils::config::properties_map& vars)
101 const UTILS_NORETURN = 0;
103 /// Computes the test cases list of a test program.
105 /// \param status The termination status of the subprocess used to execute
106 /// the exec_test() method or none if the test timed out.
107 /// \param stdout_path Path to the file containing the stdout of the test.
108 /// \param stderr_path Path to the file containing the stderr of the test.
110 /// \return A list of test cases.
111 virtual model::test_cases_map parse_list(
112 const utils::optional< utils::process::status >& status,
113 const utils::fs::path& stdout_path,
114 const utils::fs::path& stderr_path) const = 0;
116 /// Executes a test case of the test program.
118 /// This method is intended to be called within a subprocess and is expected
119 /// to terminate execution either by exec(2)ing the test program or by
120 /// exiting with a failure.
122 /// \param test_program The test program to execute.
123 /// \param test_case_name Name of the test case to invoke.
124 /// \param vars User-provided variables to pass to the test program.
125 /// \param control_directory Directory where the interface may place control
127 virtual void exec_test(const model::test_program& test_program,
128 const std::string& test_case_name,
129 const utils::config::properties_map& vars,
130 const utils::fs::path& control_directory)
131 const UTILS_NORETURN = 0;
133 /// Executes a test cleanup routine of the test program.
135 /// This method is intended to be called within a subprocess and is expected
136 /// to terminate execution either by exec(2)ing the test program or by
137 /// exiting with a failure.
139 /// \param test_program The test program to execute.
140 /// \param test_case_name Name of the test case to invoke.
141 /// \param vars User-provided variables to pass to the test program.
142 /// \param control_directory Directory where the interface may place control
144 virtual void exec_cleanup(const model::test_program& test_program,
145 const std::string& test_case_name,
146 const utils::config::properties_map& vars,
147 const utils::fs::path& control_directory)
148 const UTILS_NORETURN;
150 /// Computes the result of a test case based on its termination status.
152 /// \param status The termination status of the subprocess used to execute
153 /// the exec_test() method or none if the test timed out.
154 /// \param control_directory Directory where the interface may have placed
156 /// \param stdout_path Path to the file containing the stdout of the test.
157 /// \param stderr_path Path to the file containing the stderr of the test.
159 /// \return A test result.
160 virtual model::test_result compute_result(
161 const utils::optional< utils::process::status >& status,
162 const utils::fs::path& control_directory,
163 const utils::fs::path& stdout_path,
164 const utils::fs::path& stderr_path) const = 0;
168 /// Implementation of a test program with lazy loading of test cases.
169 class lazy_test_program : public model::test_program {
172 /// Pointer to the shared internal implementation.
173 std::shared_ptr< impl > _pimpl;
176 lazy_test_program(const std::string&, const utils::fs::path&,
177 const utils::fs::path&, const std::string&,
178 const model::metadata&,
179 const utils::config::tree&,
182 const model::test_cases_map& test_cases(void) const;
186 /// Base type containing the results of the execution of a subprocess.
187 class result_handle {
192 /// Pointer to internal implementation of the base type.
193 std::shared_ptr< bimpl > _pbimpl;
196 friend class scheduler_handle;
197 result_handle(std::shared_ptr< bimpl >);
200 virtual ~result_handle(void) = 0;
204 int original_pid(void) const;
205 const utils::datetime::timestamp& start_time() const;
206 const utils::datetime::timestamp& end_time() const;
207 utils::fs::path work_directory(void) const;
208 const utils::fs::path& stdout_file(void) const;
209 const utils::fs::path& stderr_file(void) const;
213 /// Container for all test termination data and accessor to cleanup operations.
214 class test_result_handle : public result_handle {
216 /// Pointer to internal implementation.
217 std::shared_ptr< impl > _pimpl;
219 friend class scheduler_handle;
220 test_result_handle(std::shared_ptr< bimpl >, std::shared_ptr< impl >);
223 ~test_result_handle(void);
225 const model::test_program_ptr test_program(void) const;
226 const std::string& test_case_name(void) const;
227 const model::test_result& test_result(void) const;
231 /// Stateful interface to the multiprogrammed execution of tests.
232 class scheduler_handle {
234 /// Pointer to internal implementation.
235 std::shared_ptr< impl > _pimpl;
237 friend scheduler_handle setup(void);
238 scheduler_handle(void);
241 ~scheduler_handle(void);
243 const utils::fs::path& root_work_directory(void) const;
247 model::test_cases_map list_tests(const model::test_program*,
248 const utils::config::tree&);
249 exec_handle spawn_test(const model::test_program_ptr,
251 const utils::config::tree&);
252 result_handle_ptr wait_any(void);
254 result_handle_ptr debug_test(const model::test_program_ptr,
256 const utils::config::tree&,
257 const utils::fs::path&,
258 const utils::fs::path&);
260 void check_interrupt(void) const;
264 extern utils::datetime::delta cleanup_timeout;
265 extern utils::datetime::delta list_timeout;
268 void ensure_valid_interface(const std::string&);
269 void register_interface(const std::string&, const std::shared_ptr< interface >);
270 std::set< std::string > registered_interface_names(void);
271 scheduler_handle setup(void);
273 model::context current_context(void);
274 utils::config::properties_map generate_config(const utils::config::tree&,
278 } // namespace scheduler
279 } // namespace engine
282 #endif // !defined(ENGINE_SCHEDULER_HPP)