atf-run.cpp revision 1.1 1 1.1 jmmv //
2 1.1 jmmv // Automated Testing Framework (atf)
3 1.1 jmmv //
4 1.1 jmmv // Copyright (c) 2007, 2008 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 #if defined(HAVE_CONFIG_H)
31 1.1 jmmv #include "bconfig.h"
32 1.1 jmmv #endif
33 1.1 jmmv
34 1.1 jmmv extern "C" {
35 1.1 jmmv #include <sys/types.h>
36 1.1 jmmv #include <sys/wait.h>
37 1.1 jmmv #include <unistd.h>
38 1.1 jmmv }
39 1.1 jmmv
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.1 jmmv #include "atf-c++/application.hpp"
49 1.1 jmmv #include "atf-c++/atffile.hpp"
50 1.1 jmmv #include "atf-c++/config.hpp"
51 1.1 jmmv #include "atf-c++/env.hpp"
52 1.1 jmmv #include "atf-c++/exceptions.hpp"
53 1.1 jmmv #include "atf-c++/formats.hpp"
54 1.1 jmmv #include "atf-c++/fs.hpp"
55 1.1 jmmv #include "atf-c++/io.hpp"
56 1.1 jmmv #include "atf-c++/parser.hpp"
57 1.1 jmmv #include "atf-c++/process.hpp"
58 1.1 jmmv #include "atf-c++/sanity.hpp"
59 1.1 jmmv #include "atf-c++/tests.hpp"
60 1.1 jmmv #include "atf-c++/text.hpp"
61 1.1 jmmv
62 1.1 jmmv class config : public atf::formats::atf_config_reader {
63 1.1 jmmv atf::tests::vars_map m_vars;
64 1.1 jmmv
65 1.1 jmmv void
66 1.1 jmmv got_var(const std::string& var, const std::string& name)
67 1.1 jmmv {
68 1.1 jmmv m_vars[var] = name;
69 1.1 jmmv }
70 1.1 jmmv
71 1.1 jmmv public:
72 1.1 jmmv config(std::istream& is) :
73 1.1 jmmv atf::formats::atf_config_reader(is)
74 1.1 jmmv {
75 1.1 jmmv }
76 1.1 jmmv
77 1.1 jmmv const atf::tests::vars_map&
78 1.1 jmmv get_vars(void)
79 1.1 jmmv const
80 1.1 jmmv {
81 1.1 jmmv return m_vars;
82 1.1 jmmv }
83 1.1 jmmv };
84 1.1 jmmv
85 1.1 jmmv class muxer : public atf::formats::atf_tcs_reader {
86 1.1 jmmv atf::fs::path m_tp;
87 1.1 jmmv atf::formats::atf_tps_writer m_writer;
88 1.1 jmmv
89 1.1 jmmv bool m_inited, m_finalized;
90 1.1 jmmv size_t m_ntcs;
91 1.1 jmmv std::string m_tcname;
92 1.1 jmmv
93 1.1 jmmv // Counters for the test cases run by the test program.
94 1.1 jmmv size_t m_passed, m_failed, m_skipped;
95 1.1 jmmv
96 1.1 jmmv void
97 1.1 jmmv got_ntcs(size_t ntcs)
98 1.1 jmmv {
99 1.1 jmmv m_writer.start_tp(m_tp.str(), ntcs);
100 1.1 jmmv m_inited = true;
101 1.1 jmmv if (ntcs == 0)
102 1.1 jmmv throw atf::formats::format_error("Bogus test program: reported "
103 1.1 jmmv "0 test cases");
104 1.1 jmmv }
105 1.1 jmmv
106 1.1 jmmv void
107 1.1 jmmv got_tc_start(const std::string& tcname)
108 1.1 jmmv {
109 1.1 jmmv m_tcname = tcname;
110 1.1 jmmv m_writer.start_tc(tcname);
111 1.1 jmmv }
112 1.1 jmmv
113 1.1 jmmv void
114 1.1 jmmv got_tc_end(const atf::tests::tcr& tcr)
115 1.1 jmmv {
116 1.1 jmmv const atf::tests::tcr::state& s = tcr.get_state();
117 1.1 jmmv if (s == atf::tests::tcr::passed_state) {
118 1.1 jmmv m_passed++;
119 1.1 jmmv } else if (s == atf::tests::tcr::skipped_state) {
120 1.1 jmmv m_skipped++;
121 1.1 jmmv } else if (s == atf::tests::tcr::failed_state) {
122 1.1 jmmv m_failed++;
123 1.1 jmmv } else
124 1.1 jmmv UNREACHABLE;
125 1.1 jmmv
126 1.1 jmmv m_writer.end_tc(tcr);
127 1.1 jmmv m_tcname = "";
128 1.1 jmmv }
129 1.1 jmmv
130 1.1 jmmv void
131 1.1 jmmv got_stdout_line(const std::string& line)
132 1.1 jmmv {
133 1.1 jmmv m_writer.stdout_tc(line);
134 1.1 jmmv }
135 1.1 jmmv
136 1.1 jmmv void
137 1.1 jmmv got_stderr_line(const std::string& line)
138 1.1 jmmv {
139 1.1 jmmv m_writer.stderr_tc(line);
140 1.1 jmmv }
141 1.1 jmmv
142 1.1 jmmv public:
143 1.1 jmmv muxer(const atf::fs::path& tp, atf::formats::atf_tps_writer& w,
144 1.1 jmmv atf::io::pistream& is) :
145 1.1 jmmv atf::formats::atf_tcs_reader(is),
146 1.1 jmmv m_tp(tp),
147 1.1 jmmv m_writer(w),
148 1.1 jmmv m_inited(false),
149 1.1 jmmv m_finalized(false),
150 1.1 jmmv m_passed(0),
151 1.1 jmmv m_failed(0),
152 1.1 jmmv m_skipped(0)
153 1.1 jmmv {
154 1.1 jmmv }
155 1.1 jmmv
156 1.1 jmmv size_t
157 1.1 jmmv failed(void)
158 1.1 jmmv const
159 1.1 jmmv {
160 1.1 jmmv return m_failed;
161 1.1 jmmv }
162 1.1 jmmv
163 1.1 jmmv void
164 1.1 jmmv finalize(const std::string& reason = "")
165 1.1 jmmv {
166 1.1 jmmv PRE(!m_finalized);
167 1.1 jmmv
168 1.1 jmmv if (!m_inited)
169 1.1 jmmv m_writer.start_tp(m_tp.str(), 0);
170 1.1 jmmv if (!m_tcname.empty()) {
171 1.1 jmmv INV(!reason.empty());
172 1.1 jmmv got_tc_end(atf::tests::tcr(atf::tests::tcr::failed_state,
173 1.1 jmmv "Bogus test program"));
174 1.1 jmmv }
175 1.1 jmmv
176 1.1 jmmv m_writer.end_tp(reason);
177 1.1 jmmv m_finalized = true;
178 1.1 jmmv }
179 1.1 jmmv
180 1.1 jmmv ~muxer(void)
181 1.1 jmmv {
182 1.1 jmmv // The following is incorrect because we cannot throw an exception
183 1.1 jmmv // from a destructor. Let's just hope that this never happens.
184 1.1 jmmv PRE(m_finalized);
185 1.1 jmmv }
186 1.1 jmmv };
187 1.1 jmmv
188 1.1 jmmv template< class K, class V >
189 1.1 jmmv void
190 1.1 jmmv merge_maps(std::map< K, V >& dest, const std::map< K, V >& src)
191 1.1 jmmv {
192 1.1 jmmv for (typename std::map< K, V >::const_iterator iter = src.begin();
193 1.1 jmmv iter != src.end(); iter++)
194 1.1 jmmv dest[(*iter).first] = (*iter).second;
195 1.1 jmmv }
196 1.1 jmmv
197 1.1 jmmv class atf_run : public atf::application::app {
198 1.1 jmmv static const char* m_description;
199 1.1 jmmv
200 1.1 jmmv atf::tests::vars_map m_atffile_vars;
201 1.1 jmmv atf::tests::vars_map m_cmdline_vars;
202 1.1 jmmv atf::tests::vars_map m_config_vars;
203 1.1 jmmv
204 1.1 jmmv static atf::tests::vars_map::value_type parse_var(const std::string&);
205 1.1 jmmv
206 1.1 jmmv void process_option(int, const char*);
207 1.1 jmmv std::string specific_args(void) const;
208 1.1 jmmv options_set specific_options(void) const;
209 1.1 jmmv
210 1.1 jmmv void parse_vflag(const std::string&);
211 1.1 jmmv
212 1.1 jmmv void read_one_config(const atf::fs::path&);
213 1.1 jmmv void read_config(const std::string&);
214 1.1 jmmv std::vector< std::string > conf_args(void) const;
215 1.1 jmmv
216 1.1 jmmv size_t count_tps(std::vector< std::string >) const;
217 1.1 jmmv
218 1.1 jmmv int run_test(const atf::fs::path&,
219 1.1 jmmv atf::formats::atf_tps_writer&);
220 1.1 jmmv int run_test_directory(const atf::fs::path&,
221 1.1 jmmv atf::formats::atf_tps_writer&);
222 1.1 jmmv int run_test_program(const atf::fs::path&,
223 1.1 jmmv atf::formats::atf_tps_writer&);
224 1.1 jmmv
225 1.1 jmmv void run_test_program_child(const atf::fs::path&,
226 1.1 jmmv atf::io::pipe&,
227 1.1 jmmv atf::io::pipe&,
228 1.1 jmmv atf::io::pipe&);
229 1.1 jmmv int run_test_program_parent(const atf::fs::path&,
230 1.1 jmmv atf::formats::atf_tps_writer&,
231 1.1 jmmv atf::io::pipe&,
232 1.1 jmmv atf::io::pipe&,
233 1.1 jmmv atf::io::pipe&,
234 1.1 jmmv pid_t);
235 1.1 jmmv
236 1.1 jmmv public:
237 1.1 jmmv atf_run(void);
238 1.1 jmmv
239 1.1 jmmv int main(void);
240 1.1 jmmv };
241 1.1 jmmv
242 1.1 jmmv const char* atf_run::m_description =
243 1.1 jmmv "atf-run is a tool that runs tests programs and collects their "
244 1.1 jmmv "results.";
245 1.1 jmmv
246 1.1 jmmv atf_run::atf_run(void) :
247 1.1 jmmv app(m_description, "atf-run(1)", "atf(7)")
248 1.1 jmmv {
249 1.1 jmmv }
250 1.1 jmmv
251 1.1 jmmv void
252 1.1 jmmv atf_run::process_option(int ch, const char* arg)
253 1.1 jmmv {
254 1.1 jmmv switch (ch) {
255 1.1 jmmv case 'v':
256 1.1 jmmv parse_vflag(arg);
257 1.1 jmmv break;
258 1.1 jmmv
259 1.1 jmmv default:
260 1.1 jmmv UNREACHABLE;
261 1.1 jmmv }
262 1.1 jmmv }
263 1.1 jmmv
264 1.1 jmmv std::string
265 1.1 jmmv atf_run::specific_args(void)
266 1.1 jmmv const
267 1.1 jmmv {
268 1.1 jmmv return "[test-program1 .. test-programN]";
269 1.1 jmmv }
270 1.1 jmmv
271 1.1 jmmv atf_run::options_set
272 1.1 jmmv atf_run::specific_options(void)
273 1.1 jmmv const
274 1.1 jmmv {
275 1.1 jmmv using atf::application::option;
276 1.1 jmmv options_set opts;
277 1.1 jmmv opts.insert(option('v', "var=value", "Sets the configuration variable "
278 1.1 jmmv "`var' to `value'; overrides "
279 1.1 jmmv "values in configuration files"));
280 1.1 jmmv return opts;
281 1.1 jmmv }
282 1.1 jmmv
283 1.1 jmmv void
284 1.1 jmmv atf_run::parse_vflag(const std::string& str)
285 1.1 jmmv {
286 1.1 jmmv if (str.empty())
287 1.1 jmmv throw std::runtime_error("-v requires a non-empty argument");
288 1.1 jmmv
289 1.1 jmmv std::vector< std::string > ws = atf::text::split(str, "=");
290 1.1 jmmv if (ws.size() == 1 && str[str.length() - 1] == '=') {
291 1.1 jmmv m_cmdline_vars[ws[0]] = "";
292 1.1 jmmv } else {
293 1.1 jmmv if (ws.size() != 2)
294 1.1 jmmv throw std::runtime_error("-v requires an argument of the form "
295 1.1 jmmv "var=value");
296 1.1 jmmv
297 1.1 jmmv m_cmdline_vars[ws[0]] = ws[1];
298 1.1 jmmv }
299 1.1 jmmv }
300 1.1 jmmv
301 1.1 jmmv int
302 1.1 jmmv atf_run::run_test(const atf::fs::path& tp,
303 1.1 jmmv atf::formats::atf_tps_writer& w)
304 1.1 jmmv {
305 1.1 jmmv atf::fs::file_info fi(tp);
306 1.1 jmmv
307 1.1 jmmv int errcode;
308 1.1 jmmv if (fi.get_type() == atf::fs::file_info::dir_type)
309 1.1 jmmv errcode = run_test_directory(tp, w);
310 1.1 jmmv else
311 1.1 jmmv errcode = run_test_program(tp, w);
312 1.1 jmmv return errcode;
313 1.1 jmmv }
314 1.1 jmmv
315 1.1 jmmv int
316 1.1 jmmv atf_run::run_test_directory(const atf::fs::path& tp,
317 1.1 jmmv atf::formats::atf_tps_writer& w)
318 1.1 jmmv {
319 1.1 jmmv atf::atffile af(tp / "Atffile");
320 1.1 jmmv m_atffile_vars = af.conf();
321 1.1 jmmv
322 1.1 jmmv atf::tests::vars_map oldvars = m_config_vars;
323 1.1 jmmv {
324 1.1 jmmv atf::tests::vars_map::const_iterator iter =
325 1.1 jmmv af.props().find("test-suite");
326 1.1 jmmv INV(iter != af.props().end());
327 1.1 jmmv read_config((*iter).second);
328 1.1 jmmv }
329 1.1 jmmv
330 1.1 jmmv bool ok = true;
331 1.1 jmmv for (std::vector< std::string >::const_iterator iter = af.tps().begin();
332 1.1 jmmv iter != af.tps().end(); iter++)
333 1.1 jmmv ok &= (run_test(tp / *iter, w) == EXIT_SUCCESS);
334 1.1 jmmv
335 1.1 jmmv m_config_vars = oldvars;
336 1.1 jmmv
337 1.1 jmmv return ok ? EXIT_SUCCESS : EXIT_FAILURE;
338 1.1 jmmv }
339 1.1 jmmv
340 1.1 jmmv std::vector< std::string >
341 1.1 jmmv atf_run::conf_args(void) const
342 1.1 jmmv {
343 1.1 jmmv using atf::tests::vars_map;
344 1.1 jmmv
345 1.1 jmmv atf::tests::vars_map vars;
346 1.1 jmmv std::vector< std::string > args;
347 1.1 jmmv
348 1.1 jmmv merge_maps(vars, m_atffile_vars);
349 1.1 jmmv merge_maps(vars, m_config_vars);
350 1.1 jmmv merge_maps(vars, m_cmdline_vars);
351 1.1 jmmv
352 1.1 jmmv for (vars_map::const_iterator i = vars.begin(); i != vars.end(); i++)
353 1.1 jmmv args.push_back("-v" + (*i).first + "=" + (*i).second);
354 1.1 jmmv
355 1.1 jmmv return args;
356 1.1 jmmv }
357 1.1 jmmv
358 1.1 jmmv void
359 1.1 jmmv atf_run::run_test_program_child(const atf::fs::path& tp,
360 1.1 jmmv atf::io::pipe& outpipe,
361 1.1 jmmv atf::io::pipe& errpipe,
362 1.1 jmmv atf::io::pipe& respipe)
363 1.1 jmmv {
364 1.1 jmmv // Remap stdout and stderr to point to the parent, who will capture
365 1.1 jmmv // everything sent to these.
366 1.1 jmmv outpipe.rend().close();
367 1.1 jmmv outpipe.wend().posix_remap(STDOUT_FILENO);
368 1.1 jmmv errpipe.rend().close();
369 1.1 jmmv errpipe.wend().posix_remap(STDERR_FILENO);
370 1.1 jmmv
371 1.1 jmmv // Remap the results file descriptor to point to the parent too.
372 1.1 jmmv // We use the 9th one (instead of a bigger one) because shell scripts
373 1.1 jmmv // can only use the [0..9] file descriptors in their redirections.
374 1.1 jmmv respipe.rend().close();
375 1.1 jmmv respipe.wend().posix_remap(9);
376 1.1 jmmv
377 1.1 jmmv // Prepare the test program's arguments. We use dynamic memory and
378 1.1 jmmv // do not care to release it. We are going to die anyway very soon,
379 1.1 jmmv // either due to exec(2) or to exit(3).
380 1.1 jmmv std::vector< std::string > confargs = conf_args();
381 1.1 jmmv char** args = new char*[4 + confargs.size()];
382 1.1 jmmv {
383 1.1 jmmv // 0: Program name.
384 1.1 jmmv std::string progname = tp.leaf_name();
385 1.1 jmmv args[0] = new char[progname.length() + 1];
386 1.1 jmmv std::strcpy(args[0], progname.c_str());
387 1.1 jmmv
388 1.1 jmmv // 1: The file descriptor to which the results will be printed.
389 1.1 jmmv args[1] = new char[4];
390 1.1 jmmv std::strcpy(args[1], "-r9");
391 1.1 jmmv
392 1.1 jmmv // 2: The directory where the test program lives.
393 1.1 jmmv atf::fs::path bp = tp.branch_path();
394 1.1 jmmv if (!bp.is_absolute())
395 1.1 jmmv bp = bp.to_absolute();
396 1.1 jmmv const char* dir = bp.c_str();
397 1.1 jmmv args[2] = new char[std::strlen(dir) + 3];
398 1.1 jmmv std::strcpy(args[2], "-s");
399 1.1 jmmv std::strcat(args[2], dir);
400 1.1 jmmv
401 1.1 jmmv // [3..last - 1]: Configuration arguments.
402 1.1 jmmv std::vector< std::string >::size_type i;
403 1.1 jmmv for (i = 0; i < confargs.size(); i++) {
404 1.1 jmmv const char* str = confargs[i].c_str();
405 1.1 jmmv args[3 + i] = new char[std::strlen(str) + 1];
406 1.1 jmmv std::strcpy(args[3 + i], str);
407 1.1 jmmv }
408 1.1 jmmv
409 1.1 jmmv // Last: Terminator.
410 1.1 jmmv args[3 + i] = NULL;
411 1.1 jmmv }
412 1.1 jmmv
413 1.1 jmmv // Do the real exec and report any errors to the parent through the
414 1.1 jmmv // only mechanism we can use: stderr.
415 1.1 jmmv // TODO Try to make this fail.
416 1.1 jmmv ::execv(tp.c_str(), args);
417 1.1 jmmv std::cerr << "Failed to execute `" << tp.str() << "': "
418 1.1 jmmv << std::strerror(errno) << std::endl;
419 1.1 jmmv std::exit(EXIT_FAILURE);
420 1.1 jmmv }
421 1.1 jmmv
422 1.1 jmmv int
423 1.1 jmmv atf_run::run_test_program_parent(const atf::fs::path& tp,
424 1.1 jmmv atf::formats::atf_tps_writer& w,
425 1.1 jmmv atf::io::pipe& outpipe,
426 1.1 jmmv atf::io::pipe& errpipe,
427 1.1 jmmv atf::io::pipe& respipe,
428 1.1 jmmv pid_t pid)
429 1.1 jmmv {
430 1.1 jmmv // Get the file descriptor and input stream of stdout.
431 1.1 jmmv outpipe.wend().close();
432 1.1 jmmv atf::io::unbuffered_istream outin(outpipe.rend());
433 1.1 jmmv
434 1.1 jmmv // Get the file descriptor and input stream of stderr.
435 1.1 jmmv errpipe.wend().close();
436 1.1 jmmv atf::io::unbuffered_istream errin(errpipe.rend());
437 1.1 jmmv
438 1.1 jmmv // Get the file descriptor and input stream of the results channel.
439 1.1 jmmv respipe.wend().close();
440 1.1 jmmv atf::io::pistream resin(respipe.rend());
441 1.1 jmmv
442 1.1 jmmv // Process the test case's output and multiplex it into our output
443 1.1 jmmv // stream as we read it.
444 1.1 jmmv muxer m(tp, w, resin);
445 1.1 jmmv std::string fmterr;
446 1.1 jmmv try {
447 1.1 jmmv m.read(outin, errin);
448 1.1 jmmv } catch (const atf::parser::parse_errors& e) {
449 1.1 jmmv fmterr = "There were errors parsing the output of the test "
450 1.1 jmmv "program:";
451 1.1 jmmv for (atf::parser::parse_errors::const_iterator iter = e.begin();
452 1.1 jmmv iter != e.end(); iter++) {
453 1.1 jmmv fmterr += " Line " + atf::text::to_string((*iter).first) +
454 1.1 jmmv ": " + (*iter).second + ".";
455 1.1 jmmv }
456 1.1 jmmv } catch (const atf::formats::format_error& e) {
457 1.1 jmmv fmterr = e.what();
458 1.1 jmmv } catch (...) {
459 1.1 jmmv UNREACHABLE;
460 1.1 jmmv }
461 1.1 jmmv
462 1.1 jmmv try {
463 1.1 jmmv outin.close();
464 1.1 jmmv errin.close();
465 1.1 jmmv resin.close();
466 1.1 jmmv } catch (...) {
467 1.1 jmmv UNREACHABLE;
468 1.1 jmmv }
469 1.1 jmmv
470 1.1 jmmv int code, status;
471 1.1 jmmv if (::waitpid(pid, &status, 0) != pid) {
472 1.1 jmmv m.finalize("waitpid(2) on the child process " +
473 1.1 jmmv atf::text::to_string(pid) + " failed" +
474 1.1 jmmv (fmterr.empty() ? "" : (". " + fmterr)));
475 1.1 jmmv code = EXIT_FAILURE;
476 1.1 jmmv } else {
477 1.1 jmmv if (WIFEXITED(status)) {
478 1.1 jmmv code = WEXITSTATUS(status);
479 1.1 jmmv if (m.failed() > 0 && code == EXIT_SUCCESS) {
480 1.1 jmmv code = EXIT_FAILURE;
481 1.1 jmmv m.finalize("Test program returned success but some test "
482 1.1 jmmv "cases failed" +
483 1.1 jmmv (fmterr.empty() ? "" : (". " + fmterr)));
484 1.1 jmmv } else {
485 1.1 jmmv code = fmterr.empty() ? code : EXIT_FAILURE;
486 1.1 jmmv m.finalize(fmterr);
487 1.1 jmmv }
488 1.1 jmmv } else if (WIFSIGNALED(status)) {
489 1.1 jmmv code = EXIT_FAILURE;
490 1.1 jmmv m.finalize("Test program received signal " +
491 1.1 jmmv atf::text::to_string(WTERMSIG(status)) +
492 1.1 jmmv (WCOREDUMP(status) ? " (core dumped)" : "") +
493 1.1 jmmv (fmterr.empty() ? "" : (". " + fmterr)));
494 1.1 jmmv } else
495 1.1 jmmv throw std::runtime_error
496 1.1 jmmv ("Child process " + atf::text::to_string(pid) +
497 1.1 jmmv " terminated with an unknown status condition " +
498 1.1 jmmv atf::text::to_string(status));
499 1.1 jmmv }
500 1.1 jmmv return code;
501 1.1 jmmv }
502 1.1 jmmv
503 1.1 jmmv int
504 1.1 jmmv atf_run::run_test_program(const atf::fs::path& tp,
505 1.1 jmmv atf::formats::atf_tps_writer& w)
506 1.1 jmmv {
507 1.1 jmmv int errcode;
508 1.1 jmmv
509 1.1 jmmv atf::io::pipe outpipe, errpipe, respipe;
510 1.1 jmmv pid_t pid = atf::process::fork();
511 1.1 jmmv if (pid == 0) {
512 1.1 jmmv run_test_program_child(tp, outpipe, errpipe, respipe);
513 1.1 jmmv UNREACHABLE;
514 1.1 jmmv errcode = EXIT_FAILURE;
515 1.1 jmmv } else {
516 1.1 jmmv errcode = run_test_program_parent(tp, w, outpipe, errpipe,
517 1.1 jmmv respipe, pid);
518 1.1 jmmv }
519 1.1 jmmv
520 1.1 jmmv return errcode;
521 1.1 jmmv }
522 1.1 jmmv
523 1.1 jmmv size_t
524 1.1 jmmv atf_run::count_tps(std::vector< std::string > tps)
525 1.1 jmmv const
526 1.1 jmmv {
527 1.1 jmmv size_t ntps = 0;
528 1.1 jmmv
529 1.1 jmmv for (std::vector< std::string >::const_iterator iter = tps.begin();
530 1.1 jmmv iter != tps.end(); iter++) {
531 1.1 jmmv atf::fs::path tp(*iter);
532 1.1 jmmv atf::fs::file_info fi(tp);
533 1.1 jmmv
534 1.1 jmmv if (fi.get_type() == atf::fs::file_info::dir_type) {
535 1.1 jmmv atf::atffile af = atf::atffile(tp / "Atffile");
536 1.1 jmmv std::vector< std::string > aux = af.tps();
537 1.1 jmmv for (std::vector< std::string >::iterator i2 = aux.begin();
538 1.1 jmmv i2 != aux.end(); i2++)
539 1.1 jmmv *i2 = (tp / *i2).str();
540 1.1 jmmv ntps += count_tps(aux);
541 1.1 jmmv } else
542 1.1 jmmv ntps++;
543 1.1 jmmv }
544 1.1 jmmv
545 1.1 jmmv return ntps;
546 1.1 jmmv }
547 1.1 jmmv
548 1.1 jmmv void
549 1.1 jmmv atf_run::read_one_config(const atf::fs::path& p)
550 1.1 jmmv {
551 1.1 jmmv std::ifstream is(p.c_str());
552 1.1 jmmv if (is) {
553 1.1 jmmv config reader(is);
554 1.1 jmmv reader.read();
555 1.1 jmmv merge_maps(m_config_vars, reader.get_vars());
556 1.1 jmmv }
557 1.1 jmmv }
558 1.1 jmmv
559 1.1 jmmv void
560 1.1 jmmv atf_run::read_config(const std::string& name)
561 1.1 jmmv {
562 1.1 jmmv std::vector< atf::fs::path > dirs;
563 1.1 jmmv dirs.push_back(atf::fs::path(atf::config::get("atf_confdir")));
564 1.1 jmmv if (atf::env::has("HOME"))
565 1.1 jmmv dirs.push_back(atf::fs::path(atf::env::get("HOME")) / ".atf");
566 1.1 jmmv
567 1.1 jmmv m_config_vars.clear();
568 1.1 jmmv for (std::vector< atf::fs::path >::const_iterator iter = dirs.begin();
569 1.1 jmmv iter != dirs.end(); iter++) {
570 1.1 jmmv read_one_config((*iter) / "common.conf");
571 1.1 jmmv read_one_config((*iter) / (name + ".conf"));
572 1.1 jmmv }
573 1.1 jmmv }
574 1.1 jmmv
575 1.1 jmmv static
576 1.1 jmmv void
577 1.1 jmmv call_hook(const std::string& tool, const std::string& hook)
578 1.1 jmmv {
579 1.1 jmmv std::string sh = atf::config::get("atf_shell");
580 1.1 jmmv atf::fs::path p = atf::fs::path(atf::config::get("atf_pkgdatadir")) /
581 1.1 jmmv (tool + ".hooks");
582 1.1 jmmv std::string cmd = sh + " '" + p.str() + "' '" + hook + "'";
583 1.1 jmmv int exitcode = std::system(cmd.c_str());
584 1.1 jmmv if (!WIFEXITED(exitcode) || WEXITSTATUS(exitcode) != EXIT_SUCCESS)
585 1.1 jmmv throw std::runtime_error("Failed to run the '" + hook + "' hook "
586 1.1 jmmv "for '" + tool + "'; command was '" +
587 1.1 jmmv cmd + "'; exit code " +
588 1.1 jmmv atf::text::to_string(exitcode));
589 1.1 jmmv }
590 1.1 jmmv
591 1.1 jmmv int
592 1.1 jmmv atf_run::main(void)
593 1.1 jmmv {
594 1.1 jmmv atf::atffile af(atf::fs::path("Atffile"));
595 1.1 jmmv m_atffile_vars = af.conf();
596 1.1 jmmv
597 1.1 jmmv std::vector< std::string > tps;
598 1.1 jmmv tps = af.tps();
599 1.1 jmmv if (m_argc >= 1) {
600 1.1 jmmv // TODO: Ensure that the given test names are listed in the
601 1.1 jmmv // Atffile. Take into account that the file can be using globs.
602 1.1 jmmv tps.clear();
603 1.1 jmmv for (int i = 0; i < m_argc; i++)
604 1.1 jmmv tps.push_back(m_argv[i]);
605 1.1 jmmv }
606 1.1 jmmv
607 1.1 jmmv // Read configuration data for this test suite.
608 1.1 jmmv {
609 1.1 jmmv atf::tests::vars_map::const_iterator iter =
610 1.1 jmmv af.props().find("test-suite");
611 1.1 jmmv INV(iter != af.props().end());
612 1.1 jmmv read_config((*iter).second);
613 1.1 jmmv }
614 1.1 jmmv
615 1.1 jmmv atf::formats::atf_tps_writer w(std::cout);
616 1.1 jmmv call_hook("atf-run", "info_start_hook");
617 1.1 jmmv w.ntps(count_tps(tps));
618 1.1 jmmv
619 1.1 jmmv bool ok = true;
620 1.1 jmmv for (std::vector< std::string >::const_iterator iter = tps.begin();
621 1.1 jmmv iter != tps.end(); iter++)
622 1.1 jmmv ok &= (run_test(atf::fs::path(*iter), w) == EXIT_SUCCESS);
623 1.1 jmmv
624 1.1 jmmv call_hook("atf-run", "info_end_hook");
625 1.1 jmmv
626 1.1 jmmv return ok ? EXIT_SUCCESS : EXIT_FAILURE;
627 1.1 jmmv }
628 1.1 jmmv
629 1.1 jmmv int
630 1.1 jmmv main(int argc, char* const* argv)
631 1.1 jmmv {
632 1.1 jmmv return atf_run().run(argc, argv);
633 1.1 jmmv }
634