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