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