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