xref: /minix3/external/bsd/bind/dist/unit/atf-src/atf-run/test-program.cpp (revision 00b67f09dd46474d133c95011a48590a8e8f94c7)
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 extern "C" {
31*00b67f09SDavid van Moolenbroek #include <sys/types.h>
32*00b67f09SDavid van Moolenbroek #include <sys/stat.h>
33*00b67f09SDavid van Moolenbroek #include <sys/time.h>
34*00b67f09SDavid van Moolenbroek 
35*00b67f09SDavid van Moolenbroek #include <fcntl.h>
36*00b67f09SDavid van Moolenbroek #include <signal.h>
37*00b67f09SDavid van Moolenbroek #include <unistd.h>
38*00b67f09SDavid van Moolenbroek }
39*00b67f09SDavid van Moolenbroek 
40*00b67f09SDavid van Moolenbroek #include <cerrno>
41*00b67f09SDavid van Moolenbroek #include <cstdlib>
42*00b67f09SDavid van Moolenbroek #include <cstring>
43*00b67f09SDavid van Moolenbroek #include <fstream>
44*00b67f09SDavid van Moolenbroek #include <iostream>
45*00b67f09SDavid van Moolenbroek 
46*00b67f09SDavid van Moolenbroek #include "atf-c/defs.h"
47*00b67f09SDavid van Moolenbroek 
48*00b67f09SDavid van Moolenbroek #include "atf-c++/detail/env.hpp"
49*00b67f09SDavid van Moolenbroek #include "atf-c++/detail/parser.hpp"
50*00b67f09SDavid van Moolenbroek #include "atf-c++/detail/process.hpp"
51*00b67f09SDavid van Moolenbroek #include "atf-c++/detail/sanity.hpp"
52*00b67f09SDavid van Moolenbroek #include "atf-c++/detail/text.hpp"
53*00b67f09SDavid van Moolenbroek 
54*00b67f09SDavid van Moolenbroek #include "config.hpp"
55*00b67f09SDavid van Moolenbroek #include "fs.hpp"
56*00b67f09SDavid van Moolenbroek #include "io.hpp"
57*00b67f09SDavid van Moolenbroek #include "requirements.hpp"
58*00b67f09SDavid van Moolenbroek #include "signals.hpp"
59*00b67f09SDavid van Moolenbroek #include "test-program.hpp"
60*00b67f09SDavid van Moolenbroek #include "timer.hpp"
61*00b67f09SDavid van Moolenbroek #include "user.hpp"
62*00b67f09SDavid van Moolenbroek 
63*00b67f09SDavid van Moolenbroek namespace impl = atf::atf_run;
64*00b67f09SDavid van Moolenbroek namespace detail = atf::atf_run::detail;
65*00b67f09SDavid van Moolenbroek 
66*00b67f09SDavid van Moolenbroek namespace {
67*00b67f09SDavid van Moolenbroek 
68*00b67f09SDavid van Moolenbroek static void
check_stream(std::ostream & os)69*00b67f09SDavid van Moolenbroek check_stream(std::ostream& os)
70*00b67f09SDavid van Moolenbroek {
71*00b67f09SDavid van Moolenbroek     // If we receive a signal while writing to the stream, the bad bit gets set.
72*00b67f09SDavid van Moolenbroek     // Things seem to behave fine afterwards if we clear such error condition.
73*00b67f09SDavid van Moolenbroek     // However, I'm not sure if it's safe to query errno at this point.
74*00b67f09SDavid van Moolenbroek     if (os.bad()) {
75*00b67f09SDavid van Moolenbroek         if (errno == EINTR)
76*00b67f09SDavid van Moolenbroek             os.clear();
77*00b67f09SDavid van Moolenbroek         else
78*00b67f09SDavid van Moolenbroek             throw std::runtime_error("Failed");
79*00b67f09SDavid van Moolenbroek     }
80*00b67f09SDavid van Moolenbroek }
81*00b67f09SDavid van Moolenbroek 
82*00b67f09SDavid van Moolenbroek namespace atf_tp {
83*00b67f09SDavid van Moolenbroek 
84*00b67f09SDavid van Moolenbroek static const atf::parser::token_type eof_type = 0;
85*00b67f09SDavid van Moolenbroek static const atf::parser::token_type nl_type = 1;
86*00b67f09SDavid van Moolenbroek static const atf::parser::token_type text_type = 2;
87*00b67f09SDavid van Moolenbroek static const atf::parser::token_type colon_type = 3;
88*00b67f09SDavid van Moolenbroek static const atf::parser::token_type dblquote_type = 4;
89*00b67f09SDavid van Moolenbroek 
90*00b67f09SDavid van Moolenbroek class tokenizer : public atf::parser::tokenizer< std::istream > {
91*00b67f09SDavid van Moolenbroek public:
tokenizer(std::istream & is,size_t curline)92*00b67f09SDavid van Moolenbroek     tokenizer(std::istream& is, size_t curline) :
93*00b67f09SDavid van Moolenbroek         atf::parser::tokenizer< std::istream >
94*00b67f09SDavid van Moolenbroek             (is, true, eof_type, nl_type, text_type, curline)
95*00b67f09SDavid van Moolenbroek     {
96*00b67f09SDavid van Moolenbroek         add_delim(':', colon_type);
97*00b67f09SDavid van Moolenbroek         add_quote('"', dblquote_type);
98*00b67f09SDavid van Moolenbroek     }
99*00b67f09SDavid van Moolenbroek };
100*00b67f09SDavid van Moolenbroek 
101*00b67f09SDavid van Moolenbroek } // namespace atf_tp
102*00b67f09SDavid van Moolenbroek 
103*00b67f09SDavid van Moolenbroek class metadata_reader : public detail::atf_tp_reader {
104*00b67f09SDavid van Moolenbroek     impl::test_cases_map m_tcs;
105*00b67f09SDavid van Moolenbroek 
got_tc(const std::string & ident,const atf::tests::vars_map & props)106*00b67f09SDavid van Moolenbroek     void got_tc(const std::string& ident, const atf::tests::vars_map& props)
107*00b67f09SDavid van Moolenbroek     {
108*00b67f09SDavid van Moolenbroek         if (m_tcs.find(ident) != m_tcs.end())
109*00b67f09SDavid van Moolenbroek             throw(std::runtime_error("Duplicate test case " + ident +
110*00b67f09SDavid van Moolenbroek                                      " in test program"));
111*00b67f09SDavid van Moolenbroek         m_tcs[ident] = props;
112*00b67f09SDavid van Moolenbroek 
113*00b67f09SDavid van Moolenbroek         if (m_tcs[ident].find("has.cleanup") == m_tcs[ident].end())
114*00b67f09SDavid van Moolenbroek             m_tcs[ident].insert(std::make_pair("has.cleanup", "false"));
115*00b67f09SDavid van Moolenbroek 
116*00b67f09SDavid van Moolenbroek         if (m_tcs[ident].find("timeout") == m_tcs[ident].end())
117*00b67f09SDavid van Moolenbroek             m_tcs[ident].insert(std::make_pair("timeout", "300"));
118*00b67f09SDavid van Moolenbroek     }
119*00b67f09SDavid van Moolenbroek 
120*00b67f09SDavid van Moolenbroek public:
metadata_reader(std::istream & is)121*00b67f09SDavid van Moolenbroek     metadata_reader(std::istream& is) :
122*00b67f09SDavid van Moolenbroek         detail::atf_tp_reader(is)
123*00b67f09SDavid van Moolenbroek     {
124*00b67f09SDavid van Moolenbroek     }
125*00b67f09SDavid van Moolenbroek 
126*00b67f09SDavid van Moolenbroek     const impl::test_cases_map&
get_tcs(void) const127*00b67f09SDavid van Moolenbroek     get_tcs(void)
128*00b67f09SDavid van Moolenbroek         const
129*00b67f09SDavid van Moolenbroek     {
130*00b67f09SDavid van Moolenbroek         return m_tcs;
131*00b67f09SDavid van Moolenbroek     }
132*00b67f09SDavid van Moolenbroek };
133*00b67f09SDavid van Moolenbroek 
134*00b67f09SDavid van Moolenbroek struct get_metadata_params {
135*00b67f09SDavid van Moolenbroek     const atf::fs::path& executable;
136*00b67f09SDavid van Moolenbroek     const atf::tests::vars_map& config;
137*00b67f09SDavid van Moolenbroek 
get_metadata_params__anond61b63f40111::get_metadata_params138*00b67f09SDavid van Moolenbroek     get_metadata_params(const atf::fs::path& p_executable,
139*00b67f09SDavid van Moolenbroek                         const atf::tests::vars_map& p_config) :
140*00b67f09SDavid van Moolenbroek         executable(p_executable),
141*00b67f09SDavid van Moolenbroek         config(p_config)
142*00b67f09SDavid van Moolenbroek     {
143*00b67f09SDavid van Moolenbroek     }
144*00b67f09SDavid van Moolenbroek };
145*00b67f09SDavid van Moolenbroek 
146*00b67f09SDavid van Moolenbroek struct test_case_params {
147*00b67f09SDavid van Moolenbroek     const atf::fs::path& executable;
148*00b67f09SDavid van Moolenbroek     const std::string& test_case_name;
149*00b67f09SDavid van Moolenbroek     const std::string& test_case_part;
150*00b67f09SDavid van Moolenbroek     const atf::tests::vars_map& metadata;
151*00b67f09SDavid van Moolenbroek     const atf::tests::vars_map& config;
152*00b67f09SDavid van Moolenbroek     const atf::fs::path& resfile;
153*00b67f09SDavid van Moolenbroek     const atf::fs::path& workdir;
154*00b67f09SDavid van Moolenbroek 
test_case_params__anond61b63f40111::test_case_params155*00b67f09SDavid van Moolenbroek     test_case_params(const atf::fs::path& p_executable,
156*00b67f09SDavid van Moolenbroek                      const std::string& p_test_case_name,
157*00b67f09SDavid van Moolenbroek                      const std::string& p_test_case_part,
158*00b67f09SDavid van Moolenbroek                      const atf::tests::vars_map& p_metadata,
159*00b67f09SDavid van Moolenbroek                      const atf::tests::vars_map& p_config,
160*00b67f09SDavid van Moolenbroek                      const atf::fs::path& p_resfile,
161*00b67f09SDavid van Moolenbroek                      const atf::fs::path& p_workdir) :
162*00b67f09SDavid van Moolenbroek         executable(p_executable),
163*00b67f09SDavid van Moolenbroek         test_case_name(p_test_case_name),
164*00b67f09SDavid van Moolenbroek         test_case_part(p_test_case_part),
165*00b67f09SDavid van Moolenbroek         metadata(p_metadata),
166*00b67f09SDavid van Moolenbroek         config(p_config),
167*00b67f09SDavid van Moolenbroek         resfile(p_resfile),
168*00b67f09SDavid van Moolenbroek         workdir(p_workdir)
169*00b67f09SDavid van Moolenbroek     {
170*00b67f09SDavid van Moolenbroek     }
171*00b67f09SDavid van Moolenbroek };
172*00b67f09SDavid van Moolenbroek 
173*00b67f09SDavid van Moolenbroek static
174*00b67f09SDavid van Moolenbroek std::string
generate_timestamp(void)175*00b67f09SDavid van Moolenbroek generate_timestamp(void)
176*00b67f09SDavid van Moolenbroek {
177*00b67f09SDavid van Moolenbroek     struct timeval tv;
178*00b67f09SDavid van Moolenbroek     if (gettimeofday(&tv, NULL) == -1)
179*00b67f09SDavid van Moolenbroek         return "0.0";
180*00b67f09SDavid van Moolenbroek 
181*00b67f09SDavid van Moolenbroek     char buf[32];
182*00b67f09SDavid van Moolenbroek     const int len = snprintf(buf, sizeof(buf), "%ld.%ld",
183*00b67f09SDavid van Moolenbroek                              static_cast< long >(tv.tv_sec),
184*00b67f09SDavid van Moolenbroek                              static_cast< long >(tv.tv_usec));
185*00b67f09SDavid van Moolenbroek     if (len >= static_cast< int >(sizeof(buf)) || len < 0)
186*00b67f09SDavid van Moolenbroek         return "0.0";
187*00b67f09SDavid van Moolenbroek     else
188*00b67f09SDavid van Moolenbroek         return buf;
189*00b67f09SDavid van Moolenbroek }
190*00b67f09SDavid van Moolenbroek 
191*00b67f09SDavid van Moolenbroek static
192*00b67f09SDavid van Moolenbroek void
append_to_vector(std::vector<std::string> & v1,const std::vector<std::string> & v2)193*00b67f09SDavid van Moolenbroek append_to_vector(std::vector< std::string >& v1,
194*00b67f09SDavid van Moolenbroek                  const std::vector< std::string >& v2)
195*00b67f09SDavid van Moolenbroek {
196*00b67f09SDavid van Moolenbroek     std::copy(v2.begin(), v2.end(),
197*00b67f09SDavid van Moolenbroek               std::back_insert_iterator< std::vector< std::string > >(v1));
198*00b67f09SDavid van Moolenbroek }
199*00b67f09SDavid van Moolenbroek 
200*00b67f09SDavid van Moolenbroek static
201*00b67f09SDavid van Moolenbroek char**
vector_to_argv(const std::vector<std::string> & v)202*00b67f09SDavid van Moolenbroek vector_to_argv(const std::vector< std::string >& v)
203*00b67f09SDavid van Moolenbroek {
204*00b67f09SDavid van Moolenbroek     char** argv = new char*[v.size() + 1];
205*00b67f09SDavid van Moolenbroek     for (std::vector< std::string >::size_type i = 0; i < v.size(); i++) {
206*00b67f09SDavid van Moolenbroek         argv[i] = strdup(v[i].c_str());
207*00b67f09SDavid van Moolenbroek     }
208*00b67f09SDavid van Moolenbroek     argv[v.size()] = NULL;
209*00b67f09SDavid van Moolenbroek     return argv;
210*00b67f09SDavid van Moolenbroek }
211*00b67f09SDavid van Moolenbroek 
212*00b67f09SDavid van Moolenbroek static
213*00b67f09SDavid van Moolenbroek void
exec_or_exit(const atf::fs::path & executable,const std::vector<std::string> & argv)214*00b67f09SDavid van Moolenbroek exec_or_exit(const atf::fs::path& executable,
215*00b67f09SDavid van Moolenbroek              const std::vector< std::string >& argv)
216*00b67f09SDavid van Moolenbroek {
217*00b67f09SDavid van Moolenbroek     // This leaks memory in case of a failure, but it is OK.  Exiting will
218*00b67f09SDavid van Moolenbroek     // do the necessary cleanup.
219*00b67f09SDavid van Moolenbroek     char* const* native_argv = vector_to_argv(argv);
220*00b67f09SDavid van Moolenbroek 
221*00b67f09SDavid van Moolenbroek     ::execv(executable.c_str(), native_argv);
222*00b67f09SDavid van Moolenbroek 
223*00b67f09SDavid van Moolenbroek     const std::string message = "Failed to execute '" + executable.str() +
224*00b67f09SDavid van Moolenbroek         "': " + std::strerror(errno) + "\n";
225*00b67f09SDavid van Moolenbroek     if (::write(STDERR_FILENO, message.c_str(), message.length()) == -1)
226*00b67f09SDavid van Moolenbroek         std::abort();
227*00b67f09SDavid van Moolenbroek     std::exit(EXIT_FAILURE);
228*00b67f09SDavid van Moolenbroek }
229*00b67f09SDavid van Moolenbroek 
230*00b67f09SDavid van Moolenbroek static
231*00b67f09SDavid van Moolenbroek std::vector< std::string >
config_to_args(const atf::tests::vars_map & config)232*00b67f09SDavid van Moolenbroek config_to_args(const atf::tests::vars_map& config)
233*00b67f09SDavid van Moolenbroek {
234*00b67f09SDavid van Moolenbroek     std::vector< std::string > args;
235*00b67f09SDavid van Moolenbroek 
236*00b67f09SDavid van Moolenbroek     for (atf::tests::vars_map::const_iterator iter = config.begin();
237*00b67f09SDavid van Moolenbroek          iter != config.end(); iter++)
238*00b67f09SDavid van Moolenbroek         args.push_back("-v" + (*iter).first + "=" + (*iter).second);
239*00b67f09SDavid van Moolenbroek 
240*00b67f09SDavid van Moolenbroek     return args;
241*00b67f09SDavid van Moolenbroek }
242*00b67f09SDavid van Moolenbroek 
243*00b67f09SDavid van Moolenbroek static
244*00b67f09SDavid van Moolenbroek void
silence_stdin(void)245*00b67f09SDavid van Moolenbroek silence_stdin(void)
246*00b67f09SDavid van Moolenbroek {
247*00b67f09SDavid van Moolenbroek     ::close(STDIN_FILENO);
248*00b67f09SDavid van Moolenbroek     int fd = ::open("/dev/null", O_RDONLY);
249*00b67f09SDavid van Moolenbroek     if (fd == -1)
250*00b67f09SDavid van Moolenbroek         throw std::runtime_error("Could not open /dev/null");
251*00b67f09SDavid van Moolenbroek     INV(fd == STDIN_FILENO);
252*00b67f09SDavid van Moolenbroek }
253*00b67f09SDavid van Moolenbroek 
254*00b67f09SDavid van Moolenbroek static
255*00b67f09SDavid van Moolenbroek void
prepare_child(const atf::fs::path & workdir)256*00b67f09SDavid van Moolenbroek prepare_child(const atf::fs::path& workdir)
257*00b67f09SDavid van Moolenbroek {
258*00b67f09SDavid van Moolenbroek     const int ret = ::setpgid(::getpid(), 0);
259*00b67f09SDavid van Moolenbroek     INV(ret != -1);
260*00b67f09SDavid van Moolenbroek 
261*00b67f09SDavid van Moolenbroek     ::umask(S_IWGRP | S_IWOTH);
262*00b67f09SDavid van Moolenbroek 
263*00b67f09SDavid van Moolenbroek     for (int i = 1; i <= impl::last_signo; i++)
264*00b67f09SDavid van Moolenbroek         impl::reset(i);
265*00b67f09SDavid van Moolenbroek 
266*00b67f09SDavid van Moolenbroek     atf::env::set("HOME", workdir.str());
267*00b67f09SDavid van Moolenbroek     atf::env::unset("LANG");
268*00b67f09SDavid van Moolenbroek     atf::env::unset("LC_ALL");
269*00b67f09SDavid van Moolenbroek     atf::env::unset("LC_COLLATE");
270*00b67f09SDavid van Moolenbroek     atf::env::unset("LC_CTYPE");
271*00b67f09SDavid van Moolenbroek     atf::env::unset("LC_MESSAGES");
272*00b67f09SDavid van Moolenbroek     atf::env::unset("LC_MONETARY");
273*00b67f09SDavid van Moolenbroek     atf::env::unset("LC_NUMERIC");
274*00b67f09SDavid van Moolenbroek     atf::env::unset("LC_TIME");
275*00b67f09SDavid van Moolenbroek     atf::env::set("TZ", "UTC");
276*00b67f09SDavid van Moolenbroek 
277*00b67f09SDavid van Moolenbroek     atf::env::set("__RUNNING_INSIDE_ATF_RUN", "internal-yes-value");
278*00b67f09SDavid van Moolenbroek 
279*00b67f09SDavid van Moolenbroek     impl::change_directory(workdir);
280*00b67f09SDavid van Moolenbroek 
281*00b67f09SDavid van Moolenbroek     silence_stdin();
282*00b67f09SDavid van Moolenbroek }
283*00b67f09SDavid van Moolenbroek 
284*00b67f09SDavid van Moolenbroek static
285*00b67f09SDavid van Moolenbroek void
get_metadata_child(void * raw_params)286*00b67f09SDavid van Moolenbroek get_metadata_child(void* raw_params)
287*00b67f09SDavid van Moolenbroek {
288*00b67f09SDavid van Moolenbroek     const get_metadata_params* params =
289*00b67f09SDavid van Moolenbroek         static_cast< const get_metadata_params* >(raw_params);
290*00b67f09SDavid van Moolenbroek 
291*00b67f09SDavid van Moolenbroek     std::vector< std::string > argv;
292*00b67f09SDavid van Moolenbroek     argv.push_back(params->executable.leaf_name());
293*00b67f09SDavid van Moolenbroek     argv.push_back("-l");
294*00b67f09SDavid van Moolenbroek     argv.push_back("-s" + params->executable.branch_path().str());
295*00b67f09SDavid van Moolenbroek     append_to_vector(argv, config_to_args(params->config));
296*00b67f09SDavid van Moolenbroek 
297*00b67f09SDavid van Moolenbroek     exec_or_exit(params->executable, argv);
298*00b67f09SDavid van Moolenbroek }
299*00b67f09SDavid van Moolenbroek 
300*00b67f09SDavid van Moolenbroek void
run_test_case_child(void * raw_params)301*00b67f09SDavid van Moolenbroek run_test_case_child(void* raw_params)
302*00b67f09SDavid van Moolenbroek {
303*00b67f09SDavid van Moolenbroek     const test_case_params* params =
304*00b67f09SDavid van Moolenbroek         static_cast< const test_case_params* >(raw_params);
305*00b67f09SDavid van Moolenbroek 
306*00b67f09SDavid van Moolenbroek     const std::pair< int, int > user = impl::get_required_user(
307*00b67f09SDavid van Moolenbroek         params->metadata, params->config);
308*00b67f09SDavid van Moolenbroek     if (user.first != -1 && user.second != -1)
309*00b67f09SDavid van Moolenbroek         impl::drop_privileges(user);
310*00b67f09SDavid van Moolenbroek 
311*00b67f09SDavid van Moolenbroek     // The input 'tp' parameter may be relative and become invalid once
312*00b67f09SDavid van Moolenbroek     // we change the current working directory.
313*00b67f09SDavid van Moolenbroek     const atf::fs::path absolute_executable = params->executable.to_absolute();
314*00b67f09SDavid van Moolenbroek 
315*00b67f09SDavid van Moolenbroek     // Prepare the test program's arguments.  We use dynamic memory and
316*00b67f09SDavid van Moolenbroek     // do not care to release it.  We are going to die anyway very soon,
317*00b67f09SDavid van Moolenbroek     // either due to exec(2) or to exit(3).
318*00b67f09SDavid van Moolenbroek     std::vector< std::string > argv;
319*00b67f09SDavid van Moolenbroek     argv.push_back(absolute_executable.leaf_name());
320*00b67f09SDavid van Moolenbroek     argv.push_back("-r" + params->resfile.str());
321*00b67f09SDavid van Moolenbroek     argv.push_back("-s" + absolute_executable.branch_path().str());
322*00b67f09SDavid van Moolenbroek     append_to_vector(argv, config_to_args(params->config));
323*00b67f09SDavid van Moolenbroek     argv.push_back(params->test_case_name + ":" + params->test_case_part);
324*00b67f09SDavid van Moolenbroek 
325*00b67f09SDavid van Moolenbroek     prepare_child(params->workdir);
326*00b67f09SDavid van Moolenbroek     exec_or_exit(absolute_executable, argv);
327*00b67f09SDavid van Moolenbroek }
328*00b67f09SDavid van Moolenbroek 
329*00b67f09SDavid van Moolenbroek static void
tokenize_result(const std::string & line,std::string & out_state,std::string & out_arg,std::string & out_reason)330*00b67f09SDavid van Moolenbroek tokenize_result(const std::string& line, std::string& out_state,
331*00b67f09SDavid van Moolenbroek                 std::string& out_arg, std::string& out_reason)
332*00b67f09SDavid van Moolenbroek {
333*00b67f09SDavid van Moolenbroek     const std::string::size_type pos = line.find_first_of(":(");
334*00b67f09SDavid van Moolenbroek     if (pos == std::string::npos) {
335*00b67f09SDavid van Moolenbroek         out_state = line;
336*00b67f09SDavid van Moolenbroek         out_arg = "";
337*00b67f09SDavid van Moolenbroek         out_reason = "";
338*00b67f09SDavid van Moolenbroek     } else if (line[pos] == ':') {
339*00b67f09SDavid van Moolenbroek         out_state = line.substr(0, pos);
340*00b67f09SDavid van Moolenbroek         out_arg = "";
341*00b67f09SDavid van Moolenbroek         out_reason = atf::text::trim(line.substr(pos + 1));
342*00b67f09SDavid van Moolenbroek     } else if (line[pos] == '(') {
343*00b67f09SDavid van Moolenbroek         const std::string::size_type pos2 = line.find("):", pos);
344*00b67f09SDavid van Moolenbroek         if (pos2 == std::string::npos)
345*00b67f09SDavid van Moolenbroek             throw std::runtime_error("Invalid test case result '" + line +
346*00b67f09SDavid van Moolenbroek                 "': unclosed optional argument");
347*00b67f09SDavid van Moolenbroek         out_state = line.substr(0, pos);
348*00b67f09SDavid van Moolenbroek         out_arg = line.substr(pos + 1, pos2 - pos - 1);
349*00b67f09SDavid van Moolenbroek         out_reason = atf::text::trim(line.substr(pos2 + 2));
350*00b67f09SDavid van Moolenbroek     } else
351*00b67f09SDavid van Moolenbroek         UNREACHABLE;
352*00b67f09SDavid van Moolenbroek }
353*00b67f09SDavid van Moolenbroek 
354*00b67f09SDavid van Moolenbroek static impl::test_case_result
handle_result(const std::string & state,const std::string & arg,const std::string & reason)355*00b67f09SDavid van Moolenbroek handle_result(const std::string& state, const std::string& arg,
356*00b67f09SDavid van Moolenbroek               const std::string& reason)
357*00b67f09SDavid van Moolenbroek {
358*00b67f09SDavid van Moolenbroek     PRE(state == "passed");
359*00b67f09SDavid van Moolenbroek 
360*00b67f09SDavid van Moolenbroek     if (!arg.empty() || !reason.empty())
361*00b67f09SDavid van Moolenbroek         throw std::runtime_error("The test case result '" + state + "' cannot "
362*00b67f09SDavid van Moolenbroek             "be accompanied by a reason nor an expected value");
363*00b67f09SDavid van Moolenbroek 
364*00b67f09SDavid van Moolenbroek     return impl::test_case_result(state, -1, reason);
365*00b67f09SDavid van Moolenbroek }
366*00b67f09SDavid van Moolenbroek 
367*00b67f09SDavid van Moolenbroek static impl::test_case_result
handle_result_with_reason(const std::string & state,const std::string & arg,const std::string & reason)368*00b67f09SDavid van Moolenbroek handle_result_with_reason(const std::string& state, const std::string& arg,
369*00b67f09SDavid van Moolenbroek                           const std::string& reason)
370*00b67f09SDavid van Moolenbroek {
371*00b67f09SDavid van Moolenbroek     PRE(state == "expected_death" || state == "expected_failure" ||
372*00b67f09SDavid van Moolenbroek         state == "expected_timeout" || state == "failed" || state == "skipped");
373*00b67f09SDavid van Moolenbroek 
374*00b67f09SDavid van Moolenbroek     if (!arg.empty() || reason.empty())
375*00b67f09SDavid van Moolenbroek         throw std::runtime_error("The test case result '" + state + "' must "
376*00b67f09SDavid van Moolenbroek             "be accompanied by a reason but not by an expected value");
377*00b67f09SDavid van Moolenbroek 
378*00b67f09SDavid van Moolenbroek     return impl::test_case_result(state, -1, reason);
379*00b67f09SDavid van Moolenbroek }
380*00b67f09SDavid van Moolenbroek 
381*00b67f09SDavid van Moolenbroek static impl::test_case_result
handle_result_with_reason_and_arg(const std::string & state,const std::string & arg,const std::string & reason)382*00b67f09SDavid van Moolenbroek handle_result_with_reason_and_arg(const std::string& state,
383*00b67f09SDavid van Moolenbroek                                   const std::string& arg,
384*00b67f09SDavid van Moolenbroek                                   const std::string& reason)
385*00b67f09SDavid van Moolenbroek {
386*00b67f09SDavid van Moolenbroek     PRE(state == "expected_exit" || state == "expected_signal");
387*00b67f09SDavid van Moolenbroek 
388*00b67f09SDavid van Moolenbroek     if (reason.empty())
389*00b67f09SDavid van Moolenbroek         throw std::runtime_error("The test case result '" + state + "' must "
390*00b67f09SDavid van Moolenbroek             "be accompanied by a reason");
391*00b67f09SDavid van Moolenbroek 
392*00b67f09SDavid van Moolenbroek     int value;
393*00b67f09SDavid van Moolenbroek     if (arg.empty()) {
394*00b67f09SDavid van Moolenbroek         value = -1;
395*00b67f09SDavid van Moolenbroek     } else {
396*00b67f09SDavid van Moolenbroek         try {
397*00b67f09SDavid van Moolenbroek             value = atf::text::to_type< int >(arg);
398*00b67f09SDavid van Moolenbroek         } catch (const std::runtime_error&) {
399*00b67f09SDavid van Moolenbroek             throw std::runtime_error("The value '" + arg + "' passed to the '" +
400*00b67f09SDavid van Moolenbroek                 state + "' state must be an integer");
401*00b67f09SDavid van Moolenbroek         }
402*00b67f09SDavid van Moolenbroek     }
403*00b67f09SDavid van Moolenbroek 
404*00b67f09SDavid van Moolenbroek     return impl::test_case_result(state, value, reason);
405*00b67f09SDavid van Moolenbroek }
406*00b67f09SDavid van Moolenbroek 
407*00b67f09SDavid van Moolenbroek } // anonymous namespace
408*00b67f09SDavid van Moolenbroek 
atf_tp_reader(std::istream & is)409*00b67f09SDavid van Moolenbroek detail::atf_tp_reader::atf_tp_reader(std::istream& is) :
410*00b67f09SDavid van Moolenbroek     m_is(is)
411*00b67f09SDavid van Moolenbroek {
412*00b67f09SDavid van Moolenbroek }
413*00b67f09SDavid van Moolenbroek 
~atf_tp_reader(void)414*00b67f09SDavid van Moolenbroek detail::atf_tp_reader::~atf_tp_reader(void)
415*00b67f09SDavid van Moolenbroek {
416*00b67f09SDavid van Moolenbroek }
417*00b67f09SDavid van Moolenbroek 
418*00b67f09SDavid van Moolenbroek void
got_tc(const std::string & ident ATF_DEFS_ATTRIBUTE_UNUSED,const std::map<std::string,std::string> & md ATF_DEFS_ATTRIBUTE_UNUSED)419*00b67f09SDavid van Moolenbroek detail::atf_tp_reader::got_tc(
420*00b67f09SDavid van Moolenbroek     const std::string& ident ATF_DEFS_ATTRIBUTE_UNUSED,
421*00b67f09SDavid van Moolenbroek     const std::map< std::string, std::string >& md ATF_DEFS_ATTRIBUTE_UNUSED)
422*00b67f09SDavid van Moolenbroek {
423*00b67f09SDavid van Moolenbroek }
424*00b67f09SDavid van Moolenbroek 
425*00b67f09SDavid van Moolenbroek void
got_eof(void)426*00b67f09SDavid van Moolenbroek detail::atf_tp_reader::got_eof(void)
427*00b67f09SDavid van Moolenbroek {
428*00b67f09SDavid van Moolenbroek }
429*00b67f09SDavid van Moolenbroek 
430*00b67f09SDavid van Moolenbroek void
validate_and_insert(const std::string & name,const std::string & value,const size_t lineno,std::map<std::string,std::string> & md)431*00b67f09SDavid van Moolenbroek detail::atf_tp_reader::validate_and_insert(const std::string& name,
432*00b67f09SDavid van Moolenbroek     const std::string& value, const size_t lineno,
433*00b67f09SDavid van Moolenbroek     std::map< std::string, std::string >& md)
434*00b67f09SDavid van Moolenbroek {
435*00b67f09SDavid van Moolenbroek     using atf::parser::parse_error;
436*00b67f09SDavid van Moolenbroek 
437*00b67f09SDavid van Moolenbroek     if (value.empty())
438*00b67f09SDavid van Moolenbroek         throw parse_error(lineno, "The value for '" + name +"' cannot be "
439*00b67f09SDavid van Moolenbroek                           "empty");
440*00b67f09SDavid van Moolenbroek 
441*00b67f09SDavid van Moolenbroek     const std::string ident_regex = "^[_A-Za-z0-9]+$";
442*00b67f09SDavid van Moolenbroek     const std::string integer_regex = "^[0-9]+$";
443*00b67f09SDavid van Moolenbroek 
444*00b67f09SDavid van Moolenbroek     if (name == "descr") {
445*00b67f09SDavid van Moolenbroek         // Any non-empty value is valid.
446*00b67f09SDavid van Moolenbroek     } else if (name == "has.cleanup") {
447*00b67f09SDavid van Moolenbroek         try {
448*00b67f09SDavid van Moolenbroek             (void)atf::text::to_bool(value);
449*00b67f09SDavid van Moolenbroek         } catch (const std::runtime_error&) {
450*00b67f09SDavid van Moolenbroek             throw parse_error(lineno, "The has.cleanup property requires a"
451*00b67f09SDavid van Moolenbroek                               " boolean value");
452*00b67f09SDavid van Moolenbroek         }
453*00b67f09SDavid van Moolenbroek     } else if (name == "ident") {
454*00b67f09SDavid van Moolenbroek         if (!atf::text::match(value, ident_regex))
455*00b67f09SDavid van Moolenbroek             throw parse_error(lineno, "The identifier must match " +
456*00b67f09SDavid van Moolenbroek                               ident_regex + "; was '" + value + "'");
457*00b67f09SDavid van Moolenbroek     } else if (name == "require.arch") {
458*00b67f09SDavid van Moolenbroek     } else if (name == "require.config") {
459*00b67f09SDavid van Moolenbroek     } else if (name == "require.files") {
460*00b67f09SDavid van Moolenbroek     } else if (name == "require.machine") {
461*00b67f09SDavid van Moolenbroek     } else if (name == "require.memory") {
462*00b67f09SDavid van Moolenbroek         try {
463*00b67f09SDavid van Moolenbroek             (void)atf::text::to_bytes(value);
464*00b67f09SDavid van Moolenbroek         } catch (const std::runtime_error&) {
465*00b67f09SDavid van Moolenbroek             throw parse_error(lineno, "The require.memory property requires an "
466*00b67f09SDavid van Moolenbroek                               "integer value representing an amount of bytes");
467*00b67f09SDavid van Moolenbroek         }
468*00b67f09SDavid van Moolenbroek     } else if (name == "require.progs") {
469*00b67f09SDavid van Moolenbroek     } else if (name == "require.user") {
470*00b67f09SDavid van Moolenbroek     } else if (name == "timeout") {
471*00b67f09SDavid van Moolenbroek         if (!atf::text::match(value, integer_regex))
472*00b67f09SDavid van Moolenbroek             throw parse_error(lineno, "The timeout property requires an integer"
473*00b67f09SDavid van Moolenbroek                               " value");
474*00b67f09SDavid van Moolenbroek     } else if (name == "use.fs") {
475*00b67f09SDavid van Moolenbroek         // Deprecated; ignore it.
476*00b67f09SDavid van Moolenbroek     } else if (name.size() > 2 && name[0] == 'X' && name[1] == '-') {
477*00b67f09SDavid van Moolenbroek         // Any non-empty value is valid.
478*00b67f09SDavid van Moolenbroek     } else {
479*00b67f09SDavid van Moolenbroek         throw parse_error(lineno, "Unknown property '" + name + "'");
480*00b67f09SDavid van Moolenbroek     }
481*00b67f09SDavid van Moolenbroek 
482*00b67f09SDavid van Moolenbroek     md.insert(std::make_pair(name, value));
483*00b67f09SDavid van Moolenbroek }
484*00b67f09SDavid van Moolenbroek 
485*00b67f09SDavid van Moolenbroek void
read(void)486*00b67f09SDavid van Moolenbroek detail::atf_tp_reader::read(void)
487*00b67f09SDavid van Moolenbroek {
488*00b67f09SDavid van Moolenbroek     using atf::parser::parse_error;
489*00b67f09SDavid van Moolenbroek     using namespace atf_tp;
490*00b67f09SDavid van Moolenbroek 
491*00b67f09SDavid van Moolenbroek     std::pair< size_t, atf::parser::headers_map > hml =
492*00b67f09SDavid van Moolenbroek         atf::parser::read_headers(m_is, 1);
493*00b67f09SDavid van Moolenbroek     atf::parser::validate_content_type(hml.second,
494*00b67f09SDavid van Moolenbroek         "application/X-atf-tp", 1);
495*00b67f09SDavid van Moolenbroek 
496*00b67f09SDavid van Moolenbroek     tokenizer tkz(m_is, hml.first);
497*00b67f09SDavid van Moolenbroek     atf::parser::parser< tokenizer > p(tkz);
498*00b67f09SDavid van Moolenbroek 
499*00b67f09SDavid van Moolenbroek     try {
500*00b67f09SDavid van Moolenbroek         atf::parser::token t = p.expect(text_type, "property name");
501*00b67f09SDavid van Moolenbroek         if (t.text() != "ident")
502*00b67f09SDavid van Moolenbroek             throw parse_error(t.lineno(), "First property of a test case "
503*00b67f09SDavid van Moolenbroek                               "must be 'ident'");
504*00b67f09SDavid van Moolenbroek 
505*00b67f09SDavid van Moolenbroek         std::map< std::string, std::string > props;
506*00b67f09SDavid van Moolenbroek         do {
507*00b67f09SDavid van Moolenbroek             const std::string name = t.text();
508*00b67f09SDavid van Moolenbroek             t = p.expect(colon_type, "`:'");
509*00b67f09SDavid van Moolenbroek             const std::string value = atf::text::trim(p.rest_of_line());
510*00b67f09SDavid van Moolenbroek             t = p.expect(nl_type, "new line");
511*00b67f09SDavid van Moolenbroek             validate_and_insert(name, value, t.lineno(), props);
512*00b67f09SDavid van Moolenbroek 
513*00b67f09SDavid van Moolenbroek             t = p.expect(eof_type, nl_type, text_type, "property name, new "
514*00b67f09SDavid van Moolenbroek                          "line or eof");
515*00b67f09SDavid van Moolenbroek             if (t.type() == nl_type || t.type() == eof_type) {
516*00b67f09SDavid van Moolenbroek                 const std::map< std::string, std::string >::const_iterator
517*00b67f09SDavid van Moolenbroek                     iter = props.find("ident");
518*00b67f09SDavid van Moolenbroek                 if (iter == props.end())
519*00b67f09SDavid van Moolenbroek                     throw parse_error(t.lineno(), "Test case definition did "
520*00b67f09SDavid van Moolenbroek                                       "not define an 'ident' property");
521*00b67f09SDavid van Moolenbroek                 ATF_PARSER_CALLBACK(p, got_tc((*iter).second, props));
522*00b67f09SDavid van Moolenbroek                 props.clear();
523*00b67f09SDavid van Moolenbroek 
524*00b67f09SDavid van Moolenbroek                 if (t.type() == nl_type) {
525*00b67f09SDavid van Moolenbroek                     t = p.expect(text_type, "property name");
526*00b67f09SDavid van Moolenbroek                     if (t.text() != "ident")
527*00b67f09SDavid van Moolenbroek                         throw parse_error(t.lineno(), "First property of a "
528*00b67f09SDavid van Moolenbroek                                           "test case must be 'ident'");
529*00b67f09SDavid van Moolenbroek                 }
530*00b67f09SDavid van Moolenbroek             }
531*00b67f09SDavid van Moolenbroek         } while (t.type() != eof_type);
532*00b67f09SDavid van Moolenbroek         ATF_PARSER_CALLBACK(p, got_eof());
533*00b67f09SDavid van Moolenbroek     } catch (const parse_error& pe) {
534*00b67f09SDavid van Moolenbroek         p.add_error(pe);
535*00b67f09SDavid van Moolenbroek         p.reset(nl_type);
536*00b67f09SDavid van Moolenbroek     }
537*00b67f09SDavid van Moolenbroek }
538*00b67f09SDavid van Moolenbroek 
539*00b67f09SDavid van Moolenbroek impl::test_case_result
parse_test_case_result(const std::string & line)540*00b67f09SDavid van Moolenbroek detail::parse_test_case_result(const std::string& line)
541*00b67f09SDavid van Moolenbroek {
542*00b67f09SDavid van Moolenbroek     std::string state, arg, reason;
543*00b67f09SDavid van Moolenbroek     tokenize_result(line, state, arg, reason);
544*00b67f09SDavid van Moolenbroek 
545*00b67f09SDavid van Moolenbroek     if (state == "expected_death")
546*00b67f09SDavid van Moolenbroek         return handle_result_with_reason(state, arg, reason);
547*00b67f09SDavid van Moolenbroek     else if (state.compare(0, 13, "expected_exit") == 0)
548*00b67f09SDavid van Moolenbroek         return handle_result_with_reason_and_arg(state, arg, reason);
549*00b67f09SDavid van Moolenbroek     else if (state.compare(0, 16, "expected_failure") == 0)
550*00b67f09SDavid van Moolenbroek         return handle_result_with_reason(state, arg, reason);
551*00b67f09SDavid van Moolenbroek     else if (state.compare(0, 15, "expected_signal") == 0)
552*00b67f09SDavid van Moolenbroek         return handle_result_with_reason_and_arg(state, arg, reason);
553*00b67f09SDavid van Moolenbroek     else if (state.compare(0, 16, "expected_timeout") == 0)
554*00b67f09SDavid van Moolenbroek         return handle_result_with_reason(state, arg, reason);
555*00b67f09SDavid van Moolenbroek     else if (state == "failed")
556*00b67f09SDavid van Moolenbroek         return handle_result_with_reason(state, arg, reason);
557*00b67f09SDavid van Moolenbroek     else if (state == "passed")
558*00b67f09SDavid van Moolenbroek         return handle_result(state, arg, reason);
559*00b67f09SDavid van Moolenbroek     else if (state == "skipped")
560*00b67f09SDavid van Moolenbroek         return handle_result_with_reason(state, arg, reason);
561*00b67f09SDavid van Moolenbroek     else
562*00b67f09SDavid van Moolenbroek         throw std::runtime_error("Unknown test case result type in: " + line);
563*00b67f09SDavid van Moolenbroek }
564*00b67f09SDavid van Moolenbroek 
atf_tps_writer(std::ostream & os)565*00b67f09SDavid van Moolenbroek impl::atf_tps_writer::atf_tps_writer(std::ostream& os) :
566*00b67f09SDavid van Moolenbroek     m_os(os)
567*00b67f09SDavid van Moolenbroek {
568*00b67f09SDavid van Moolenbroek     atf::parser::headers_map hm;
569*00b67f09SDavid van Moolenbroek     atf::parser::attrs_map ct_attrs;
570*00b67f09SDavid van Moolenbroek     ct_attrs["version"] = "3";
571*00b67f09SDavid van Moolenbroek     hm["Content-Type"] =
572*00b67f09SDavid van Moolenbroek         atf::parser::header_entry("Content-Type", "application/X-atf-tps",
573*00b67f09SDavid van Moolenbroek                                   ct_attrs);
574*00b67f09SDavid van Moolenbroek     atf::parser::write_headers(hm, m_os);
575*00b67f09SDavid van Moolenbroek }
576*00b67f09SDavid van Moolenbroek 
577*00b67f09SDavid van Moolenbroek void
info(const std::string & what,const std::string & val)578*00b67f09SDavid van Moolenbroek impl::atf_tps_writer::info(const std::string& what, const std::string& val)
579*00b67f09SDavid van Moolenbroek {
580*00b67f09SDavid van Moolenbroek     m_os << "info: " << what << ", " << val << "\n";
581*00b67f09SDavid van Moolenbroek     m_os.flush();
582*00b67f09SDavid van Moolenbroek }
583*00b67f09SDavid van Moolenbroek 
584*00b67f09SDavid van Moolenbroek void
ntps(size_t p_ntps)585*00b67f09SDavid van Moolenbroek impl::atf_tps_writer::ntps(size_t p_ntps)
586*00b67f09SDavid van Moolenbroek {
587*00b67f09SDavid van Moolenbroek     m_os << "tps-count: " << p_ntps << "\n";
588*00b67f09SDavid van Moolenbroek     m_os.flush();
589*00b67f09SDavid van Moolenbroek }
590*00b67f09SDavid van Moolenbroek 
591*00b67f09SDavid van Moolenbroek void
start_tp(const std::string & tp,size_t ntcs)592*00b67f09SDavid van Moolenbroek impl::atf_tps_writer::start_tp(const std::string& tp, size_t ntcs)
593*00b67f09SDavid van Moolenbroek {
594*00b67f09SDavid van Moolenbroek     m_tpname = tp;
595*00b67f09SDavid van Moolenbroek     m_os << "tp-start: " << generate_timestamp() << ", " << tp << ", "
596*00b67f09SDavid van Moolenbroek          << ntcs << "\n";
597*00b67f09SDavid van Moolenbroek     m_os.flush();
598*00b67f09SDavid van Moolenbroek }
599*00b67f09SDavid van Moolenbroek 
600*00b67f09SDavid van Moolenbroek void
end_tp(const std::string & reason)601*00b67f09SDavid van Moolenbroek impl::atf_tps_writer::end_tp(const std::string& reason)
602*00b67f09SDavid van Moolenbroek {
603*00b67f09SDavid van Moolenbroek     PRE(reason.find('\n') == std::string::npos);
604*00b67f09SDavid van Moolenbroek     if (reason.empty())
605*00b67f09SDavid van Moolenbroek         m_os << "tp-end: " << generate_timestamp() << ", " << m_tpname << "\n";
606*00b67f09SDavid van Moolenbroek     else
607*00b67f09SDavid van Moolenbroek         m_os << "tp-end: " << generate_timestamp() << ", " << m_tpname
608*00b67f09SDavid van Moolenbroek              << ", " << reason << "\n";
609*00b67f09SDavid van Moolenbroek     m_os.flush();
610*00b67f09SDavid van Moolenbroek }
611*00b67f09SDavid van Moolenbroek 
612*00b67f09SDavid van Moolenbroek void
start_tc(const std::string & tcname)613*00b67f09SDavid van Moolenbroek impl::atf_tps_writer::start_tc(const std::string& tcname)
614*00b67f09SDavid van Moolenbroek {
615*00b67f09SDavid van Moolenbroek     m_tcname = tcname;
616*00b67f09SDavid van Moolenbroek     m_os << "tc-start: " << generate_timestamp() << ", " << tcname << "\n";
617*00b67f09SDavid van Moolenbroek     m_os.flush();
618*00b67f09SDavid van Moolenbroek }
619*00b67f09SDavid van Moolenbroek 
620*00b67f09SDavid van Moolenbroek void
stdout_tc(const std::string & line)621*00b67f09SDavid van Moolenbroek impl::atf_tps_writer::stdout_tc(const std::string& line)
622*00b67f09SDavid van Moolenbroek {
623*00b67f09SDavid van Moolenbroek     m_os << "tc-so:" << line << "\n";
624*00b67f09SDavid van Moolenbroek     check_stream(m_os);
625*00b67f09SDavid van Moolenbroek     m_os.flush();
626*00b67f09SDavid van Moolenbroek     check_stream(m_os);
627*00b67f09SDavid van Moolenbroek }
628*00b67f09SDavid van Moolenbroek 
629*00b67f09SDavid van Moolenbroek void
stderr_tc(const std::string & line)630*00b67f09SDavid van Moolenbroek impl::atf_tps_writer::stderr_tc(const std::string& line)
631*00b67f09SDavid van Moolenbroek {
632*00b67f09SDavid van Moolenbroek     m_os << "tc-se:" << line << "\n";
633*00b67f09SDavid van Moolenbroek     check_stream(m_os);
634*00b67f09SDavid van Moolenbroek     m_os.flush();
635*00b67f09SDavid van Moolenbroek     check_stream(m_os);
636*00b67f09SDavid van Moolenbroek }
637*00b67f09SDavid van Moolenbroek 
638*00b67f09SDavid van Moolenbroek void
end_tc(const std::string & state,const std::string & reason)639*00b67f09SDavid van Moolenbroek impl::atf_tps_writer::end_tc(const std::string& state,
640*00b67f09SDavid van Moolenbroek                              const std::string& reason)
641*00b67f09SDavid van Moolenbroek {
642*00b67f09SDavid van Moolenbroek     std::string str =  ", " + m_tcname + ", " + state;
643*00b67f09SDavid van Moolenbroek     if (!reason.empty())
644*00b67f09SDavid van Moolenbroek         str += ", " + reason;
645*00b67f09SDavid van Moolenbroek     m_os << "tc-end: " << generate_timestamp() << str << "\n";
646*00b67f09SDavid van Moolenbroek     m_os.flush();
647*00b67f09SDavid van Moolenbroek }
648*00b67f09SDavid van Moolenbroek 
649*00b67f09SDavid van Moolenbroek impl::metadata
get_metadata(const atf::fs::path & executable,const atf::tests::vars_map & config)650*00b67f09SDavid van Moolenbroek impl::get_metadata(const atf::fs::path& executable,
651*00b67f09SDavid van Moolenbroek                    const atf::tests::vars_map& config)
652*00b67f09SDavid van Moolenbroek {
653*00b67f09SDavid van Moolenbroek     get_metadata_params params(executable, config);
654*00b67f09SDavid van Moolenbroek     atf::process::child child =
655*00b67f09SDavid van Moolenbroek         atf::process::fork(get_metadata_child,
656*00b67f09SDavid van Moolenbroek                            atf::process::stream_capture(),
657*00b67f09SDavid van Moolenbroek                            atf::process::stream_inherit(),
658*00b67f09SDavid van Moolenbroek                            static_cast< void * >(&params));
659*00b67f09SDavid van Moolenbroek 
660*00b67f09SDavid van Moolenbroek     impl::pistream outin(child.stdout_fd());
661*00b67f09SDavid van Moolenbroek 
662*00b67f09SDavid van Moolenbroek     metadata_reader parser(outin);
663*00b67f09SDavid van Moolenbroek     parser.read();
664*00b67f09SDavid van Moolenbroek 
665*00b67f09SDavid van Moolenbroek     const atf::process::status status = child.wait();
666*00b67f09SDavid van Moolenbroek     if (!status.exited() || status.exitstatus() != EXIT_SUCCESS)
667*00b67f09SDavid van Moolenbroek         throw atf::parser::format_error("Test program returned failure "
668*00b67f09SDavid van Moolenbroek                                         "exit status for test case list");
669*00b67f09SDavid van Moolenbroek 
670*00b67f09SDavid van Moolenbroek     return metadata(parser.get_tcs());
671*00b67f09SDavid van Moolenbroek }
672*00b67f09SDavid van Moolenbroek 
673*00b67f09SDavid van Moolenbroek impl::test_case_result
read_test_case_result(const atf::fs::path & results_path)674*00b67f09SDavid van Moolenbroek impl::read_test_case_result(const atf::fs::path& results_path)
675*00b67f09SDavid van Moolenbroek {
676*00b67f09SDavid van Moolenbroek     std::ifstream results_file(results_path.c_str());
677*00b67f09SDavid van Moolenbroek     if (!results_file)
678*00b67f09SDavid van Moolenbroek         throw std::runtime_error("Failed to open " + results_path.str());
679*00b67f09SDavid van Moolenbroek 
680*00b67f09SDavid van Moolenbroek     std::string line, extra_line;
681*00b67f09SDavid van Moolenbroek     std::getline(results_file, line);
682*00b67f09SDavid van Moolenbroek     if (!results_file.good())
683*00b67f09SDavid van Moolenbroek         throw std::runtime_error("Results file is empty");
684*00b67f09SDavid van Moolenbroek 
685*00b67f09SDavid van Moolenbroek     while (std::getline(results_file, extra_line).good())
686*00b67f09SDavid van Moolenbroek         line += "<<NEWLINE UNEXPECTED>>" + extra_line;
687*00b67f09SDavid van Moolenbroek 
688*00b67f09SDavid van Moolenbroek     results_file.close();
689*00b67f09SDavid van Moolenbroek 
690*00b67f09SDavid van Moolenbroek     return detail::parse_test_case_result(line);
691*00b67f09SDavid van Moolenbroek }
692*00b67f09SDavid van Moolenbroek 
693*00b67f09SDavid van Moolenbroek namespace {
694*00b67f09SDavid van Moolenbroek 
695*00b67f09SDavid van Moolenbroek static volatile bool terminate_poll;
696*00b67f09SDavid van Moolenbroek 
697*00b67f09SDavid van Moolenbroek static void
sigchld_handler(const int signo ATF_DEFS_ATTRIBUTE_UNUSED)698*00b67f09SDavid van Moolenbroek sigchld_handler(const int signo ATF_DEFS_ATTRIBUTE_UNUSED)
699*00b67f09SDavid van Moolenbroek {
700*00b67f09SDavid van Moolenbroek     terminate_poll = true;
701*00b67f09SDavid van Moolenbroek }
702*00b67f09SDavid van Moolenbroek 
703*00b67f09SDavid van Moolenbroek class child_muxer : public impl::muxer {
704*00b67f09SDavid van Moolenbroek     impl::atf_tps_writer& m_writer;
705*00b67f09SDavid van Moolenbroek 
706*00b67f09SDavid van Moolenbroek     void
line_callback(const size_t index,const std::string & line)707*00b67f09SDavid van Moolenbroek     line_callback(const size_t index, const std::string& line)
708*00b67f09SDavid van Moolenbroek     {
709*00b67f09SDavid van Moolenbroek         switch (index) {
710*00b67f09SDavid van Moolenbroek         case 0: m_writer.stdout_tc(line); break;
711*00b67f09SDavid van Moolenbroek         case 1: m_writer.stderr_tc(line); break;
712*00b67f09SDavid van Moolenbroek         default: UNREACHABLE;
713*00b67f09SDavid van Moolenbroek         }
714*00b67f09SDavid van Moolenbroek     }
715*00b67f09SDavid van Moolenbroek 
716*00b67f09SDavid van Moolenbroek public:
child_muxer(const int * fds,const size_t nfds,impl::atf_tps_writer & writer)717*00b67f09SDavid van Moolenbroek     child_muxer(const int* fds, const size_t nfds,
718*00b67f09SDavid van Moolenbroek                 impl::atf_tps_writer& writer) :
719*00b67f09SDavid van Moolenbroek         muxer(fds, nfds),
720*00b67f09SDavid van Moolenbroek         m_writer(writer)
721*00b67f09SDavid van Moolenbroek     {
722*00b67f09SDavid van Moolenbroek     }
723*00b67f09SDavid van Moolenbroek };
724*00b67f09SDavid van Moolenbroek 
725*00b67f09SDavid van Moolenbroek } // anonymous namespace
726*00b67f09SDavid van Moolenbroek 
727*00b67f09SDavid van Moolenbroek std::pair< std::string, atf::process::status >
run_test_case(const atf::fs::path & executable,const std::string & test_case_name,const std::string & test_case_part,const atf::tests::vars_map & metadata,const atf::tests::vars_map & config,const atf::fs::path & resfile,const atf::fs::path & workdir,atf_tps_writer & writer)728*00b67f09SDavid van Moolenbroek impl::run_test_case(const atf::fs::path& executable,
729*00b67f09SDavid van Moolenbroek                     const std::string& test_case_name,
730*00b67f09SDavid van Moolenbroek                     const std::string& test_case_part,
731*00b67f09SDavid van Moolenbroek                     const atf::tests::vars_map& metadata,
732*00b67f09SDavid van Moolenbroek                     const atf::tests::vars_map& config,
733*00b67f09SDavid van Moolenbroek                     const atf::fs::path& resfile,
734*00b67f09SDavid van Moolenbroek                     const atf::fs::path& workdir,
735*00b67f09SDavid van Moolenbroek                     atf_tps_writer& writer)
736*00b67f09SDavid van Moolenbroek {
737*00b67f09SDavid van Moolenbroek     // TODO: Capture termination signals and deliver them to the subprocess
738*00b67f09SDavid van Moolenbroek     // instead.  Or maybe do something else; think about it.
739*00b67f09SDavid van Moolenbroek 
740*00b67f09SDavid van Moolenbroek     test_case_params params(executable, test_case_name, test_case_part,
741*00b67f09SDavid van Moolenbroek                             metadata, config, resfile, workdir);
742*00b67f09SDavid van Moolenbroek     atf::process::child child =
743*00b67f09SDavid van Moolenbroek         atf::process::fork(run_test_case_child,
744*00b67f09SDavid van Moolenbroek                            atf::process::stream_capture(),
745*00b67f09SDavid van Moolenbroek                            atf::process::stream_capture(),
746*00b67f09SDavid van Moolenbroek                            static_cast< void * >(&params));
747*00b67f09SDavid van Moolenbroek 
748*00b67f09SDavid van Moolenbroek     terminate_poll = false;
749*00b67f09SDavid van Moolenbroek 
750*00b67f09SDavid van Moolenbroek     const atf::tests::vars_map::const_iterator iter = metadata.find("timeout");
751*00b67f09SDavid van Moolenbroek     INV(iter != metadata.end());
752*00b67f09SDavid van Moolenbroek     const unsigned int timeout =
753*00b67f09SDavid van Moolenbroek         atf::text::to_type< unsigned int >((*iter).second);
754*00b67f09SDavid van Moolenbroek     const pid_t child_pid = child.pid();
755*00b67f09SDavid van Moolenbroek 
756*00b67f09SDavid van Moolenbroek     // Get the input stream of stdout and stderr.
757*00b67f09SDavid van Moolenbroek     impl::file_handle outfh = child.stdout_fd();
758*00b67f09SDavid van Moolenbroek     impl::file_handle errfh = child.stderr_fd();
759*00b67f09SDavid van Moolenbroek 
760*00b67f09SDavid van Moolenbroek     bool timed_out = false;
761*00b67f09SDavid van Moolenbroek 
762*00b67f09SDavid van Moolenbroek     // Process the test case's output and multiplex it into our output
763*00b67f09SDavid van Moolenbroek     // stream as we read it.
764*00b67f09SDavid van Moolenbroek     int fds[2] = {outfh.get(), errfh.get()};
765*00b67f09SDavid van Moolenbroek     child_muxer mux(fds, 2, writer);
766*00b67f09SDavid van Moolenbroek     try {
767*00b67f09SDavid van Moolenbroek         child_timer timeout_timer(timeout, child_pid, terminate_poll);
768*00b67f09SDavid van Moolenbroek         signal_programmer sigchld(SIGCHLD, sigchld_handler);
769*00b67f09SDavid van Moolenbroek         mux.mux(terminate_poll);
770*00b67f09SDavid van Moolenbroek         timed_out = timeout_timer.fired();
771*00b67f09SDavid van Moolenbroek     } catch (...) {
772*00b67f09SDavid van Moolenbroek         UNREACHABLE;
773*00b67f09SDavid van Moolenbroek     }
774*00b67f09SDavid van Moolenbroek 
775*00b67f09SDavid van Moolenbroek     ::killpg(child_pid, SIGKILL);
776*00b67f09SDavid van Moolenbroek     mux.flush();
777*00b67f09SDavid van Moolenbroek     atf::process::status status = child.wait();
778*00b67f09SDavid van Moolenbroek 
779*00b67f09SDavid van Moolenbroek     std::string reason;
780*00b67f09SDavid van Moolenbroek 
781*00b67f09SDavid van Moolenbroek     if (timed_out) {
782*00b67f09SDavid van Moolenbroek         // Don't assume the child process has been signaled due to the timeout
783*00b67f09SDavid van Moolenbroek         // expiration as older versions did.  The child process may have exited
784*00b67f09SDavid van Moolenbroek         // but we may have timed out due to a subchild process getting stuck.
785*00b67f09SDavid van Moolenbroek         reason = "Test case timed out after " + atf::text::to_string(timeout) +
786*00b67f09SDavid van Moolenbroek             " " + (timeout == 1 ? "second" : "seconds");
787*00b67f09SDavid van Moolenbroek     }
788*00b67f09SDavid van Moolenbroek 
789*00b67f09SDavid van Moolenbroek     return std::make_pair(reason, status);
790*00b67f09SDavid van Moolenbroek }
791