Home | History | Annotate | Line # | Download | only in engine
test_case_test.cpp revision 1.1.1.1.4.2
      1 // Copyright 2010 Google Inc.
      2 // All rights reserved.
      3 //
      4 // Redistribution and use in source and binary forms, with or without
      5 // modification, are permitted provided that the following conditions are
      6 // met:
      7 //
      8 // * Redistributions of source code must retain the above copyright
      9 //   notice, this list of conditions and the following disclaimer.
     10 // * Redistributions in binary form must reproduce the above copyright
     11 //   notice, this list of conditions and the following disclaimer in the
     12 //   documentation and/or other materials provided with the distribution.
     13 // * Neither the name of Google Inc. nor the names of its contributors
     14 //   may be used to endorse or promote products derived from this software
     15 //   without specific prior written permission.
     16 //
     17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     28 
     29 #include "engine/test_case.hpp"
     30 
     31 extern "C" {
     32 #include <sys/stat.h>
     33 
     34 #include <signal.h>
     35 #include <unistd.h>
     36 }
     37 
     38 #include <cerrno>
     39 #include <cstdlib>
     40 #include <fstream>
     41 #include <iostream>
     42 #include <sstream>
     43 #include <stdexcept>
     44 #include <string>
     45 
     46 #include <atf-c++.hpp>
     47 
     48 #include "engine/config.hpp"
     49 #include "engine/exceptions.hpp"
     50 #include "engine/kyuafile.hpp"
     51 #include "engine/test_program.hpp"
     52 #include "engine/test_result.hpp"
     53 #include "utils/config/tree.ipp"
     54 #include "utils/datetime.hpp"
     55 #include "utils/env.hpp"
     56 #include "utils/format/macros.hpp"
     57 #include "utils/fs/operations.hpp"
     58 #include "utils/noncopyable.hpp"
     59 #include "utils/optional.ipp"
     60 #include "utils/passwd.hpp"
     61 #include "utils/process/child.ipp"
     62 #include "utils/sanity.hpp"
     63 #include "utils/stream.hpp"
     64 
     65 namespace config = utils::config;
     66 namespace datetime = utils::datetime;
     67 namespace fs = utils::fs;
     68 namespace passwd = utils::passwd;
     69 namespace process = utils::process;
     70 
     71 using utils::none;
     72 using utils::optional;
     73 
     74 
     75 namespace {
     76 
     77 
     78 /// Test case hooks to capture stdout and stderr in memory.
     79 class capture_hooks : public engine::test_case_hooks {
     80 public:
     81     /// Contents of the stdout of the test case.
     82     std::string stdout_contents;
     83 
     84     /// Contents of the stderr of the test case.
     85     std::string stderr_contents;
     86 
     87     /// Stores the stdout of the test case into stdout_contents.
     88     ///
     89     /// \param file The path to the file containing the stdout.
     90     void
     91     got_stdout(const fs::path& file)
     92     {
     93         atf::utils::cat_file(file.str(), "helper stdout:");
     94         ATF_REQUIRE(stdout_contents.empty());
     95 
     96         std::ifstream input(file.c_str());
     97         ATF_REQUIRE(input);
     98         stdout_contents = utils::read_stream(input);
     99     }
    100 
    101     /// Stores the stderr of the test case into stderr_contents.
    102     ///
    103     /// \param file The path to the file containing the stderr.
    104     void
    105     got_stderr(const fs::path& file)
    106     {
    107         atf::utils::cat_file(file.str(), "helper stderr:");
    108         ATF_REQUIRE(stderr_contents.empty());
    109 
    110         std::ifstream input(file.c_str());
    111         ATF_REQUIRE(input);
    112         stderr_contents = utils::read_stream(input);
    113     }
    114 };
    115 
    116 
    117 /// Launcher for the helper test cases.
    118 ///
    119 /// This builder class can be used to construct the runtime state of the helper
    120 /// test cases and later run them.  The class also provides other helper methods
    121 /// to interact with the helper binary.
    122 class atf_helper : utils::noncopyable {
    123     /// Path to the test program's source directory.
    124     const fs::path _srcdir;
    125 
    126     /// The root of the test suite.
    127     fs::path _root;
    128 
    129     /// Path to the helper test program, relative to _root.
    130     fs::path _binary_path;
    131 
    132     /// Name of the helper test case to run.
    133     const std::string _name;
    134 
    135     /// Metadata of the test case.
    136     engine::metadata_builder _mdbuilder;
    137 
    138     /// Run-time configuration for the test case.
    139     config::tree _user_config;
    140 
    141 public:
    142     /// Constructs a new helper.
    143     ///
    144     /// \param atf_tc A pointer to the calling test case.  Needed to obtain
    145     ///     run-time configuration variables.
    146     /// \param name The name of the helper to run.
    147     atf_helper(const atf::tests::tc* atf_tc, const char* name) :
    148         _srcdir(atf_tc->get_config_var("srcdir")),
    149         _root(_srcdir),
    150         _binary_path("test_case_atf_helpers"),
    151         _name(name),
    152         _user_config(engine::default_config())
    153     {
    154         _user_config.set_string("architecture", "mock-architecture");
    155         _user_config.set_string("platform", "mock-platform");
    156     }
    157 
    158     /// Provides raw access to the run-time configuration.
    159     ///
    160     /// To override test-suite-specific variables, use set_config() as it
    161     /// abstracts away the name of the fake test suite.
    162     ///
    163     /// \returns A reference to the test case configuration.
    164     config::tree&
    165     config(void)
    166     {
    167         return _user_config;
    168     }
    169 
    170     /// Sets a test-suite-specific configuration variable for the helper.
    171     ///
    172     /// \param variable The name of the environment variable to set.
    173     /// \param value The value of the variable; must be convertible to a string.
    174     template< typename T >
    175     void
    176     set_config(const char* variable, const T& value)
    177     {
    178         _user_config.set_string(F("test_suites.the-suite.%s") % variable,
    179                                 F("%s") % value);
    180     }
    181 
    182     /// Sets a metadata variable for the helper.
    183     ///
    184     /// \param variable The name of the environment variable to set.
    185     /// \param value The value of the variable; must be convertible to a string.
    186     template< typename T >
    187     void
    188     set_metadata(const char* variable, const T& value)
    189     {
    190         _mdbuilder.set_string(variable, F("%s") % value);
    191     }
    192 
    193     /// Places the helper in a different location.
    194     ///
    195     /// This prepares the helper to be run from a different location than the
    196     /// source directory so that the runtime execution can be validated.
    197     ///
    198     /// \param new_binary_path The new path to the binary, relative to the test
    199     ///     suite root.
    200     /// \param new_root The new test suite root.
    201     ///
    202     /// \pre The directory holding the target test program must exist.
    203     ///     Otherwise, the relocation of the binary will fail.
    204     void
    205     move(const char* new_binary_path, const char* new_root)
    206     {
    207         _binary_path = fs::path(new_binary_path);
    208         _root = fs::path(new_root);
    209 
    210         const fs::path src_path = fs::path(_srcdir / "test_case_atf_helpers");
    211         const fs::path new_path = _root / _binary_path;
    212         ATF_REQUIRE(
    213             ::symlink(src_path.c_str(), new_path.c_str()) != -1);
    214     }
    215 
    216     /// Runs the helper.
    217     ///
    218     /// \return The result of the execution.
    219     engine::test_result
    220     run(void) const
    221     {
    222         engine::test_case_hooks dummy_hooks;
    223         return run(dummy_hooks);
    224     }
    225 
    226     /// Runs the helper.
    227     ///
    228     /// \param hooks The hooks to pass to the test case.
    229     ///
    230     /// \return The result of the execution.
    231     engine::test_result
    232     run(engine::test_case_hooks& hooks) const
    233     {
    234         const engine::test_program test_program(
    235             "atf", _binary_path, _root, "the-suite",
    236             engine::metadata_builder().build());
    237         const engine::test_case test_case("atf", test_program, _name,
    238                                           _mdbuilder.build());
    239 
    240         const fs::path workdir("work");
    241         fs::mkdir(workdir, 0755);
    242 
    243         const engine::test_result result = engine::run_test_case(
    244             &test_case, _user_config, hooks, workdir);
    245         ATF_REQUIRE(::rmdir(workdir.c_str()) != -1);
    246         return result;
    247     }
    248 };
    249 
    250 
    251 /// Hooks to retrieve stdout and stderr.
    252 class fetch_output_hooks : public engine::test_case_hooks {
    253 public:
    254     /// Copies the stdout of the test case outside of its work directory.
    255     ///
    256     /// \param file The location of the test case's stdout.
    257     void
    258     got_stdout(const fs::path& file)
    259     {
    260         atf::utils::copy_file(file.str(), "helper-stdout.txt");
    261         atf::utils::cat_file("helper-stdout.txt", "helper stdout: ");
    262     }
    263 
    264     /// Copies the stderr of the test case outside of its work directory.
    265     ///
    266     /// \param file The location of the test case's stderr.
    267     void
    268     got_stderr(const fs::path& file)
    269     {
    270         atf::utils::copy_file(file.str(), "helper-stderr.txt");
    271         atf::utils::cat_file("helper-stderr.txt", "helper stderr: ");
    272     }
    273 };
    274 
    275 
    276 /// Simplifies the execution of the helper test cases.
    277 class plain_helper {
    278     /// Path to the test program's source directory.
    279     const fs::path _srcdir;
    280 
    281     /// The root of the test suite.
    282     fs::path _root;
    283 
    284     /// Path to the helper test program, relative to _root.
    285     fs::path _binary_path;
    286 
    287     /// Optional timeout for the test program.
    288     optional< datetime::delta > _timeout;
    289 
    290 public:
    291     /// Constructs a new helper.
    292     ///
    293     /// \param atf_tc A pointer to the calling test case.  Needed to obtain
    294     ///     run-time configuration variables.
    295     /// \param name The name of the helper to run.
    296     /// \param timeout An optional timeout for the test case.
    297     plain_helper(const atf::tests::tc* atf_tc, const char* name,
    298                  const optional< datetime::delta > timeout = none) :
    299         _srcdir(atf_tc->get_config_var("srcdir")),
    300         _root(_srcdir),
    301         _binary_path("test_case_plain_helpers"),
    302         _timeout(timeout)
    303     {
    304         utils::setenv("TEST_CASE", name);
    305     }
    306 
    307     /// Sets an environment variable for the helper.
    308     ///
    309     /// This is simply syntactic sugar for utils::setenv.
    310     ///
    311     /// \param variable The name of the environment variable to set.
    312     /// \param value The value of the variable; must be convertible to a string.
    313     template< typename T >
    314     void
    315     set(const char* variable, const T& value)
    316     {
    317         utils::setenv(variable, F("%s") % value);
    318     }
    319 
    320     /// Places the helper in a different location.
    321     ///
    322     /// This prepares the helper to be run from a different location than the
    323     /// source directory so that the runtime execution can be validated.
    324     ///
    325     /// \param new_binary_path The new path to the binary, relative to the test
    326     ///     suite root.
    327     /// \param new_root The new test suite root.
    328     ///
    329     /// \pre The directory holding the target test program must exist.
    330     ///     Otherwise, the relocation of the binary will fail.
    331     void
    332     move(const char* new_binary_path, const char* new_root)
    333     {
    334         _binary_path = fs::path(new_binary_path);
    335         _root = fs::path(new_root);
    336 
    337         const fs::path src_path = fs::path(_srcdir) / "test_case_plain_helpers";
    338         const fs::path new_path = _root / _binary_path;
    339         ATF_REQUIRE(
    340             ::symlink(src_path.c_str(), new_path.c_str()) != -1);
    341     }
    342 
    343     /// Runs the helper.
    344     ///
    345     /// \param user_config The runtime engine configuration, if different to the
    346     /// defaults.
    347     ///
    348     /// \return The result of the execution.
    349     engine::test_result
    350     run(const config::tree& user_config = engine::default_config()) const
    351     {
    352         engine::metadata_builder mdbuilder;
    353         if (_timeout)
    354             mdbuilder.set_timeout(_timeout.get());
    355         const engine::test_program test_program(
    356             "plain", _binary_path, _root, "unit-tests", mdbuilder.build());
    357         const engine::test_cases_vector& tcs = test_program.test_cases();
    358         fetch_output_hooks fetcher;
    359         const engine::test_result result = engine::run_test_case(
    360             tcs[0].get(), user_config, fetcher, fs::path("."));
    361         std::cerr << "Result is: " << result << '\n';
    362         return result;
    363     }
    364 };
    365 
    366 
    367 }  // anonymous namespace
    368 
    369 
    370 ATF_TEST_CASE_WITHOUT_HEAD(test_case__ctor_and_getters)
    371 ATF_TEST_CASE_BODY(test_case__ctor_and_getters)
    372 {
    373     const engine::metadata md = engine::metadata_builder()
    374         .add_custom("first", "value")
    375         .build();
    376     const engine::test_program test_program(
    377         "mock", fs::path("abc"), fs::path("unused-root"),
    378         "unused-suite-name", engine::metadata_builder().build());
    379     const engine::test_case test_case("mock", test_program, "foo", md);
    380     ATF_REQUIRE_EQ(&test_program, &test_case.container_test_program());
    381     ATF_REQUIRE_EQ("foo", test_case.name());
    382     ATF_REQUIRE(md == test_case.get_metadata());
    383 }
    384 
    385 
    386 ATF_TEST_CASE_WITHOUT_HEAD(test_case__fake_result)
    387 ATF_TEST_CASE_BODY(test_case__fake_result)
    388 {
    389     const engine::test_result result(engine::test_result::skipped,
    390                                      "Some reason");
    391     const engine::test_program test_program(
    392         "mock", fs::path("abc"), fs::path("unused-root"),
    393         "unused-suite-name", engine::metadata_builder().build());
    394     const engine::test_case test_case("mock", test_program, "__foo__",
    395                                       "Some description", result);
    396     ATF_REQUIRE_EQ(&test_program, &test_case.container_test_program());
    397     ATF_REQUIRE_EQ("__foo__", test_case.name());
    398     ATF_REQUIRE(result == test_case.fake_result().get());
    399 }
    400 
    401 
    402 ATF_TEST_CASE_WITHOUT_HEAD(test_case__operators_eq_and_ne__copy);
    403 ATF_TEST_CASE_BODY(test_case__operators_eq_and_ne__copy)
    404 {
    405     const engine::test_program tp(
    406         "plain", fs::path("non-existent"), fs::path("."), "suite-name",
    407         engine::metadata_builder().build());
    408 
    409     const engine::test_case tc1("plain", tp, "name",
    410                                 engine::metadata_builder().build());
    411     const engine::test_case tc2 = tc1;
    412     ATF_REQUIRE(  tc1 == tc2);
    413     ATF_REQUIRE(!(tc1 != tc2));
    414 }
    415 
    416 
    417 ATF_TEST_CASE_WITHOUT_HEAD(test_case__output);
    418 ATF_TEST_CASE_BODY(test_case__output)
    419 {
    420     const engine::test_program tp(
    421         "plain", fs::path("non-existent"), fs::path("."), "suite-name",
    422         engine::metadata_builder().build());
    423 
    424     const engine::test_case tc1(
    425         "plain", tp, "the-name", engine::metadata_builder()
    426         .add_allowed_platform("foo").add_custom("X-bar", "baz").build());
    427     std::ostringstream str;
    428     str << tc1;
    429     ATF_REQUIRE_EQ(
    430         "test_case{interface='plain', name='the-name', "
    431         "metadata=metadata{allowed_architectures='', allowed_platforms='foo', "
    432         "custom.X-bar='baz', description='', has_cleanup='false', "
    433         "required_configs='', required_files='', required_memory='0', "
    434         "required_programs='', required_user='', timeout='300'}}",
    435         str.str());
    436 }
    437 
    438 
    439 ATF_TEST_CASE_WITHOUT_HEAD(test_case__operators_eq_and_ne__not_copy);
    440 ATF_TEST_CASE_BODY(test_case__operators_eq_and_ne__not_copy)
    441 {
    442     const std::string base_interface("plain");
    443     const engine::test_program base_tp(
    444         "plain", fs::path("non-existent"), fs::path("."), "suite-name",
    445         engine::metadata_builder().build());
    446     const std::string base_name("name");
    447     const engine::metadata base_metadata = engine::metadata_builder()
    448         .add_custom("X-foo", "bar")
    449         .build();
    450 
    451     const engine::test_case base_tc(base_interface, base_tp, base_name,
    452                                     base_metadata);
    453 
    454     // Construct with all same values.
    455     {
    456         const engine::test_case other_tc(base_interface, base_tp, base_name,
    457                                         base_metadata);
    458 
    459         ATF_REQUIRE(  base_tc == other_tc);
    460         ATF_REQUIRE(!(base_tc != other_tc));
    461     }
    462 
    463     // Different interface.
    464     {
    465         const engine::test_case other_tc("atf", base_tp, base_name,
    466                                          base_metadata);
    467 
    468         ATF_REQUIRE(!(base_tc == other_tc));
    469         ATF_REQUIRE(  base_tc != other_tc);
    470     }
    471 
    472     // Different test program, different identifier.
    473     {
    474         const engine::test_program other_tp(
    475             "plain", fs::path("another-name"), fs::path("."), "suite2-name",
    476         engine::metadata_builder().build());
    477         const engine::test_case other_tc(base_interface, other_tp, base_name,
    478                                          base_metadata);
    479 
    480         ATF_REQUIRE(!(base_tc == other_tc));
    481         ATF_REQUIRE(  base_tc != other_tc);
    482     }
    483 
    484     // Different test program, same identifier.  Cannot be detected!
    485     {
    486         const engine::test_program other_tp(
    487             "plain", fs::path("non-existent"), fs::path("."), "suite2-name",
    488         engine::metadata_builder().build());
    489         const engine::test_case other_tc(base_interface, other_tp, base_name,
    490                                          base_metadata);
    491 
    492         ATF_REQUIRE(  base_tc == other_tc);
    493         ATF_REQUIRE(!(base_tc != other_tc));
    494     }
    495 
    496     // Different name.
    497     {
    498         const engine::test_case other_tc(base_interface, base_tp, "other",
    499                                          base_metadata);
    500 
    501         ATF_REQUIRE(!(base_tc == other_tc));
    502         ATF_REQUIRE(  base_tc != other_tc);
    503     }
    504 
    505     // Different metadata.
    506     {
    507         const engine::test_case other_tc(base_interface, base_tp, base_name,
    508                                          engine::metadata_builder().build());
    509 
    510         ATF_REQUIRE(!(base_tc == other_tc));
    511         ATF_REQUIRE(  base_tc != other_tc);
    512     }
    513 }
    514 
    515 
    516 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__current_directory);
    517 ATF_TEST_CASE_BODY(run_test_case__atf__current_directory)
    518 {
    519     atf_helper helper(this, "pass");
    520     helper.move("program", ".");
    521     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed),
    522                    helper.run());
    523 }
    524 
    525 
    526 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__subdirectory);
    527 ATF_TEST_CASE_BODY(run_test_case__atf__subdirectory)
    528 {
    529     atf_helper helper(this, "pass");
    530     ATF_REQUIRE(::mkdir("dir1", 0755) != -1);
    531     ATF_REQUIRE(::mkdir("dir1/dir2", 0755) != -1);
    532     helper.move("dir2/program", "dir1");
    533     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed),
    534                    helper.run());
    535 }
    536 
    537 
    538 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__config_variables);
    539 ATF_TEST_CASE_BODY(run_test_case__atf__config_variables)
    540 {
    541     atf_helper helper(this, "create_cookie_in_control_dir");
    542     helper.set_config("control_dir", fs::current_path());
    543     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed),
    544                    helper.run());
    545 
    546     if (!fs::exists(fs::path("cookie")))
    547         fail("The cookie was not created where we expected; the test program "
    548              "probably received an invalid configuration variable");
    549 }
    550 
    551 
    552 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__cleanup_shares_workdir);
    553 ATF_TEST_CASE_BODY(run_test_case__atf__cleanup_shares_workdir)
    554 {
    555     atf_helper helper(this, "check_cleanup_workdir");
    556     helper.set_metadata("has_cleanup", "true");
    557     helper.set_config("control_dir", fs::current_path());
    558     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::skipped,
    559                                        "cookie created"), helper.run());
    560 
    561     if (fs::exists(fs::path("missing_cookie")))
    562         fail("The cleanup part did not see the cookie; the work directory "
    563              "is probably not shared");
    564     if (fs::exists(fs::path("invalid_cookie")))
    565         fail("The cleanup part read an invalid cookie");
    566     if (!fs::exists(fs::path("cookie_ok")))
    567         fail("The cleanup part was not executed");
    568 }
    569 
    570 
    571 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__has_cleanup__atf__false);
    572 ATF_TEST_CASE_BODY(run_test_case__atf__has_cleanup__atf__false)
    573 {
    574     atf_helper helper(this, "create_cookie_from_cleanup");
    575     helper.set_metadata("has_cleanup", "false");
    576     helper.set_config("control_dir", fs::current_path());
    577     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed),
    578                    helper.run());
    579 
    580     if (fs::exists(fs::path("cookie")))
    581         fail("The cleanup part was executed even though the test case set "
    582              "has.cleanup to false");
    583 }
    584 
    585 
    586 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__has_cleanup__atf__true);
    587 ATF_TEST_CASE_BODY(run_test_case__atf__has_cleanup__atf__true)
    588 {
    589     atf_helper helper(this, "create_cookie_from_cleanup");
    590     helper.set_metadata("has_cleanup", "true");
    591     helper.set_config("control_dir", fs::current_path());
    592     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed),
    593                    helper.run());
    594 
    595     if (!fs::exists(fs::path("cookie")))
    596         fail("The cleanup part was not executed even though the test case set "
    597              "has.cleanup to true");
    598 }
    599 
    600 
    601 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__kill_children);
    602 ATF_TEST_CASE_BODY(run_test_case__atf__kill_children)
    603 {
    604     atf_helper helper(this, "spawn_blocking_child");
    605     helper.set_config("control_dir", fs::current_path());
    606     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed),
    607                    helper.run());
    608 
    609     if (!fs::exists(fs::path("pid")))
    610         fail("The pid file was not created");
    611     std::ifstream pidfile("pid");
    612     ATF_REQUIRE(pidfile);
    613     pid_t pid;
    614     pidfile >> pid;
    615     pidfile.close();
    616 
    617     int attempts = 30;
    618 retry:
    619     if (::kill(pid, SIGCONT) != -1 || errno != ESRCH) {
    620         // Looks like the subchild did not die.
    621         //
    622         // Note that this might be inaccurate for two reasons:
    623         // 1) The system may have spawned a new process with the same pid as
    624         //    our subchild... but in practice, this does not happen because
    625         //    most systems do not immediately reuse pid numbers.  If that
    626         //    happens... well, we get a false test failure.
    627         // 2) We ran so fast that even if the process was sent a signal to
    628         //    die, it has not had enough time to process it yet.  This is why
    629         //    we retry this a few times.
    630         if (attempts > 0) {
    631             std::cout << "Subprocess not dead yet; retrying wait\n";
    632             --attempts;
    633             ::usleep(100000);
    634             goto retry;
    635         }
    636         fail(F("The subprocess %s of our child was not killed") % pid);
    637     }
    638 }
    639 
    640 
    641 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__isolation);
    642 ATF_TEST_CASE_BODY(run_test_case__atf__isolation)
    643 {
    644     atf_helper helper(this, "validate_isolation");
    645     // Simple checks to make sure that the test case has been isolated.
    646     utils::setenv("HOME", "fake-value");
    647     utils::setenv("LANG", "C");
    648     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed),
    649                    helper.run());
    650 }
    651 
    652 
    653 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__allowed_architectures);
    654 ATF_TEST_CASE_BODY(run_test_case__atf__allowed_architectures)
    655 {
    656     atf_helper helper(this, "create_cookie_in_control_dir");
    657     helper.set_metadata("allowed_architectures", "i386 x86_64");
    658     helper.config().set_string("architecture", "powerpc");
    659     helper.config().set_string("platform", "");
    660     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::skipped, "Current "
    661                                        "architecture 'powerpc' not supported"),
    662                    helper.run());
    663 
    664     if (fs::exists(fs::path("cookie")))
    665         fail("The test case was not really skipped when the requirements "
    666              "check failed");
    667 }
    668 
    669 
    670 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__allowed_platforms);
    671 ATF_TEST_CASE_BODY(run_test_case__atf__allowed_platforms)
    672 {
    673     atf_helper helper(this, "create_cookie_in_control_dir");
    674     helper.set_metadata("allowed_platforms", "i386 amd64");
    675     helper.config().set_string("architecture", "");
    676     helper.config().set_string("platform", "macppc");
    677     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::skipped, "Current "
    678                                        "platform 'macppc' not supported"),
    679                    helper.run());
    680 
    681     if (fs::exists(fs::path("cookie")))
    682         fail("The test case was not really skipped when the requirements "
    683              "check failed");
    684 }
    685 
    686 
    687 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__required_configs);
    688 ATF_TEST_CASE_BODY(run_test_case__atf__required_configs)
    689 {
    690     atf_helper helper(this, "create_cookie_in_control_dir");
    691     helper.set_metadata("required_configs", "used-var");
    692     helper.set_config("control_dir", fs::current_path());
    693     helper.set_config("unused-var", "value");
    694     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::skipped, "Required "
    695                                        "configuration property 'used-var' not "
    696                                        "defined"),
    697                    helper.run());
    698 
    699     if (fs::exists(fs::path("cookie")))
    700         fail("The test case was not really skipped when the requirements "
    701              "check failed");
    702 }
    703 
    704 
    705 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__required_programs);
    706 ATF_TEST_CASE_BODY(run_test_case__atf__required_programs)
    707 {
    708     atf_helper helper(this, "create_cookie_in_control_dir");
    709     helper.set_metadata("required_programs", "/non-existent/program");
    710     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::skipped, "Required "
    711                                        "program '/non-existent/program' not "
    712                                        "found"),
    713                    helper.run());
    714 
    715     if (fs::exists(fs::path("cookie")))
    716         fail("The test case was not really skipped when the requirements "
    717              "check failed");
    718 }
    719 
    720 
    721 ATF_TEST_CASE(run_test_case__atf__required_user__atf__root__atf__ok);
    722 ATF_TEST_CASE_HEAD(run_test_case__atf__required_user__atf__root__atf__ok)
    723 {
    724     set_md_var("require.user", "root");
    725 }
    726 ATF_TEST_CASE_BODY(run_test_case__atf__required_user__atf__root__atf__ok)
    727 {
    728     atf_helper helper(this, "create_cookie_in_workdir");
    729     helper.set_metadata("required_user", "root");
    730     ATF_REQUIRE(passwd::current_user().is_root());
    731     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed),
    732                    helper.run());
    733 }
    734 
    735 
    736 ATF_TEST_CASE(run_test_case__atf__required_user__atf__root__atf__skip);
    737 ATF_TEST_CASE_HEAD(run_test_case__atf__required_user__atf__root__atf__skip)
    738 {
    739     set_md_var("require.user", "unprivileged");
    740 }
    741 ATF_TEST_CASE_BODY(run_test_case__atf__required_user__atf__root__atf__skip)
    742 {
    743     atf_helper helper(this, "create_cookie_in_workdir");
    744     helper.set_metadata("required_user", "root");
    745     ATF_REQUIRE(!passwd::current_user().is_root());
    746     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::skipped, "Requires "
    747                                        "root privileges"),
    748                    helper.run());
    749 }
    750 
    751 
    752 ATF_TEST_CASE(run_test_case__atf__required_user__atf__unprivileged__atf__ok);
    753 ATF_TEST_CASE_HEAD(run_test_case__atf__required_user__atf__unprivileged__atf__ok)
    754 {
    755     set_md_var("require.user", "unprivileged");
    756 }
    757 ATF_TEST_CASE_BODY(run_test_case__atf__required_user__atf__unprivileged__atf__ok)
    758 {
    759     atf_helper helper(this, "create_cookie_in_workdir");
    760     helper.set_metadata("required_user", "unprivileged");
    761     ATF_REQUIRE(!helper.config().is_set("unprivileged_user"));
    762     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed),
    763                    helper.run());
    764 }
    765 
    766 
    767 ATF_TEST_CASE(run_test_case__atf__required_user__atf__unprivileged__atf__skip);
    768 ATF_TEST_CASE_HEAD(run_test_case__atf__required_user__atf__unprivileged__atf__skip)
    769 {
    770     set_md_var("require.user", "root");
    771 }
    772 ATF_TEST_CASE_BODY(run_test_case__atf__required_user__atf__unprivileged__atf__skip)
    773 {
    774     atf_helper helper(this, "create_cookie_in_workdir");
    775     helper.set_metadata("required_user", "unprivileged");
    776     ATF_REQUIRE(!helper.config().is_set("unprivileged_user"));
    777     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::skipped, "Requires "
    778                                        "an unprivileged user but the "
    779                                        "unprivileged-user configuration "
    780                                        "variable is not defined"),
    781                    helper.run());
    782 }
    783 
    784 
    785 ATF_TEST_CASE(run_test_case__atf__required_user__atf__unprivileged__atf__drop);
    786 ATF_TEST_CASE_HEAD(run_test_case__atf__required_user__atf__unprivileged__atf__drop)
    787 {
    788     set_md_var("require.config", "unprivileged-user");
    789     set_md_var("require.user", "root");
    790 }
    791 ATF_TEST_CASE_BODY(run_test_case__atf__required_user__atf__unprivileged__atf__drop)
    792 {
    793     atf_helper helper(this, "check_unprivileged");
    794     helper.set_metadata("required_user", "unprivileged");
    795     helper.config().set< engine::user_node >(
    796         "unprivileged_user",
    797         passwd::find_user_by_name(get_config_var("unprivileged-user")));
    798     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed),
    799                    helper.run());
    800 }
    801 
    802 
    803 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__timeout_body);
    804 ATF_TEST_CASE_BODY(run_test_case__atf__timeout_body)
    805 {
    806     atf_helper helper(this, "timeout_body");
    807     helper.set_metadata("timeout", "1");
    808     helper.set_config("control_dir", fs::current_path());
    809     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::broken,
    810                                        "Test case body timed out"),
    811                    helper.run());
    812 
    813     if (fs::exists(fs::path("cookie")))
    814         fail("It seems that the test case was not killed after it timed out");
    815 }
    816 
    817 
    818 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__timeout_cleanup);
    819 ATF_TEST_CASE_BODY(run_test_case__atf__timeout_cleanup)
    820 {
    821     atf_helper helper(this, "timeout_cleanup");
    822     helper.set_metadata("has_cleanup", "true");
    823     helper.set_metadata("timeout", "1");
    824     helper.set_config("control_dir", fs::current_path());
    825     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::broken,
    826                                        "Test case cleanup timed out"),
    827                    helper.run());
    828 
    829     if (fs::exists(fs::path("cookie")))
    830         fail("It seems that the test case was not killed after it timed out");
    831 }
    832 
    833 
    834 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__stacktrace__atf__body);
    835 ATF_TEST_CASE_BODY(run_test_case__atf__stacktrace__atf__body)
    836 {
    837     atf_helper helper(this, "crash");
    838     capture_hooks hooks;
    839     const engine::test_result result = helper.run(hooks);
    840     ATF_REQUIRE(engine::test_result::broken == result.type());
    841     ATF_REQUIRE_MATCH("received signal.*core dumped", result.reason());
    842 
    843     ATF_REQUIRE(!atf::utils::grep_string("attempting to gather stack trace",
    844                                          hooks.stdout_contents));
    845     ATF_REQUIRE( atf::utils::grep_string("attempting to gather stack trace",
    846                                          hooks.stderr_contents));
    847 }
    848 
    849 
    850 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__stacktrace__atf__cleanup);
    851 ATF_TEST_CASE_BODY(run_test_case__atf__stacktrace__atf__cleanup)
    852 {
    853     atf_helper helper(this, "crash_cleanup");
    854     helper.set_metadata("has_cleanup", "true");
    855     capture_hooks hooks;
    856     const engine::test_result result = helper.run(hooks);
    857     ATF_REQUIRE(engine::test_result::broken == result.type());
    858     ATF_REQUIRE_MATCH(F("cleanup received signal %s") % SIGABRT,
    859                       result.reason());
    860 
    861     ATF_REQUIRE(!atf::utils::grep_string("attempting to gather stack trace",
    862                                          hooks.stdout_contents));
    863     ATF_REQUIRE( atf::utils::grep_string("attempting to gather stack trace",
    864                                          hooks.stderr_contents));
    865 }
    866 
    867 
    868 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__missing_results_file);
    869 ATF_TEST_CASE_BODY(run_test_case__atf__missing_results_file)
    870 {
    871     atf_helper helper(this, "crash");
    872     const engine::test_result result = helper.run();
    873     ATF_REQUIRE(engine::test_result::broken == result.type());
    874     // Need to match instead of doing an explicit comparison because the string
    875     // may include the "core dumped" substring.
    876     ATF_REQUIRE_MATCH(F("test case received signal %s") % SIGABRT,
    877                       result.reason());
    878 }
    879 
    880 
    881 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__missing_test_program);
    882 ATF_TEST_CASE_BODY(run_test_case__atf__missing_test_program)
    883 {
    884     atf_helper helper(this, "crash");
    885     ATF_REQUIRE(::mkdir("dir", 0755) != -1);
    886     helper.move("test_case_atf_helpers", "dir");
    887     ATF_REQUIRE(::unlink("dir/test_case_atf_helpers") != -1);
    888     const engine::test_result result = helper.run();
    889     ATF_REQUIRE(engine::test_result::broken == result.type());
    890     ATF_REQUIRE_MATCH("Test program does not exist", result.reason());
    891 }
    892 
    893 
    894 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__output);
    895 ATF_TEST_CASE_BODY(run_test_case__atf__output)
    896 {
    897     atf_helper helper(this, "output");
    898     helper.set_metadata("has_cleanup", "true");
    899 
    900     capture_hooks hooks;
    901     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed),
    902                    helper.run(hooks));
    903 
    904     ATF_REQUIRE_EQ("Body message to stdout\nCleanup message to stdout\n",
    905                    hooks.stdout_contents);
    906     ATF_REQUIRE_EQ("Body message to stderr\nCleanup message to stderr\n",
    907                    hooks.stderr_contents);
    908 }
    909 
    910 
    911 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__result_pass);
    912 ATF_TEST_CASE_BODY(run_test_case__plain__result_pass)
    913 {
    914     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed),
    915                    plain_helper(this, "pass").run());
    916 }
    917 
    918 
    919 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__result_fail);
    920 ATF_TEST_CASE_BODY(run_test_case__plain__result_fail)
    921 {
    922     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::failed,
    923                                        "Returned non-success exit status 8"),
    924                    plain_helper(this, "fail").run());
    925 }
    926 
    927 
    928 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__result_crash);
    929 ATF_TEST_CASE_BODY(run_test_case__plain__result_crash)
    930 {
    931     const engine::test_result result = plain_helper(this, "crash").run();
    932     ATF_REQUIRE(engine::test_result::broken == result.type());
    933     ATF_REQUIRE_MATCH(F("Received signal %s") % SIGABRT, result.reason());
    934 }
    935 
    936 
    937 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__current_directory);
    938 ATF_TEST_CASE_BODY(run_test_case__plain__current_directory)
    939 {
    940     plain_helper helper(this, "pass");
    941     helper.move("program", ".");
    942     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed),
    943                    helper.run());
    944 }
    945 
    946 
    947 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__subdirectory);
    948 ATF_TEST_CASE_BODY(run_test_case__plain__subdirectory)
    949 {
    950     plain_helper helper(this, "pass");
    951     ATF_REQUIRE(::mkdir("dir1", 0755) != -1);
    952     ATF_REQUIRE(::mkdir("dir1/dir2", 0755) != -1);
    953     helper.move("dir2/program", "dir1");
    954     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed),
    955                    helper.run());
    956 }
    957 
    958 
    959 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__kill_children);
    960 ATF_TEST_CASE_BODY(run_test_case__plain__kill_children)
    961 {
    962     plain_helper helper(this, "spawn_blocking_child");
    963     helper.set("CONTROL_DIR", fs::current_path());
    964     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed),
    965                    helper.run());
    966 
    967     if (!fs::exists(fs::path("pid")))
    968         fail("The pid file was not created");
    969     std::ifstream pidfile("pid");
    970     ATF_REQUIRE(pidfile);
    971     pid_t pid;
    972     pidfile >> pid;
    973     pidfile.close();
    974 
    975     int attempts = 30;
    976 retry:
    977     if (::kill(pid, SIGCONT) != -1 || errno != ESRCH) {
    978         // Looks like the subchild did not die.
    979         //
    980         // Note that this might be inaccurate for two reasons:
    981         // 1) The system may have spawned a new process with the same pid as
    982         //    our subchild... but in practice, this does not happen because
    983         //    most systems do not immediately reuse pid numbers.  If that
    984         //    happens... well, we get a false test failure.
    985         // 2) We ran so fast that even if the process was sent a signal to
    986         //    die, it has not had enough time to process it yet.  This is why
    987         //    we retry this a few times.
    988         if (attempts > 0) {
    989             std::cout << "Subprocess not dead yet; retrying wait\n";
    990             --attempts;
    991             ::usleep(100000);
    992             goto retry;
    993         }
    994         fail(F("The subprocess %s of our child was not killed") % pid);
    995     }
    996 }
    997 
    998 
    999 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__isolation);
   1000 ATF_TEST_CASE_BODY(run_test_case__plain__isolation)
   1001 {
   1002     const plain_helper helper(this, "validate_isolation");
   1003     utils::setenv("TEST_CASE", "validate_isolation");
   1004     // Simple checks to make sure that the test case has been isolated.
   1005     utils::setenv("HOME", "fake-value");
   1006     utils::setenv("LANG", "C");
   1007     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed),
   1008                    helper.run());
   1009 }
   1010 
   1011 
   1012 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__timeout);
   1013 ATF_TEST_CASE_BODY(run_test_case__plain__timeout)
   1014 {
   1015     plain_helper helper(this, "timeout",
   1016                         utils::make_optional(datetime::delta(1, 0)));
   1017     helper.set("CONTROL_DIR", fs::current_path());
   1018     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::broken,
   1019                                        "Test case timed out"),
   1020                    helper.run());
   1021 
   1022     if (fs::exists(fs::path("cookie")))
   1023         fail("It seems that the test case was not killed after it timed out");
   1024 }
   1025 
   1026 
   1027 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__stacktrace);
   1028 ATF_TEST_CASE_BODY(run_test_case__plain__stacktrace)
   1029 {
   1030     plain_helper helper(this, "crash");
   1031     helper.set("CONTROL_DIR", fs::current_path());
   1032 
   1033     const engine::test_result result = plain_helper(this, "crash").run();
   1034     ATF_REQUIRE(engine::test_result::broken == result.type());
   1035     ATF_REQUIRE_MATCH(F("Received signal %s") % SIGABRT, result.reason());
   1036 
   1037     ATF_REQUIRE(!atf::utils::grep_file("attempting to gather stack trace",
   1038                                        "helper-stdout.txt"));
   1039     ATF_REQUIRE( atf::utils::grep_file("attempting to gather stack trace",
   1040                                        "helper-stderr.txt"));
   1041 }
   1042 
   1043 
   1044 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__missing_test_program);
   1045 ATF_TEST_CASE_BODY(run_test_case__plain__missing_test_program)
   1046 {
   1047     plain_helper helper(this, "pass");
   1048     ATF_REQUIRE(::mkdir("dir", 0755) != -1);
   1049     helper.move("test_case_helpers", "dir");
   1050     ATF_REQUIRE(::unlink("dir/test_case_helpers") != -1);
   1051     const engine::test_result result = helper.run();
   1052     ATF_REQUIRE(engine::test_result::broken == result.type());
   1053     ATF_REQUIRE_MATCH("Test program does not exist", result.reason());
   1054 }
   1055 
   1056 
   1057 ATF_INIT_TEST_CASES(tcs)
   1058 {
   1059     ATF_ADD_TEST_CASE(tcs, test_case__ctor_and_getters);
   1060     ATF_ADD_TEST_CASE(tcs, test_case__fake_result);
   1061 
   1062     ATF_ADD_TEST_CASE(tcs, test_case__operators_eq_and_ne__copy);
   1063     ATF_ADD_TEST_CASE(tcs, test_case__operators_eq_and_ne__not_copy);
   1064 
   1065     ATF_ADD_TEST_CASE(tcs, test_case__output);
   1066 
   1067     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__current_directory);
   1068     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__subdirectory);
   1069     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__config_variables);
   1070     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__cleanup_shares_workdir);
   1071     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__has_cleanup__atf__false);
   1072     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__has_cleanup__atf__true);
   1073     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__kill_children);
   1074     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__isolation);
   1075     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__allowed_architectures);
   1076     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__allowed_platforms);
   1077     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__required_configs);
   1078     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__required_programs);
   1079     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__required_user__atf__root__atf__ok);
   1080     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__required_user__atf__root__atf__skip);
   1081     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__required_user__atf__unprivileged__atf__ok);
   1082     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__required_user__atf__unprivileged__atf__skip);
   1083     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__required_user__atf__unprivileged__atf__drop);
   1084     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__timeout_body);
   1085     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__timeout_cleanup);
   1086     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__stacktrace__atf__body);
   1087     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__stacktrace__atf__cleanup);
   1088     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__missing_results_file);
   1089     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__missing_test_program);
   1090     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__output);
   1091 
   1092     ATF_ADD_TEST_CASE(tcs, run_test_case__plain__result_pass);
   1093     ATF_ADD_TEST_CASE(tcs, run_test_case__plain__result_fail);
   1094     ATF_ADD_TEST_CASE(tcs, run_test_case__plain__result_crash);
   1095     ATF_ADD_TEST_CASE(tcs, run_test_case__plain__current_directory);
   1096     ATF_ADD_TEST_CASE(tcs, run_test_case__plain__subdirectory);
   1097     ATF_ADD_TEST_CASE(tcs, run_test_case__plain__kill_children);
   1098     ATF_ADD_TEST_CASE(tcs, run_test_case__plain__isolation);
   1099     ATF_ADD_TEST_CASE(tcs, run_test_case__plain__timeout);
   1100     ATF_ADD_TEST_CASE(tcs, run_test_case__plain__stacktrace);
   1101     ATF_ADD_TEST_CASE(tcs, run_test_case__plain__missing_test_program);
   1102 
   1103     // TODO(jmmv): Add test cases for debug.
   1104 }
   1105