1 // Copyright 2014 The Kyua Authors.
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/scheduler.hpp"
30
31 extern "C" {
32 #include <sys/types.h>
33
34 #include <signal.h>
35 #include <unistd.h>
36 }
37
38 #include <cstdlib>
39 #include <fstream>
40 #include <iostream>
41 #include <string>
42
43 #include <atf-c++.hpp>
44
45 #include "engine/config.hpp"
46 #include "engine/exceptions.hpp"
47 #include "model/context.hpp"
48 #include "model/metadata.hpp"
49 #include "model/test_case.hpp"
50 #include "model/test_program.hpp"
51 #include "model/test_result.hpp"
52 #include "utils/config/tree.ipp"
53 #include "utils/datetime.hpp"
54 #include "utils/defs.hpp"
55 #include "utils/env.hpp"
56 #include "utils/format/containers.ipp"
57 #include "utils/format/macros.hpp"
58 #include "utils/fs/operations.hpp"
59 #include "utils/fs/path.hpp"
60 #include "utils/optional.ipp"
61 #include "utils/passwd.hpp"
62 #include "utils/process/status.hpp"
63 #include "utils/sanity.hpp"
64 #include "utils/stacktrace.hpp"
65 #include "utils/stream.hpp"
66 #include "utils/test_utils.ipp"
67 #include "utils/text/exceptions.hpp"
68 #include "utils/text/operations.ipp"
69
70 namespace config = utils::config;
71 namespace datetime = utils::datetime;
72 namespace fs = utils::fs;
73 namespace passwd = utils::passwd;
74 namespace process = utils::process;
75 namespace scheduler = engine::scheduler;
76 namespace text = utils::text;
77
78 using utils::none;
79 using utils::optional;
80
81
82 namespace {
83
84
85 /// Checks if a string starts with a prefix.
86 ///
87 /// \param str The string to be tested.
88 /// \param prefix The prefix to look for.
89 ///
90 /// \return True if the string is prefixed as specified.
91 static bool
starts_with(const std::string & str,const std::string & prefix)92 starts_with(const std::string& str, const std::string& prefix)
93 {
94 return (str.length() >= prefix.length() &&
95 str.substr(0, prefix.length()) == prefix);
96 }
97
98
99 /// Strips a prefix from a string and converts the rest to an integer.
100 ///
101 /// \param str The string to be tested.
102 /// \param prefix The prefix to strip from the string.
103 ///
104 /// \return The part of the string after the prefix converted to an integer.
105 static int
suffix_to_int(const std::string & str,const std::string & prefix)106 suffix_to_int(const std::string& str, const std::string& prefix)
107 {
108 PRE(starts_with(str, prefix));
109 try {
110 return text::to_type< int >(str.substr(prefix.length()));
111 } catch (const text::value_error& error) {
112 std::cerr << F("Failed: %s\n") % error.what();
113 std::abort();
114 }
115 }
116
117
118 /// Mock interface definition for testing.
119 ///
120 /// This scheduler interface does not execute external binaries. It is designed
121 /// to simulate the scheduler of various programs with different exit statuses.
122 class mock_interface : public scheduler::interface {
123 /// Executes the subprocess simulating an exec.
124 ///
125 /// This is just a simple wrapper over _exit(2) because we cannot use
126 /// std::exit on exit from this mock interface. The reason is that we do
127 /// not want to invoke any destructors as otherwise we'd clear up the global
128 /// scheduler state by mistake. This wouldn't be a major problem if it
129 /// wasn't because doing so deletes on-disk files and we want to leave them
130 /// in place so that the parent process can test for them!
131 ///
132 /// \param exit_code Exit code.
133 void
do_exit(const int exit_code) const134 do_exit(const int exit_code) const UTILS_NORETURN
135 {
136 std::cout.flush();
137 std::cerr.flush();
138 ::_exit(exit_code);
139 }
140
141 /// Executes a test case that creates various files and then fails.
142 void
exec_create_files_and_fail(void) const143 exec_create_files_and_fail(void) const UTILS_NORETURN
144 {
145 std::cerr << "This should not be clobbered\n";
146 atf::utils::create_file("first file", "");
147 atf::utils::create_file("second-file", "");
148 fs::mkdir_p(fs::path("dir1/dir2"), 0755);
149 ::kill(::getpid(), SIGTERM);
150 std::abort();
151 }
152
153 /// Executes a test case that deletes all files in the current directory.
154 ///
155 /// This is intended to validate that the test runs in an empty directory,
156 /// separate from any control files that the scheduler may have created.
157 void
exec_delete_all(void) const158 exec_delete_all(void) const UTILS_NORETURN
159 {
160 const int exit_code = ::system("rm *") == -1
161 ? EXIT_FAILURE : EXIT_SUCCESS;
162
163 // Recreate our own cookie.
164 atf::utils::create_file("exec_test_was_called", "");
165
166 do_exit(exit_code);
167 }
168
169 /// Executes a test case that returns a specific exit code.
170 ///
171 /// \param exit_code Exit status to terminate the program with.
172 void
exec_exit(const int exit_code) const173 exec_exit(const int exit_code) const UTILS_NORETURN
174 {
175 do_exit(exit_code);
176 }
177
178 /// Executes a test case that just fails.
179 void
exec_fail(void) const180 exec_fail(void) const UTILS_NORETURN
181 {
182 std::cerr << "This should not be clobbered\n";
183 ::kill(::getpid(), SIGTERM);
184 std::abort();
185 }
186
187 /// Executes a test case that prints all input parameters to the functor.
188 ///
189 /// \param test_program The test program to execute.
190 /// \param test_case_name Name of the test case to invoke, which must be a
191 /// number.
192 /// \param vars User-provided variables to pass to the test program.
193 void
exec_print_params(const model::test_program & test_program,const std::string & test_case_name,const config::properties_map & vars) const194 exec_print_params(const model::test_program& test_program,
195 const std::string& test_case_name,
196 const config::properties_map& vars) const
197 UTILS_NORETURN
198 {
199 std::cout << F("Test program: %s\n") % test_program.relative_path();
200 std::cout << F("Test case: %s\n") % test_case_name;
201 for (config::properties_map::const_iterator iter = vars.begin();
202 iter != vars.end(); ++iter) {
203 std::cout << F("%s=%s\n") % (*iter).first % (*iter).second;
204 }
205
206 std::cerr << F("stderr: %s\n") % test_case_name;
207
208 do_exit(EXIT_SUCCESS);
209 }
210
211 public:
212 /// Executes a test program's list operation.
213 ///
214 /// This method is intended to be called within a subprocess and is expected
215 /// to terminate execution either by exec(2)ing the test program or by
216 /// exiting with a failure.
217 ///
218 /// \param test_program The test program to execute.
219 /// \param vars User-provided variables to pass to the test program.
220 void
exec_list(const model::test_program & test_program,const config::properties_map & vars) const221 exec_list(const model::test_program& test_program,
222 const config::properties_map& vars)
223 const UTILS_NORETURN
224 {
225 const std::string name = test_program.absolute_path().leaf_name();
226
227 std::cerr << name;
228 std::cerr.flush();
229 if (name == "check_i_exist") {
230 if (fs::exists(test_program.absolute_path())) {
231 std::cout << "found\n";
232 do_exit(EXIT_SUCCESS);
233 } else {
234 std::cout << "not_found\n";
235 do_exit(EXIT_FAILURE);
236 }
237 } else if (name == "empty") {
238 do_exit(EXIT_SUCCESS);
239 } else if (name == "misbehave") {
240 utils::abort_without_coredump();
241 } else if (name == "timeout") {
242 std::cout << "sleeping\n";
243 std::cout.flush();
244 ::sleep(100);
245 utils::abort_without_coredump();
246 } else if (name == "vars") {
247 for (config::properties_map::const_iterator iter = vars.begin();
248 iter != vars.end(); ++iter) {
249 std::cout << F("%s_%s\n") % (*iter).first % (*iter).second;
250 }
251 do_exit(15);
252 } else {
253 std::abort();
254 }
255 }
256
257 /// Computes the test cases list of a test program.
258 ///
259 /// \param status The termination status of the subprocess used to execute
260 /// the exec_test() method or none if the test timed out.
261 /// \param stdout_path Path to the file containing the stdout of the test.
262 /// \param stderr_path Path to the file containing the stderr of the test.
263 ///
264 /// \return A list of test cases.
265 model::test_cases_map
parse_list(const optional<process::status> & status,const fs::path & stdout_path,const fs::path & stderr_path) const266 parse_list(const optional< process::status >& status,
267 const fs::path& stdout_path,
268 const fs::path& stderr_path) const
269 {
270 const std::string name = utils::read_file(stderr_path);
271 if (name == "check_i_exist") {
272 ATF_REQUIRE(status.get().exited());
273 ATF_REQUIRE_EQ(EXIT_SUCCESS, status.get().exitstatus());
274 } else if (name == "empty") {
275 ATF_REQUIRE(status.get().exited());
276 ATF_REQUIRE_EQ(EXIT_SUCCESS, status.get().exitstatus());
277 } else if (name == "misbehave") {
278 throw std::runtime_error("misbehaved in parse_list");
279 } else if (name == "timeout") {
280 ATF_REQUIRE(!status);
281 } else if (name == "vars") {
282 ATF_REQUIRE(status.get().exited());
283 ATF_REQUIRE_EQ(15, status.get().exitstatus());
284 } else {
285 ATF_FAIL("Invalid stderr contents; got " + name);
286 }
287
288 model::test_cases_map_builder test_cases_builder;
289
290 std::ifstream input(stdout_path.c_str());
291 ATF_REQUIRE(input);
292 std::string line;
293 while (std::getline(input, line).good()) {
294 test_cases_builder.add(line);
295 }
296
297 return test_cases_builder.build();
298 }
299
300 /// Executes a test case of the test program.
301 ///
302 /// This method is intended to be called within a subprocess and is expected
303 /// to terminate execution either by exec(2)ing the test program or by
304 /// exiting with a failure.
305 ///
306 /// \param test_program The test program to execute.
307 /// \param test_case_name Name of the test case to invoke.
308 /// \param vars User-provided variables to pass to the test program.
309 /// \param control_directory Directory where the interface may place control
310 /// files.
311 void
exec_test(const model::test_program & test_program,const std::string & test_case_name,const config::properties_map & vars,const fs::path & control_directory) const312 exec_test(const model::test_program& test_program,
313 const std::string& test_case_name,
314 const config::properties_map& vars,
315 const fs::path& control_directory) const
316 {
317 const fs::path cookie = control_directory / "exec_test_was_called";
318 std::ofstream control_file(cookie.c_str());
319 if (!control_file) {
320 std::cerr << "Failed to create " << cookie << '\n';
321 std::abort();
322 }
323 control_file << test_case_name;
324 control_file.close();
325
326 if (test_case_name == "check_i_exist") {
327 do_exit(fs::exists(test_program.absolute_path()) ? 0 : 1);
328 } else if (starts_with(test_case_name, "cleanup_timeout")) {
329 exec_exit(EXIT_SUCCESS);
330 } else if (starts_with(test_case_name, "create_files_and_fail")) {
331 exec_create_files_and_fail();
332 } else if (test_case_name == "delete_all") {
333 exec_delete_all();
334 } else if (starts_with(test_case_name, "exit ")) {
335 exec_exit(suffix_to_int(test_case_name, "exit "));
336 } else if (starts_with(test_case_name, "fail")) {
337 exec_fail();
338 } else if (starts_with(test_case_name, "fail_body_fail_cleanup")) {
339 exec_fail();
340 } else if (starts_with(test_case_name, "fail_body_pass_cleanup")) {
341 exec_fail();
342 } else if (starts_with(test_case_name, "pass_body_fail_cleanup")) {
343 exec_exit(EXIT_SUCCESS);
344 } else if (starts_with(test_case_name, "print_params")) {
345 exec_print_params(test_program, test_case_name, vars);
346 } else if (starts_with(test_case_name, "skip_body_pass_cleanup")) {
347 exec_exit(EXIT_SUCCESS);
348 } else {
349 std::cerr << "Unknown test case " << test_case_name << '\n';
350 std::abort();
351 }
352 }
353
354 /// Executes a test cleanup routine of the test program.
355 ///
356 /// This method is intended to be called within a subprocess and is expected
357 /// to terminate execution either by exec(2)ing the test program or by
358 /// exiting with a failure.
359 ///
360 /// \param test_case_name Name of the test case to invoke.
361 void
exec_cleanup(const model::test_program &,const std::string & test_case_name,const config::properties_map &,const fs::path &) const362 exec_cleanup(const model::test_program& /* test_program */,
363 const std::string& test_case_name,
364 const config::properties_map& /* vars */,
365 const fs::path& /* control_directory */) const
366 {
367 std::cout << "exec_cleanup was called\n";
368 std::cout.flush();
369
370 if (starts_with(test_case_name, "cleanup_timeout")) {
371 ::sleep(100);
372 std::abort();
373 } else if (starts_with(test_case_name, "fail_body_fail_cleanup")) {
374 exec_fail();
375 } else if (starts_with(test_case_name, "fail_body_pass_cleanup")) {
376 exec_exit(EXIT_SUCCESS);
377 } else if (starts_with(test_case_name, "pass_body_fail_cleanup")) {
378 exec_fail();
379 } else if (starts_with(test_case_name, "skip_body_pass_cleanup")) {
380 exec_exit(EXIT_SUCCESS);
381 } else {
382 std::cerr << "Should not have been called for a test without "
383 "a cleanup routine" << '\n';
384 std::abort();
385 }
386 }
387
388 /// Computes the result of a test case based on its termination status.
389 ///
390 /// \param status The termination status of the subprocess used to execute
391 /// the exec_test() method or none if the test timed out.
392 /// \param control_directory Path to the directory where the interface may
393 /// have placed control files.
394 /// \param stdout_path Path to the file containing the stdout of the test.
395 /// \param stderr_path Path to the file containing the stderr of the test.
396 ///
397 /// \return A test result.
398 model::test_result
compute_result(const optional<process::status> & status,const fs::path & control_directory,const fs::path & stdout_path,const fs::path & stderr_path) const399 compute_result(const optional< process::status >& status,
400 const fs::path& control_directory,
401 const fs::path& stdout_path,
402 const fs::path& stderr_path) const
403 {
404 // Do not use any ATF_* macros here. Some of the tests below invoke
405 // this code in a subprocess, and terminating such subprocess due to a
406 // failed ATF_* macro yields mysterious failures that are incredibly
407 // hard to debug. (Case in point: the signal_handling test is racy by
408 // nature, and the test run by exec_test() above may not have created
409 // the cookie we expect below. We don't want to "silently" exit if the
410 // file is not there.)
411
412 if (!status) {
413 return model::test_result(model::test_result_broken,
414 "Timed out");
415 }
416
417 if (status.get().exited()) {
418 // Only sanity-check the work directory-related parameters in case
419 // of a clean exit. In all other cases, there is no guarantee that
420 // these were ever created.
421 const fs::path cookie = control_directory / "exec_test_was_called";
422 if (!atf::utils::file_exists(cookie.str())) {
423 return model::test_result(
424 model::test_result_broken,
425 "compute_result's control_directory does not seem to point "
426 "to the right location");
427 }
428 const std::string test_case_name = utils::read_file(cookie);
429
430 if (!atf::utils::file_exists(stdout_path.str())) {
431 return model::test_result(
432 model::test_result_broken,
433 "compute_result's stdout_path does not exist");
434 }
435 if (!atf::utils::file_exists(stderr_path.str())) {
436 return model::test_result(
437 model::test_result_broken,
438 "compute_result's stderr_path does not exist");
439 }
440
441 if (test_case_name == "skip_body_pass_cleanup") {
442 return model::test_result(
443 model::test_result_skipped,
444 F("Exit %s") % status.get().exitstatus());
445 } else {
446 return model::test_result(
447 model::test_result_passed,
448 F("Exit %s") % status.get().exitstatus());
449 }
450 } else {
451 return model::test_result(
452 model::test_result_failed,
453 F("Signal %s") % status.get().termsig());
454 }
455 }
456 };
457
458
459 } // anonymous namespace
460
461
462 /// Runs list_tests on the scheduler and returns the results.
463 ///
464 /// \param test_name The name of the test supported by our exec_list function.
465 /// \param user_config Optional user settings for the test.
466 ///
467 /// \return The loaded list of test cases.
468 static model::test_cases_map
check_integration_list(const char * test_name,const fs::path root,const config::tree & user_config=engine::empty_config ())469 check_integration_list(const char* test_name, const fs::path root,
470 const config::tree& user_config = engine::empty_config())
471 {
472 const model::test_program program = model::test_program_builder(
473 "mock", fs::path(test_name), root, "the-suite")
474 .build();
475
476 scheduler::scheduler_handle handle = scheduler::setup();
477 const model::test_cases_map test_cases = handle.list_tests(&program,
478 user_config);
479 handle.cleanup();
480
481 return test_cases;
482 }
483
484
485 ATF_TEST_CASE_WITHOUT_HEAD(integration__list_some);
ATF_TEST_CASE_BODY(integration__list_some)486 ATF_TEST_CASE_BODY(integration__list_some)
487 {
488 config::tree user_config = engine::empty_config();
489 user_config.set_string("test_suites.the-suite.first", "test");
490 user_config.set_string("test_suites.the-suite.second", "TEST");
491 user_config.set_string("test_suites.abc.unused", "unused");
492
493 const model::test_cases_map test_cases = check_integration_list(
494 "vars", fs::path("."), user_config);
495
496 const model::test_cases_map exp_test_cases = model::test_cases_map_builder()
497 .add("first_test").add("second_TEST").build();
498 ATF_REQUIRE_EQ(exp_test_cases, test_cases);
499 }
500
501
502 ATF_TEST_CASE_WITHOUT_HEAD(integration__list_check_paths);
ATF_TEST_CASE_BODY(integration__list_check_paths)503 ATF_TEST_CASE_BODY(integration__list_check_paths)
504 {
505 fs::mkdir_p(fs::path("dir1/dir2/dir3"), 0755);
506 atf::utils::create_file("dir1/dir2/dir3/check_i_exist", "");
507
508 const model::test_cases_map test_cases = check_integration_list(
509 "dir2/dir3/check_i_exist", fs::path("dir1"));
510
511 const model::test_cases_map exp_test_cases = model::test_cases_map_builder()
512 .add("found").build();
513 ATF_REQUIRE_EQ(exp_test_cases, test_cases);
514 }
515
516
517 ATF_TEST_CASE_WITHOUT_HEAD(integration__list_timeout);
ATF_TEST_CASE_BODY(integration__list_timeout)518 ATF_TEST_CASE_BODY(integration__list_timeout)
519 {
520 scheduler::list_timeout = datetime::delta(1, 0);
521 const model::test_cases_map test_cases = check_integration_list(
522 "timeout", fs::path("."));
523
524 const model::test_cases_map exp_test_cases = model::test_cases_map_builder()
525 .add("sleeping").build();
526 ATF_REQUIRE_EQ(exp_test_cases, test_cases);
527 }
528
529
530 ATF_TEST_CASE_WITHOUT_HEAD(integration__list_fail);
ATF_TEST_CASE_BODY(integration__list_fail)531 ATF_TEST_CASE_BODY(integration__list_fail)
532 {
533 const model::test_cases_map test_cases = check_integration_list(
534 "misbehave", fs::path("."));
535
536 ATF_REQUIRE_EQ(1, test_cases.size());
537 const model::test_case& test_case = test_cases.begin()->second;
538 ATF_REQUIRE_EQ("__test_cases_list__", test_case.name());
539 ATF_REQUIRE(test_case.fake_result());
540 ATF_REQUIRE_EQ(model::test_result(model::test_result_broken,
541 "misbehaved in parse_list"),
542 test_case.fake_result().get());
543 }
544
545
546 ATF_TEST_CASE_WITHOUT_HEAD(integration__list_empty);
ATF_TEST_CASE_BODY(integration__list_empty)547 ATF_TEST_CASE_BODY(integration__list_empty)
548 {
549 const model::test_cases_map test_cases = check_integration_list(
550 "empty", fs::path("."));
551
552 ATF_REQUIRE_EQ(1, test_cases.size());
553 const model::test_case& test_case = test_cases.begin()->second;
554 ATF_REQUIRE_EQ("__test_cases_list__", test_case.name());
555 ATF_REQUIRE(test_case.fake_result());
556 ATF_REQUIRE_EQ(model::test_result(model::test_result_broken,
557 "Empty test cases list"),
558 test_case.fake_result().get());
559 }
560
561
562 ATF_TEST_CASE_WITHOUT_HEAD(integration__run_one);
ATF_TEST_CASE_BODY(integration__run_one)563 ATF_TEST_CASE_BODY(integration__run_one)
564 {
565 const model::test_program_ptr program = model::test_program_builder(
566 "mock", fs::path("the-program"), fs::current_path(), "the-suite")
567 .add_test_case("exit 41").build_ptr();
568
569 const config::tree user_config = engine::empty_config();
570
571 scheduler::scheduler_handle handle = scheduler::setup();
572
573 const scheduler::exec_handle exec_handle = handle.spawn_test(
574 program, "exit 41", user_config);
575
576 scheduler::result_handle_ptr result_handle = handle.wait_any();
577 const scheduler::test_result_handle* test_result_handle =
578 dynamic_cast< const scheduler::test_result_handle* >(
579 result_handle.get());
580 ATF_REQUIRE_EQ(exec_handle, result_handle->original_pid());
581 ATF_REQUIRE_EQ(model::test_result(model::test_result_passed, "Exit 41"),
582 test_result_handle->test_result());
583 result_handle->cleanup();
584 result_handle.reset();
585
586 handle.cleanup();
587 }
588
589
590 ATF_TEST_CASE_WITHOUT_HEAD(integration__run_many);
ATF_TEST_CASE_BODY(integration__run_many)591 ATF_TEST_CASE_BODY(integration__run_many)
592 {
593 static const std::size_t num_test_programs = 30;
594
595 const config::tree user_config = engine::empty_config();
596
597 scheduler::scheduler_handle handle = scheduler::setup();
598
599 // We mess around with the "current time" below, so make sure the tests do
600 // not spuriously exceed their deadline by bumping it to a large number.
601 const model::metadata infinite_timeout = model::metadata_builder()
602 .set_timeout(datetime::delta(1000000L, 0)).build();
603
604 std::size_t total_tests = 0;
605 std::map< scheduler::exec_handle, model::test_program_ptr >
606 exp_test_programs;
607 std::map< scheduler::exec_handle, std::string > exp_test_case_names;
608 std::map< scheduler::exec_handle, datetime::timestamp > exp_start_times;
609 std::map< scheduler::exec_handle, int > exp_exit_statuses;
610 for (std::size_t i = 0; i < num_test_programs; ++i) {
611 const std::string test_case_0 = F("exit %s") % (i * 3 + 0);
612 const std::string test_case_1 = F("exit %s") % (i * 3 + 1);
613 const std::string test_case_2 = F("exit %s") % (i * 3 + 2);
614
615 const model::test_program_ptr program = model::test_program_builder(
616 "mock", fs::path(F("program-%s") % i),
617 fs::current_path(), "the-suite")
618 .set_metadata(infinite_timeout)
619 .add_test_case(test_case_0)
620 .add_test_case(test_case_1)
621 .add_test_case(test_case_2)
622 .build_ptr();
623
624 const datetime::timestamp start_time = datetime::timestamp::from_values(
625 2014, 12, 8, 9, 40, 0, i);
626
627 scheduler::exec_handle exec_handle;
628
629 datetime::set_mock_now(start_time);
630 exec_handle = handle.spawn_test(program, test_case_0, user_config);
631 exp_test_programs.insert(std::make_pair(exec_handle, program));
632 exp_test_case_names.insert(std::make_pair(exec_handle, test_case_0));
633 exp_start_times.insert(std::make_pair(exec_handle, start_time));
634 exp_exit_statuses.insert(std::make_pair(exec_handle, i * 3));
635 ++total_tests;
636
637 datetime::set_mock_now(start_time);
638 exec_handle = handle.spawn_test(program, test_case_1, user_config);
639 exp_test_programs.insert(std::make_pair(exec_handle, program));
640 exp_test_case_names.insert(std::make_pair(exec_handle, test_case_1));
641 exp_start_times.insert(std::make_pair(exec_handle, start_time));
642 exp_exit_statuses.insert(std::make_pair(exec_handle, i * 3 + 1));
643 ++total_tests;
644
645 datetime::set_mock_now(start_time);
646 exec_handle = handle.spawn_test(program, test_case_2, user_config);
647 exp_test_programs.insert(std::make_pair(exec_handle, program));
648 exp_test_case_names.insert(std::make_pair(exec_handle, test_case_2));
649 exp_start_times.insert(std::make_pair(exec_handle, start_time));
650 exp_exit_statuses.insert(std::make_pair(exec_handle, i * 3 + 2));
651 ++total_tests;
652 }
653
654 for (std::size_t i = 0; i < total_tests; ++i) {
655 const datetime::timestamp end_time = datetime::timestamp::from_values(
656 2014, 12, 8, 9, 50, 10, i);
657 datetime::set_mock_now(end_time);
658 scheduler::result_handle_ptr result_handle = handle.wait_any();
659 const scheduler::test_result_handle* test_result_handle =
660 dynamic_cast< const scheduler::test_result_handle* >(
661 result_handle.get());
662
663 const scheduler::exec_handle exec_handle =
664 result_handle->original_pid();
665
666 const model::test_program_ptr test_program = exp_test_programs.find(
667 exec_handle)->second;
668 const std::string& test_case_name = exp_test_case_names.find(
669 exec_handle)->second;
670 const datetime::timestamp& start_time = exp_start_times.find(
671 exec_handle)->second;
672 const int exit_status = exp_exit_statuses.find(exec_handle)->second;
673
674 ATF_REQUIRE_EQ(model::test_result(model::test_result_passed,
675 F("Exit %s") % exit_status),
676 test_result_handle->test_result());
677
678 ATF_REQUIRE_EQ(test_program, test_result_handle->test_program());
679 ATF_REQUIRE_EQ(test_case_name, test_result_handle->test_case_name());
680
681 ATF_REQUIRE_EQ(start_time, result_handle->start_time());
682 ATF_REQUIRE_EQ(end_time, result_handle->end_time());
683
684 result_handle->cleanup();
685
686 ATF_REQUIRE(!atf::utils::file_exists(
687 result_handle->stdout_file().str()));
688 ATF_REQUIRE(!atf::utils::file_exists(
689 result_handle->stderr_file().str()));
690 ATF_REQUIRE(!atf::utils::file_exists(
691 result_handle->work_directory().str()));
692
693 result_handle.reset();
694 }
695
696 handle.cleanup();
697 }
698
699
700 ATF_TEST_CASE_WITHOUT_HEAD(integration__run_check_paths);
ATF_TEST_CASE_BODY(integration__run_check_paths)701 ATF_TEST_CASE_BODY(integration__run_check_paths)
702 {
703 fs::mkdir_p(fs::path("dir1/dir2/dir3"), 0755);
704 atf::utils::create_file("dir1/dir2/dir3/program", "");
705
706 const model::test_program_ptr program = model::test_program_builder(
707 "mock", fs::path("dir2/dir3/program"), fs::path("dir1"), "the-suite")
708 .add_test_case("check_i_exist").build_ptr();
709
710 scheduler::scheduler_handle handle = scheduler::setup();
711
712 (void)handle.spawn_test(program, "check_i_exist", engine::default_config());
713 scheduler::result_handle_ptr result_handle = handle.wait_any();
714 const scheduler::test_result_handle* test_result_handle =
715 dynamic_cast< const scheduler::test_result_handle* >(
716 result_handle.get());
717
718 ATF_REQUIRE_EQ(model::test_result(model::test_result_passed, "Exit 0"),
719 test_result_handle->test_result());
720
721 result_handle->cleanup();
722 result_handle.reset();
723
724 handle.cleanup();
725 }
726
727
728 ATF_TEST_CASE_WITHOUT_HEAD(integration__parameters_and_output);
ATF_TEST_CASE_BODY(integration__parameters_and_output)729 ATF_TEST_CASE_BODY(integration__parameters_and_output)
730 {
731 const model::test_program_ptr program = model::test_program_builder(
732 "mock", fs::path("the-program"), fs::current_path(), "the-suite")
733 .add_test_case("print_params").build_ptr();
734
735 config::tree user_config = engine::empty_config();
736 user_config.set_string("test_suites.the-suite.one", "first variable");
737 user_config.set_string("test_suites.the-suite.two", "second variable");
738
739 scheduler::scheduler_handle handle = scheduler::setup();
740
741 const scheduler::exec_handle exec_handle = handle.spawn_test(
742 program, "print_params", user_config);
743
744 scheduler::result_handle_ptr result_handle = handle.wait_any();
745 const scheduler::test_result_handle* test_result_handle =
746 dynamic_cast< const scheduler::test_result_handle* >(
747 result_handle.get());
748
749 ATF_REQUIRE_EQ(exec_handle, result_handle->original_pid());
750 ATF_REQUIRE_EQ(program, test_result_handle->test_program());
751 ATF_REQUIRE_EQ("print_params", test_result_handle->test_case_name());
752 ATF_REQUIRE_EQ(model::test_result(model::test_result_passed, "Exit 0"),
753 test_result_handle->test_result());
754
755 const fs::path stdout_file = result_handle->stdout_file();
756 ATF_REQUIRE(atf::utils::compare_file(
757 stdout_file.str(),
758 "Test program: the-program\n"
759 "Test case: print_params\n"
760 "one=first variable\n"
761 "two=second variable\n"));
762 const fs::path stderr_file = result_handle->stderr_file();
763 ATF_REQUIRE(atf::utils::compare_file(
764 stderr_file.str(), "stderr: print_params\n"));
765
766 result_handle->cleanup();
767 ATF_REQUIRE(!fs::exists(stdout_file));
768 ATF_REQUIRE(!fs::exists(stderr_file));
769 result_handle.reset();
770
771 handle.cleanup();
772 }
773
774
775 ATF_TEST_CASE_WITHOUT_HEAD(integration__fake_result);
ATF_TEST_CASE_BODY(integration__fake_result)776 ATF_TEST_CASE_BODY(integration__fake_result)
777 {
778 const model::test_result fake_result(model::test_result_skipped,
779 "Some fake details");
780
781 model::test_cases_map test_cases;
782 test_cases.insert(model::test_cases_map::value_type(
783 "__fake__", model::test_case("__fake__", "ABC", fake_result)));
784
785 const model::test_program_ptr program(new model::test_program(
786 "mock", fs::path("the-program"), fs::current_path(), "the-suite",
787 model::metadata_builder().build(), test_cases));
788
789 const config::tree user_config = engine::empty_config();
790
791 scheduler::scheduler_handle handle = scheduler::setup();
792
793 (void)handle.spawn_test(program, "__fake__", user_config);
794
795 scheduler::result_handle_ptr result_handle = handle.wait_any();
796 const scheduler::test_result_handle* test_result_handle =
797 dynamic_cast< const scheduler::test_result_handle* >(
798 result_handle.get());
799 ATF_REQUIRE_EQ(fake_result, test_result_handle->test_result());
800 result_handle->cleanup();
801 result_handle.reset();
802
803 handle.cleanup();
804 }
805
806
807 ATF_TEST_CASE_WITHOUT_HEAD(integration__cleanup__head_skips);
ATF_TEST_CASE_BODY(integration__cleanup__head_skips)808 ATF_TEST_CASE_BODY(integration__cleanup__head_skips)
809 {
810 const model::test_program_ptr program = model::test_program_builder(
811 "mock", fs::path("the-program"), fs::current_path(), "the-suite")
812 .add_test_case("skip_me",
813 model::metadata_builder()
814 .add_required_config("variable-that-does-not-exist")
815 .set_has_cleanup(true)
816 .build())
817 .build_ptr();
818
819 const config::tree user_config = engine::empty_config();
820
821 scheduler::scheduler_handle handle = scheduler::setup();
822
823 (void)handle.spawn_test(program, "skip_me", user_config);
824
825 scheduler::result_handle_ptr result_handle = handle.wait_any();
826 const scheduler::test_result_handle* test_result_handle =
827 dynamic_cast< const scheduler::test_result_handle* >(
828 result_handle.get());
829 ATF_REQUIRE_EQ(model::test_result(
830 model::test_result_skipped,
831 "Required configuration property "
832 "'variable-that-does-not-exist' not defined"),
833 test_result_handle->test_result());
834 ATF_REQUIRE(!atf::utils::grep_file("exec_cleanup was called",
835 result_handle->stdout_file().str()));
836 result_handle->cleanup();
837 result_handle.reset();
838
839 handle.cleanup();
840 }
841
842
843 /// Runs a test to verify the behavior of cleanup routines.
844 ///
845 /// \param test_case The name of the test case to invoke.
846 /// \param exp_result The expected test result of the execution.
847 static void
do_cleanup_test(const char * test_case,const model::test_result & exp_result)848 do_cleanup_test(const char* test_case,
849 const model::test_result& exp_result)
850 {
851 const model::test_program_ptr program = model::test_program_builder(
852 "mock", fs::path("the-program"), fs::current_path(), "the-suite")
853 .add_test_case(test_case)
854 .set_metadata(model::metadata_builder().set_has_cleanup(true).build())
855 .build_ptr();
856
857 const config::tree user_config = engine::empty_config();
858
859 scheduler::scheduler_handle handle = scheduler::setup();
860
861 (void)handle.spawn_test(program, test_case, user_config);
862
863 scheduler::result_handle_ptr result_handle = handle.wait_any();
864 const scheduler::test_result_handle* test_result_handle =
865 dynamic_cast< const scheduler::test_result_handle* >(
866 result_handle.get());
867 ATF_REQUIRE_EQ(exp_result, test_result_handle->test_result());
868 ATF_REQUIRE(atf::utils::compare_file(
869 result_handle->stdout_file().str(),
870 "exec_cleanup was called\n"));
871 result_handle->cleanup();
872 result_handle.reset();
873
874 handle.cleanup();
875 }
876
877
878 ATF_TEST_CASE_WITHOUT_HEAD(integration__cleanup__body_skips);
ATF_TEST_CASE_BODY(integration__cleanup__body_skips)879 ATF_TEST_CASE_BODY(integration__cleanup__body_skips)
880 {
881 do_cleanup_test(
882 "skip_body_pass_cleanup",
883 model::test_result(model::test_result_skipped, "Exit 0"));
884 }
885
886
887 ATF_TEST_CASE_WITHOUT_HEAD(integration__cleanup__body_bad__cleanup_ok);
ATF_TEST_CASE_BODY(integration__cleanup__body_bad__cleanup_ok)888 ATF_TEST_CASE_BODY(integration__cleanup__body_bad__cleanup_ok)
889 {
890 do_cleanup_test(
891 "fail_body_pass_cleanup",
892 model::test_result(model::test_result_failed, "Signal 15"));
893 }
894
895
896 ATF_TEST_CASE_WITHOUT_HEAD(integration__cleanup__body_ok__cleanup_bad);
ATF_TEST_CASE_BODY(integration__cleanup__body_ok__cleanup_bad)897 ATF_TEST_CASE_BODY(integration__cleanup__body_ok__cleanup_bad)
898 {
899 do_cleanup_test(
900 "pass_body_fail_cleanup",
901 model::test_result(model::test_result_broken, "Test case cleanup "
902 "did not terminate successfully"));
903 }
904
905
906 ATF_TEST_CASE_WITHOUT_HEAD(integration__cleanup__body_bad__cleanup_bad);
ATF_TEST_CASE_BODY(integration__cleanup__body_bad__cleanup_bad)907 ATF_TEST_CASE_BODY(integration__cleanup__body_bad__cleanup_bad)
908 {
909 do_cleanup_test(
910 "fail_body_fail_cleanup",
911 model::test_result(model::test_result_failed, "Signal 15"));
912 }
913
914
915 ATF_TEST_CASE_WITHOUT_HEAD(integration__cleanup__timeout);
ATF_TEST_CASE_BODY(integration__cleanup__timeout)916 ATF_TEST_CASE_BODY(integration__cleanup__timeout)
917 {
918 scheduler::cleanup_timeout = datetime::delta(1, 0);
919 do_cleanup_test(
920 "cleanup_timeout",
921 model::test_result(model::test_result_broken, "Test case cleanup "
922 "timed out"));
923 }
924
925
926 ATF_TEST_CASE_WITHOUT_HEAD(integration__check_requirements);
ATF_TEST_CASE_BODY(integration__check_requirements)927 ATF_TEST_CASE_BODY(integration__check_requirements)
928 {
929 const model::test_program_ptr program = model::test_program_builder(
930 "mock", fs::path("the-program"), fs::current_path(), "the-suite")
931 .add_test_case("exit 12")
932 .set_metadata(model::metadata_builder()
933 .add_required_config("abcde").build())
934 .build_ptr();
935
936 const config::tree user_config = engine::empty_config();
937
938 scheduler::scheduler_handle handle = scheduler::setup();
939
940 (void)handle.spawn_test(program, "exit 12", user_config);
941
942 scheduler::result_handle_ptr result_handle = handle.wait_any();
943 const scheduler::test_result_handle* test_result_handle =
944 dynamic_cast< const scheduler::test_result_handle* >(
945 result_handle.get());
946 ATF_REQUIRE_EQ(model::test_result(
947 model::test_result_skipped,
948 "Required configuration property 'abcde' not defined"),
949 test_result_handle->test_result());
950 result_handle->cleanup();
951 result_handle.reset();
952
953 handle.cleanup();
954 }
955
956
957 ATF_TEST_CASE_WITHOUT_HEAD(integration__stacktrace);
ATF_TEST_CASE_BODY(integration__stacktrace)958 ATF_TEST_CASE_BODY(integration__stacktrace)
959 {
960 utils::prepare_coredump_test(this);
961
962 const model::test_program_ptr program = model::test_program_builder(
963 "mock", fs::path("the-program"), fs::current_path(), "the-suite")
964 .add_test_case("unknown-dumps-core").build_ptr();
965
966 const config::tree user_config = engine::empty_config();
967
968 scheduler::scheduler_handle handle = scheduler::setup();
969
970 (void)handle.spawn_test(program, "unknown-dumps-core", user_config);
971
972 scheduler::result_handle_ptr result_handle = handle.wait_any();
973 const scheduler::test_result_handle* test_result_handle =
974 dynamic_cast< const scheduler::test_result_handle* >(
975 result_handle.get());
976 ATF_REQUIRE_EQ(model::test_result(model::test_result_failed,
977 F("Signal %s") % SIGABRT),
978 test_result_handle->test_result());
979 ATF_REQUIRE(!atf::utils::grep_file("attempting to gather stack trace",
980 result_handle->stdout_file().str()));
981 ATF_REQUIRE( atf::utils::grep_file("attempting to gather stack trace",
982 result_handle->stderr_file().str()));
983 result_handle->cleanup();
984 result_handle.reset();
985
986 handle.cleanup();
987 }
988
989
990 /// Runs a test to verify the dumping of the list of existing files on failure.
991 ///
992 /// \param test_case The name of the test case to invoke.
993 /// \param exp_stderr Expected contents of stderr.
994 static void
do_check_list_files_on_failure(const char * test_case,const char * exp_stderr)995 do_check_list_files_on_failure(const char* test_case, const char* exp_stderr)
996 {
997 const model::test_program_ptr program = model::test_program_builder(
998 "mock", fs::path("the-program"), fs::current_path(), "the-suite")
999 .add_test_case(test_case).build_ptr();
1000
1001 const config::tree user_config = engine::empty_config();
1002
1003 scheduler::scheduler_handle handle = scheduler::setup();
1004
1005 (void)handle.spawn_test(program, test_case, user_config);
1006
1007 scheduler::result_handle_ptr result_handle = handle.wait_any();
1008 atf::utils::cat_file(result_handle->stdout_file().str(), "child stdout: ");
1009 ATF_REQUIRE(atf::utils::compare_file(result_handle->stdout_file().str(),
1010 ""));
1011 atf::utils::cat_file(result_handle->stderr_file().str(), "child stderr: ");
1012 ATF_REQUIRE(atf::utils::compare_file(result_handle->stderr_file().str(),
1013 exp_stderr));
1014 result_handle->cleanup();
1015 result_handle.reset();
1016
1017 handle.cleanup();
1018 }
1019
1020
1021 ATF_TEST_CASE_WITHOUT_HEAD(integration__list_files_on_failure__none);
ATF_TEST_CASE_BODY(integration__list_files_on_failure__none)1022 ATF_TEST_CASE_BODY(integration__list_files_on_failure__none)
1023 {
1024 do_check_list_files_on_failure("fail", "This should not be clobbered\n");
1025 }
1026
1027
1028 ATF_TEST_CASE_WITHOUT_HEAD(integration__list_files_on_failure__some);
ATF_TEST_CASE_BODY(integration__list_files_on_failure__some)1029 ATF_TEST_CASE_BODY(integration__list_files_on_failure__some)
1030 {
1031 do_check_list_files_on_failure(
1032 "create_files_and_fail",
1033 "This should not be clobbered\n"
1034 "Files left in work directory after failure: "
1035 "dir1, first file, second-file\n");
1036 }
1037
1038
1039 ATF_TEST_CASE_WITHOUT_HEAD(integration__prevent_clobbering_control_files);
ATF_TEST_CASE_BODY(integration__prevent_clobbering_control_files)1040 ATF_TEST_CASE_BODY(integration__prevent_clobbering_control_files)
1041 {
1042 const model::test_program_ptr program = model::test_program_builder(
1043 "mock", fs::path("the-program"), fs::current_path(), "the-suite")
1044 .add_test_case("delete_all").build_ptr();
1045
1046 const config::tree user_config = engine::empty_config();
1047
1048 scheduler::scheduler_handle handle = scheduler::setup();
1049
1050 (void)handle.spawn_test(program, "delete_all", user_config);
1051
1052 scheduler::result_handle_ptr result_handle = handle.wait_any();
1053 const scheduler::test_result_handle* test_result_handle =
1054 dynamic_cast< const scheduler::test_result_handle* >(
1055 result_handle.get());
1056 ATF_REQUIRE_EQ(model::test_result(model::test_result_passed, "Exit 0"),
1057 test_result_handle->test_result());
1058 result_handle->cleanup();
1059 result_handle.reset();
1060
1061 handle.cleanup();
1062 }
1063
1064
1065 ATF_TEST_CASE_WITHOUT_HEAD(debug_test);
ATF_TEST_CASE_BODY(debug_test)1066 ATF_TEST_CASE_BODY(debug_test)
1067 {
1068 const model::test_program_ptr program = model::test_program_builder(
1069 "mock", fs::path("the-program"), fs::current_path(), "the-suite")
1070 .add_test_case("print_params").build_ptr();
1071
1072 config::tree user_config = engine::empty_config();
1073 user_config.set_string("test_suites.the-suite.one", "first variable");
1074 user_config.set_string("test_suites.the-suite.two", "second variable");
1075
1076 scheduler::scheduler_handle handle = scheduler::setup();
1077
1078 const fs::path stdout_file("custom-stdout.txt");
1079 const fs::path stderr_file("custom-stderr.txt");
1080
1081 scheduler::result_handle_ptr result_handle = handle.debug_test(
1082 program, "print_params", user_config, stdout_file, stderr_file);
1083 const scheduler::test_result_handle* test_result_handle =
1084 dynamic_cast< const scheduler::test_result_handle* >(
1085 result_handle.get());
1086
1087 ATF_REQUIRE_EQ(program, test_result_handle->test_program());
1088 ATF_REQUIRE_EQ("print_params", test_result_handle->test_case_name());
1089 ATF_REQUIRE_EQ(model::test_result(model::test_result_passed, "Exit 0"),
1090 test_result_handle->test_result());
1091
1092 // The original output went to a file. It's only an artifact of
1093 // debug_test() that we later get a copy in our own files.
1094 ATF_REQUIRE(stdout_file != result_handle->stdout_file());
1095 ATF_REQUIRE(stderr_file != result_handle->stderr_file());
1096
1097 result_handle->cleanup();
1098 result_handle.reset();
1099
1100 handle.cleanup();
1101
1102 ATF_REQUIRE(atf::utils::compare_file(
1103 stdout_file.str(),
1104 "Test program: the-program\n"
1105 "Test case: print_params\n"
1106 "one=first variable\n"
1107 "two=second variable\n"));
1108 ATF_REQUIRE(atf::utils::compare_file(
1109 stderr_file.str(), "stderr: print_params\n"));
1110 }
1111
1112
1113 ATF_TEST_CASE_WITHOUT_HEAD(ensure_valid_interface);
ATF_TEST_CASE_BODY(ensure_valid_interface)1114 ATF_TEST_CASE_BODY(ensure_valid_interface)
1115 {
1116 scheduler::ensure_valid_interface("mock");
1117
1118 ATF_REQUIRE_THROW_RE(engine::error, "Unsupported test interface 'mock2'",
1119 scheduler::ensure_valid_interface("mock2"));
1120 scheduler::register_interface(
1121 "mock2", std::shared_ptr< scheduler::interface >(new mock_interface()));
1122 scheduler::ensure_valid_interface("mock2");
1123
1124 // Standard interfaces should not be present unless registered.
1125 ATF_REQUIRE_THROW_RE(engine::error, "Unsupported test interface 'plain'",
1126 scheduler::ensure_valid_interface("plain"));
1127 }
1128
1129
1130 ATF_TEST_CASE_WITHOUT_HEAD(registered_interface_names);
ATF_TEST_CASE_BODY(registered_interface_names)1131 ATF_TEST_CASE_BODY(registered_interface_names)
1132 {
1133 std::set< std::string > exp_names;
1134
1135 exp_names.insert("mock");
1136 ATF_REQUIRE_EQ(exp_names, scheduler::registered_interface_names());
1137
1138 scheduler::register_interface(
1139 "mock2", std::shared_ptr< scheduler::interface >(new mock_interface()));
1140 exp_names.insert("mock2");
1141 ATF_REQUIRE_EQ(exp_names, scheduler::registered_interface_names());
1142 }
1143
1144
1145 ATF_TEST_CASE_WITHOUT_HEAD(current_context);
ATF_TEST_CASE_BODY(current_context)1146 ATF_TEST_CASE_BODY(current_context)
1147 {
1148 const model::context context = scheduler::current_context();
1149 ATF_REQUIRE_EQ(fs::current_path(), context.cwd());
1150 ATF_REQUIRE(utils::getallenv() == context.env());
1151 }
1152
1153
1154 ATF_TEST_CASE_WITHOUT_HEAD(generate_config__empty);
ATF_TEST_CASE_BODY(generate_config__empty)1155 ATF_TEST_CASE_BODY(generate_config__empty)
1156 {
1157 const config::tree user_config = engine::empty_config();
1158
1159 const config::properties_map exp_props;
1160
1161 ATF_REQUIRE_EQ(exp_props,
1162 scheduler::generate_config(user_config, "missing"));
1163 }
1164
1165
1166 ATF_TEST_CASE_WITHOUT_HEAD(generate_config__no_matches);
ATF_TEST_CASE_BODY(generate_config__no_matches)1167 ATF_TEST_CASE_BODY(generate_config__no_matches)
1168 {
1169 config::tree user_config = engine::empty_config();
1170 user_config.set_string("architecture", "foo");
1171 user_config.set_string("test_suites.one.var1", "value 1");
1172
1173 const config::properties_map exp_props;
1174
1175 ATF_REQUIRE_EQ(exp_props,
1176 scheduler::generate_config(user_config, "two"));
1177 }
1178
1179
1180 ATF_TEST_CASE_WITHOUT_HEAD(generate_config__some_matches);
ATF_TEST_CASE_BODY(generate_config__some_matches)1181 ATF_TEST_CASE_BODY(generate_config__some_matches)
1182 {
1183 std::vector< passwd::user > mock_users;
1184 mock_users.push_back(passwd::user("nobody", 1234, 5678));
1185 passwd::set_mock_users_for_testing(mock_users);
1186
1187 config::tree user_config = engine::empty_config();
1188 user_config.set_string("architecture", "foo");
1189 user_config.set_string("unprivileged_user", "nobody");
1190 user_config.set_string("test_suites.one.var1", "value 1");
1191 user_config.set_string("test_suites.two.var2", "value 2");
1192
1193 config::properties_map exp_props;
1194 exp_props["unprivileged-user"] = "nobody";
1195 exp_props["var1"] = "value 1";
1196
1197 ATF_REQUIRE_EQ(exp_props,
1198 scheduler::generate_config(user_config, "one"));
1199 }
1200
1201
ATF_INIT_TEST_CASES(tcs)1202 ATF_INIT_TEST_CASES(tcs)
1203 {
1204 scheduler::register_interface(
1205 "mock", std::shared_ptr< scheduler::interface >(new mock_interface()));
1206
1207 ATF_ADD_TEST_CASE(tcs, integration__list_some);
1208 ATF_ADD_TEST_CASE(tcs, integration__list_check_paths);
1209 ATF_ADD_TEST_CASE(tcs, integration__list_timeout);
1210 ATF_ADD_TEST_CASE(tcs, integration__list_fail);
1211 ATF_ADD_TEST_CASE(tcs, integration__list_empty);
1212
1213 ATF_ADD_TEST_CASE(tcs, integration__run_one);
1214 ATF_ADD_TEST_CASE(tcs, integration__run_many);
1215
1216 ATF_ADD_TEST_CASE(tcs, integration__run_check_paths);
1217 ATF_ADD_TEST_CASE(tcs, integration__parameters_and_output);
1218
1219 ATF_ADD_TEST_CASE(tcs, integration__fake_result);
1220 ATF_ADD_TEST_CASE(tcs, integration__cleanup__head_skips);
1221 ATF_ADD_TEST_CASE(tcs, integration__cleanup__body_skips);
1222 ATF_ADD_TEST_CASE(tcs, integration__cleanup__body_ok__cleanup_bad);
1223 ATF_ADD_TEST_CASE(tcs, integration__cleanup__body_bad__cleanup_ok);
1224 ATF_ADD_TEST_CASE(tcs, integration__cleanup__body_bad__cleanup_bad);
1225 ATF_ADD_TEST_CASE(tcs, integration__cleanup__timeout);
1226 ATF_ADD_TEST_CASE(tcs, integration__check_requirements);
1227 ATF_ADD_TEST_CASE(tcs, integration__stacktrace);
1228 ATF_ADD_TEST_CASE(tcs, integration__list_files_on_failure__none);
1229 ATF_ADD_TEST_CASE(tcs, integration__list_files_on_failure__some);
1230 ATF_ADD_TEST_CASE(tcs, integration__prevent_clobbering_control_files);
1231
1232 ATF_ADD_TEST_CASE(tcs, debug_test);
1233
1234 ATF_ADD_TEST_CASE(tcs, ensure_valid_interface);
1235 ATF_ADD_TEST_CASE(tcs, registered_interface_names);
1236
1237 ATF_ADD_TEST_CASE(tcs, current_context);
1238
1239 ATF_ADD_TEST_CASE(tcs, generate_config__empty);
1240 ATF_ADD_TEST_CASE(tcs, generate_config__no_matches);
1241 ATF_ADD_TEST_CASE(tcs, generate_config__some_matches);
1242 }
1243