xref: /minix3/external/bsd/kyua-cli/dist/engine/test_case_test.cpp (revision 84d9c625bfea59e274550651111ae9edfdc40fbd)
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
got_stdout(const fs::path & file)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
got_stderr(const fs::path & file)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.
atf_helper(const atf::tests::tc * atf_tc,const char * name)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&
config(void)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
set_config(const char * variable,const T & value)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
set_metadata(const char * variable,const T & value)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
move(const char * new_binary_path,const char * new_root)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
run(void) const220     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
run(engine::test_case_hooks & hooks) const232     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
got_stdout(const fs::path & file)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
got_stderr(const fs::path & file)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.
plain_helper(const atf::tests::tc * atf_tc,const char * name,const optional<datetime::delta> timeout=none)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
set(const char * variable,const T & value)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
move(const char * new_binary_path,const char * new_root)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
run(const config::tree & user_config=engine::default_config ()) const350     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 /// Creates a mock tester that receives a signal.
368 ///
369 /// \param interface The name of the interface implemented by the tester.
370 /// \param term_sig Signal to deliver to the tester.  If the tester does not
371 ///     exit due to this reason, it exits with an arbitrary non-zero code.
372 static void
create_mock_tester_signal(const char * interface,const int term_sig)373 create_mock_tester_signal(const char* interface, const int term_sig)
374 {
375     const std::string tester_name = F("kyua-%s-tester") % interface;
376 
377     atf::utils::create_file(
378         tester_name,
379         F("#! /bin/sh\n"
380           "echo 'stdout stuff'\n"
381           "echo 'stderr stuff' 1>&2\n"
382           "kill -%s $$\n"
383           "echo 'not reachable' 1>&2\n"
384           "exit 0\n") % term_sig);
385     ATF_REQUIRE(::chmod(tester_name.c_str(), 0755) != -1);
386 
387     utils::setenv("KYUA_TESTERSDIR", fs::current_path().str());
388 }
389 
390 
391 }  // anonymous namespace
392 
393 
394 ATF_TEST_CASE_WITHOUT_HEAD(test_case__ctor_and_getters)
ATF_TEST_CASE_BODY(test_case__ctor_and_getters)395 ATF_TEST_CASE_BODY(test_case__ctor_and_getters)
396 {
397     const engine::metadata md = engine::metadata_builder()
398         .add_custom("first", "value")
399         .build();
400     const engine::test_program test_program(
401         "mock", fs::path("abc"), fs::path("unused-root"),
402         "unused-suite-name", engine::metadata_builder().build());
403     const engine::test_case test_case("mock", test_program, "foo", md);
404     ATF_REQUIRE_EQ(&test_program, &test_case.container_test_program());
405     ATF_REQUIRE_EQ("foo", test_case.name());
406     ATF_REQUIRE(md == test_case.get_metadata());
407 }
408 
409 
410 ATF_TEST_CASE_WITHOUT_HEAD(test_case__fake_result)
ATF_TEST_CASE_BODY(test_case__fake_result)411 ATF_TEST_CASE_BODY(test_case__fake_result)
412 {
413     const engine::test_result result(engine::test_result::skipped,
414                                      "Some reason");
415     const engine::test_program test_program(
416         "mock", fs::path("abc"), fs::path("unused-root"),
417         "unused-suite-name", engine::metadata_builder().build());
418     const engine::test_case test_case("mock", test_program, "__foo__",
419                                       "Some description", result);
420     ATF_REQUIRE_EQ(&test_program, &test_case.container_test_program());
421     ATF_REQUIRE_EQ("__foo__", test_case.name());
422     ATF_REQUIRE(result == test_case.fake_result().get());
423 }
424 
425 
426 ATF_TEST_CASE_WITHOUT_HEAD(test_case__operators_eq_and_ne__copy);
ATF_TEST_CASE_BODY(test_case__operators_eq_and_ne__copy)427 ATF_TEST_CASE_BODY(test_case__operators_eq_and_ne__copy)
428 {
429     const engine::test_program tp(
430         "plain", fs::path("non-existent"), fs::path("."), "suite-name",
431         engine::metadata_builder().build());
432 
433     const engine::test_case tc1("plain", tp, "name",
434                                 engine::metadata_builder().build());
435     const engine::test_case tc2 = tc1;
436     ATF_REQUIRE(  tc1 == tc2);
437     ATF_REQUIRE(!(tc1 != tc2));
438 }
439 
440 
441 ATF_TEST_CASE_WITHOUT_HEAD(test_case__output);
ATF_TEST_CASE_BODY(test_case__output)442 ATF_TEST_CASE_BODY(test_case__output)
443 {
444     const engine::test_program tp(
445         "plain", fs::path("non-existent"), fs::path("."), "suite-name",
446         engine::metadata_builder().build());
447 
448     const engine::test_case tc1(
449         "plain", tp, "the-name", engine::metadata_builder()
450         .add_allowed_platform("foo").add_custom("X-bar", "baz").build());
451     std::ostringstream str;
452     str << tc1;
453     ATF_REQUIRE_EQ(
454         "test_case{interface='plain', name='the-name', "
455         "metadata=metadata{allowed_architectures='', allowed_platforms='foo', "
456         "custom.X-bar='baz', description='', has_cleanup='false', "
457         "required_configs='', required_files='', required_memory='0', "
458         "required_programs='', required_user='', timeout='300'}}",
459         str.str());
460 }
461 
462 
463 ATF_TEST_CASE_WITHOUT_HEAD(test_case__operators_eq_and_ne__not_copy);
ATF_TEST_CASE_BODY(test_case__operators_eq_and_ne__not_copy)464 ATF_TEST_CASE_BODY(test_case__operators_eq_and_ne__not_copy)
465 {
466     const std::string base_interface("plain");
467     const engine::test_program base_tp(
468         "plain", fs::path("non-existent"), fs::path("."), "suite-name",
469         engine::metadata_builder().build());
470     const std::string base_name("name");
471     const engine::metadata base_metadata = engine::metadata_builder()
472         .add_custom("X-foo", "bar")
473         .build();
474 
475     const engine::test_case base_tc(base_interface, base_tp, base_name,
476                                     base_metadata);
477 
478     // Construct with all same values.
479     {
480         const engine::test_case other_tc(base_interface, base_tp, base_name,
481                                         base_metadata);
482 
483         ATF_REQUIRE(  base_tc == other_tc);
484         ATF_REQUIRE(!(base_tc != other_tc));
485     }
486 
487     // Different interface.
488     {
489         const engine::test_case other_tc("atf", base_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 test program, different identifier.
497     {
498         const engine::test_program other_tp(
499             "plain", fs::path("another-name"), fs::path("."), "suite2-name",
500         engine::metadata_builder().build());
501         const engine::test_case other_tc(base_interface, other_tp, base_name,
502                                          base_metadata);
503 
504         ATF_REQUIRE(!(base_tc == other_tc));
505         ATF_REQUIRE(  base_tc != other_tc);
506     }
507 
508     // Different test program, same identifier.  Cannot be detected!
509     {
510         const engine::test_program other_tp(
511             "plain", fs::path("non-existent"), fs::path("."), "suite2-name",
512         engine::metadata_builder().build());
513         const engine::test_case other_tc(base_interface, other_tp, base_name,
514                                          base_metadata);
515 
516         ATF_REQUIRE(  base_tc == other_tc);
517         ATF_REQUIRE(!(base_tc != other_tc));
518     }
519 
520     // Different name.
521     {
522         const engine::test_case other_tc(base_interface, base_tp, "other",
523                                          base_metadata);
524 
525         ATF_REQUIRE(!(base_tc == other_tc));
526         ATF_REQUIRE(  base_tc != other_tc);
527     }
528 
529     // Different metadata.
530     {
531         const engine::test_case other_tc(base_interface, base_tp, base_name,
532                                          engine::metadata_builder().build());
533 
534         ATF_REQUIRE(!(base_tc == other_tc));
535         ATF_REQUIRE(  base_tc != other_tc);
536     }
537 }
538 
539 
540 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__tester_crashes);
ATF_TEST_CASE_BODY(run_test_case__tester_crashes)541 ATF_TEST_CASE_BODY(run_test_case__tester_crashes)
542 {
543     atf_helper helper(this, "pass");
544     helper.move("program", ".");
545     create_mock_tester_signal("atf", SIGSEGV);
546     capture_hooks hooks;
547     const engine::test_result result = helper.run(hooks);
548 
549     ATF_REQUIRE(engine::test_result::broken == result.type());
550     ATF_REQUIRE_MATCH("Tester received signal.*bug", result.reason());
551 
552     ATF_REQUIRE_EQ("stdout stuff\n", hooks.stdout_contents);
553     ATF_REQUIRE_EQ("stderr stuff\n", hooks.stderr_contents);
554 }
555 
556 
557 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__current_directory);
ATF_TEST_CASE_BODY(run_test_case__atf__current_directory)558 ATF_TEST_CASE_BODY(run_test_case__atf__current_directory)
559 {
560     atf_helper helper(this, "pass");
561     helper.move("program", ".");
562     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed),
563                    helper.run());
564 }
565 
566 
567 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__subdirectory);
ATF_TEST_CASE_BODY(run_test_case__atf__subdirectory)568 ATF_TEST_CASE_BODY(run_test_case__atf__subdirectory)
569 {
570     atf_helper helper(this, "pass");
571     ATF_REQUIRE(::mkdir("dir1", 0755) != -1);
572     ATF_REQUIRE(::mkdir("dir1/dir2", 0755) != -1);
573     helper.move("dir2/program", "dir1");
574     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed),
575                    helper.run());
576 }
577 
578 
579 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__config_variables);
ATF_TEST_CASE_BODY(run_test_case__atf__config_variables)580 ATF_TEST_CASE_BODY(run_test_case__atf__config_variables)
581 {
582     atf_helper helper(this, "create_cookie_in_control_dir");
583     helper.set_config("control_dir", fs::current_path());
584     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed),
585                    helper.run());
586 
587     if (!fs::exists(fs::path("cookie")))
588         fail("The cookie was not created where we expected; the test program "
589              "probably received an invalid configuration variable");
590 }
591 
592 
593 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__cleanup_shares_workdir);
ATF_TEST_CASE_BODY(run_test_case__atf__cleanup_shares_workdir)594 ATF_TEST_CASE_BODY(run_test_case__atf__cleanup_shares_workdir)
595 {
596     atf_helper helper(this, "check_cleanup_workdir");
597     helper.set_metadata("has_cleanup", "true");
598     helper.set_config("control_dir", fs::current_path());
599     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::skipped,
600                                        "cookie created"), helper.run());
601 
602     if (fs::exists(fs::path("missing_cookie")))
603         fail("The cleanup part did not see the cookie; the work directory "
604              "is probably not shared");
605     if (fs::exists(fs::path("invalid_cookie")))
606         fail("The cleanup part read an invalid cookie");
607     if (!fs::exists(fs::path("cookie_ok")))
608         fail("The cleanup part was not executed");
609 }
610 
611 
612 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__has_cleanup__atf__false);
ATF_TEST_CASE_BODY(run_test_case__atf__has_cleanup__atf__false)613 ATF_TEST_CASE_BODY(run_test_case__atf__has_cleanup__atf__false)
614 {
615     atf_helper helper(this, "create_cookie_from_cleanup");
616     helper.set_metadata("has_cleanup", "false");
617     helper.set_config("control_dir", fs::current_path());
618     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed),
619                    helper.run());
620 
621     if (fs::exists(fs::path("cookie")))
622         fail("The cleanup part was executed even though the test case set "
623              "has.cleanup to false");
624 }
625 
626 
627 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__has_cleanup__atf__true);
ATF_TEST_CASE_BODY(run_test_case__atf__has_cleanup__atf__true)628 ATF_TEST_CASE_BODY(run_test_case__atf__has_cleanup__atf__true)
629 {
630     atf_helper helper(this, "create_cookie_from_cleanup");
631     helper.set_metadata("has_cleanup", "true");
632     helper.set_config("control_dir", fs::current_path());
633     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed),
634                    helper.run());
635 
636     if (!fs::exists(fs::path("cookie")))
637         fail("The cleanup part was not executed even though the test case set "
638              "has.cleanup to true");
639 }
640 
641 
642 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__kill_children);
ATF_TEST_CASE_BODY(run_test_case__atf__kill_children)643 ATF_TEST_CASE_BODY(run_test_case__atf__kill_children)
644 {
645     atf_helper helper(this, "spawn_blocking_child");
646     helper.set_config("control_dir", fs::current_path());
647     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed),
648                    helper.run());
649 
650     if (!fs::exists(fs::path("pid")))
651         fail("The pid file was not created");
652     std::ifstream pidfile("pid");
653     ATF_REQUIRE(pidfile);
654     pid_t pid;
655     pidfile >> pid;
656     pidfile.close();
657 
658     int attempts = 30;
659 retry:
660     if (::kill(pid, SIGCONT) != -1 || errno != ESRCH) {
661         // Looks like the subchild did not die.
662         //
663         // Note that this might be inaccurate for two reasons:
664         // 1) The system may have spawned a new process with the same pid as
665         //    our subchild... but in practice, this does not happen because
666         //    most systems do not immediately reuse pid numbers.  If that
667         //    happens... well, we get a false test failure.
668         // 2) We ran so fast that even if the process was sent a signal to
669         //    die, it has not had enough time to process it yet.  This is why
670         //    we retry this a few times.
671         if (attempts > 0) {
672             std::cout << "Subprocess not dead yet; retrying wait\n";
673             --attempts;
674             ::usleep(100000);
675             goto retry;
676         }
677         fail(F("The subprocess %s of our child was not killed") % pid);
678     }
679 }
680 
681 
682 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__isolation);
ATF_TEST_CASE_BODY(run_test_case__atf__isolation)683 ATF_TEST_CASE_BODY(run_test_case__atf__isolation)
684 {
685     atf_helper helper(this, "validate_isolation");
686     // Simple checks to make sure that the test case has been isolated.
687     utils::setenv("HOME", "fake-value");
688     utils::setenv("LANG", "C");
689     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed),
690                    helper.run());
691 }
692 
693 
694 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__allowed_architectures);
ATF_TEST_CASE_BODY(run_test_case__atf__allowed_architectures)695 ATF_TEST_CASE_BODY(run_test_case__atf__allowed_architectures)
696 {
697     atf_helper helper(this, "create_cookie_in_control_dir");
698     helper.set_metadata("allowed_architectures", "i386 x86_64");
699     helper.config().set_string("architecture", "powerpc");
700     helper.config().set_string("platform", "");
701     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::skipped, "Current "
702                                        "architecture 'powerpc' not supported"),
703                    helper.run());
704 
705     if (fs::exists(fs::path("cookie")))
706         fail("The test case was not really skipped when the requirements "
707              "check failed");
708 }
709 
710 
711 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__allowed_platforms);
ATF_TEST_CASE_BODY(run_test_case__atf__allowed_platforms)712 ATF_TEST_CASE_BODY(run_test_case__atf__allowed_platforms)
713 {
714     atf_helper helper(this, "create_cookie_in_control_dir");
715     helper.set_metadata("allowed_platforms", "i386 amd64");
716     helper.config().set_string("architecture", "");
717     helper.config().set_string("platform", "macppc");
718     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::skipped, "Current "
719                                        "platform 'macppc' not supported"),
720                    helper.run());
721 
722     if (fs::exists(fs::path("cookie")))
723         fail("The test case was not really skipped when the requirements "
724              "check failed");
725 }
726 
727 
728 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__required_configs);
ATF_TEST_CASE_BODY(run_test_case__atf__required_configs)729 ATF_TEST_CASE_BODY(run_test_case__atf__required_configs)
730 {
731     atf_helper helper(this, "create_cookie_in_control_dir");
732     helper.set_metadata("required_configs", "used-var");
733     helper.set_config("control_dir", fs::current_path());
734     helper.set_config("unused-var", "value");
735     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::skipped, "Required "
736                                        "configuration property 'used-var' not "
737                                        "defined"),
738                    helper.run());
739 
740     if (fs::exists(fs::path("cookie")))
741         fail("The test case was not really skipped when the requirements "
742              "check failed");
743 }
744 
745 
746 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__required_programs);
ATF_TEST_CASE_BODY(run_test_case__atf__required_programs)747 ATF_TEST_CASE_BODY(run_test_case__atf__required_programs)
748 {
749     atf_helper helper(this, "create_cookie_in_control_dir");
750     helper.set_metadata("required_programs", "/non-existent/program");
751     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::skipped, "Required "
752                                        "program '/non-existent/program' not "
753                                        "found"),
754                    helper.run());
755 
756     if (fs::exists(fs::path("cookie")))
757         fail("The test case was not really skipped when the requirements "
758              "check failed");
759 }
760 
761 
762 ATF_TEST_CASE(run_test_case__atf__required_user__atf__root__atf__ok);
ATF_TEST_CASE_HEAD(run_test_case__atf__required_user__atf__root__atf__ok)763 ATF_TEST_CASE_HEAD(run_test_case__atf__required_user__atf__root__atf__ok)
764 {
765     set_md_var("require.user", "root");
766 }
ATF_TEST_CASE_BODY(run_test_case__atf__required_user__atf__root__atf__ok)767 ATF_TEST_CASE_BODY(run_test_case__atf__required_user__atf__root__atf__ok)
768 {
769     atf_helper helper(this, "create_cookie_in_workdir");
770     helper.set_metadata("required_user", "root");
771     ATF_REQUIRE(passwd::current_user().is_root());
772     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed),
773                    helper.run());
774 }
775 
776 
777 ATF_TEST_CASE(run_test_case__atf__required_user__atf__root__atf__skip);
ATF_TEST_CASE_HEAD(run_test_case__atf__required_user__atf__root__atf__skip)778 ATF_TEST_CASE_HEAD(run_test_case__atf__required_user__atf__root__atf__skip)
779 {
780     set_md_var("require.user", "unprivileged");
781 }
ATF_TEST_CASE_BODY(run_test_case__atf__required_user__atf__root__atf__skip)782 ATF_TEST_CASE_BODY(run_test_case__atf__required_user__atf__root__atf__skip)
783 {
784     atf_helper helper(this, "create_cookie_in_workdir");
785     helper.set_metadata("required_user", "root");
786     ATF_REQUIRE(!passwd::current_user().is_root());
787     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::skipped, "Requires "
788                                        "root privileges"),
789                    helper.run());
790 }
791 
792 
793 ATF_TEST_CASE(run_test_case__atf__required_user__atf__unprivileged__atf__ok);
ATF_TEST_CASE_HEAD(run_test_case__atf__required_user__atf__unprivileged__atf__ok)794 ATF_TEST_CASE_HEAD(run_test_case__atf__required_user__atf__unprivileged__atf__ok)
795 {
796     set_md_var("require.user", "unprivileged");
797 }
ATF_TEST_CASE_BODY(run_test_case__atf__required_user__atf__unprivileged__atf__ok)798 ATF_TEST_CASE_BODY(run_test_case__atf__required_user__atf__unprivileged__atf__ok)
799 {
800     atf_helper helper(this, "create_cookie_in_workdir");
801     helper.set_metadata("required_user", "unprivileged");
802     ATF_REQUIRE(!helper.config().is_set("unprivileged_user"));
803     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed),
804                    helper.run());
805 }
806 
807 
808 ATF_TEST_CASE(run_test_case__atf__required_user__atf__unprivileged__atf__skip);
ATF_TEST_CASE_HEAD(run_test_case__atf__required_user__atf__unprivileged__atf__skip)809 ATF_TEST_CASE_HEAD(run_test_case__atf__required_user__atf__unprivileged__atf__skip)
810 {
811     set_md_var("require.user", "root");
812 }
ATF_TEST_CASE_BODY(run_test_case__atf__required_user__atf__unprivileged__atf__skip)813 ATF_TEST_CASE_BODY(run_test_case__atf__required_user__atf__unprivileged__atf__skip)
814 {
815     atf_helper helper(this, "create_cookie_in_workdir");
816     helper.set_metadata("required_user", "unprivileged");
817     ATF_REQUIRE(!helper.config().is_set("unprivileged_user"));
818     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::skipped, "Requires "
819                                        "an unprivileged user but the "
820                                        "unprivileged-user configuration "
821                                        "variable is not defined"),
822                    helper.run());
823 }
824 
825 
826 ATF_TEST_CASE(run_test_case__atf__required_user__atf__unprivileged__atf__drop);
ATF_TEST_CASE_HEAD(run_test_case__atf__required_user__atf__unprivileged__atf__drop)827 ATF_TEST_CASE_HEAD(run_test_case__atf__required_user__atf__unprivileged__atf__drop)
828 {
829     set_md_var("require.config", "unprivileged-user");
830     set_md_var("require.user", "root");
831 }
ATF_TEST_CASE_BODY(run_test_case__atf__required_user__atf__unprivileged__atf__drop)832 ATF_TEST_CASE_BODY(run_test_case__atf__required_user__atf__unprivileged__atf__drop)
833 {
834     atf_helper helper(this, "check_unprivileged");
835     helper.set_metadata("required_user", "unprivileged");
836     helper.config().set< engine::user_node >(
837         "unprivileged_user",
838         passwd::find_user_by_name(get_config_var("unprivileged-user")));
839     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed),
840                    helper.run());
841 }
842 
843 
844 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__timeout_body);
ATF_TEST_CASE_BODY(run_test_case__atf__timeout_body)845 ATF_TEST_CASE_BODY(run_test_case__atf__timeout_body)
846 {
847     atf_helper helper(this, "timeout_body");
848     helper.set_metadata("timeout", "1");
849     helper.set_config("control_dir", fs::current_path());
850     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::broken,
851                                        "Test case body timed out"),
852                    helper.run());
853 
854     if (fs::exists(fs::path("cookie")))
855         fail("It seems that the test case was not killed after it timed out");
856 }
857 
858 
859 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__timeout_cleanup);
ATF_TEST_CASE_BODY(run_test_case__atf__timeout_cleanup)860 ATF_TEST_CASE_BODY(run_test_case__atf__timeout_cleanup)
861 {
862     atf_helper helper(this, "timeout_cleanup");
863     helper.set_metadata("has_cleanup", "true");
864     helper.set_metadata("timeout", "1");
865     helper.set_config("control_dir", fs::current_path());
866     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::broken,
867                                        "Test case cleanup timed out"),
868                    helper.run());
869 
870     if (fs::exists(fs::path("cookie")))
871         fail("It seems that the test case was not killed after it timed out");
872 }
873 
874 
875 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__stacktrace__atf__body);
ATF_TEST_CASE_BODY(run_test_case__atf__stacktrace__atf__body)876 ATF_TEST_CASE_BODY(run_test_case__atf__stacktrace__atf__body)
877 {
878     atf_helper helper(this, "crash");
879     capture_hooks hooks;
880     const engine::test_result result = helper.run(hooks);
881     ATF_REQUIRE(engine::test_result::broken == result.type());
882     ATF_REQUIRE_MATCH("received signal.*core dumped", result.reason());
883 
884     ATF_REQUIRE(!atf::utils::grep_string("attempting to gather stack trace",
885                                          hooks.stdout_contents));
886     ATF_REQUIRE( atf::utils::grep_string("attempting to gather stack trace",
887                                          hooks.stderr_contents));
888 }
889 
890 
891 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__stacktrace__atf__cleanup);
ATF_TEST_CASE_BODY(run_test_case__atf__stacktrace__atf__cleanup)892 ATF_TEST_CASE_BODY(run_test_case__atf__stacktrace__atf__cleanup)
893 {
894     atf_helper helper(this, "crash_cleanup");
895     helper.set_metadata("has_cleanup", "true");
896     capture_hooks hooks;
897     const engine::test_result result = helper.run(hooks);
898     ATF_REQUIRE(engine::test_result::broken == result.type());
899     ATF_REQUIRE_MATCH(F("cleanup received signal %s") % SIGABRT,
900                       result.reason());
901 
902     ATF_REQUIRE(!atf::utils::grep_string("attempting to gather stack trace",
903                                          hooks.stdout_contents));
904     ATF_REQUIRE( atf::utils::grep_string("attempting to gather stack trace",
905                                          hooks.stderr_contents));
906 }
907 
908 
909 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__missing_results_file);
ATF_TEST_CASE_BODY(run_test_case__atf__missing_results_file)910 ATF_TEST_CASE_BODY(run_test_case__atf__missing_results_file)
911 {
912     atf_helper helper(this, "crash");
913     const engine::test_result result = helper.run();
914     ATF_REQUIRE(engine::test_result::broken == result.type());
915     // Need to match instead of doing an explicit comparison because the string
916     // may include the "core dumped" substring.
917     ATF_REQUIRE_MATCH(F("test case received signal %s") % SIGABRT,
918                       result.reason());
919 }
920 
921 
922 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__missing_test_program);
ATF_TEST_CASE_BODY(run_test_case__atf__missing_test_program)923 ATF_TEST_CASE_BODY(run_test_case__atf__missing_test_program)
924 {
925     atf_helper helper(this, "crash");
926     ATF_REQUIRE(::mkdir("dir", 0755) != -1);
927     helper.move("test_case_atf_helpers", "dir");
928     ATF_REQUIRE(::unlink("dir/test_case_atf_helpers") != -1);
929     const engine::test_result result = helper.run();
930     ATF_REQUIRE(engine::test_result::broken == result.type());
931     ATF_REQUIRE_MATCH("Test program does not exist", result.reason());
932 }
933 
934 
935 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__output);
ATF_TEST_CASE_BODY(run_test_case__atf__output)936 ATF_TEST_CASE_BODY(run_test_case__atf__output)
937 {
938     atf_helper helper(this, "output");
939     helper.set_metadata("has_cleanup", "true");
940 
941     capture_hooks hooks;
942     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed),
943                    helper.run(hooks));
944 
945     ATF_REQUIRE_EQ("Body message to stdout\nCleanup message to stdout\n",
946                    hooks.stdout_contents);
947     ATF_REQUIRE_EQ("Body message to stderr\nCleanup message to stderr\n",
948                    hooks.stderr_contents);
949 }
950 
951 
952 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__result_pass);
ATF_TEST_CASE_BODY(run_test_case__plain__result_pass)953 ATF_TEST_CASE_BODY(run_test_case__plain__result_pass)
954 {
955     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed),
956                    plain_helper(this, "pass").run());
957 }
958 
959 
960 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__result_fail);
ATF_TEST_CASE_BODY(run_test_case__plain__result_fail)961 ATF_TEST_CASE_BODY(run_test_case__plain__result_fail)
962 {
963     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::failed,
964                                        "Returned non-success exit status 8"),
965                    plain_helper(this, "fail").run());
966 }
967 
968 
969 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__result_crash);
ATF_TEST_CASE_BODY(run_test_case__plain__result_crash)970 ATF_TEST_CASE_BODY(run_test_case__plain__result_crash)
971 {
972     const engine::test_result result = plain_helper(this, "crash").run();
973     ATF_REQUIRE(engine::test_result::broken == result.type());
974     ATF_REQUIRE_MATCH(F("Received signal %s") % SIGABRT, result.reason());
975 }
976 
977 
978 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__current_directory);
ATF_TEST_CASE_BODY(run_test_case__plain__current_directory)979 ATF_TEST_CASE_BODY(run_test_case__plain__current_directory)
980 {
981     plain_helper helper(this, "pass");
982     helper.move("program", ".");
983     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed),
984                    helper.run());
985 }
986 
987 
988 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__subdirectory);
ATF_TEST_CASE_BODY(run_test_case__plain__subdirectory)989 ATF_TEST_CASE_BODY(run_test_case__plain__subdirectory)
990 {
991     plain_helper helper(this, "pass");
992     ATF_REQUIRE(::mkdir("dir1", 0755) != -1);
993     ATF_REQUIRE(::mkdir("dir1/dir2", 0755) != -1);
994     helper.move("dir2/program", "dir1");
995     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed),
996                    helper.run());
997 }
998 
999 
1000 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__kill_children);
ATF_TEST_CASE_BODY(run_test_case__plain__kill_children)1001 ATF_TEST_CASE_BODY(run_test_case__plain__kill_children)
1002 {
1003     plain_helper helper(this, "spawn_blocking_child");
1004     helper.set("CONTROL_DIR", fs::current_path());
1005     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed),
1006                    helper.run());
1007 
1008     if (!fs::exists(fs::path("pid")))
1009         fail("The pid file was not created");
1010     std::ifstream pidfile("pid");
1011     ATF_REQUIRE(pidfile);
1012     pid_t pid;
1013     pidfile >> pid;
1014     pidfile.close();
1015 
1016     int attempts = 30;
1017 retry:
1018     if (::kill(pid, SIGCONT) != -1 || errno != ESRCH) {
1019         // Looks like the subchild did not die.
1020         //
1021         // Note that this might be inaccurate for two reasons:
1022         // 1) The system may have spawned a new process with the same pid as
1023         //    our subchild... but in practice, this does not happen because
1024         //    most systems do not immediately reuse pid numbers.  If that
1025         //    happens... well, we get a false test failure.
1026         // 2) We ran so fast that even if the process was sent a signal to
1027         //    die, it has not had enough time to process it yet.  This is why
1028         //    we retry this a few times.
1029         if (attempts > 0) {
1030             std::cout << "Subprocess not dead yet; retrying wait\n";
1031             --attempts;
1032             ::usleep(100000);
1033             goto retry;
1034         }
1035         fail(F("The subprocess %s of our child was not killed") % pid);
1036     }
1037 }
1038 
1039 
1040 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__isolation);
ATF_TEST_CASE_BODY(run_test_case__plain__isolation)1041 ATF_TEST_CASE_BODY(run_test_case__plain__isolation)
1042 {
1043     const plain_helper helper(this, "validate_isolation");
1044     utils::setenv("TEST_CASE", "validate_isolation");
1045     // Simple checks to make sure that the test case has been isolated.
1046     utils::setenv("HOME", "fake-value");
1047     utils::setenv("LANG", "C");
1048     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed),
1049                    helper.run());
1050 }
1051 
1052 
1053 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__timeout);
ATF_TEST_CASE_BODY(run_test_case__plain__timeout)1054 ATF_TEST_CASE_BODY(run_test_case__plain__timeout)
1055 {
1056     plain_helper helper(this, "timeout",
1057                         utils::make_optional(datetime::delta(1, 0)));
1058     helper.set("CONTROL_DIR", fs::current_path());
1059     ATF_REQUIRE_EQ(engine::test_result(engine::test_result::broken,
1060                                        "Test case timed out"),
1061                    helper.run());
1062 
1063     if (fs::exists(fs::path("cookie")))
1064         fail("It seems that the test case was not killed after it timed out");
1065 }
1066 
1067 
1068 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__stacktrace);
ATF_TEST_CASE_BODY(run_test_case__plain__stacktrace)1069 ATF_TEST_CASE_BODY(run_test_case__plain__stacktrace)
1070 {
1071     plain_helper helper(this, "crash");
1072     helper.set("CONTROL_DIR", fs::current_path());
1073 
1074     const engine::test_result result = plain_helper(this, "crash").run();
1075     ATF_REQUIRE(engine::test_result::broken == result.type());
1076     ATF_REQUIRE_MATCH(F("Received signal %s") % SIGABRT, result.reason());
1077 
1078     ATF_REQUIRE(!atf::utils::grep_file("attempting to gather stack trace",
1079                                        "helper-stdout.txt"));
1080     ATF_REQUIRE( atf::utils::grep_file("attempting to gather stack trace",
1081                                        "helper-stderr.txt"));
1082 }
1083 
1084 
1085 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__missing_test_program);
ATF_TEST_CASE_BODY(run_test_case__plain__missing_test_program)1086 ATF_TEST_CASE_BODY(run_test_case__plain__missing_test_program)
1087 {
1088     plain_helper helper(this, "pass");
1089     ATF_REQUIRE(::mkdir("dir", 0755) != -1);
1090     helper.move("test_case_helpers", "dir");
1091     ATF_REQUIRE(::unlink("dir/test_case_helpers") != -1);
1092     const engine::test_result result = helper.run();
1093     ATF_REQUIRE(engine::test_result::broken == result.type());
1094     ATF_REQUIRE_MATCH("Test program does not exist", result.reason());
1095 }
1096 
1097 
ATF_INIT_TEST_CASES(tcs)1098 ATF_INIT_TEST_CASES(tcs)
1099 {
1100     ATF_ADD_TEST_CASE(tcs, test_case__ctor_and_getters);
1101     ATF_ADD_TEST_CASE(tcs, test_case__fake_result);
1102 
1103     ATF_ADD_TEST_CASE(tcs, test_case__operators_eq_and_ne__copy);
1104     ATF_ADD_TEST_CASE(tcs, test_case__operators_eq_and_ne__not_copy);
1105 
1106     ATF_ADD_TEST_CASE(tcs, test_case__output);
1107 
1108     ATF_ADD_TEST_CASE(tcs, run_test_case__tester_crashes);
1109 
1110     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__current_directory);
1111     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__subdirectory);
1112     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__config_variables);
1113     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__cleanup_shares_workdir);
1114     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__has_cleanup__atf__false);
1115     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__has_cleanup__atf__true);
1116     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__kill_children);
1117     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__isolation);
1118     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__allowed_architectures);
1119     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__allowed_platforms);
1120     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__required_configs);
1121     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__required_programs);
1122     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__required_user__atf__root__atf__ok);
1123     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__required_user__atf__root__atf__skip);
1124     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__required_user__atf__unprivileged__atf__ok);
1125     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__required_user__atf__unprivileged__atf__skip);
1126     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__required_user__atf__unprivileged__atf__drop);
1127     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__timeout_body);
1128     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__timeout_cleanup);
1129     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__stacktrace__atf__body);
1130     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__stacktrace__atf__cleanup);
1131     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__missing_results_file);
1132     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__missing_test_program);
1133     ATF_ADD_TEST_CASE(tcs, run_test_case__atf__output);
1134 
1135     ATF_ADD_TEST_CASE(tcs, run_test_case__plain__result_pass);
1136     ATF_ADD_TEST_CASE(tcs, run_test_case__plain__result_fail);
1137     ATF_ADD_TEST_CASE(tcs, run_test_case__plain__result_crash);
1138     ATF_ADD_TEST_CASE(tcs, run_test_case__plain__current_directory);
1139     ATF_ADD_TEST_CASE(tcs, run_test_case__plain__subdirectory);
1140     ATF_ADD_TEST_CASE(tcs, run_test_case__plain__kill_children);
1141     ATF_ADD_TEST_CASE(tcs, run_test_case__plain__isolation);
1142     ATF_ADD_TEST_CASE(tcs, run_test_case__plain__timeout);
1143     ATF_ADD_TEST_CASE(tcs, run_test_case__plain__stacktrace);
1144     ATF_ADD_TEST_CASE(tcs, run_test_case__plain__missing_test_program);
1145 
1146     // TODO(jmmv): Add test cases for debug.
1147 }
1148