Improve the process for GNU tools
[minix3.git] / external / bsd / kyua-cli / dist / engine / test_case_test.cpp
blob4b89a6526482eb74f8f17dcd0a8f7a40d741d8de
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.
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.
29 #include "engine/test_case.hpp"
31 extern "C" {
32 #include <sys/stat.h>
34 #include <signal.h>
35 #include <unistd.h>
38 #include <cerrno>
39 #include <cstdlib>
40 #include <fstream>
41 #include <iostream>
42 #include <sstream>
43 #include <stdexcept>
44 #include <string>
46 #include <atf-c++.hpp>
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"
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;
71 using utils::none;
72 using utils::optional;
75 namespace {
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;
84 /// Contents of the stderr of the test case.
85 std::string stderr_contents;
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)
93 atf::utils::cat_file(file.str(), "helper stdout:");
94 ATF_REQUIRE(stdout_contents.empty());
96 std::ifstream input(file.c_str());
97 ATF_REQUIRE(input);
98 stdout_contents = utils::read_stream(input);
101 /// Stores the stderr of the test case into stderr_contents.
103 /// \param file The path to the file containing the stderr.
104 void
105 got_stderr(const fs::path& file)
107 atf::utils::cat_file(file.str(), "helper stderr:");
108 ATF_REQUIRE(stderr_contents.empty());
110 std::ifstream input(file.c_str());
111 ATF_REQUIRE(input);
112 stderr_contents = utils::read_stream(input);
117 /// Launcher for the helper test cases.
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;
126 /// The root of the test suite.
127 fs::path _root;
129 /// Path to the helper test program, relative to _root.
130 fs::path _binary_path;
132 /// Name of the helper test case to run.
133 const std::string _name;
135 /// Metadata of the test case.
136 engine::metadata_builder _mdbuilder;
138 /// Run-time configuration for the test case.
139 config::tree _user_config;
141 public:
142 /// Constructs a new helper.
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())
154 _user_config.set_string("architecture", "mock-architecture");
155 _user_config.set_string("platform", "mock-platform");
158 /// Provides raw access to the run-time configuration.
160 /// To override test-suite-specific variables, use set_config() as it
161 /// abstracts away the name of the fake test suite.
163 /// \returns A reference to the test case configuration.
164 config::tree&
165 config(void)
167 return _user_config;
170 /// Sets a test-suite-specific configuration variable for the helper.
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)
178 _user_config.set_string(F("test_suites.the-suite.%s") % variable,
179 F("%s") % value);
182 /// Sets a metadata variable for the helper.
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)
190 _mdbuilder.set_string(variable, F("%s") % value);
193 /// Places the helper in a different location.
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.
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.
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)
207 _binary_path = fs::path(new_binary_path);
208 _root = fs::path(new_root);
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);
216 /// Runs the helper.
218 /// \return The result of the execution.
219 engine::test_result
220 run(void) const
222 engine::test_case_hooks dummy_hooks;
223 return run(dummy_hooks);
226 /// Runs the helper.
228 /// \param hooks The hooks to pass to the test case.
230 /// \return The result of the execution.
231 engine::test_result
232 run(engine::test_case_hooks& hooks) const
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());
240 const fs::path workdir("work");
241 fs::mkdir(workdir, 0755);
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;
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.
256 /// \param file The location of the test case's stdout.
257 void
258 got_stdout(const fs::path& file)
260 atf::utils::copy_file(file.str(), "helper-stdout.txt");
261 atf::utils::cat_file("helper-stdout.txt", "helper stdout: ");
264 /// Copies the stderr of the test case outside of its work directory.
266 /// \param file The location of the test case's stderr.
267 void
268 got_stderr(const fs::path& file)
270 atf::utils::copy_file(file.str(), "helper-stderr.txt");
271 atf::utils::cat_file("helper-stderr.txt", "helper stderr: ");
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;
281 /// The root of the test suite.
282 fs::path _root;
284 /// Path to the helper test program, relative to _root.
285 fs::path _binary_path;
287 /// Optional timeout for the test program.
288 optional< datetime::delta > _timeout;
290 public:
291 /// Constructs a new helper.
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)
304 utils::setenv("TEST_CASE", name);
307 /// Sets an environment variable for the helper.
309 /// This is simply syntactic sugar for utils::setenv.
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)
317 utils::setenv(variable, F("%s") % value);
320 /// Places the helper in a different location.
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.
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.
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)
334 _binary_path = fs::path(new_binary_path);
335 _root = fs::path(new_root);
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);
343 /// Runs the helper.
345 /// \param user_config The runtime engine configuration, if different to the
346 /// defaults.
348 /// \return The result of the execution.
349 engine::test_result
350 run(const config::tree& user_config = engine::default_config()) const
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;
367 /// Creates a mock tester that receives a signal.
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
373 create_mock_tester_signal(const char* interface, const int term_sig)
375 const std::string tester_name = F("kyua-%s-tester") % interface;
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);
387 utils::setenv("KYUA_TESTERSDIR", fs::current_path().str());
391 } // anonymous namespace
394 ATF_TEST_CASE_WITHOUT_HEAD(test_case__ctor_and_getters)
395 ATF_TEST_CASE_BODY(test_case__ctor_and_getters)
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());
410 ATF_TEST_CASE_WITHOUT_HEAD(test_case__fake_result)
411 ATF_TEST_CASE_BODY(test_case__fake_result)
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());
426 ATF_TEST_CASE_WITHOUT_HEAD(test_case__operators_eq_and_ne__copy);
427 ATF_TEST_CASE_BODY(test_case__operators_eq_and_ne__copy)
429 const engine::test_program tp(
430 "plain", fs::path("non-existent"), fs::path("."), "suite-name",
431 engine::metadata_builder().build());
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));
441 ATF_TEST_CASE_WITHOUT_HEAD(test_case__output);
442 ATF_TEST_CASE_BODY(test_case__output)
444 const engine::test_program tp(
445 "plain", fs::path("non-existent"), fs::path("."), "suite-name",
446 engine::metadata_builder().build());
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());
463 ATF_TEST_CASE_WITHOUT_HEAD(test_case__operators_eq_and_ne__not_copy);
464 ATF_TEST_CASE_BODY(test_case__operators_eq_and_ne__not_copy)
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();
475 const engine::test_case base_tc(base_interface, base_tp, base_name,
476 base_metadata);
478 // Construct with all same values.
480 const engine::test_case other_tc(base_interface, base_tp, base_name,
481 base_metadata);
483 ATF_REQUIRE( base_tc == other_tc);
484 ATF_REQUIRE(!(base_tc != other_tc));
487 // Different interface.
489 const engine::test_case other_tc("atf", base_tp, base_name,
490 base_metadata);
492 ATF_REQUIRE(!(base_tc == other_tc));
493 ATF_REQUIRE( base_tc != other_tc);
496 // Different test program, different identifier.
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);
504 ATF_REQUIRE(!(base_tc == other_tc));
505 ATF_REQUIRE( base_tc != other_tc);
508 // Different test program, same identifier. Cannot be detected!
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);
516 ATF_REQUIRE( base_tc == other_tc);
517 ATF_REQUIRE(!(base_tc != other_tc));
520 // Different name.
522 const engine::test_case other_tc(base_interface, base_tp, "other",
523 base_metadata);
525 ATF_REQUIRE(!(base_tc == other_tc));
526 ATF_REQUIRE( base_tc != other_tc);
529 // Different metadata.
531 const engine::test_case other_tc(base_interface, base_tp, base_name,
532 engine::metadata_builder().build());
534 ATF_REQUIRE(!(base_tc == other_tc));
535 ATF_REQUIRE( base_tc != other_tc);
540 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__tester_crashes);
541 ATF_TEST_CASE_BODY(run_test_case__tester_crashes)
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);
549 ATF_REQUIRE(engine::test_result::broken == result.type());
550 ATF_REQUIRE_MATCH("Tester received signal.*bug", result.reason());
552 ATF_REQUIRE_EQ("stdout stuff\n", hooks.stdout_contents);
553 ATF_REQUIRE_EQ("stderr stuff\n", hooks.stderr_contents);
557 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__current_directory);
558 ATF_TEST_CASE_BODY(run_test_case__atf__current_directory)
560 atf_helper helper(this, "pass");
561 helper.move("program", ".");
562 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed),
563 helper.run());
567 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__subdirectory);
568 ATF_TEST_CASE_BODY(run_test_case__atf__subdirectory)
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());
579 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__config_variables);
580 ATF_TEST_CASE_BODY(run_test_case__atf__config_variables)
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());
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");
593 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__cleanup_shares_workdir);
594 ATF_TEST_CASE_BODY(run_test_case__atf__cleanup_shares_workdir)
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());
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");
612 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__has_cleanup__atf__false);
613 ATF_TEST_CASE_BODY(run_test_case__atf__has_cleanup__atf__false)
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());
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");
627 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__has_cleanup__atf__true);
628 ATF_TEST_CASE_BODY(run_test_case__atf__has_cleanup__atf__true)
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());
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");
642 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__kill_children);
643 ATF_TEST_CASE_BODY(run_test_case__atf__kill_children)
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());
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();
658 int attempts = 30;
659 retry:
660 if (::kill(pid, SIGCONT) != -1 || errno != ESRCH) {
661 // Looks like the subchild did not die.
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;
677 fail(F("The subprocess %s of our child was not killed") % pid);
682 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__isolation);
683 ATF_TEST_CASE_BODY(run_test_case__atf__isolation)
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());
694 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__allowed_architectures);
695 ATF_TEST_CASE_BODY(run_test_case__atf__allowed_architectures)
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());
705 if (fs::exists(fs::path("cookie")))
706 fail("The test case was not really skipped when the requirements "
707 "check failed");
711 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__allowed_platforms);
712 ATF_TEST_CASE_BODY(run_test_case__atf__allowed_platforms)
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());
722 if (fs::exists(fs::path("cookie")))
723 fail("The test case was not really skipped when the requirements "
724 "check failed");
728 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__required_configs);
729 ATF_TEST_CASE_BODY(run_test_case__atf__required_configs)
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());
740 if (fs::exists(fs::path("cookie")))
741 fail("The test case was not really skipped when the requirements "
742 "check failed");
746 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__required_programs);
747 ATF_TEST_CASE_BODY(run_test_case__atf__required_programs)
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());
756 if (fs::exists(fs::path("cookie")))
757 fail("The test case was not really skipped when the requirements "
758 "check failed");
762 ATF_TEST_CASE(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)
765 set_md_var("require.user", "root");
767 ATF_TEST_CASE_BODY(run_test_case__atf__required_user__atf__root__atf__ok)
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());
777 ATF_TEST_CASE(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)
780 set_md_var("require.user", "unprivileged");
782 ATF_TEST_CASE_BODY(run_test_case__atf__required_user__atf__root__atf__skip)
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());
793 ATF_TEST_CASE(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)
796 set_md_var("require.user", "unprivileged");
798 ATF_TEST_CASE_BODY(run_test_case__atf__required_user__atf__unprivileged__atf__ok)
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());
808 ATF_TEST_CASE(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)
811 set_md_var("require.user", "root");
813 ATF_TEST_CASE_BODY(run_test_case__atf__required_user__atf__unprivileged__atf__skip)
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());
826 ATF_TEST_CASE(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)
829 set_md_var("require.config", "unprivileged-user");
830 set_md_var("require.user", "root");
832 ATF_TEST_CASE_BODY(run_test_case__atf__required_user__atf__unprivileged__atf__drop)
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());
844 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__timeout_body);
845 ATF_TEST_CASE_BODY(run_test_case__atf__timeout_body)
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());
854 if (fs::exists(fs::path("cookie")))
855 fail("It seems that the test case was not killed after it timed out");
859 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__timeout_cleanup);
860 ATF_TEST_CASE_BODY(run_test_case__atf__timeout_cleanup)
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());
870 if (fs::exists(fs::path("cookie")))
871 fail("It seems that the test case was not killed after it timed out");
875 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__stacktrace__atf__body);
876 ATF_TEST_CASE_BODY(run_test_case__atf__stacktrace__atf__body)
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());
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));
891 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__stacktrace__atf__cleanup);
892 ATF_TEST_CASE_BODY(run_test_case__atf__stacktrace__atf__cleanup)
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());
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));
909 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__missing_results_file);
910 ATF_TEST_CASE_BODY(run_test_case__atf__missing_results_file)
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());
922 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__missing_test_program);
923 ATF_TEST_CASE_BODY(run_test_case__atf__missing_test_program)
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());
935 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__atf__output);
936 ATF_TEST_CASE_BODY(run_test_case__atf__output)
938 atf_helper helper(this, "output");
939 helper.set_metadata("has_cleanup", "true");
941 capture_hooks hooks;
942 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed),
943 helper.run(hooks));
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);
952 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__result_pass);
953 ATF_TEST_CASE_BODY(run_test_case__plain__result_pass)
955 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed),
956 plain_helper(this, "pass").run());
960 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__result_fail);
961 ATF_TEST_CASE_BODY(run_test_case__plain__result_fail)
963 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::failed,
964 "Returned non-success exit status 8"),
965 plain_helper(this, "fail").run());
969 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__result_crash);
970 ATF_TEST_CASE_BODY(run_test_case__plain__result_crash)
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());
978 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__current_directory);
979 ATF_TEST_CASE_BODY(run_test_case__plain__current_directory)
981 plain_helper helper(this, "pass");
982 helper.move("program", ".");
983 ATF_REQUIRE_EQ(engine::test_result(engine::test_result::passed),
984 helper.run());
988 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__subdirectory);
989 ATF_TEST_CASE_BODY(run_test_case__plain__subdirectory)
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());
1000 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__kill_children);
1001 ATF_TEST_CASE_BODY(run_test_case__plain__kill_children)
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());
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();
1016 int attempts = 30;
1017 retry:
1018 if (::kill(pid, SIGCONT) != -1 || errno != ESRCH) {
1019 // Looks like the subchild did not die.
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;
1035 fail(F("The subprocess %s of our child was not killed") % pid);
1040 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__isolation);
1041 ATF_TEST_CASE_BODY(run_test_case__plain__isolation)
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());
1053 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__timeout);
1054 ATF_TEST_CASE_BODY(run_test_case__plain__timeout)
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());
1063 if (fs::exists(fs::path("cookie")))
1064 fail("It seems that the test case was not killed after it timed out");
1068 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__stacktrace);
1069 ATF_TEST_CASE_BODY(run_test_case__plain__stacktrace)
1071 plain_helper helper(this, "crash");
1072 helper.set("CONTROL_DIR", fs::current_path());
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());
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"));
1085 ATF_TEST_CASE_WITHOUT_HEAD(run_test_case__plain__missing_test_program);
1086 ATF_TEST_CASE_BODY(run_test_case__plain__missing_test_program)
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());
1098 ATF_INIT_TEST_CASES(tcs)
1100 ATF_ADD_TEST_CASE(tcs, test_case__ctor_and_getters);
1101 ATF_ADD_TEST_CASE(tcs, test_case__fake_result);
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);
1106 ATF_ADD_TEST_CASE(tcs, test_case__output);
1108 ATF_ADD_TEST_CASE(tcs, run_test_case__tester_crashes);
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);
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);
1146 // TODO(jmmv): Add test cases for debug.