xref: /netbsd-src/external/bsd/kyua-cli/dist/engine/test_case_test.cpp (revision ba65fde2d7fefa7d39838fa5fa855e62bd606b5e)
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