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