108334c51SBrooks Davis // Copyright 2015 The Kyua Authors.
208334c51SBrooks Davis // All rights reserved.
308334c51SBrooks Davis //
408334c51SBrooks Davis // Redistribution and use in source and binary forms, with or without
508334c51SBrooks Davis // modification, are permitted provided that the following conditions are
608334c51SBrooks Davis // met:
708334c51SBrooks Davis //
808334c51SBrooks Davis // * Redistributions of source code must retain the above copyright
908334c51SBrooks Davis // notice, this list of conditions and the following disclaimer.
1008334c51SBrooks Davis // * Redistributions in binary form must reproduce the above copyright
1108334c51SBrooks Davis // notice, this list of conditions and the following disclaimer in the
1208334c51SBrooks Davis // documentation and/or other materials provided with the distribution.
1308334c51SBrooks Davis // * Neither the name of Google Inc. nor the names of its contributors
1408334c51SBrooks Davis // may be used to endorse or promote products derived from this software
1508334c51SBrooks Davis // without specific prior written permission.
1608334c51SBrooks Davis //
1708334c51SBrooks Davis // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1808334c51SBrooks Davis // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1908334c51SBrooks Davis // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
2008334c51SBrooks Davis // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2108334c51SBrooks Davis // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2208334c51SBrooks Davis // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2308334c51SBrooks Davis // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2408334c51SBrooks Davis // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2508334c51SBrooks Davis // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2608334c51SBrooks Davis // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2708334c51SBrooks Davis // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2808334c51SBrooks Davis
2908334c51SBrooks Davis #include "utils/process/executor.ipp"
3008334c51SBrooks Davis
3108334c51SBrooks Davis extern "C" {
3208334c51SBrooks Davis #include <sys/types.h>
3308334c51SBrooks Davis #include <sys/time.h>
3408334c51SBrooks Davis #include <sys/wait.h>
3508334c51SBrooks Davis
3608334c51SBrooks Davis #include <signal.h>
3708334c51SBrooks Davis #include <unistd.h>
3808334c51SBrooks Davis }
3908334c51SBrooks Davis
4008334c51SBrooks Davis #include <cerrno>
4108334c51SBrooks Davis #include <cstdlib>
4208334c51SBrooks Davis #include <fstream>
4308334c51SBrooks Davis #include <iostream>
4408334c51SBrooks Davis #include <vector>
4508334c51SBrooks Davis
4608334c51SBrooks Davis #include <atf-c++.hpp>
4708334c51SBrooks Davis
4808334c51SBrooks Davis #include "utils/datetime.hpp"
4908334c51SBrooks Davis #include "utils/defs.hpp"
5008334c51SBrooks Davis #include "utils/env.hpp"
5108334c51SBrooks Davis #include "utils/format/containers.ipp"
5208334c51SBrooks Davis #include "utils/format/macros.hpp"
5308334c51SBrooks Davis #include "utils/fs/operations.hpp"
5408334c51SBrooks Davis #include "utils/fs/path.hpp"
5508334c51SBrooks Davis #include "utils/optional.ipp"
5608334c51SBrooks Davis #include "utils/passwd.hpp"
5708334c51SBrooks Davis #include "utils/process/status.hpp"
5808334c51SBrooks Davis #include "utils/sanity.hpp"
5908334c51SBrooks Davis #include "utils/signals/exceptions.hpp"
6008334c51SBrooks Davis #include "utils/stacktrace.hpp"
6108334c51SBrooks Davis #include "utils/text/exceptions.hpp"
6208334c51SBrooks Davis #include "utils/text/operations.ipp"
6308334c51SBrooks Davis
6408334c51SBrooks Davis namespace datetime = utils::datetime;
6508334c51SBrooks Davis namespace executor = utils::process::executor;
6608334c51SBrooks Davis namespace fs = utils::fs;
6708334c51SBrooks Davis namespace passwd = utils::passwd;
6808334c51SBrooks Davis namespace process = utils::process;
6908334c51SBrooks Davis namespace signals = utils::signals;
7008334c51SBrooks Davis namespace text = utils::text;
7108334c51SBrooks Davis
7208334c51SBrooks Davis using utils::none;
7308334c51SBrooks Davis using utils::optional;
7408334c51SBrooks Davis
7508334c51SBrooks Davis
7608334c51SBrooks Davis /// Large timeout for the processes we spawn.
7708334c51SBrooks Davis ///
7808334c51SBrooks Davis /// This number is supposed to be (much) larger than the timeout of the test
7908334c51SBrooks Davis /// cases that use it so that children processes are not killed spuriously.
8008334c51SBrooks Davis static const datetime::delta infinite_timeout(1000000, 0);
8108334c51SBrooks Davis
8208334c51SBrooks Davis
8308334c51SBrooks Davis static void do_exit(const int) UTILS_NORETURN;
8408334c51SBrooks Davis
8508334c51SBrooks Davis
8608334c51SBrooks Davis /// Terminates a subprocess without invoking destructors.
8708334c51SBrooks Davis ///
8808334c51SBrooks Davis /// This is just a simple wrapper over _exit(2) because we cannot use std::exit
8908334c51SBrooks Davis /// on exit from a subprocess. The reason is that we do not want to invoke any
9008334c51SBrooks Davis /// destructors as otherwise we'd clear up the global executor state by mistake.
9108334c51SBrooks Davis /// This wouldn't be a major problem if it wasn't because doing so deletes
9208334c51SBrooks Davis /// on-disk files and we want to leave them in place so that the parent process
9308334c51SBrooks Davis /// can test for them!
9408334c51SBrooks Davis ///
9508334c51SBrooks Davis /// \param exit_code Code to exit with.
9608334c51SBrooks Davis static void
do_exit(const int exit_code)9708334c51SBrooks Davis do_exit(const int exit_code)
9808334c51SBrooks Davis {
9908334c51SBrooks Davis std::cout.flush();
10008334c51SBrooks Davis std::cerr.flush();
10108334c51SBrooks Davis ::_exit(exit_code);
10208334c51SBrooks Davis }
10308334c51SBrooks Davis
10408334c51SBrooks Davis
10508334c51SBrooks Davis /// Subprocess that creates a cookie file in its work directory.
10608334c51SBrooks Davis class child_create_cookie {
10708334c51SBrooks Davis /// Name of the cookie to create.
10808334c51SBrooks Davis const std::string _cookie_name;
10908334c51SBrooks Davis
11008334c51SBrooks Davis public:
11108334c51SBrooks Davis /// Constructor.
11208334c51SBrooks Davis ///
11308334c51SBrooks Davis /// \param cookie_name Name of the cookie to create.
child_create_cookie(const std::string & cookie_name)11408334c51SBrooks Davis child_create_cookie(const std::string& cookie_name) :
11508334c51SBrooks Davis _cookie_name(cookie_name)
11608334c51SBrooks Davis {
11708334c51SBrooks Davis }
11808334c51SBrooks Davis
11908334c51SBrooks Davis /// Runs the subprocess.
12008334c51SBrooks Davis void
operator ()(const fs::path &)12108334c51SBrooks Davis operator()(const fs::path& /* control_directory */)
12208334c51SBrooks Davis UTILS_NORETURN
12308334c51SBrooks Davis {
12408334c51SBrooks Davis std::cout << "Creating cookie: " << _cookie_name << " (stdout)\n";
12508334c51SBrooks Davis std::cerr << "Creating cookie: " << _cookie_name << " (stderr)\n";
12608334c51SBrooks Davis atf::utils::create_file(_cookie_name, "");
12708334c51SBrooks Davis do_exit(EXIT_SUCCESS);
12808334c51SBrooks Davis }
12908334c51SBrooks Davis };
13008334c51SBrooks Davis
13108334c51SBrooks Davis
13208334c51SBrooks Davis static void child_delete_all(const fs::path&) UTILS_NORETURN;
13308334c51SBrooks Davis
13408334c51SBrooks Davis
13508334c51SBrooks Davis /// Subprocess that deletes all files in the current directory.
13608334c51SBrooks Davis ///
13708334c51SBrooks Davis /// This is intended to validate that the test runs in an empty directory,
13808334c51SBrooks Davis /// separate from any control files that the executor may have created.
13908334c51SBrooks Davis ///
14008334c51SBrooks Davis /// \param control_directory Directory where control files separate from the
14108334c51SBrooks Davis /// work directory can be placed.
14208334c51SBrooks Davis static void
child_delete_all(const fs::path & control_directory)14308334c51SBrooks Davis child_delete_all(const fs::path& control_directory)
14408334c51SBrooks Davis {
14508334c51SBrooks Davis const fs::path cookie = control_directory / "exec_was_called";
14608334c51SBrooks Davis std::ofstream control_file(cookie.c_str());
14708334c51SBrooks Davis if (!control_file) {
14808334c51SBrooks Davis std::cerr << "Failed to create " << cookie << '\n';
14908334c51SBrooks Davis std::abort();
15008334c51SBrooks Davis }
15108334c51SBrooks Davis
15208334c51SBrooks Davis const int exit_code = ::system("rm *") == -1
15308334c51SBrooks Davis ? EXIT_FAILURE : EXIT_SUCCESS;
15408334c51SBrooks Davis do_exit(exit_code);
15508334c51SBrooks Davis }
15608334c51SBrooks Davis
15708334c51SBrooks Davis
15808334c51SBrooks Davis static void child_dump_unprivileged_user(const fs::path&) UTILS_NORETURN;
15908334c51SBrooks Davis
16008334c51SBrooks Davis
16108334c51SBrooks Davis /// Subprocess that dumps user configuration.
16208334c51SBrooks Davis static void
child_dump_unprivileged_user(const fs::path &)16308334c51SBrooks Davis child_dump_unprivileged_user(const fs::path& /* control_directory */)
16408334c51SBrooks Davis {
16508334c51SBrooks Davis const passwd::user current_user = passwd::current_user();
16608334c51SBrooks Davis std::cout << F("UID = %s\n") % current_user.uid;
16708334c51SBrooks Davis do_exit(EXIT_SUCCESS);
16808334c51SBrooks Davis }
16908334c51SBrooks Davis
17008334c51SBrooks Davis
17108334c51SBrooks Davis /// Subprocess that returns a specific exit code.
17208334c51SBrooks Davis class child_exit {
17308334c51SBrooks Davis /// Exit code to return.
17408334c51SBrooks Davis int _exit_code;
17508334c51SBrooks Davis
17608334c51SBrooks Davis public:
17708334c51SBrooks Davis /// Constructor.
17808334c51SBrooks Davis ///
17908334c51SBrooks Davis /// \param exit_code Exit code to return.
child_exit(const int exit_code)18008334c51SBrooks Davis child_exit(const int exit_code) : _exit_code(exit_code)
18108334c51SBrooks Davis {
18208334c51SBrooks Davis }
18308334c51SBrooks Davis
18408334c51SBrooks Davis /// Runs the subprocess.
18508334c51SBrooks Davis void
operator ()(const fs::path &)18608334c51SBrooks Davis operator()(const fs::path& /* control_directory */)
18708334c51SBrooks Davis UTILS_NORETURN
18808334c51SBrooks Davis {
18908334c51SBrooks Davis do_exit(_exit_code);
19008334c51SBrooks Davis }
19108334c51SBrooks Davis };
19208334c51SBrooks Davis
19308334c51SBrooks Davis
19408334c51SBrooks Davis static void child_pause(const fs::path&) UTILS_NORETURN;
19508334c51SBrooks Davis
19608334c51SBrooks Davis
19708334c51SBrooks Davis /// Subprocess that just blocks.
19808334c51SBrooks Davis static void
child_pause(const fs::path &)19908334c51SBrooks Davis child_pause(const fs::path& /* control_directory */)
20008334c51SBrooks Davis {
20108334c51SBrooks Davis sigset_t mask;
20208334c51SBrooks Davis sigemptyset(&mask);
20308334c51SBrooks Davis for (;;) {
20408334c51SBrooks Davis ::sigsuspend(&mask);
20508334c51SBrooks Davis }
20608334c51SBrooks Davis std::abort();
20708334c51SBrooks Davis }
20808334c51SBrooks Davis
20908334c51SBrooks Davis
21008334c51SBrooks Davis static void child_print(const fs::path&) UTILS_NORETURN;
21108334c51SBrooks Davis
21208334c51SBrooks Davis
21308334c51SBrooks Davis /// Subprocess that writes to stdout and stderr.
21408334c51SBrooks Davis static void
child_print(const fs::path &)21508334c51SBrooks Davis child_print(const fs::path& /* control_directory */)
21608334c51SBrooks Davis {
21708334c51SBrooks Davis std::cout << "stdout: some text\n";
21808334c51SBrooks Davis std::cerr << "stderr: some other text\n";
21908334c51SBrooks Davis
22008334c51SBrooks Davis do_exit(EXIT_SUCCESS);
22108334c51SBrooks Davis }
22208334c51SBrooks Davis
22308334c51SBrooks Davis
22408334c51SBrooks Davis /// Subprocess that sleeps for a period of time before exiting.
22508334c51SBrooks Davis class child_sleep {
22608334c51SBrooks Davis /// Seconds to sleep for before termination.
22708334c51SBrooks Davis int _seconds;
22808334c51SBrooks Davis
22908334c51SBrooks Davis public:
23008334c51SBrooks Davis /// Construtor.
23108334c51SBrooks Davis ///
23208334c51SBrooks Davis /// \param seconds Seconds to sleep for before termination.
child_sleep(const int seconds)23308334c51SBrooks Davis child_sleep(const int seconds) : _seconds(seconds)
23408334c51SBrooks Davis {
23508334c51SBrooks Davis }
23608334c51SBrooks Davis
23708334c51SBrooks Davis /// Runs the subprocess.
23808334c51SBrooks Davis void
operator ()(const fs::path &)23908334c51SBrooks Davis operator()(const fs::path& /* control_directory */)
24008334c51SBrooks Davis UTILS_NORETURN
24108334c51SBrooks Davis {
24208334c51SBrooks Davis ::sleep(_seconds);
24308334c51SBrooks Davis do_exit(EXIT_SUCCESS);
24408334c51SBrooks Davis }
24508334c51SBrooks Davis };
24608334c51SBrooks Davis
24708334c51SBrooks Davis
24808334c51SBrooks Davis static void child_spawn_blocking_child(const fs::path&) UTILS_NORETURN;
24908334c51SBrooks Davis
25008334c51SBrooks Davis
25108334c51SBrooks Davis /// Subprocess that spawns a subchild that gets stuck.
25208334c51SBrooks Davis ///
25308334c51SBrooks Davis /// Used by the caller to validate that the whole process tree is terminated
25408334c51SBrooks Davis /// when this subprocess is killed.
25508334c51SBrooks Davis static void
child_spawn_blocking_child(const fs::path &)25608334c51SBrooks Davis child_spawn_blocking_child(
25708334c51SBrooks Davis const fs::path& /* control_directory */)
25808334c51SBrooks Davis {
25908334c51SBrooks Davis pid_t pid = ::fork();
26008334c51SBrooks Davis if (pid == -1) {
26108334c51SBrooks Davis std::cerr << "Cannot fork subprocess\n";
26208334c51SBrooks Davis do_exit(EXIT_FAILURE);
26308334c51SBrooks Davis } else if (pid == 0) {
26408334c51SBrooks Davis for (;;)
26508334c51SBrooks Davis ::pause();
26608334c51SBrooks Davis } else {
26708334c51SBrooks Davis const fs::path name = fs::path(utils::getenv("CONTROL_DIR").get()) /
26808334c51SBrooks Davis "pid";
26908334c51SBrooks Davis std::ofstream pidfile(name.c_str());
27008334c51SBrooks Davis if (!pidfile) {
27108334c51SBrooks Davis std::cerr << "Failed to create the pidfile\n";
27208334c51SBrooks Davis do_exit(EXIT_FAILURE);
27308334c51SBrooks Davis }
27408334c51SBrooks Davis pidfile << pid;
27508334c51SBrooks Davis pidfile.close();
27608334c51SBrooks Davis do_exit(EXIT_SUCCESS);
27708334c51SBrooks Davis }
27808334c51SBrooks Davis }
27908334c51SBrooks Davis
28008334c51SBrooks Davis
28108334c51SBrooks Davis static void child_validate_isolation(const fs::path&) UTILS_NORETURN;
28208334c51SBrooks Davis
28308334c51SBrooks Davis
28408334c51SBrooks Davis /// Subprocess that checks if isolate_child() has been called.
28508334c51SBrooks Davis static void
child_validate_isolation(const fs::path &)28608334c51SBrooks Davis child_validate_isolation(const fs::path& /* control_directory */)
28708334c51SBrooks Davis {
28808334c51SBrooks Davis if (utils::getenv("HOME").get() == "fake-value") {
28908334c51SBrooks Davis std::cerr << "HOME not reset\n";
29008334c51SBrooks Davis do_exit(EXIT_FAILURE);
29108334c51SBrooks Davis }
29208334c51SBrooks Davis if (utils::getenv("LANG")) {
29308334c51SBrooks Davis std::cerr << "LANG not unset\n";
29408334c51SBrooks Davis do_exit(EXIT_FAILURE);
29508334c51SBrooks Davis }
29608334c51SBrooks Davis do_exit(EXIT_SUCCESS);
29708334c51SBrooks Davis }
29808334c51SBrooks Davis
29908334c51SBrooks Davis
30008334c51SBrooks Davis /// Invokes executor::spawn() with default arguments.
30108334c51SBrooks Davis ///
30208334c51SBrooks Davis /// \param handle The executor on which to invoke spawn().
30308334c51SBrooks Davis /// \param args Arguments to the binary.
30408334c51SBrooks Davis /// \param timeout Maximum time the program can run for.
30508334c51SBrooks Davis /// \param unprivileged_user If set, user to switch to when running the child
30608334c51SBrooks Davis /// program.
30708334c51SBrooks Davis /// \param stdout_target If not none, file to which to write the stdout of the
30808334c51SBrooks Davis /// test case.
30908334c51SBrooks Davis /// \param stderr_target If not none, file to which to write the stderr of the
31008334c51SBrooks Davis /// test case.
31108334c51SBrooks Davis ///
31208334c51SBrooks Davis /// \return The exec handle for the spawned binary.
31308334c51SBrooks Davis template< class Hook >
31408334c51SBrooks Davis static executor::exec_handle
do_spawn(executor::executor_handle & handle,Hook hook,const datetime::delta & timeout=infinite_timeout,const optional<passwd::user> unprivileged_user=none,const optional<fs::path> stdout_target=none,const optional<fs::path> stderr_target=none)31508334c51SBrooks Davis do_spawn(executor::executor_handle& handle, Hook hook,
31608334c51SBrooks Davis const datetime::delta& timeout = infinite_timeout,
31708334c51SBrooks Davis const optional< passwd::user > unprivileged_user = none,
31808334c51SBrooks Davis const optional< fs::path > stdout_target = none,
31908334c51SBrooks Davis const optional< fs::path > stderr_target = none)
32008334c51SBrooks Davis {
32108334c51SBrooks Davis const executor::exec_handle exec_handle = handle.spawn< Hook >(
32208334c51SBrooks Davis hook, timeout, unprivileged_user, stdout_target, stderr_target);
32308334c51SBrooks Davis return exec_handle;
32408334c51SBrooks Davis }
32508334c51SBrooks Davis
32608334c51SBrooks Davis
32708334c51SBrooks Davis /// Checks for a specific exit status in the status of a exit_handle.
32808334c51SBrooks Davis ///
32908334c51SBrooks Davis /// \param exit_status The expected exit status.
33008334c51SBrooks Davis /// \param status The value of exit_handle.status().
33108334c51SBrooks Davis ///
33208334c51SBrooks Davis /// \post Terminates the calling test case if the status does not match the
33308334c51SBrooks Davis /// required value.
33408334c51SBrooks Davis static void
require_exit(const int exit_status,const optional<process::status> status)33508334c51SBrooks Davis require_exit(const int exit_status, const optional< process::status > status)
33608334c51SBrooks Davis {
33708334c51SBrooks Davis ATF_REQUIRE(status);
33808334c51SBrooks Davis ATF_REQUIRE(status.get().exited());
33908334c51SBrooks Davis ATF_REQUIRE_EQ(exit_status, status.get().exitstatus());
34008334c51SBrooks Davis }
34108334c51SBrooks Davis
34208334c51SBrooks Davis
34308334c51SBrooks Davis /// Ensures that a killed process is gone.
34408334c51SBrooks Davis ///
34508334c51SBrooks Davis /// The way we do this is by sending an idempotent signal to the given PID
34608334c51SBrooks Davis /// and checking if the signal was delivered. If it was, the process is
34708334c51SBrooks Davis /// still alive; if it was not, then it is gone.
34808334c51SBrooks Davis ///
34908334c51SBrooks Davis /// Note that this might be inaccurate for two reasons:
35008334c51SBrooks Davis ///
35108334c51SBrooks Davis /// 1) The system may have spawned a new process with the same pid as
35208334c51SBrooks Davis /// our subchild... but in practice, this does not happen because
35308334c51SBrooks Davis /// most systems do not immediately reuse pid numbers. If that
35408334c51SBrooks Davis /// happens... well, we get a false test failure.
35508334c51SBrooks Davis ///
35608334c51SBrooks Davis /// 2) We ran so fast that even if the process was sent a signal to
35708334c51SBrooks Davis /// die, it has not had enough time to process it yet. This is why
35808334c51SBrooks Davis /// we retry this a few times.
35908334c51SBrooks Davis ///
36008334c51SBrooks Davis /// \param pid PID of the process to check.
36108334c51SBrooks Davis static void
ensure_dead(const pid_t pid)36208334c51SBrooks Davis ensure_dead(const pid_t pid)
36308334c51SBrooks Davis {
36408334c51SBrooks Davis int attempts = 30;
36508334c51SBrooks Davis retry:
36608334c51SBrooks Davis if (::kill(pid, SIGCONT) != -1 || errno != ESRCH) {
36708334c51SBrooks Davis if (attempts > 0) {
36808334c51SBrooks Davis std::cout << "Subprocess not dead yet; retrying wait\n";
36908334c51SBrooks Davis --attempts;
37008334c51SBrooks Davis ::usleep(100000);
37108334c51SBrooks Davis goto retry;
37208334c51SBrooks Davis }
37308334c51SBrooks Davis ATF_FAIL(F("The subprocess %s of our child was not killed") % pid);
37408334c51SBrooks Davis }
37508334c51SBrooks Davis }
37608334c51SBrooks Davis
37708334c51SBrooks Davis
37808334c51SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__run_one);
ATF_TEST_CASE_BODY(integration__run_one)37908334c51SBrooks Davis ATF_TEST_CASE_BODY(integration__run_one)
38008334c51SBrooks Davis {
38108334c51SBrooks Davis executor::executor_handle handle = executor::setup();
38208334c51SBrooks Davis
38308334c51SBrooks Davis const executor::exec_handle exec_handle = do_spawn(handle, child_exit(41));
38408334c51SBrooks Davis
38508334c51SBrooks Davis executor::exit_handle exit_handle = handle.wait_any();
38608334c51SBrooks Davis ATF_REQUIRE_EQ(exec_handle.pid(), exit_handle.original_pid());
38708334c51SBrooks Davis require_exit(41, exit_handle.status());
38808334c51SBrooks Davis exit_handle.cleanup();
38908334c51SBrooks Davis
39008334c51SBrooks Davis handle.cleanup();
39108334c51SBrooks Davis }
39208334c51SBrooks Davis
39308334c51SBrooks Davis
39408334c51SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__run_many);
ATF_TEST_CASE_BODY(integration__run_many)39508334c51SBrooks Davis ATF_TEST_CASE_BODY(integration__run_many)
39608334c51SBrooks Davis {
39708334c51SBrooks Davis static const std::size_t num_children = 30;
39808334c51SBrooks Davis
39908334c51SBrooks Davis executor::executor_handle handle = executor::setup();
40008334c51SBrooks Davis
40108334c51SBrooks Davis std::size_t total_children = 0;
40208334c51SBrooks Davis std::map< int, int > exp_exit_statuses;
40308334c51SBrooks Davis std::map< int, datetime::timestamp > exp_start_times;
40408334c51SBrooks Davis for (std::size_t i = 0; i < num_children; ++i) {
40508334c51SBrooks Davis const datetime::timestamp start_time = datetime::timestamp::from_values(
40608334c51SBrooks Davis 2014, 12, 8, 9, 40, 0, i);
40708334c51SBrooks Davis
40808334c51SBrooks Davis for (std::size_t j = 0; j < 3; j++) {
40908334c51SBrooks Davis const std::size_t id = i * 3 + j;
41008334c51SBrooks Davis
41108334c51SBrooks Davis datetime::set_mock_now(start_time);
41208334c51SBrooks Davis const int pid = do_spawn(handle, child_exit(id)).pid();
41308334c51SBrooks Davis exp_exit_statuses.insert(std::make_pair(pid, id));
41408334c51SBrooks Davis exp_start_times.insert(std::make_pair(pid, start_time));
41508334c51SBrooks Davis ++total_children;
41608334c51SBrooks Davis }
41708334c51SBrooks Davis }
41808334c51SBrooks Davis
41908334c51SBrooks Davis for (std::size_t i = 0; i < total_children; ++i) {
42008334c51SBrooks Davis const datetime::timestamp end_time = datetime::timestamp::from_values(
42108334c51SBrooks Davis 2014, 12, 8, 9, 50, 10, i);
42208334c51SBrooks Davis datetime::set_mock_now(end_time);
42308334c51SBrooks Davis executor::exit_handle exit_handle = handle.wait_any();
42408334c51SBrooks Davis const int original_pid = exit_handle.original_pid();
42508334c51SBrooks Davis
42608334c51SBrooks Davis const int exit_status = exp_exit_statuses.find(original_pid)->second;
42708334c51SBrooks Davis const datetime::timestamp& start_time = exp_start_times.find(
42808334c51SBrooks Davis original_pid)->second;
42908334c51SBrooks Davis
43008334c51SBrooks Davis require_exit(exit_status, exit_handle.status());
43108334c51SBrooks Davis
43208334c51SBrooks Davis ATF_REQUIRE_EQ(start_time, exit_handle.start_time());
43308334c51SBrooks Davis ATF_REQUIRE_EQ(end_time, exit_handle.end_time());
43408334c51SBrooks Davis
43508334c51SBrooks Davis exit_handle.cleanup();
43608334c51SBrooks Davis
43708334c51SBrooks Davis ATF_REQUIRE(!atf::utils::file_exists(
43808334c51SBrooks Davis exit_handle.stdout_file().str()));
43908334c51SBrooks Davis ATF_REQUIRE(!atf::utils::file_exists(
44008334c51SBrooks Davis exit_handle.stderr_file().str()));
44108334c51SBrooks Davis ATF_REQUIRE(!atf::utils::file_exists(
44208334c51SBrooks Davis exit_handle.work_directory().str()));
44308334c51SBrooks Davis }
44408334c51SBrooks Davis
44508334c51SBrooks Davis handle.cleanup();
44608334c51SBrooks Davis }
44708334c51SBrooks Davis
44808334c51SBrooks Davis
44908334c51SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__parameters_and_output);
ATF_TEST_CASE_BODY(integration__parameters_and_output)45008334c51SBrooks Davis ATF_TEST_CASE_BODY(integration__parameters_and_output)
45108334c51SBrooks Davis {
45208334c51SBrooks Davis executor::executor_handle handle = executor::setup();
45308334c51SBrooks Davis
45408334c51SBrooks Davis const executor::exec_handle exec_handle = do_spawn(handle, child_print);
45508334c51SBrooks Davis
45608334c51SBrooks Davis executor::exit_handle exit_handle = handle.wait_any();
45708334c51SBrooks Davis
45808334c51SBrooks Davis ATF_REQUIRE_EQ(exec_handle.pid(), exit_handle.original_pid());
45908334c51SBrooks Davis
46008334c51SBrooks Davis require_exit(EXIT_SUCCESS, exit_handle.status());
46108334c51SBrooks Davis
46208334c51SBrooks Davis const fs::path stdout_file = exit_handle.stdout_file();
46308334c51SBrooks Davis ATF_REQUIRE(atf::utils::compare_file(
46408334c51SBrooks Davis stdout_file.str(), "stdout: some text\n"));
46508334c51SBrooks Davis const fs::path stderr_file = exit_handle.stderr_file();
46608334c51SBrooks Davis ATF_REQUIRE(atf::utils::compare_file(
46708334c51SBrooks Davis stderr_file.str(), "stderr: some other text\n"));
46808334c51SBrooks Davis
46908334c51SBrooks Davis exit_handle.cleanup();
47008334c51SBrooks Davis ATF_REQUIRE(!fs::exists(stdout_file));
47108334c51SBrooks Davis ATF_REQUIRE(!fs::exists(stderr_file));
47208334c51SBrooks Davis
47308334c51SBrooks Davis handle.cleanup();
47408334c51SBrooks Davis }
47508334c51SBrooks Davis
47608334c51SBrooks Davis
47708334c51SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__custom_output_files);
ATF_TEST_CASE_BODY(integration__custom_output_files)47808334c51SBrooks Davis ATF_TEST_CASE_BODY(integration__custom_output_files)
47908334c51SBrooks Davis {
48008334c51SBrooks Davis executor::executor_handle handle = executor::setup();
48108334c51SBrooks Davis
48208334c51SBrooks Davis const fs::path stdout_file("custom-stdout.txt");
48308334c51SBrooks Davis const fs::path stderr_file("custom-stderr.txt");
48408334c51SBrooks Davis
48508334c51SBrooks Davis const executor::exec_handle exec_handle = do_spawn(
48608334c51SBrooks Davis handle, child_print, infinite_timeout, none,
48708334c51SBrooks Davis utils::make_optional(stdout_file),
48808334c51SBrooks Davis utils::make_optional(stderr_file));
48908334c51SBrooks Davis
49008334c51SBrooks Davis executor::exit_handle exit_handle = handle.wait_any();
49108334c51SBrooks Davis
49208334c51SBrooks Davis ATF_REQUIRE_EQ(exec_handle.pid(), exit_handle.original_pid());
49308334c51SBrooks Davis
49408334c51SBrooks Davis require_exit(EXIT_SUCCESS, exit_handle.status());
49508334c51SBrooks Davis
49608334c51SBrooks Davis ATF_REQUIRE_EQ(stdout_file, exit_handle.stdout_file());
49708334c51SBrooks Davis ATF_REQUIRE_EQ(stderr_file, exit_handle.stderr_file());
49808334c51SBrooks Davis
49908334c51SBrooks Davis exit_handle.cleanup();
50008334c51SBrooks Davis
50108334c51SBrooks Davis handle.cleanup();
50208334c51SBrooks Davis
50308334c51SBrooks Davis // Must compare after cleanup to ensure the files did not get deleted.
50408334c51SBrooks Davis ATF_REQUIRE(atf::utils::compare_file(
50508334c51SBrooks Davis stdout_file.str(), "stdout: some text\n"));
50608334c51SBrooks Davis ATF_REQUIRE(atf::utils::compare_file(
50708334c51SBrooks Davis stderr_file.str(), "stderr: some other text\n"));
50808334c51SBrooks Davis }
50908334c51SBrooks Davis
51008334c51SBrooks Davis
51108334c51SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__timestamps);
ATF_TEST_CASE_BODY(integration__timestamps)51208334c51SBrooks Davis ATF_TEST_CASE_BODY(integration__timestamps)
51308334c51SBrooks Davis {
51408334c51SBrooks Davis executor::executor_handle handle = executor::setup();
51508334c51SBrooks Davis
51608334c51SBrooks Davis const datetime::timestamp start_time = datetime::timestamp::from_values(
51708334c51SBrooks Davis 2014, 12, 8, 9, 35, 10, 1000);
51808334c51SBrooks Davis const datetime::timestamp end_time = datetime::timestamp::from_values(
51908334c51SBrooks Davis 2014, 12, 8, 9, 35, 20, 2000);
52008334c51SBrooks Davis
52108334c51SBrooks Davis datetime::set_mock_now(start_time);
52208334c51SBrooks Davis do_spawn(handle, child_exit(70));
52308334c51SBrooks Davis
52408334c51SBrooks Davis datetime::set_mock_now(end_time);
52508334c51SBrooks Davis executor::exit_handle exit_handle = handle.wait_any();
52608334c51SBrooks Davis
52708334c51SBrooks Davis require_exit(70, exit_handle.status());
52808334c51SBrooks Davis
52908334c51SBrooks Davis ATF_REQUIRE_EQ(start_time, exit_handle.start_time());
53008334c51SBrooks Davis ATF_REQUIRE_EQ(end_time, exit_handle.end_time());
53108334c51SBrooks Davis exit_handle.cleanup();
53208334c51SBrooks Davis
53308334c51SBrooks Davis handle.cleanup();
53408334c51SBrooks Davis }
53508334c51SBrooks Davis
53608334c51SBrooks Davis
53708334c51SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__files);
ATF_TEST_CASE_BODY(integration__files)53808334c51SBrooks Davis ATF_TEST_CASE_BODY(integration__files)
53908334c51SBrooks Davis {
54008334c51SBrooks Davis executor::executor_handle handle = executor::setup();
54108334c51SBrooks Davis
54208334c51SBrooks Davis do_spawn(handle, child_create_cookie("cookie.12345"));
54308334c51SBrooks Davis
54408334c51SBrooks Davis executor::exit_handle exit_handle = handle.wait_any();
54508334c51SBrooks Davis
54608334c51SBrooks Davis ATF_REQUIRE(atf::utils::file_exists(
54708334c51SBrooks Davis (exit_handle.work_directory() / "cookie.12345").str()));
54808334c51SBrooks Davis
54908334c51SBrooks Davis exit_handle.cleanup();
55008334c51SBrooks Davis
55108334c51SBrooks Davis ATF_REQUIRE(!atf::utils::file_exists(exit_handle.stdout_file().str()));
55208334c51SBrooks Davis ATF_REQUIRE(!atf::utils::file_exists(exit_handle.stderr_file().str()));
55308334c51SBrooks Davis ATF_REQUIRE(!atf::utils::file_exists(exit_handle.work_directory().str()));
55408334c51SBrooks Davis
55508334c51SBrooks Davis handle.cleanup();
55608334c51SBrooks Davis }
55708334c51SBrooks Davis
55808334c51SBrooks Davis
55908334c51SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__followup);
ATF_TEST_CASE_BODY(integration__followup)56008334c51SBrooks Davis ATF_TEST_CASE_BODY(integration__followup)
56108334c51SBrooks Davis {
56208334c51SBrooks Davis executor::executor_handle handle = executor::setup();
56308334c51SBrooks Davis
56408334c51SBrooks Davis (void)handle.spawn(child_create_cookie("cookie.1"), infinite_timeout, none);
56508334c51SBrooks Davis executor::exit_handle exit_1_handle = handle.wait_any();
56608334c51SBrooks Davis
56708334c51SBrooks Davis (void)handle.spawn_followup(child_create_cookie("cookie.2"), exit_1_handle,
56808334c51SBrooks Davis infinite_timeout);
56908334c51SBrooks Davis executor::exit_handle exit_2_handle = handle.wait_any();
57008334c51SBrooks Davis
57108334c51SBrooks Davis ATF_REQUIRE_EQ(exit_1_handle.stdout_file(), exit_2_handle.stdout_file());
57208334c51SBrooks Davis ATF_REQUIRE_EQ(exit_1_handle.stderr_file(), exit_2_handle.stderr_file());
57308334c51SBrooks Davis ATF_REQUIRE_EQ(exit_1_handle.control_directory(),
57408334c51SBrooks Davis exit_2_handle.control_directory());
57508334c51SBrooks Davis ATF_REQUIRE_EQ(exit_1_handle.work_directory(),
57608334c51SBrooks Davis exit_2_handle.work_directory());
57708334c51SBrooks Davis
57808334c51SBrooks Davis (void)handle.spawn_followup(child_create_cookie("cookie.3"), exit_2_handle,
57908334c51SBrooks Davis infinite_timeout);
58008334c51SBrooks Davis exit_2_handle.cleanup();
58108334c51SBrooks Davis exit_1_handle.cleanup();
58208334c51SBrooks Davis executor::exit_handle exit_3_handle = handle.wait_any();
58308334c51SBrooks Davis
58408334c51SBrooks Davis ATF_REQUIRE_EQ(exit_1_handle.stdout_file(), exit_3_handle.stdout_file());
58508334c51SBrooks Davis ATF_REQUIRE_EQ(exit_1_handle.stderr_file(), exit_3_handle.stderr_file());
58608334c51SBrooks Davis ATF_REQUIRE_EQ(exit_1_handle.control_directory(),
58708334c51SBrooks Davis exit_3_handle.control_directory());
58808334c51SBrooks Davis ATF_REQUIRE_EQ(exit_1_handle.work_directory(),
58908334c51SBrooks Davis exit_3_handle.work_directory());
59008334c51SBrooks Davis
59108334c51SBrooks Davis ATF_REQUIRE(atf::utils::file_exists(
59208334c51SBrooks Davis (exit_1_handle.work_directory() / "cookie.1").str()));
59308334c51SBrooks Davis ATF_REQUIRE(atf::utils::file_exists(
59408334c51SBrooks Davis (exit_1_handle.work_directory() / "cookie.2").str()));
59508334c51SBrooks Davis ATF_REQUIRE(atf::utils::file_exists(
59608334c51SBrooks Davis (exit_1_handle.work_directory() / "cookie.3").str()));
59708334c51SBrooks Davis
59808334c51SBrooks Davis ATF_REQUIRE(atf::utils::compare_file(
59908334c51SBrooks Davis exit_1_handle.stdout_file().str(),
60008334c51SBrooks Davis "Creating cookie: cookie.1 (stdout)\n"
60108334c51SBrooks Davis "Creating cookie: cookie.2 (stdout)\n"
60208334c51SBrooks Davis "Creating cookie: cookie.3 (stdout)\n"));
60308334c51SBrooks Davis
60408334c51SBrooks Davis ATF_REQUIRE(atf::utils::compare_file(
60508334c51SBrooks Davis exit_1_handle.stderr_file().str(),
60608334c51SBrooks Davis "Creating cookie: cookie.1 (stderr)\n"
60708334c51SBrooks Davis "Creating cookie: cookie.2 (stderr)\n"
60808334c51SBrooks Davis "Creating cookie: cookie.3 (stderr)\n"));
60908334c51SBrooks Davis
61008334c51SBrooks Davis exit_3_handle.cleanup();
61108334c51SBrooks Davis
61208334c51SBrooks Davis ATF_REQUIRE(!atf::utils::file_exists(exit_1_handle.stdout_file().str()));
61308334c51SBrooks Davis ATF_REQUIRE(!atf::utils::file_exists(exit_1_handle.stderr_file().str()));
61408334c51SBrooks Davis ATF_REQUIRE(!atf::utils::file_exists(exit_1_handle.work_directory().str()));
61508334c51SBrooks Davis
61608334c51SBrooks Davis handle.cleanup();
61708334c51SBrooks Davis }
61808334c51SBrooks Davis
61908334c51SBrooks Davis
62008334c51SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__output_files_always_exist);
ATF_TEST_CASE_BODY(integration__output_files_always_exist)62108334c51SBrooks Davis ATF_TEST_CASE_BODY(integration__output_files_always_exist)
62208334c51SBrooks Davis {
62308334c51SBrooks Davis executor::executor_handle handle = executor::setup();
62408334c51SBrooks Davis
62508334c51SBrooks Davis // This test is racy: we specify a very short timeout for the subprocess so
62608334c51SBrooks Davis // that we cause the subprocess to exit before it has had time to set up the
62708334c51SBrooks Davis // output files. However, for scheduling reasons, the subprocess may
62808334c51SBrooks Davis // actually run to completion before the timer triggers. Retry this a few
62908334c51SBrooks Davis // times to attempt to catch a "good test".
63008334c51SBrooks Davis for (int i = 0; i < 50; i++) {
63108334c51SBrooks Davis const executor::exec_handle exec_handle =
63208334c51SBrooks Davis do_spawn(handle, child_exit(0), datetime::delta(0, 100000));
63308334c51SBrooks Davis executor::exit_handle exit_handle = handle.wait(exec_handle);
63408334c51SBrooks Davis ATF_REQUIRE(fs::exists(exit_handle.stdout_file()));
63508334c51SBrooks Davis ATF_REQUIRE(fs::exists(exit_handle.stderr_file()));
63608334c51SBrooks Davis exit_handle.cleanup();
63708334c51SBrooks Davis }
63808334c51SBrooks Davis
63908334c51SBrooks Davis handle.cleanup();
64008334c51SBrooks Davis }
64108334c51SBrooks Davis
64208334c51SBrooks Davis
64308334c51SBrooks Davis ATF_TEST_CASE(integration__timeouts);
ATF_TEST_CASE_HEAD(integration__timeouts)64408334c51SBrooks Davis ATF_TEST_CASE_HEAD(integration__timeouts)
64508334c51SBrooks Davis {
64608334c51SBrooks Davis set_md_var("timeout", "60");
64708334c51SBrooks Davis }
ATF_TEST_CASE_BODY(integration__timeouts)64808334c51SBrooks Davis ATF_TEST_CASE_BODY(integration__timeouts)
64908334c51SBrooks Davis {
65008334c51SBrooks Davis executor::executor_handle handle = executor::setup();
65108334c51SBrooks Davis
65208334c51SBrooks Davis const executor::exec_handle exec_handle1 =
65308334c51SBrooks Davis do_spawn(handle, child_sleep(30), datetime::delta(2, 0));
65408334c51SBrooks Davis const executor::exec_handle exec_handle2 =
65508334c51SBrooks Davis do_spawn(handle, child_sleep(40), datetime::delta(5, 0));
65608334c51SBrooks Davis const executor::exec_handle exec_handle3 =
65708334c51SBrooks Davis do_spawn(handle, child_exit(15));
65808334c51SBrooks Davis
65908334c51SBrooks Davis {
66008334c51SBrooks Davis executor::exit_handle exit_handle = handle.wait_any();
66108334c51SBrooks Davis ATF_REQUIRE_EQ(exec_handle3.pid(), exit_handle.original_pid());
66208334c51SBrooks Davis require_exit(15, exit_handle.status());
66308334c51SBrooks Davis exit_handle.cleanup();
66408334c51SBrooks Davis }
66508334c51SBrooks Davis
66608334c51SBrooks Davis {
66708334c51SBrooks Davis executor::exit_handle exit_handle = handle.wait_any();
66808334c51SBrooks Davis ATF_REQUIRE_EQ(exec_handle1.pid(), exit_handle.original_pid());
66908334c51SBrooks Davis ATF_REQUIRE(!exit_handle.status());
67008334c51SBrooks Davis const datetime::delta duration =
67108334c51SBrooks Davis exit_handle.end_time() - exit_handle.start_time();
67208334c51SBrooks Davis ATF_REQUIRE(duration < datetime::delta(10, 0));
67308334c51SBrooks Davis ATF_REQUIRE(duration >= datetime::delta(2, 0));
67408334c51SBrooks Davis exit_handle.cleanup();
67508334c51SBrooks Davis }
67608334c51SBrooks Davis
67708334c51SBrooks Davis {
67808334c51SBrooks Davis executor::exit_handle exit_handle = handle.wait_any();
67908334c51SBrooks Davis ATF_REQUIRE_EQ(exec_handle2.pid(), exit_handle.original_pid());
68008334c51SBrooks Davis ATF_REQUIRE(!exit_handle.status());
68108334c51SBrooks Davis const datetime::delta duration =
68208334c51SBrooks Davis exit_handle.end_time() - exit_handle.start_time();
68308334c51SBrooks Davis ATF_REQUIRE(duration < datetime::delta(10, 0));
68408334c51SBrooks Davis ATF_REQUIRE(duration >= datetime::delta(4, 0));
68508334c51SBrooks Davis exit_handle.cleanup();
68608334c51SBrooks Davis }
68708334c51SBrooks Davis
68808334c51SBrooks Davis handle.cleanup();
68908334c51SBrooks Davis }
69008334c51SBrooks Davis
69108334c51SBrooks Davis
69208334c51SBrooks Davis ATF_TEST_CASE(integration__unprivileged_user);
ATF_TEST_CASE_HEAD(integration__unprivileged_user)69308334c51SBrooks Davis ATF_TEST_CASE_HEAD(integration__unprivileged_user)
69408334c51SBrooks Davis {
69508334c51SBrooks Davis set_md_var("require.config", "unprivileged-user");
69608334c51SBrooks Davis set_md_var("require.user", "root");
69708334c51SBrooks Davis }
ATF_TEST_CASE_BODY(integration__unprivileged_user)69808334c51SBrooks Davis ATF_TEST_CASE_BODY(integration__unprivileged_user)
69908334c51SBrooks Davis {
70008334c51SBrooks Davis executor::executor_handle handle = executor::setup();
70108334c51SBrooks Davis
70208334c51SBrooks Davis const passwd::user unprivileged_user = passwd::find_user_by_name(
70308334c51SBrooks Davis get_config_var("unprivileged-user"));
70408334c51SBrooks Davis
70508334c51SBrooks Davis do_spawn(handle, child_dump_unprivileged_user,
70608334c51SBrooks Davis infinite_timeout, utils::make_optional(unprivileged_user));
70708334c51SBrooks Davis
70808334c51SBrooks Davis executor::exit_handle exit_handle = handle.wait_any();
70908334c51SBrooks Davis ATF_REQUIRE(atf::utils::compare_file(
71008334c51SBrooks Davis exit_handle.stdout_file().str(),
71108334c51SBrooks Davis F("UID = %s\n") % unprivileged_user.uid));
71208334c51SBrooks Davis exit_handle.cleanup();
71308334c51SBrooks Davis
71408334c51SBrooks Davis handle.cleanup();
71508334c51SBrooks Davis }
71608334c51SBrooks Davis
71708334c51SBrooks Davis
71808334c51SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__auto_cleanup);
ATF_TEST_CASE_BODY(integration__auto_cleanup)71908334c51SBrooks Davis ATF_TEST_CASE_BODY(integration__auto_cleanup)
72008334c51SBrooks Davis {
72108334c51SBrooks Davis std::vector< int > pids;
72208334c51SBrooks Davis std::vector< fs::path > paths;
72308334c51SBrooks Davis {
72408334c51SBrooks Davis executor::executor_handle handle = executor::setup();
72508334c51SBrooks Davis
72608334c51SBrooks Davis pids.push_back(do_spawn(handle, child_exit(10)).pid());
72708334c51SBrooks Davis pids.push_back(do_spawn(handle, child_exit(20)).pid());
72808334c51SBrooks Davis
72908334c51SBrooks Davis // This invocation is never waited for below. This is intentional: we
73008334c51SBrooks Davis // want the destructor to clean the "leaked" test automatically so that
73108334c51SBrooks Davis // the clean up of the parent work directory also happens correctly.
73208334c51SBrooks Davis pids.push_back(do_spawn(handle, child_pause).pid());
73308334c51SBrooks Davis
73408334c51SBrooks Davis executor::exit_handle exit_handle1 = handle.wait_any();
73508334c51SBrooks Davis paths.push_back(exit_handle1.stdout_file());
73608334c51SBrooks Davis paths.push_back(exit_handle1.stderr_file());
73708334c51SBrooks Davis paths.push_back(exit_handle1.work_directory());
73808334c51SBrooks Davis
73908334c51SBrooks Davis executor::exit_handle exit_handle2 = handle.wait_any();
74008334c51SBrooks Davis paths.push_back(exit_handle2.stdout_file());
74108334c51SBrooks Davis paths.push_back(exit_handle2.stderr_file());
74208334c51SBrooks Davis paths.push_back(exit_handle2.work_directory());
74308334c51SBrooks Davis }
74408334c51SBrooks Davis for (std::vector< int >::const_iterator iter = pids.begin();
74508334c51SBrooks Davis iter != pids.end(); ++iter) {
74608334c51SBrooks Davis ensure_dead(*iter);
74708334c51SBrooks Davis }
74808334c51SBrooks Davis for (std::vector< fs::path >::const_iterator iter = paths.begin();
74908334c51SBrooks Davis iter != paths.end(); ++iter) {
75008334c51SBrooks Davis ATF_REQUIRE(!atf::utils::file_exists((*iter).str()));
75108334c51SBrooks Davis }
75208334c51SBrooks Davis }
75308334c51SBrooks Davis
75408334c51SBrooks Davis
75508334c51SBrooks Davis /// Ensures that interrupting an executor cleans things up correctly.
75608334c51SBrooks Davis ///
75708334c51SBrooks Davis /// This test scenario is tricky. We spawn a master child process that runs the
75808334c51SBrooks Davis /// executor code and we send a signal to it externally. The child process
75908334c51SBrooks Davis /// spawns a bunch of tests that block indefinitely and tries to wait for their
76008334c51SBrooks Davis /// results. When the signal is received, we expect an interrupt_error to be
76108334c51SBrooks Davis /// raised, which in turn should clean up all test resources and exit the master
76208334c51SBrooks Davis /// child process successfully.
76308334c51SBrooks Davis ///
76408334c51SBrooks Davis /// \param signo Signal to deliver to the executor.
76508334c51SBrooks Davis static void
do_signal_handling_test(const int signo)76608334c51SBrooks Davis do_signal_handling_test(const int signo)
76708334c51SBrooks Davis {
76808334c51SBrooks Davis static const char* cookie = "spawned.txt";
76908334c51SBrooks Davis
77008334c51SBrooks Davis const pid_t pid = ::fork();
77108334c51SBrooks Davis ATF_REQUIRE(pid != -1);
77208334c51SBrooks Davis if (pid == 0) {
77308334c51SBrooks Davis static const std::size_t num_children = 3;
77408334c51SBrooks Davis
77508334c51SBrooks Davis optional< fs::path > root_work_directory;
77608334c51SBrooks Davis try {
77708334c51SBrooks Davis executor::executor_handle handle = executor::setup();
77808334c51SBrooks Davis root_work_directory = handle.root_work_directory();
77908334c51SBrooks Davis
78008334c51SBrooks Davis for (std::size_t i = 0; i < num_children; ++i) {
78108334c51SBrooks Davis std::cout << "Spawned child number " << i << '\n';
78208334c51SBrooks Davis do_spawn(handle, child_pause);
78308334c51SBrooks Davis }
78408334c51SBrooks Davis
78508334c51SBrooks Davis std::cout << "Creating " << cookie << " cookie\n";
78608334c51SBrooks Davis atf::utils::create_file(cookie, "");
78708334c51SBrooks Davis
78808334c51SBrooks Davis std::cout << "Waiting for subprocess termination\n";
78908334c51SBrooks Davis for (std::size_t i = 0; i < num_children; ++i) {
79008334c51SBrooks Davis executor::exit_handle exit_handle = handle.wait_any();
79108334c51SBrooks Davis // We may never reach this point in the test, but if we do let's
79208334c51SBrooks Davis // make sure the subprocess was terminated as expected.
79308334c51SBrooks Davis if (exit_handle.status()) {
79408334c51SBrooks Davis if (exit_handle.status().get().signaled() &&
79508334c51SBrooks Davis exit_handle.status().get().termsig() == SIGKILL) {
79608334c51SBrooks Davis // OK.
79708334c51SBrooks Davis } else {
79808334c51SBrooks Davis std::cerr << "Child exited with unexpected code: "
79908334c51SBrooks Davis << exit_handle.status().get();
80008334c51SBrooks Davis std::exit(EXIT_FAILURE);
80108334c51SBrooks Davis }
80208334c51SBrooks Davis } else {
80308334c51SBrooks Davis std::cerr << "Child timed out\n";
80408334c51SBrooks Davis std::exit(EXIT_FAILURE);
80508334c51SBrooks Davis }
80608334c51SBrooks Davis exit_handle.cleanup();
80708334c51SBrooks Davis }
80808334c51SBrooks Davis std::cerr << "Terminating without reception of signal\n";
80908334c51SBrooks Davis std::exit(EXIT_FAILURE);
81008334c51SBrooks Davis } catch (const signals::interrupted_error& unused_error) {
81108334c51SBrooks Davis std::cerr << "Terminating due to interrupted_error\n";
81208334c51SBrooks Davis // We never kill ourselves until the cookie is created, so it is
81308334c51SBrooks Davis // guaranteed that the optional root_work_directory has been
81408334c51SBrooks Davis // initialized at this point.
81508334c51SBrooks Davis if (atf::utils::file_exists(root_work_directory.get().str())) {
81608334c51SBrooks Davis // Some cleanup did not happen; error out.
81708334c51SBrooks Davis std::exit(EXIT_FAILURE);
81808334c51SBrooks Davis } else {
81908334c51SBrooks Davis std::exit(EXIT_SUCCESS);
82008334c51SBrooks Davis }
82108334c51SBrooks Davis }
82208334c51SBrooks Davis std::abort();
82308334c51SBrooks Davis }
82408334c51SBrooks Davis
82508334c51SBrooks Davis std::cout << "Waiting for " << cookie << " cookie creation\n";
82608334c51SBrooks Davis while (!atf::utils::file_exists(cookie)) {
82708334c51SBrooks Davis // Wait for processes.
82808334c51SBrooks Davis }
82908334c51SBrooks Davis ATF_REQUIRE(::unlink(cookie) != -1);
83008334c51SBrooks Davis std::cout << "Killing process\n";
83108334c51SBrooks Davis ATF_REQUIRE(::kill(pid, signo) != -1);
83208334c51SBrooks Davis
83308334c51SBrooks Davis int status;
83408334c51SBrooks Davis std::cout << "Waiting for process termination\n";
83508334c51SBrooks Davis ATF_REQUIRE(::waitpid(pid, &status, 0) != -1);
83608334c51SBrooks Davis ATF_REQUIRE(WIFEXITED(status));
83708334c51SBrooks Davis ATF_REQUIRE_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
83808334c51SBrooks Davis }
83908334c51SBrooks Davis
84008334c51SBrooks Davis
84108334c51SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__signal_handling);
ATF_TEST_CASE_BODY(integration__signal_handling)84208334c51SBrooks Davis ATF_TEST_CASE_BODY(integration__signal_handling)
84308334c51SBrooks Davis {
84408334c51SBrooks Davis // This test scenario is racy so run it multiple times to have higher
84508334c51SBrooks Davis // chances of exposing problems.
84608334c51SBrooks Davis const std::size_t rounds = 20;
84708334c51SBrooks Davis
84808334c51SBrooks Davis for (std::size_t i = 0; i < rounds; ++i) {
84908334c51SBrooks Davis std::cout << F("Testing round %s\n") % i;
85008334c51SBrooks Davis do_signal_handling_test(SIGHUP);
85108334c51SBrooks Davis do_signal_handling_test(SIGINT);
85208334c51SBrooks Davis do_signal_handling_test(SIGTERM);
85308334c51SBrooks Davis }
85408334c51SBrooks Davis }
85508334c51SBrooks Davis
85608334c51SBrooks Davis
85708334c51SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__isolate_child_is_called);
ATF_TEST_CASE_BODY(integration__isolate_child_is_called)85808334c51SBrooks Davis ATF_TEST_CASE_BODY(integration__isolate_child_is_called)
85908334c51SBrooks Davis {
86008334c51SBrooks Davis executor::executor_handle handle = executor::setup();
86108334c51SBrooks Davis
86208334c51SBrooks Davis utils::setenv("HOME", "fake-value");
86308334c51SBrooks Davis utils::setenv("LANG", "es_ES");
86408334c51SBrooks Davis do_spawn(handle, child_validate_isolation);
86508334c51SBrooks Davis
86608334c51SBrooks Davis executor::exit_handle exit_handle = handle.wait_any();
86708334c51SBrooks Davis require_exit(EXIT_SUCCESS, exit_handle.status());
86808334c51SBrooks Davis exit_handle.cleanup();
86908334c51SBrooks Davis
87008334c51SBrooks Davis handle.cleanup();
87108334c51SBrooks Davis }
87208334c51SBrooks Davis
87308334c51SBrooks Davis
87408334c51SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__process_group_is_terminated);
ATF_TEST_CASE_BODY(integration__process_group_is_terminated)87508334c51SBrooks Davis ATF_TEST_CASE_BODY(integration__process_group_is_terminated)
87608334c51SBrooks Davis {
87708334c51SBrooks Davis utils::setenv("CONTROL_DIR", fs::current_path().str());
87808334c51SBrooks Davis
87908334c51SBrooks Davis executor::executor_handle handle = executor::setup();
88008334c51SBrooks Davis do_spawn(handle, child_spawn_blocking_child);
88108334c51SBrooks Davis
88208334c51SBrooks Davis executor::exit_handle exit_handle = handle.wait_any();
88308334c51SBrooks Davis require_exit(EXIT_SUCCESS, exit_handle.status());
88408334c51SBrooks Davis exit_handle.cleanup();
88508334c51SBrooks Davis
88608334c51SBrooks Davis handle.cleanup();
88708334c51SBrooks Davis
88808334c51SBrooks Davis if (!fs::exists(fs::path("pid")))
88908334c51SBrooks Davis fail("The pid file was not created");
89008334c51SBrooks Davis
89108334c51SBrooks Davis std::ifstream pidfile("pid");
89208334c51SBrooks Davis ATF_REQUIRE(pidfile);
89308334c51SBrooks Davis pid_t pid;
89408334c51SBrooks Davis pidfile >> pid;
89508334c51SBrooks Davis pidfile.close();
89608334c51SBrooks Davis
89708334c51SBrooks Davis ensure_dead(pid);
89808334c51SBrooks Davis }
89908334c51SBrooks Davis
90008334c51SBrooks Davis
90108334c51SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(integration__prevent_clobbering_control_files);
ATF_TEST_CASE_BODY(integration__prevent_clobbering_control_files)90208334c51SBrooks Davis ATF_TEST_CASE_BODY(integration__prevent_clobbering_control_files)
90308334c51SBrooks Davis {
90408334c51SBrooks Davis executor::executor_handle handle = executor::setup();
90508334c51SBrooks Davis
90608334c51SBrooks Davis do_spawn(handle, child_delete_all);
90708334c51SBrooks Davis
90808334c51SBrooks Davis executor::exit_handle exit_handle = handle.wait_any();
90908334c51SBrooks Davis require_exit(EXIT_SUCCESS, exit_handle.status());
91008334c51SBrooks Davis ATF_REQUIRE(atf::utils::file_exists(
91108334c51SBrooks Davis (exit_handle.control_directory() / "exec_was_called").str()));
91208334c51SBrooks Davis ATF_REQUIRE(!atf::utils::file_exists(
91308334c51SBrooks Davis (exit_handle.work_directory() / "exec_was_called").str()));
91408334c51SBrooks Davis exit_handle.cleanup();
91508334c51SBrooks Davis
91608334c51SBrooks Davis handle.cleanup();
91708334c51SBrooks Davis }
91808334c51SBrooks Davis
91908334c51SBrooks Davis
ATF_INIT_TEST_CASES(tcs)92008334c51SBrooks Davis ATF_INIT_TEST_CASES(tcs)
92108334c51SBrooks Davis {
92208334c51SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__run_one);
92308334c51SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__run_many);
92408334c51SBrooks Davis
92508334c51SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__parameters_and_output);
92608334c51SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__custom_output_files);
92708334c51SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__timestamps);
92808334c51SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__files);
92908334c51SBrooks Davis
93008334c51SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__followup);
93108334c51SBrooks Davis
93208334c51SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__output_files_always_exist);
93308334c51SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__timeouts);
93408334c51SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__unprivileged_user);
93508334c51SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__auto_cleanup);
93608334c51SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__signal_handling);
93708334c51SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__isolate_child_is_called);
93808334c51SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__process_group_is_terminated);
93908334c51SBrooks Davis ATF_ADD_TEST_CASE(tcs, integration__prevent_clobbering_control_files);
94008334c51SBrooks Davis }
941