atf-run.cpp revision 1.6 1 1.1 jmmv //
2 1.1 jmmv // Automated Testing Framework (atf)
3 1.1 jmmv //
4 1.3 jmmv // Copyright (c) 2007 The NetBSD Foundation, Inc.
5 1.1 jmmv // All rights reserved.
6 1.1 jmmv //
7 1.1 jmmv // Redistribution and use in source and binary forms, with or without
8 1.1 jmmv // modification, are permitted provided that the following conditions
9 1.1 jmmv // are met:
10 1.1 jmmv // 1. Redistributions of source code must retain the above copyright
11 1.1 jmmv // notice, this list of conditions and the following disclaimer.
12 1.1 jmmv // 2. Redistributions in binary form must reproduce the above copyright
13 1.1 jmmv // notice, this list of conditions and the following disclaimer in the
14 1.1 jmmv // documentation and/or other materials provided with the distribution.
15 1.1 jmmv //
16 1.1 jmmv // THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
17 1.1 jmmv // CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
18 1.1 jmmv // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 1.1 jmmv // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 1.1 jmmv // IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
21 1.1 jmmv // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 1.1 jmmv // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23 1.1 jmmv // GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 1.1 jmmv // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25 1.1 jmmv // IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26 1.1 jmmv // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27 1.1 jmmv // IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 1.1 jmmv //
29 1.1 jmmv
30 1.1 jmmv extern "C" {
31 1.1 jmmv #include <sys/types.h>
32 1.3 jmmv #include <sys/param.h>
33 1.3 jmmv #include <sys/stat.h>
34 1.1 jmmv #include <sys/wait.h>
35 1.1 jmmv #include <unistd.h>
36 1.1 jmmv }
37 1.1 jmmv
38 1.3 jmmv #include <algorithm>
39 1.3 jmmv #include <cassert>
40 1.1 jmmv #include <cerrno>
41 1.1 jmmv #include <cstdlib>
42 1.1 jmmv #include <cstring>
43 1.1 jmmv #include <fstream>
44 1.1 jmmv #include <iostream>
45 1.1 jmmv #include <map>
46 1.1 jmmv #include <string>
47 1.1 jmmv
48 1.3 jmmv #include "application.hpp"
49 1.3 jmmv #include "atffile.hpp"
50 1.3 jmmv #include "config.hpp"
51 1.3 jmmv #include "config_file.hpp"
52 1.3 jmmv #include "env.hpp"
53 1.3 jmmv #include "exceptions.hpp"
54 1.3 jmmv #include "fs.hpp"
55 1.3 jmmv #include "parser.hpp"
56 1.3 jmmv #include "process.hpp"
57 1.3 jmmv #include "requirements.hpp"
58 1.3 jmmv #include "test-program.hpp"
59 1.3 jmmv #include "text.hpp"
60 1.3 jmmv
61 1.3 jmmv namespace {
62 1.3 jmmv
63 1.3 jmmv typedef std::map< std::string, std::string > vars_map;
64 1.3 jmmv
65 1.3 jmmv } // anonymous namespace
66 1.3 jmmv
67 1.3 jmmv class atf_run : public tools::application::app {
68 1.3 jmmv static const char* m_description;
69 1.1 jmmv
70 1.3 jmmv vars_map m_cmdline_vars;
71 1.1 jmmv
72 1.3 jmmv static vars_map::value_type parse_var(const std::string&);
73 1.1 jmmv
74 1.3 jmmv void process_option(int, const char*);
75 1.3 jmmv std::string specific_args(void) const;
76 1.3 jmmv options_set specific_options(void) const;
77 1.1 jmmv
78 1.3 jmmv void parse_vflag(const std::string&);
79 1.1 jmmv
80 1.3 jmmv std::vector< std::string > conf_args(void) const;
81 1.1 jmmv
82 1.3 jmmv size_t count_tps(std::vector< std::string >) const;
83 1.1 jmmv
84 1.6 gson int run_test(const tools::fs::path&, const std::string &,
85 1.6 gson tools::test_program::atf_tps_writer&,
86 1.3 jmmv const vars_map&);
87 1.3 jmmv int run_test_directory(const tools::fs::path&,
88 1.3 jmmv tools::test_program::atf_tps_writer&);
89 1.3 jmmv int run_test_program(const tools::fs::path&,
90 1.6 gson const std::string tc,
91 1.3 jmmv tools::test_program::atf_tps_writer&,
92 1.3 jmmv const vars_map&);
93 1.3 jmmv
94 1.3 jmmv tools::test_program::test_case_result get_test_case_result(
95 1.3 jmmv const std::string&, const tools::process::status&,
96 1.3 jmmv const tools::fs::path&) const;
97 1.1 jmmv
98 1.3 jmmv public:
99 1.3 jmmv atf_run(void);
100 1.1 jmmv
101 1.3 jmmv int main(void);
102 1.3 jmmv };
103 1.1 jmmv
104 1.3 jmmv static void
105 1.3 jmmv sanitize_gdb_env(void)
106 1.3 jmmv {
107 1.3 jmmv try {
108 1.3 jmmv tools::env::unset("TERM");
109 1.3 jmmv } catch (...) {
110 1.3 jmmv // Just swallow exceptions here; they cannot propagate into C, which
111 1.3 jmmv // is where this function is called from, and even if these exceptions
112 1.3 jmmv // appear they are benign.
113 1.1 jmmv }
114 1.3 jmmv }
115 1.1 jmmv
116 1.3 jmmv static void
117 1.3 jmmv dump_stacktrace(const tools::fs::path& tp, const tools::process::status& s,
118 1.3 jmmv const tools::fs::path& workdir,
119 1.3 jmmv tools::test_program::atf_tps_writer& w)
120 1.3 jmmv {
121 1.3 jmmv assert(s.signaled() && s.coredump());
122 1.1 jmmv
123 1.3 jmmv w.stderr_tc("Test program crashed; attempting to get stack trace");
124 1.1 jmmv
125 1.3 jmmv const tools::fs::path corename = workdir /
126 1.5 jmmv (tp.leaf_name().substr(0, MAXCOMLEN) + ".core");
127 1.3 jmmv if (!tools::fs::exists(corename)) {
128 1.3 jmmv w.stderr_tc("Expected file " + corename.str() + " not found");
129 1.3 jmmv return;
130 1.1 jmmv }
131 1.1 jmmv
132 1.3 jmmv const tools::fs::path gdb(GDB);
133 1.3 jmmv const tools::fs::path gdbout = workdir / "gdb.out";
134 1.3 jmmv const tools::process::argv_array args(gdb.leaf_name().c_str(), "-batch",
135 1.3 jmmv "-q", "-ex", "bt", tp.c_str(),
136 1.3 jmmv corename.c_str(), NULL);
137 1.3 jmmv tools::process::status status = tools::process::exec(
138 1.3 jmmv gdb, args,
139 1.3 jmmv tools::process::stream_redirect_path(gdbout),
140 1.3 jmmv tools::process::stream_redirect_path(tools::fs::path("/dev/null")),
141 1.3 jmmv sanitize_gdb_env);
142 1.3 jmmv if (!status.exited() || status.exitstatus() != EXIT_SUCCESS) {
143 1.3 jmmv w.stderr_tc("Execution of " GDB " failed");
144 1.3 jmmv return;
145 1.1 jmmv }
146 1.1 jmmv
147 1.3 jmmv std::ifstream input(gdbout.c_str());
148 1.3 jmmv if (input) {
149 1.3 jmmv std::string line;
150 1.3 jmmv while (std::getline(input, line).good())
151 1.3 jmmv w.stderr_tc(line);
152 1.3 jmmv input.close();
153 1.1 jmmv }
154 1.1 jmmv
155 1.3 jmmv w.stderr_tc("Stack trace complete");
156 1.1 jmmv }
157 1.1 jmmv
158 1.1 jmmv const char* atf_run::m_description =
159 1.1 jmmv "atf-run is a tool that runs tests programs and collects their "
160 1.1 jmmv "results.";
161 1.1 jmmv
162 1.1 jmmv atf_run::atf_run(void) :
163 1.1 jmmv app(m_description, "atf-run(1)", "atf(7)")
164 1.1 jmmv {
165 1.1 jmmv }
166 1.1 jmmv
167 1.1 jmmv void
168 1.1 jmmv atf_run::process_option(int ch, const char* arg)
169 1.1 jmmv {
170 1.1 jmmv switch (ch) {
171 1.1 jmmv case 'v':
172 1.1 jmmv parse_vflag(arg);
173 1.1 jmmv break;
174 1.1 jmmv
175 1.1 jmmv default:
176 1.3 jmmv std::abort();
177 1.1 jmmv }
178 1.1 jmmv }
179 1.1 jmmv
180 1.1 jmmv std::string
181 1.1 jmmv atf_run::specific_args(void)
182 1.1 jmmv const
183 1.1 jmmv {
184 1.6 gson return "[test1 .. testN]";
185 1.1 jmmv }
186 1.1 jmmv
187 1.1 jmmv atf_run::options_set
188 1.1 jmmv atf_run::specific_options(void)
189 1.1 jmmv const
190 1.1 jmmv {
191 1.3 jmmv using tools::application::option;
192 1.1 jmmv options_set opts;
193 1.1 jmmv opts.insert(option('v', "var=value", "Sets the configuration variable "
194 1.1 jmmv "`var' to `value'; overrides "
195 1.1 jmmv "values in configuration files"));
196 1.1 jmmv return opts;
197 1.1 jmmv }
198 1.1 jmmv
199 1.1 jmmv void
200 1.1 jmmv atf_run::parse_vflag(const std::string& str)
201 1.1 jmmv {
202 1.1 jmmv if (str.empty())
203 1.1 jmmv throw std::runtime_error("-v requires a non-empty argument");
204 1.1 jmmv
205 1.3 jmmv std::vector< std::string > ws = tools::text::split(str, "=");
206 1.1 jmmv if (ws.size() == 1 && str[str.length() - 1] == '=') {
207 1.1 jmmv m_cmdline_vars[ws[0]] = "";
208 1.1 jmmv } else {
209 1.1 jmmv if (ws.size() != 2)
210 1.1 jmmv throw std::runtime_error("-v requires an argument of the form "
211 1.1 jmmv "var=value");
212 1.1 jmmv
213 1.1 jmmv m_cmdline_vars[ws[0]] = ws[1];
214 1.1 jmmv }
215 1.1 jmmv }
216 1.1 jmmv
217 1.1 jmmv int
218 1.3 jmmv atf_run::run_test(const tools::fs::path& tp,
219 1.6 gson const std::string &tc,
220 1.3 jmmv tools::test_program::atf_tps_writer& w,
221 1.3 jmmv const vars_map& config)
222 1.1 jmmv {
223 1.3 jmmv tools::fs::file_info fi(tp);
224 1.1 jmmv
225 1.1 jmmv int errcode;
226 1.3 jmmv if (fi.get_type() == tools::fs::file_info::dir_type)
227 1.1 jmmv errcode = run_test_directory(tp, w);
228 1.3 jmmv else {
229 1.3 jmmv const vars_map effective_config =
230 1.3 jmmv tools::config_file::merge_configs(config, m_cmdline_vars);
231 1.3 jmmv
232 1.6 gson errcode = run_test_program(tp, tc, w, effective_config);
233 1.3 jmmv }
234 1.1 jmmv return errcode;
235 1.1 jmmv }
236 1.1 jmmv
237 1.1 jmmv int
238 1.3 jmmv atf_run::run_test_directory(const tools::fs::path& tp,
239 1.3 jmmv tools::test_program::atf_tps_writer& w)
240 1.1 jmmv {
241 1.3 jmmv tools::atffile af = tools::read_atffile(tp / "Atffile");
242 1.1 jmmv
243 1.3 jmmv vars_map test_suite_vars;
244 1.1 jmmv {
245 1.3 jmmv vars_map::const_iterator iter = af.props().find("test-suite");
246 1.3 jmmv assert(iter != af.props().end());
247 1.3 jmmv test_suite_vars = tools::config_file::read_config_files((*iter).second);
248 1.1 jmmv }
249 1.1 jmmv
250 1.1 jmmv bool ok = true;
251 1.1 jmmv for (std::vector< std::string >::const_iterator iter = af.tps().begin();
252 1.3 jmmv iter != af.tps().end(); iter++) {
253 1.6 gson const bool result = run_test(tp / *iter, "", w,
254 1.3 jmmv tools::config_file::merge_configs(af.conf(), test_suite_vars));
255 1.3 jmmv ok &= (result == EXIT_SUCCESS);
256 1.3 jmmv }
257 1.1 jmmv
258 1.1 jmmv return ok ? EXIT_SUCCESS : EXIT_FAILURE;
259 1.1 jmmv }
260 1.1 jmmv
261 1.3 jmmv tools::test_program::test_case_result
262 1.3 jmmv atf_run::get_test_case_result(const std::string& broken_reason,
263 1.3 jmmv const tools::process::status& s,
264 1.3 jmmv const tools::fs::path& resfile)
265 1.3 jmmv const
266 1.1 jmmv {
267 1.3 jmmv using tools::text::to_string;
268 1.3 jmmv using tools::test_program::read_test_case_result;
269 1.3 jmmv using tools::test_program::test_case_result;
270 1.1 jmmv
271 1.3 jmmv if (!broken_reason.empty()) {
272 1.3 jmmv test_case_result tcr;
273 1.1 jmmv
274 1.3 jmmv try {
275 1.3 jmmv tcr = read_test_case_result(resfile);
276 1.1 jmmv
277 1.3 jmmv if (tcr.state() == "expected_timeout") {
278 1.3 jmmv return tcr;
279 1.3 jmmv } else {
280 1.3 jmmv return test_case_result("failed", -1, broken_reason);
281 1.3 jmmv }
282 1.3 jmmv } catch (const std::runtime_error&) {
283 1.3 jmmv return test_case_result("failed", -1, broken_reason);
284 1.3 jmmv }
285 1.3 jmmv }
286 1.1 jmmv
287 1.3 jmmv if (s.exited()) {
288 1.3 jmmv test_case_result tcr;
289 1.1 jmmv
290 1.3 jmmv try {
291 1.3 jmmv tcr = read_test_case_result(resfile);
292 1.3 jmmv } catch (const std::runtime_error& e) {
293 1.3 jmmv return test_case_result("failed", -1, "Test case exited "
294 1.3 jmmv "normally but failed to create the results file: " +
295 1.3 jmmv std::string(e.what()));
296 1.1 jmmv }
297 1.1 jmmv
298 1.3 jmmv if (tcr.state() == "expected_death") {
299 1.3 jmmv return tcr;
300 1.3 jmmv } else if (tcr.state() == "expected_exit") {
301 1.3 jmmv if (tcr.value() == -1 || s.exitstatus() == tcr.value())
302 1.3 jmmv return tcr;
303 1.3 jmmv else
304 1.3 jmmv return test_case_result("failed", -1, "Test case was "
305 1.3 jmmv "expected to exit with a " + to_string(tcr.value()) +
306 1.3 jmmv " error code but returned " + to_string(s.exitstatus()));
307 1.3 jmmv } else if (tcr.state() == "expected_failure") {
308 1.3 jmmv if (s.exitstatus() == EXIT_SUCCESS)
309 1.3 jmmv return tcr;
310 1.3 jmmv else
311 1.3 jmmv return test_case_result("failed", -1, "Test case returned an "
312 1.3 jmmv "error in expected_failure mode but it should not have");
313 1.3 jmmv } else if (tcr.state() == "expected_signal") {
314 1.3 jmmv return test_case_result("failed", -1, "Test case exited cleanly "
315 1.3 jmmv "but was expected to receive a signal");
316 1.3 jmmv } else if (tcr.state() == "failed") {
317 1.3 jmmv if (s.exitstatus() == EXIT_SUCCESS)
318 1.3 jmmv return test_case_result("failed", -1, "Test case "
319 1.3 jmmv "exited successfully but reported failure");
320 1.3 jmmv else
321 1.3 jmmv return tcr;
322 1.3 jmmv } else if (tcr.state() == "passed") {
323 1.3 jmmv if (s.exitstatus() == EXIT_SUCCESS)
324 1.3 jmmv return tcr;
325 1.3 jmmv else
326 1.3 jmmv return test_case_result("failed", -1, "Test case exited as "
327 1.3 jmmv "passed but reported an error");
328 1.3 jmmv } else if (tcr.state() == "skipped") {
329 1.3 jmmv if (s.exitstatus() == EXIT_SUCCESS)
330 1.3 jmmv return tcr;
331 1.3 jmmv else
332 1.3 jmmv return test_case_result("failed", -1, "Test case exited as "
333 1.3 jmmv "skipped but reported an error");
334 1.3 jmmv }
335 1.3 jmmv } else if (s.signaled()) {
336 1.3 jmmv test_case_result tcr;
337 1.1 jmmv
338 1.3 jmmv try {
339 1.3 jmmv tcr = read_test_case_result(resfile);
340 1.3 jmmv } catch (const std::runtime_error&) {
341 1.3 jmmv return test_case_result("failed", -1, "Test program received "
342 1.3 jmmv "signal " + tools::text::to_string(s.termsig()) +
343 1.3 jmmv (s.coredump() ? " (core dumped)" : ""));
344 1.3 jmmv }
345 1.1 jmmv
346 1.3 jmmv if (tcr.state() == "expected_death") {
347 1.3 jmmv return tcr;
348 1.3 jmmv } else if (tcr.state() == "expected_signal") {
349 1.3 jmmv if (tcr.value() == -1 || s.termsig() == tcr.value())
350 1.3 jmmv return tcr;
351 1.3 jmmv else
352 1.3 jmmv return test_case_result("failed", -1, "Test case was "
353 1.3 jmmv "expected to exit due to a " + to_string(tcr.value()) +
354 1.3 jmmv " signal but got " + to_string(s.termsig()));
355 1.3 jmmv } else {
356 1.3 jmmv return test_case_result("failed", -1, "Test program received "
357 1.3 jmmv "signal " + tools::text::to_string(s.termsig()) +
358 1.3 jmmv (s.coredump() ? " (core dumped)" : "") + " and created a "
359 1.3 jmmv "bogus results file");
360 1.1 jmmv }
361 1.1 jmmv }
362 1.3 jmmv std::abort();
363 1.3 jmmv return test_case_result();
364 1.1 jmmv }
365 1.1 jmmv
366 1.1 jmmv int
367 1.3 jmmv atf_run::run_test_program(const tools::fs::path& tp,
368 1.6 gson const std::string tc,
369 1.3 jmmv tools::test_program::atf_tps_writer& w,
370 1.3 jmmv const vars_map& config)
371 1.1 jmmv {
372 1.3 jmmv int errcode = EXIT_SUCCESS;
373 1.1 jmmv
374 1.3 jmmv tools::test_program::metadata md;
375 1.3 jmmv try {
376 1.3 jmmv md = tools::test_program::get_metadata(tp, config);
377 1.3 jmmv } catch (const tools::parser::format_error& e) {
378 1.3 jmmv w.start_tp(tp.str(), 0);
379 1.3 jmmv w.end_tp("Invalid format for test case list: " + std::string(e.what()));
380 1.3 jmmv return EXIT_FAILURE;
381 1.3 jmmv } catch (const tools::parser::parse_errors& e) {
382 1.3 jmmv const std::string reason = tools::text::join(e, "; ");
383 1.3 jmmv w.start_tp(tp.str(), 0);
384 1.3 jmmv w.end_tp("Invalid format for test case list: " + reason);
385 1.3 jmmv return EXIT_FAILURE;
386 1.3 jmmv }
387 1.3 jmmv
388 1.3 jmmv tools::fs::temp_dir resdir(
389 1.3 jmmv tools::fs::path(tools::config::get("atf_workdir")) / "atf-run.XXXXXX");
390 1.3 jmmv
391 1.3 jmmv w.start_tp(tp.str(), md.test_cases.size());
392 1.3 jmmv if (md.test_cases.empty()) {
393 1.3 jmmv w.end_tp("Bogus test program: reported 0 test cases");
394 1.1 jmmv errcode = EXIT_FAILURE;
395 1.1 jmmv } else {
396 1.3 jmmv for (std::map< std::string, vars_map >::const_iterator iter
397 1.3 jmmv = md.test_cases.begin(); iter != md.test_cases.end(); iter++) {
398 1.3 jmmv const std::string& tcname = (*iter).first;
399 1.3 jmmv const vars_map& tcmd = (*iter).second;
400 1.3 jmmv
401 1.6 gson if (! tc.empty() && tcname != tc)
402 1.6 gson continue;
403 1.6 gson
404 1.3 jmmv w.start_tc(tcname);
405 1.3 jmmv
406 1.3 jmmv try {
407 1.3 jmmv const std::string& reqfail = tools::check_requirements(
408 1.3 jmmv tcmd, config);
409 1.3 jmmv if (!reqfail.empty()) {
410 1.3 jmmv w.end_tc("skipped", reqfail);
411 1.3 jmmv continue;
412 1.3 jmmv }
413 1.3 jmmv } catch (const std::runtime_error& e) {
414 1.3 jmmv w.end_tc("failed", e.what());
415 1.3 jmmv errcode = EXIT_FAILURE;
416 1.3 jmmv continue;
417 1.3 jmmv }
418 1.3 jmmv
419 1.3 jmmv const std::pair< int, int > user = tools::get_required_user(
420 1.3 jmmv tcmd, config);
421 1.3 jmmv
422 1.3 jmmv tools::fs::path resfile = resdir.get_path() / "tcr";
423 1.3 jmmv assert(!tools::fs::exists(resfile));
424 1.3 jmmv try {
425 1.3 jmmv const bool has_cleanup = tools::text::to_bool(
426 1.3 jmmv (*tcmd.find("has.cleanup")).second);
427 1.3 jmmv
428 1.3 jmmv tools::fs::temp_dir workdir(tools::fs::path(tools::config::get(
429 1.3 jmmv "atf_workdir")) / "atf-run.XXXXXX");
430 1.3 jmmv if (user.first != -1 && user.second != -1) {
431 1.3 jmmv if (::chown(workdir.get_path().c_str(), user.first,
432 1.3 jmmv user.second) == -1) {
433 1.3 jmmv throw tools::system_error("chown(" +
434 1.3 jmmv workdir.get_path().str() + ")", "chown(2) failed",
435 1.3 jmmv errno);
436 1.3 jmmv }
437 1.3 jmmv resfile = workdir.get_path() / "tcr";
438 1.3 jmmv }
439 1.3 jmmv
440 1.3 jmmv std::pair< std::string, const tools::process::status > s =
441 1.3 jmmv tools::test_program::run_test_case(
442 1.3 jmmv tp, tcname, "body", tcmd, config,
443 1.3 jmmv resfile, workdir.get_path(), w);
444 1.3 jmmv if (s.second.signaled() && s.second.coredump())
445 1.3 jmmv dump_stacktrace(tp, s.second, workdir.get_path(), w);
446 1.3 jmmv if (has_cleanup)
447 1.3 jmmv (void)tools::test_program::run_test_case(
448 1.3 jmmv tp, tcname, "cleanup", tcmd,
449 1.3 jmmv config, resfile, workdir.get_path(), w);
450 1.3 jmmv
451 1.3 jmmv // TODO: Force deletion of workdir.
452 1.3 jmmv
453 1.3 jmmv tools::test_program::test_case_result tcr =
454 1.3 jmmv get_test_case_result(s.first, s.second, resfile);
455 1.3 jmmv
456 1.3 jmmv w.end_tc(tcr.state(), tcr.reason());
457 1.3 jmmv if (tcr.state() == "failed")
458 1.3 jmmv errcode = EXIT_FAILURE;
459 1.3 jmmv } catch (...) {
460 1.3 jmmv if (tools::fs::exists(resfile))
461 1.3 jmmv tools::fs::remove(resfile);
462 1.3 jmmv throw;
463 1.3 jmmv }
464 1.3 jmmv if (tools::fs::exists(resfile))
465 1.3 jmmv tools::fs::remove(resfile);
466 1.3 jmmv
467 1.3 jmmv }
468 1.3 jmmv w.end_tp("");
469 1.1 jmmv }
470 1.1 jmmv
471 1.1 jmmv return errcode;
472 1.1 jmmv }
473 1.1 jmmv
474 1.6 gson static void
475 1.6 gson colon_split(const std::string &s, std::string &tp, std::string &tc)
476 1.6 gson {
477 1.6 gson size_t colon_pos = s.rfind(':');
478 1.6 gson if (colon_pos != std::string::npos && colon_pos < s.size() - 1) {
479 1.6 gson tp = s.substr(0, colon_pos);
480 1.6 gson tc = s.substr(colon_pos + 1);
481 1.6 gson } else {
482 1.6 gson tp = s;
483 1.6 gson tc = "";
484 1.6 gson }
485 1.6 gson }
486 1.6 gson
487 1.1 jmmv size_t
488 1.1 jmmv atf_run::count_tps(std::vector< std::string > tps)
489 1.1 jmmv const
490 1.1 jmmv {
491 1.1 jmmv size_t ntps = 0;
492 1.1 jmmv
493 1.1 jmmv for (std::vector< std::string >::const_iterator iter = tps.begin();
494 1.1 jmmv iter != tps.end(); iter++) {
495 1.6 gson std::string tpname, tcname;
496 1.6 gson colon_split(*iter, tpname, tcname);
497 1.6 gson tools::fs::path tp(tpname);
498 1.3 jmmv tools::fs::file_info fi(tp);
499 1.1 jmmv
500 1.3 jmmv if (fi.get_type() == tools::fs::file_info::dir_type) {
501 1.3 jmmv tools::atffile af = tools::read_atffile(tp / "Atffile");
502 1.1 jmmv std::vector< std::string > aux = af.tps();
503 1.1 jmmv for (std::vector< std::string >::iterator i2 = aux.begin();
504 1.1 jmmv i2 != aux.end(); i2++)
505 1.1 jmmv *i2 = (tp / *i2).str();
506 1.1 jmmv ntps += count_tps(aux);
507 1.1 jmmv } else
508 1.1 jmmv ntps++;
509 1.1 jmmv }
510 1.1 jmmv
511 1.1 jmmv return ntps;
512 1.1 jmmv }
513 1.1 jmmv
514 1.3 jmmv static
515 1.1 jmmv void
516 1.3 jmmv call_hook(const std::string& tool, const std::string& hook)
517 1.1 jmmv {
518 1.3 jmmv const tools::fs::path sh(tools::config::get("atf_shell"));
519 1.3 jmmv const tools::fs::path hooks =
520 1.3 jmmv tools::fs::path(tools::config::get("atf_pkgdatadir")) / (tool + ".hooks");
521 1.3 jmmv
522 1.3 jmmv const tools::process::status s =
523 1.3 jmmv tools::process::exec(sh,
524 1.3 jmmv tools::process::argv_array(sh.c_str(), hooks.c_str(),
525 1.3 jmmv hook.c_str(), NULL),
526 1.3 jmmv tools::process::stream_inherit(),
527 1.3 jmmv tools::process::stream_inherit());
528 1.1 jmmv
529 1.1 jmmv
530 1.3 jmmv if (!s.exited() || s.exitstatus() != EXIT_SUCCESS)
531 1.1 jmmv throw std::runtime_error("Failed to run the '" + hook + "' hook "
532 1.3 jmmv "for '" + tool + "'");
533 1.1 jmmv }
534 1.1 jmmv
535 1.1 jmmv int
536 1.1 jmmv atf_run::main(void)
537 1.1 jmmv {
538 1.3 jmmv tools::atffile af = tools::read_atffile(tools::fs::path("Atffile"));
539 1.1 jmmv
540 1.1 jmmv std::vector< std::string > tps;
541 1.1 jmmv tps = af.tps();
542 1.1 jmmv if (m_argc >= 1) {
543 1.1 jmmv // TODO: Ensure that the given test names are listed in the
544 1.1 jmmv // Atffile. Take into account that the file can be using globs.
545 1.1 jmmv tps.clear();
546 1.1 jmmv for (int i = 0; i < m_argc; i++)
547 1.1 jmmv tps.push_back(m_argv[i]);
548 1.1 jmmv }
549 1.1 jmmv
550 1.1 jmmv // Read configuration data for this test suite.
551 1.3 jmmv vars_map test_suite_vars;
552 1.1 jmmv {
553 1.3 jmmv vars_map::const_iterator iter = af.props().find("test-suite");
554 1.3 jmmv assert(iter != af.props().end());
555 1.3 jmmv test_suite_vars = tools::config_file::read_config_files((*iter).second);
556 1.1 jmmv }
557 1.1 jmmv
558 1.3 jmmv tools::test_program::atf_tps_writer w(std::cout);
559 1.1 jmmv call_hook("atf-run", "info_start_hook");
560 1.1 jmmv w.ntps(count_tps(tps));
561 1.1 jmmv
562 1.1 jmmv bool ok = true;
563 1.1 jmmv for (std::vector< std::string >::const_iterator iter = tps.begin();
564 1.3 jmmv iter != tps.end(); iter++) {
565 1.6 gson std::string tp, tc;
566 1.6 gson colon_split(*iter, tp, tc);
567 1.6 gson const bool result = run_test(tools::fs::path(tp), tc, w,
568 1.3 jmmv tools::config_file::merge_configs(af.conf(), test_suite_vars));
569 1.3 jmmv ok &= (result == EXIT_SUCCESS);
570 1.3 jmmv }
571 1.1 jmmv
572 1.1 jmmv call_hook("atf-run", "info_end_hook");
573 1.1 jmmv
574 1.1 jmmv return ok ? EXIT_SUCCESS : EXIT_FAILURE;
575 1.1 jmmv }
576 1.1 jmmv
577 1.1 jmmv int
578 1.1 jmmv main(int argc, char* const* argv)
579 1.1 jmmv {
580 1.1 jmmv return atf_run().run(argc, argv);
581 1.1 jmmv }
582