xref: /freebsd/contrib/atf/atf-c++/tests.cpp (revision c243e4902be8df1e643c76b5f18b68bb77cc5268)
1*c243e490SMarcel Moolenaar //
2*c243e490SMarcel Moolenaar // Automated Testing Framework (atf)
3*c243e490SMarcel Moolenaar //
4*c243e490SMarcel Moolenaar // Copyright (c) 2007 The NetBSD Foundation, Inc.
5*c243e490SMarcel Moolenaar // All rights reserved.
6*c243e490SMarcel Moolenaar //
7*c243e490SMarcel Moolenaar // Redistribution and use in source and binary forms, with or without
8*c243e490SMarcel Moolenaar // modification, are permitted provided that the following conditions
9*c243e490SMarcel Moolenaar // are met:
10*c243e490SMarcel Moolenaar // 1. Redistributions of source code must retain the above copyright
11*c243e490SMarcel Moolenaar //    notice, this list of conditions and the following disclaimer.
12*c243e490SMarcel Moolenaar // 2. Redistributions in binary form must reproduce the above copyright
13*c243e490SMarcel Moolenaar //    notice, this list of conditions and the following disclaimer in the
14*c243e490SMarcel Moolenaar //    documentation and/or other materials provided with the distribution.
15*c243e490SMarcel Moolenaar //
16*c243e490SMarcel Moolenaar // THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
17*c243e490SMarcel Moolenaar // CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
18*c243e490SMarcel Moolenaar // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19*c243e490SMarcel Moolenaar // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20*c243e490SMarcel Moolenaar // IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
21*c243e490SMarcel Moolenaar // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22*c243e490SMarcel Moolenaar // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23*c243e490SMarcel Moolenaar // GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24*c243e490SMarcel Moolenaar // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25*c243e490SMarcel Moolenaar // IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26*c243e490SMarcel Moolenaar // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27*c243e490SMarcel Moolenaar // IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28*c243e490SMarcel Moolenaar //
29*c243e490SMarcel Moolenaar 
30*c243e490SMarcel Moolenaar extern "C" {
31*c243e490SMarcel Moolenaar #include <sys/types.h>
32*c243e490SMarcel Moolenaar #include <sys/stat.h>
33*c243e490SMarcel Moolenaar #include <sys/time.h>
34*c243e490SMarcel Moolenaar #include <sys/wait.h>
35*c243e490SMarcel Moolenaar #include <signal.h>
36*c243e490SMarcel Moolenaar #include <unistd.h>
37*c243e490SMarcel Moolenaar }
38*c243e490SMarcel Moolenaar 
39*c243e490SMarcel Moolenaar #include <algorithm>
40*c243e490SMarcel Moolenaar #include <cctype>
41*c243e490SMarcel Moolenaar #include <cerrno>
42*c243e490SMarcel Moolenaar #include <cstdlib>
43*c243e490SMarcel Moolenaar #include <cstring>
44*c243e490SMarcel Moolenaar #include <fstream>
45*c243e490SMarcel Moolenaar #include <iostream>
46*c243e490SMarcel Moolenaar #include <map>
47*c243e490SMarcel Moolenaar #include <memory>
48*c243e490SMarcel Moolenaar #include <sstream>
49*c243e490SMarcel Moolenaar #include <stdexcept>
50*c243e490SMarcel Moolenaar #include <vector>
51*c243e490SMarcel Moolenaar 
52*c243e490SMarcel Moolenaar extern "C" {
53*c243e490SMarcel Moolenaar #include "atf-c/error.h"
54*c243e490SMarcel Moolenaar #include "atf-c/tc.h"
55*c243e490SMarcel Moolenaar #include "atf-c/utils.h"
56*c243e490SMarcel Moolenaar }
57*c243e490SMarcel Moolenaar 
58*c243e490SMarcel Moolenaar #include "tests.hpp"
59*c243e490SMarcel Moolenaar 
60*c243e490SMarcel Moolenaar #include "detail/application.hpp"
61*c243e490SMarcel Moolenaar #include "detail/env.hpp"
62*c243e490SMarcel Moolenaar #include "detail/exceptions.hpp"
63*c243e490SMarcel Moolenaar #include "detail/fs.hpp"
64*c243e490SMarcel Moolenaar #include "detail/parser.hpp"
65*c243e490SMarcel Moolenaar #include "detail/sanity.hpp"
66*c243e490SMarcel Moolenaar #include "detail/text.hpp"
67*c243e490SMarcel Moolenaar 
68*c243e490SMarcel Moolenaar namespace impl = atf::tests;
69*c243e490SMarcel Moolenaar namespace detail = atf::tests::detail;
70*c243e490SMarcel Moolenaar #define IMPL_NAME "atf::tests"
71*c243e490SMarcel Moolenaar 
72*c243e490SMarcel Moolenaar // ------------------------------------------------------------------------
73*c243e490SMarcel Moolenaar // The "atf_tp_writer" class.
74*c243e490SMarcel Moolenaar // ------------------------------------------------------------------------
75*c243e490SMarcel Moolenaar 
76*c243e490SMarcel Moolenaar detail::atf_tp_writer::atf_tp_writer(std::ostream& os) :
77*c243e490SMarcel Moolenaar     m_os(os),
78*c243e490SMarcel Moolenaar     m_is_first(true)
79*c243e490SMarcel Moolenaar {
80*c243e490SMarcel Moolenaar     atf::parser::headers_map hm;
81*c243e490SMarcel Moolenaar     atf::parser::attrs_map ct_attrs;
82*c243e490SMarcel Moolenaar     ct_attrs["version"] = "1";
83*c243e490SMarcel Moolenaar     hm["Content-Type"] = atf::parser::header_entry("Content-Type",
84*c243e490SMarcel Moolenaar         "application/X-atf-tp", ct_attrs);
85*c243e490SMarcel Moolenaar     atf::parser::write_headers(hm, m_os);
86*c243e490SMarcel Moolenaar }
87*c243e490SMarcel Moolenaar 
88*c243e490SMarcel Moolenaar void
89*c243e490SMarcel Moolenaar detail::atf_tp_writer::start_tc(const std::string& ident)
90*c243e490SMarcel Moolenaar {
91*c243e490SMarcel Moolenaar     if (!m_is_first)
92*c243e490SMarcel Moolenaar         m_os << "\n";
93*c243e490SMarcel Moolenaar     m_os << "ident: " << ident << "\n";
94*c243e490SMarcel Moolenaar     m_os.flush();
95*c243e490SMarcel Moolenaar }
96*c243e490SMarcel Moolenaar 
97*c243e490SMarcel Moolenaar void
98*c243e490SMarcel Moolenaar detail::atf_tp_writer::end_tc(void)
99*c243e490SMarcel Moolenaar {
100*c243e490SMarcel Moolenaar     if (m_is_first)
101*c243e490SMarcel Moolenaar         m_is_first = false;
102*c243e490SMarcel Moolenaar }
103*c243e490SMarcel Moolenaar 
104*c243e490SMarcel Moolenaar void
105*c243e490SMarcel Moolenaar detail::atf_tp_writer::tc_meta_data(const std::string& name,
106*c243e490SMarcel Moolenaar                                     const std::string& value)
107*c243e490SMarcel Moolenaar {
108*c243e490SMarcel Moolenaar     PRE(name != "ident");
109*c243e490SMarcel Moolenaar     m_os << name << ": " << value << "\n";
110*c243e490SMarcel Moolenaar     m_os.flush();
111*c243e490SMarcel Moolenaar }
112*c243e490SMarcel Moolenaar 
113*c243e490SMarcel Moolenaar // ------------------------------------------------------------------------
114*c243e490SMarcel Moolenaar // Free helper functions.
115*c243e490SMarcel Moolenaar // ------------------------------------------------------------------------
116*c243e490SMarcel Moolenaar 
117*c243e490SMarcel Moolenaar bool
118*c243e490SMarcel Moolenaar detail::match(const std::string& regexp, const std::string& str)
119*c243e490SMarcel Moolenaar {
120*c243e490SMarcel Moolenaar     return atf::text::match(str, regexp);
121*c243e490SMarcel Moolenaar }
122*c243e490SMarcel Moolenaar 
123*c243e490SMarcel Moolenaar // ------------------------------------------------------------------------
124*c243e490SMarcel Moolenaar // The "tc" class.
125*c243e490SMarcel Moolenaar // ------------------------------------------------------------------------
126*c243e490SMarcel Moolenaar 
127*c243e490SMarcel Moolenaar static std::map< atf_tc_t*, impl::tc* > wraps;
128*c243e490SMarcel Moolenaar static std::map< const atf_tc_t*, const impl::tc* > cwraps;
129*c243e490SMarcel Moolenaar 
130*c243e490SMarcel Moolenaar struct impl::tc_impl : atf::utils::noncopyable {
131*c243e490SMarcel Moolenaar     std::string m_ident;
132*c243e490SMarcel Moolenaar     atf_tc_t m_tc;
133*c243e490SMarcel Moolenaar     bool m_has_cleanup;
134*c243e490SMarcel Moolenaar 
135*c243e490SMarcel Moolenaar     tc_impl(const std::string& ident, const bool has_cleanup) :
136*c243e490SMarcel Moolenaar         m_ident(ident),
137*c243e490SMarcel Moolenaar         m_has_cleanup(has_cleanup)
138*c243e490SMarcel Moolenaar     {
139*c243e490SMarcel Moolenaar     }
140*c243e490SMarcel Moolenaar 
141*c243e490SMarcel Moolenaar     static void
142*c243e490SMarcel Moolenaar     wrap_head(atf_tc_t *tc)
143*c243e490SMarcel Moolenaar     {
144*c243e490SMarcel Moolenaar         std::map< atf_tc_t*, impl::tc* >::iterator iter = wraps.find(tc);
145*c243e490SMarcel Moolenaar         INV(iter != wraps.end());
146*c243e490SMarcel Moolenaar         (*iter).second->head();
147*c243e490SMarcel Moolenaar     }
148*c243e490SMarcel Moolenaar 
149*c243e490SMarcel Moolenaar     static void
150*c243e490SMarcel Moolenaar     wrap_body(const atf_tc_t *tc)
151*c243e490SMarcel Moolenaar     {
152*c243e490SMarcel Moolenaar         std::map< const atf_tc_t*, const impl::tc* >::const_iterator iter =
153*c243e490SMarcel Moolenaar             cwraps.find(tc);
154*c243e490SMarcel Moolenaar         INV(iter != cwraps.end());
155*c243e490SMarcel Moolenaar         try {
156*c243e490SMarcel Moolenaar             (*iter).second->body();
157*c243e490SMarcel Moolenaar         } catch (const std::exception& e) {
158*c243e490SMarcel Moolenaar             (*iter).second->fail("Caught unhandled exception: " + std::string(
159*c243e490SMarcel Moolenaar                                      e.what()));
160*c243e490SMarcel Moolenaar         } catch (...) {
161*c243e490SMarcel Moolenaar             (*iter).second->fail("Caught unknown exception");
162*c243e490SMarcel Moolenaar         }
163*c243e490SMarcel Moolenaar     }
164*c243e490SMarcel Moolenaar 
165*c243e490SMarcel Moolenaar     static void
166*c243e490SMarcel Moolenaar     wrap_cleanup(const atf_tc_t *tc)
167*c243e490SMarcel Moolenaar     {
168*c243e490SMarcel Moolenaar         std::map< const atf_tc_t*, const impl::tc* >::const_iterator iter =
169*c243e490SMarcel Moolenaar             cwraps.find(tc);
170*c243e490SMarcel Moolenaar         INV(iter != cwraps.end());
171*c243e490SMarcel Moolenaar         (*iter).second->cleanup();
172*c243e490SMarcel Moolenaar     }
173*c243e490SMarcel Moolenaar };
174*c243e490SMarcel Moolenaar 
175*c243e490SMarcel Moolenaar impl::tc::tc(const std::string& ident, const bool has_cleanup) :
176*c243e490SMarcel Moolenaar     pimpl(new tc_impl(ident, has_cleanup))
177*c243e490SMarcel Moolenaar {
178*c243e490SMarcel Moolenaar }
179*c243e490SMarcel Moolenaar 
180*c243e490SMarcel Moolenaar impl::tc::~tc(void)
181*c243e490SMarcel Moolenaar {
182*c243e490SMarcel Moolenaar     cwraps.erase(&pimpl->m_tc);
183*c243e490SMarcel Moolenaar     wraps.erase(&pimpl->m_tc);
184*c243e490SMarcel Moolenaar 
185*c243e490SMarcel Moolenaar     atf_tc_fini(&pimpl->m_tc);
186*c243e490SMarcel Moolenaar }
187*c243e490SMarcel Moolenaar 
188*c243e490SMarcel Moolenaar void
189*c243e490SMarcel Moolenaar impl::tc::init(const vars_map& config)
190*c243e490SMarcel Moolenaar {
191*c243e490SMarcel Moolenaar     atf_error_t err;
192*c243e490SMarcel Moolenaar 
193*c243e490SMarcel Moolenaar     utils::auto_array< const char * > array(
194*c243e490SMarcel Moolenaar         new const char*[(config.size() * 2) + 1]);
195*c243e490SMarcel Moolenaar     const char **ptr = array.get();
196*c243e490SMarcel Moolenaar     for (vars_map::const_iterator iter = config.begin();
197*c243e490SMarcel Moolenaar          iter != config.end(); iter++) {
198*c243e490SMarcel Moolenaar          *ptr = (*iter).first.c_str();
199*c243e490SMarcel Moolenaar          *(ptr + 1) = (*iter).second.c_str();
200*c243e490SMarcel Moolenaar          ptr += 2;
201*c243e490SMarcel Moolenaar     }
202*c243e490SMarcel Moolenaar     *ptr = NULL;
203*c243e490SMarcel Moolenaar 
204*c243e490SMarcel Moolenaar     wraps[&pimpl->m_tc] = this;
205*c243e490SMarcel Moolenaar     cwraps[&pimpl->m_tc] = this;
206*c243e490SMarcel Moolenaar 
207*c243e490SMarcel Moolenaar     err = atf_tc_init(&pimpl->m_tc, pimpl->m_ident.c_str(), pimpl->wrap_head,
208*c243e490SMarcel Moolenaar         pimpl->wrap_body, pimpl->m_has_cleanup ? pimpl->wrap_cleanup : NULL,
209*c243e490SMarcel Moolenaar         array.get());
210*c243e490SMarcel Moolenaar     if (atf_is_error(err))
211*c243e490SMarcel Moolenaar         throw_atf_error(err);
212*c243e490SMarcel Moolenaar }
213*c243e490SMarcel Moolenaar 
214*c243e490SMarcel Moolenaar bool
215*c243e490SMarcel Moolenaar impl::tc::has_config_var(const std::string& var)
216*c243e490SMarcel Moolenaar     const
217*c243e490SMarcel Moolenaar {
218*c243e490SMarcel Moolenaar     return atf_tc_has_config_var(&pimpl->m_tc, var.c_str());
219*c243e490SMarcel Moolenaar }
220*c243e490SMarcel Moolenaar 
221*c243e490SMarcel Moolenaar bool
222*c243e490SMarcel Moolenaar impl::tc::has_md_var(const std::string& var)
223*c243e490SMarcel Moolenaar     const
224*c243e490SMarcel Moolenaar {
225*c243e490SMarcel Moolenaar     return atf_tc_has_md_var(&pimpl->m_tc, var.c_str());
226*c243e490SMarcel Moolenaar }
227*c243e490SMarcel Moolenaar 
228*c243e490SMarcel Moolenaar const std::string
229*c243e490SMarcel Moolenaar impl::tc::get_config_var(const std::string& var)
230*c243e490SMarcel Moolenaar     const
231*c243e490SMarcel Moolenaar {
232*c243e490SMarcel Moolenaar     return atf_tc_get_config_var(&pimpl->m_tc, var.c_str());
233*c243e490SMarcel Moolenaar }
234*c243e490SMarcel Moolenaar 
235*c243e490SMarcel Moolenaar const std::string
236*c243e490SMarcel Moolenaar impl::tc::get_config_var(const std::string& var, const std::string& defval)
237*c243e490SMarcel Moolenaar     const
238*c243e490SMarcel Moolenaar {
239*c243e490SMarcel Moolenaar     return atf_tc_get_config_var_wd(&pimpl->m_tc, var.c_str(), defval.c_str());
240*c243e490SMarcel Moolenaar }
241*c243e490SMarcel Moolenaar 
242*c243e490SMarcel Moolenaar const std::string
243*c243e490SMarcel Moolenaar impl::tc::get_md_var(const std::string& var)
244*c243e490SMarcel Moolenaar     const
245*c243e490SMarcel Moolenaar {
246*c243e490SMarcel Moolenaar     return atf_tc_get_md_var(&pimpl->m_tc, var.c_str());
247*c243e490SMarcel Moolenaar }
248*c243e490SMarcel Moolenaar 
249*c243e490SMarcel Moolenaar const impl::vars_map
250*c243e490SMarcel Moolenaar impl::tc::get_md_vars(void)
251*c243e490SMarcel Moolenaar     const
252*c243e490SMarcel Moolenaar {
253*c243e490SMarcel Moolenaar     vars_map vars;
254*c243e490SMarcel Moolenaar 
255*c243e490SMarcel Moolenaar     char **array = atf_tc_get_md_vars(&pimpl->m_tc);
256*c243e490SMarcel Moolenaar     try {
257*c243e490SMarcel Moolenaar         char **ptr;
258*c243e490SMarcel Moolenaar         for (ptr = array; *ptr != NULL; ptr += 2)
259*c243e490SMarcel Moolenaar             vars[*ptr] = *(ptr + 1);
260*c243e490SMarcel Moolenaar     } catch (...) {
261*c243e490SMarcel Moolenaar         atf_utils_free_charpp(array);
262*c243e490SMarcel Moolenaar         throw;
263*c243e490SMarcel Moolenaar     }
264*c243e490SMarcel Moolenaar 
265*c243e490SMarcel Moolenaar     return vars;
266*c243e490SMarcel Moolenaar }
267*c243e490SMarcel Moolenaar 
268*c243e490SMarcel Moolenaar void
269*c243e490SMarcel Moolenaar impl::tc::set_md_var(const std::string& var, const std::string& val)
270*c243e490SMarcel Moolenaar {
271*c243e490SMarcel Moolenaar     atf_error_t err = atf_tc_set_md_var(&pimpl->m_tc, var.c_str(), val.c_str());
272*c243e490SMarcel Moolenaar     if (atf_is_error(err))
273*c243e490SMarcel Moolenaar         throw_atf_error(err);
274*c243e490SMarcel Moolenaar }
275*c243e490SMarcel Moolenaar 
276*c243e490SMarcel Moolenaar void
277*c243e490SMarcel Moolenaar impl::tc::run(const std::string& resfile)
278*c243e490SMarcel Moolenaar     const
279*c243e490SMarcel Moolenaar {
280*c243e490SMarcel Moolenaar     atf_error_t err = atf_tc_run(&pimpl->m_tc, resfile.c_str());
281*c243e490SMarcel Moolenaar     if (atf_is_error(err))
282*c243e490SMarcel Moolenaar         throw_atf_error(err);
283*c243e490SMarcel Moolenaar }
284*c243e490SMarcel Moolenaar 
285*c243e490SMarcel Moolenaar void
286*c243e490SMarcel Moolenaar impl::tc::run_cleanup(void)
287*c243e490SMarcel Moolenaar     const
288*c243e490SMarcel Moolenaar {
289*c243e490SMarcel Moolenaar     atf_error_t err = atf_tc_cleanup(&pimpl->m_tc);
290*c243e490SMarcel Moolenaar     if (atf_is_error(err))
291*c243e490SMarcel Moolenaar         throw_atf_error(err);
292*c243e490SMarcel Moolenaar }
293*c243e490SMarcel Moolenaar 
294*c243e490SMarcel Moolenaar void
295*c243e490SMarcel Moolenaar impl::tc::head(void)
296*c243e490SMarcel Moolenaar {
297*c243e490SMarcel Moolenaar }
298*c243e490SMarcel Moolenaar 
299*c243e490SMarcel Moolenaar void
300*c243e490SMarcel Moolenaar impl::tc::cleanup(void)
301*c243e490SMarcel Moolenaar     const
302*c243e490SMarcel Moolenaar {
303*c243e490SMarcel Moolenaar }
304*c243e490SMarcel Moolenaar 
305*c243e490SMarcel Moolenaar void
306*c243e490SMarcel Moolenaar impl::tc::require_prog(const std::string& prog)
307*c243e490SMarcel Moolenaar     const
308*c243e490SMarcel Moolenaar {
309*c243e490SMarcel Moolenaar     atf_tc_require_prog(prog.c_str());
310*c243e490SMarcel Moolenaar }
311*c243e490SMarcel Moolenaar 
312*c243e490SMarcel Moolenaar void
313*c243e490SMarcel Moolenaar impl::tc::pass(void)
314*c243e490SMarcel Moolenaar {
315*c243e490SMarcel Moolenaar     atf_tc_pass();
316*c243e490SMarcel Moolenaar }
317*c243e490SMarcel Moolenaar 
318*c243e490SMarcel Moolenaar void
319*c243e490SMarcel Moolenaar impl::tc::fail(const std::string& reason)
320*c243e490SMarcel Moolenaar {
321*c243e490SMarcel Moolenaar     atf_tc_fail("%s", reason.c_str());
322*c243e490SMarcel Moolenaar }
323*c243e490SMarcel Moolenaar 
324*c243e490SMarcel Moolenaar void
325*c243e490SMarcel Moolenaar impl::tc::fail_nonfatal(const std::string& reason)
326*c243e490SMarcel Moolenaar {
327*c243e490SMarcel Moolenaar     atf_tc_fail_nonfatal("%s", reason.c_str());
328*c243e490SMarcel Moolenaar }
329*c243e490SMarcel Moolenaar 
330*c243e490SMarcel Moolenaar void
331*c243e490SMarcel Moolenaar impl::tc::skip(const std::string& reason)
332*c243e490SMarcel Moolenaar {
333*c243e490SMarcel Moolenaar     atf_tc_skip("%s", reason.c_str());
334*c243e490SMarcel Moolenaar }
335*c243e490SMarcel Moolenaar 
336*c243e490SMarcel Moolenaar void
337*c243e490SMarcel Moolenaar impl::tc::check_errno(const char* file, const int line, const int exp_errno,
338*c243e490SMarcel Moolenaar                       const char* expr_str, const bool result)
339*c243e490SMarcel Moolenaar {
340*c243e490SMarcel Moolenaar     atf_tc_check_errno(file, line, exp_errno, expr_str, result);
341*c243e490SMarcel Moolenaar }
342*c243e490SMarcel Moolenaar 
343*c243e490SMarcel Moolenaar void
344*c243e490SMarcel Moolenaar impl::tc::require_errno(const char* file, const int line, const int exp_errno,
345*c243e490SMarcel Moolenaar                         const char* expr_str, const bool result)
346*c243e490SMarcel Moolenaar {
347*c243e490SMarcel Moolenaar     atf_tc_require_errno(file, line, exp_errno, expr_str, result);
348*c243e490SMarcel Moolenaar }
349*c243e490SMarcel Moolenaar 
350*c243e490SMarcel Moolenaar void
351*c243e490SMarcel Moolenaar impl::tc::expect_pass(void)
352*c243e490SMarcel Moolenaar {
353*c243e490SMarcel Moolenaar     atf_tc_expect_pass();
354*c243e490SMarcel Moolenaar }
355*c243e490SMarcel Moolenaar 
356*c243e490SMarcel Moolenaar void
357*c243e490SMarcel Moolenaar impl::tc::expect_fail(const std::string& reason)
358*c243e490SMarcel Moolenaar {
359*c243e490SMarcel Moolenaar     atf_tc_expect_fail("%s", reason.c_str());
360*c243e490SMarcel Moolenaar }
361*c243e490SMarcel Moolenaar 
362*c243e490SMarcel Moolenaar void
363*c243e490SMarcel Moolenaar impl::tc::expect_exit(const int exitcode, const std::string& reason)
364*c243e490SMarcel Moolenaar {
365*c243e490SMarcel Moolenaar     atf_tc_expect_exit(exitcode, "%s", reason.c_str());
366*c243e490SMarcel Moolenaar }
367*c243e490SMarcel Moolenaar 
368*c243e490SMarcel Moolenaar void
369*c243e490SMarcel Moolenaar impl::tc::expect_signal(const int signo, const std::string& reason)
370*c243e490SMarcel Moolenaar {
371*c243e490SMarcel Moolenaar     atf_tc_expect_signal(signo, "%s", reason.c_str());
372*c243e490SMarcel Moolenaar }
373*c243e490SMarcel Moolenaar 
374*c243e490SMarcel Moolenaar void
375*c243e490SMarcel Moolenaar impl::tc::expect_death(const std::string& reason)
376*c243e490SMarcel Moolenaar {
377*c243e490SMarcel Moolenaar     atf_tc_expect_death("%s", reason.c_str());
378*c243e490SMarcel Moolenaar }
379*c243e490SMarcel Moolenaar 
380*c243e490SMarcel Moolenaar void
381*c243e490SMarcel Moolenaar impl::tc::expect_timeout(const std::string& reason)
382*c243e490SMarcel Moolenaar {
383*c243e490SMarcel Moolenaar     atf_tc_expect_timeout("%s", reason.c_str());
384*c243e490SMarcel Moolenaar }
385*c243e490SMarcel Moolenaar 
386*c243e490SMarcel Moolenaar // ------------------------------------------------------------------------
387*c243e490SMarcel Moolenaar // The "tp" class.
388*c243e490SMarcel Moolenaar // ------------------------------------------------------------------------
389*c243e490SMarcel Moolenaar 
390*c243e490SMarcel Moolenaar class tp : public atf::application::app {
391*c243e490SMarcel Moolenaar public:
392*c243e490SMarcel Moolenaar     typedef std::vector< impl::tc * > tc_vector;
393*c243e490SMarcel Moolenaar 
394*c243e490SMarcel Moolenaar private:
395*c243e490SMarcel Moolenaar     static const char* m_description;
396*c243e490SMarcel Moolenaar 
397*c243e490SMarcel Moolenaar     bool m_lflag;
398*c243e490SMarcel Moolenaar     atf::fs::path m_resfile;
399*c243e490SMarcel Moolenaar     std::string m_srcdir_arg;
400*c243e490SMarcel Moolenaar     atf::fs::path m_srcdir;
401*c243e490SMarcel Moolenaar 
402*c243e490SMarcel Moolenaar     atf::tests::vars_map m_vars;
403*c243e490SMarcel Moolenaar 
404*c243e490SMarcel Moolenaar     std::string specific_args(void) const;
405*c243e490SMarcel Moolenaar     options_set specific_options(void) const;
406*c243e490SMarcel Moolenaar     void process_option(int, const char*);
407*c243e490SMarcel Moolenaar 
408*c243e490SMarcel Moolenaar     void (*m_add_tcs)(tc_vector&);
409*c243e490SMarcel Moolenaar     tc_vector m_tcs;
410*c243e490SMarcel Moolenaar 
411*c243e490SMarcel Moolenaar     void parse_vflag(const std::string&);
412*c243e490SMarcel Moolenaar     void handle_srcdir(void);
413*c243e490SMarcel Moolenaar 
414*c243e490SMarcel Moolenaar     tc_vector init_tcs(void);
415*c243e490SMarcel Moolenaar 
416*c243e490SMarcel Moolenaar     enum tc_part {
417*c243e490SMarcel Moolenaar         BODY,
418*c243e490SMarcel Moolenaar         CLEANUP,
419*c243e490SMarcel Moolenaar     };
420*c243e490SMarcel Moolenaar 
421*c243e490SMarcel Moolenaar     void list_tcs(void);
422*c243e490SMarcel Moolenaar     impl::tc* find_tc(tc_vector, const std::string&);
423*c243e490SMarcel Moolenaar     static std::pair< std::string, tc_part > process_tcarg(const std::string&);
424*c243e490SMarcel Moolenaar     int run_tc(const std::string&);
425*c243e490SMarcel Moolenaar 
426*c243e490SMarcel Moolenaar public:
427*c243e490SMarcel Moolenaar     tp(void (*)(tc_vector&));
428*c243e490SMarcel Moolenaar     ~tp(void);
429*c243e490SMarcel Moolenaar 
430*c243e490SMarcel Moolenaar     int main(void);
431*c243e490SMarcel Moolenaar };
432*c243e490SMarcel Moolenaar 
433*c243e490SMarcel Moolenaar const char* tp::m_description =
434*c243e490SMarcel Moolenaar     "This is an independent atf test program.";
435*c243e490SMarcel Moolenaar 
436*c243e490SMarcel Moolenaar tp::tp(void (*add_tcs)(tc_vector&)) :
437*c243e490SMarcel Moolenaar     app(m_description, "atf-test-program(1)", "atf(7)", false),
438*c243e490SMarcel Moolenaar     m_lflag(false),
439*c243e490SMarcel Moolenaar     m_resfile("/dev/stdout"),
440*c243e490SMarcel Moolenaar     m_srcdir("."),
441*c243e490SMarcel Moolenaar     m_add_tcs(add_tcs)
442*c243e490SMarcel Moolenaar {
443*c243e490SMarcel Moolenaar }
444*c243e490SMarcel Moolenaar 
445*c243e490SMarcel Moolenaar tp::~tp(void)
446*c243e490SMarcel Moolenaar {
447*c243e490SMarcel Moolenaar     for (tc_vector::iterator iter = m_tcs.begin();
448*c243e490SMarcel Moolenaar          iter != m_tcs.end(); iter++) {
449*c243e490SMarcel Moolenaar         impl::tc* tc = *iter;
450*c243e490SMarcel Moolenaar 
451*c243e490SMarcel Moolenaar         delete tc;
452*c243e490SMarcel Moolenaar     }
453*c243e490SMarcel Moolenaar }
454*c243e490SMarcel Moolenaar 
455*c243e490SMarcel Moolenaar std::string
456*c243e490SMarcel Moolenaar tp::specific_args(void)
457*c243e490SMarcel Moolenaar     const
458*c243e490SMarcel Moolenaar {
459*c243e490SMarcel Moolenaar     return "test_case";
460*c243e490SMarcel Moolenaar }
461*c243e490SMarcel Moolenaar 
462*c243e490SMarcel Moolenaar tp::options_set
463*c243e490SMarcel Moolenaar tp::specific_options(void)
464*c243e490SMarcel Moolenaar     const
465*c243e490SMarcel Moolenaar {
466*c243e490SMarcel Moolenaar     using atf::application::option;
467*c243e490SMarcel Moolenaar     options_set opts;
468*c243e490SMarcel Moolenaar     opts.insert(option('l', "", "List test cases and their purpose"));
469*c243e490SMarcel Moolenaar     opts.insert(option('r', "resfile", "The file to which the test program "
470*c243e490SMarcel Moolenaar                                        "will write the results of the "
471*c243e490SMarcel Moolenaar                                        "executed test case"));
472*c243e490SMarcel Moolenaar     opts.insert(option('s', "srcdir", "Directory where the test's data "
473*c243e490SMarcel Moolenaar                                       "files are located"));
474*c243e490SMarcel Moolenaar     opts.insert(option('v', "var=value", "Sets the configuration variable "
475*c243e490SMarcel Moolenaar                                          "`var' to `value'"));
476*c243e490SMarcel Moolenaar     return opts;
477*c243e490SMarcel Moolenaar }
478*c243e490SMarcel Moolenaar 
479*c243e490SMarcel Moolenaar void
480*c243e490SMarcel Moolenaar tp::process_option(int ch, const char* arg)
481*c243e490SMarcel Moolenaar {
482*c243e490SMarcel Moolenaar     switch (ch) {
483*c243e490SMarcel Moolenaar     case 'l':
484*c243e490SMarcel Moolenaar         m_lflag = true;
485*c243e490SMarcel Moolenaar         break;
486*c243e490SMarcel Moolenaar 
487*c243e490SMarcel Moolenaar     case 'r':
488*c243e490SMarcel Moolenaar         m_resfile = atf::fs::path(arg);
489*c243e490SMarcel Moolenaar         break;
490*c243e490SMarcel Moolenaar 
491*c243e490SMarcel Moolenaar     case 's':
492*c243e490SMarcel Moolenaar         m_srcdir_arg = arg;
493*c243e490SMarcel Moolenaar         break;
494*c243e490SMarcel Moolenaar 
495*c243e490SMarcel Moolenaar     case 'v':
496*c243e490SMarcel Moolenaar         parse_vflag(arg);
497*c243e490SMarcel Moolenaar         break;
498*c243e490SMarcel Moolenaar 
499*c243e490SMarcel Moolenaar     default:
500*c243e490SMarcel Moolenaar         UNREACHABLE;
501*c243e490SMarcel Moolenaar     }
502*c243e490SMarcel Moolenaar }
503*c243e490SMarcel Moolenaar 
504*c243e490SMarcel Moolenaar void
505*c243e490SMarcel Moolenaar tp::parse_vflag(const std::string& str)
506*c243e490SMarcel Moolenaar {
507*c243e490SMarcel Moolenaar     if (str.empty())
508*c243e490SMarcel Moolenaar         throw std::runtime_error("-v requires a non-empty argument");
509*c243e490SMarcel Moolenaar 
510*c243e490SMarcel Moolenaar     std::vector< std::string > ws = atf::text::split(str, "=");
511*c243e490SMarcel Moolenaar     if (ws.size() == 1 && str[str.length() - 1] == '=') {
512*c243e490SMarcel Moolenaar         m_vars[ws[0]] = "";
513*c243e490SMarcel Moolenaar     } else {
514*c243e490SMarcel Moolenaar         if (ws.size() != 2)
515*c243e490SMarcel Moolenaar             throw std::runtime_error("-v requires an argument of the form "
516*c243e490SMarcel Moolenaar                                      "var=value");
517*c243e490SMarcel Moolenaar 
518*c243e490SMarcel Moolenaar         m_vars[ws[0]] = ws[1];
519*c243e490SMarcel Moolenaar     }
520*c243e490SMarcel Moolenaar }
521*c243e490SMarcel Moolenaar 
522*c243e490SMarcel Moolenaar void
523*c243e490SMarcel Moolenaar tp::handle_srcdir(void)
524*c243e490SMarcel Moolenaar {
525*c243e490SMarcel Moolenaar     if (m_srcdir_arg.empty()) {
526*c243e490SMarcel Moolenaar         m_srcdir = atf::fs::path(m_argv0).branch_path();
527*c243e490SMarcel Moolenaar         if (m_srcdir.leaf_name() == ".libs")
528*c243e490SMarcel Moolenaar             m_srcdir = m_srcdir.branch_path();
529*c243e490SMarcel Moolenaar     } else
530*c243e490SMarcel Moolenaar         m_srcdir = atf::fs::path(m_srcdir_arg);
531*c243e490SMarcel Moolenaar 
532*c243e490SMarcel Moolenaar     if (!atf::fs::exists(m_srcdir / m_prog_name))
533*c243e490SMarcel Moolenaar         throw std::runtime_error("Cannot find the test program in the "
534*c243e490SMarcel Moolenaar                                  "source directory `" + m_srcdir.str() + "'");
535*c243e490SMarcel Moolenaar 
536*c243e490SMarcel Moolenaar     if (!m_srcdir.is_absolute())
537*c243e490SMarcel Moolenaar         m_srcdir = m_srcdir.to_absolute();
538*c243e490SMarcel Moolenaar 
539*c243e490SMarcel Moolenaar     m_vars["srcdir"] = m_srcdir.str();
540*c243e490SMarcel Moolenaar }
541*c243e490SMarcel Moolenaar 
542*c243e490SMarcel Moolenaar tp::tc_vector
543*c243e490SMarcel Moolenaar tp::init_tcs(void)
544*c243e490SMarcel Moolenaar {
545*c243e490SMarcel Moolenaar     m_add_tcs(m_tcs);
546*c243e490SMarcel Moolenaar     for (tc_vector::iterator iter = m_tcs.begin();
547*c243e490SMarcel Moolenaar          iter != m_tcs.end(); iter++) {
548*c243e490SMarcel Moolenaar         impl::tc* tc = *iter;
549*c243e490SMarcel Moolenaar 
550*c243e490SMarcel Moolenaar         tc->init(m_vars);
551*c243e490SMarcel Moolenaar     }
552*c243e490SMarcel Moolenaar     return m_tcs;
553*c243e490SMarcel Moolenaar }
554*c243e490SMarcel Moolenaar 
555*c243e490SMarcel Moolenaar //
556*c243e490SMarcel Moolenaar // An auxiliary unary predicate that compares the given test case's
557*c243e490SMarcel Moolenaar // identifier to the identifier stored in it.
558*c243e490SMarcel Moolenaar //
559*c243e490SMarcel Moolenaar class tc_equal_to_ident {
560*c243e490SMarcel Moolenaar     const std::string& m_ident;
561*c243e490SMarcel Moolenaar 
562*c243e490SMarcel Moolenaar public:
563*c243e490SMarcel Moolenaar     tc_equal_to_ident(const std::string& i) :
564*c243e490SMarcel Moolenaar         m_ident(i)
565*c243e490SMarcel Moolenaar     {
566*c243e490SMarcel Moolenaar     }
567*c243e490SMarcel Moolenaar 
568*c243e490SMarcel Moolenaar     bool operator()(const impl::tc* tc)
569*c243e490SMarcel Moolenaar     {
570*c243e490SMarcel Moolenaar         return tc->get_md_var("ident") == m_ident;
571*c243e490SMarcel Moolenaar     }
572*c243e490SMarcel Moolenaar };
573*c243e490SMarcel Moolenaar 
574*c243e490SMarcel Moolenaar void
575*c243e490SMarcel Moolenaar tp::list_tcs(void)
576*c243e490SMarcel Moolenaar {
577*c243e490SMarcel Moolenaar     tc_vector tcs = init_tcs();
578*c243e490SMarcel Moolenaar     detail::atf_tp_writer writer(std::cout);
579*c243e490SMarcel Moolenaar 
580*c243e490SMarcel Moolenaar     for (tc_vector::const_iterator iter = tcs.begin();
581*c243e490SMarcel Moolenaar          iter != tcs.end(); iter++) {
582*c243e490SMarcel Moolenaar         const impl::vars_map vars = (*iter)->get_md_vars();
583*c243e490SMarcel Moolenaar 
584*c243e490SMarcel Moolenaar         {
585*c243e490SMarcel Moolenaar             impl::vars_map::const_iterator iter2 = vars.find("ident");
586*c243e490SMarcel Moolenaar             INV(iter2 != vars.end());
587*c243e490SMarcel Moolenaar             writer.start_tc((*iter2).second);
588*c243e490SMarcel Moolenaar         }
589*c243e490SMarcel Moolenaar 
590*c243e490SMarcel Moolenaar         for (impl::vars_map::const_iterator iter2 = vars.begin();
591*c243e490SMarcel Moolenaar              iter2 != vars.end(); iter2++) {
592*c243e490SMarcel Moolenaar             const std::string& key = (*iter2).first;
593*c243e490SMarcel Moolenaar             if (key != "ident")
594*c243e490SMarcel Moolenaar                 writer.tc_meta_data(key, (*iter2).second);
595*c243e490SMarcel Moolenaar         }
596*c243e490SMarcel Moolenaar 
597*c243e490SMarcel Moolenaar         writer.end_tc();
598*c243e490SMarcel Moolenaar     }
599*c243e490SMarcel Moolenaar }
600*c243e490SMarcel Moolenaar 
601*c243e490SMarcel Moolenaar impl::tc*
602*c243e490SMarcel Moolenaar tp::find_tc(tc_vector tcs, const std::string& name)
603*c243e490SMarcel Moolenaar {
604*c243e490SMarcel Moolenaar     std::vector< std::string > ids;
605*c243e490SMarcel Moolenaar     for (tc_vector::iterator iter = tcs.begin();
606*c243e490SMarcel Moolenaar          iter != tcs.end(); iter++) {
607*c243e490SMarcel Moolenaar         impl::tc* tc = *iter;
608*c243e490SMarcel Moolenaar 
609*c243e490SMarcel Moolenaar         if (tc->get_md_var("ident") == name)
610*c243e490SMarcel Moolenaar             return tc;
611*c243e490SMarcel Moolenaar     }
612*c243e490SMarcel Moolenaar     throw atf::application::usage_error("Unknown test case `%s'",
613*c243e490SMarcel Moolenaar                                         name.c_str());
614*c243e490SMarcel Moolenaar }
615*c243e490SMarcel Moolenaar 
616*c243e490SMarcel Moolenaar std::pair< std::string, tp::tc_part >
617*c243e490SMarcel Moolenaar tp::process_tcarg(const std::string& tcarg)
618*c243e490SMarcel Moolenaar {
619*c243e490SMarcel Moolenaar     const std::string::size_type pos = tcarg.find(':');
620*c243e490SMarcel Moolenaar     if (pos == std::string::npos) {
621*c243e490SMarcel Moolenaar         return std::make_pair(tcarg, BODY);
622*c243e490SMarcel Moolenaar     } else {
623*c243e490SMarcel Moolenaar         const std::string tcname = tcarg.substr(0, pos);
624*c243e490SMarcel Moolenaar 
625*c243e490SMarcel Moolenaar         const std::string partname = tcarg.substr(pos + 1);
626*c243e490SMarcel Moolenaar         if (partname == "body")
627*c243e490SMarcel Moolenaar             return std::make_pair(tcname, BODY);
628*c243e490SMarcel Moolenaar         else if (partname == "cleanup")
629*c243e490SMarcel Moolenaar             return std::make_pair(tcname, CLEANUP);
630*c243e490SMarcel Moolenaar         else {
631*c243e490SMarcel Moolenaar             using atf::application::usage_error;
632*c243e490SMarcel Moolenaar             throw usage_error("Invalid test case part `%s'", partname.c_str());
633*c243e490SMarcel Moolenaar         }
634*c243e490SMarcel Moolenaar     }
635*c243e490SMarcel Moolenaar }
636*c243e490SMarcel Moolenaar 
637*c243e490SMarcel Moolenaar int
638*c243e490SMarcel Moolenaar tp::run_tc(const std::string& tcarg)
639*c243e490SMarcel Moolenaar {
640*c243e490SMarcel Moolenaar     const std::pair< std::string, tc_part > fields = process_tcarg(tcarg);
641*c243e490SMarcel Moolenaar 
642*c243e490SMarcel Moolenaar     impl::tc* tc = find_tc(init_tcs(), fields.first);
643*c243e490SMarcel Moolenaar 
644*c243e490SMarcel Moolenaar     if (!atf::env::has("__RUNNING_INSIDE_ATF_RUN") || atf::env::get(
645*c243e490SMarcel Moolenaar         "__RUNNING_INSIDE_ATF_RUN") != "internal-yes-value")
646*c243e490SMarcel Moolenaar     {
647*c243e490SMarcel Moolenaar         std::cerr << m_prog_name << ": WARNING: Running test cases without "
648*c243e490SMarcel Moolenaar             "atf-run(1) is unsupported\n";
649*c243e490SMarcel Moolenaar         std::cerr << m_prog_name << ": WARNING: No isolation nor timeout "
650*c243e490SMarcel Moolenaar             "control is being applied; you may get unexpected failures; see "
651*c243e490SMarcel Moolenaar             "atf-test-case(4)\n";
652*c243e490SMarcel Moolenaar     }
653*c243e490SMarcel Moolenaar 
654*c243e490SMarcel Moolenaar     try {
655*c243e490SMarcel Moolenaar         switch (fields.second) {
656*c243e490SMarcel Moolenaar         case BODY:
657*c243e490SMarcel Moolenaar             tc->run(m_resfile.str());
658*c243e490SMarcel Moolenaar             break;
659*c243e490SMarcel Moolenaar         case CLEANUP:
660*c243e490SMarcel Moolenaar             tc->run_cleanup();
661*c243e490SMarcel Moolenaar             break;
662*c243e490SMarcel Moolenaar         default:
663*c243e490SMarcel Moolenaar             UNREACHABLE;
664*c243e490SMarcel Moolenaar         }
665*c243e490SMarcel Moolenaar         return EXIT_SUCCESS;
666*c243e490SMarcel Moolenaar     } catch (const std::runtime_error& e) {
667*c243e490SMarcel Moolenaar         std::cerr << "ERROR: " << e.what() << "\n";
668*c243e490SMarcel Moolenaar         return EXIT_FAILURE;
669*c243e490SMarcel Moolenaar     }
670*c243e490SMarcel Moolenaar }
671*c243e490SMarcel Moolenaar 
672*c243e490SMarcel Moolenaar int
673*c243e490SMarcel Moolenaar tp::main(void)
674*c243e490SMarcel Moolenaar {
675*c243e490SMarcel Moolenaar     using atf::application::usage_error;
676*c243e490SMarcel Moolenaar 
677*c243e490SMarcel Moolenaar     int errcode;
678*c243e490SMarcel Moolenaar 
679*c243e490SMarcel Moolenaar     handle_srcdir();
680*c243e490SMarcel Moolenaar 
681*c243e490SMarcel Moolenaar     if (m_lflag) {
682*c243e490SMarcel Moolenaar         if (m_argc > 0)
683*c243e490SMarcel Moolenaar             throw usage_error("Cannot provide test case names with -l");
684*c243e490SMarcel Moolenaar 
685*c243e490SMarcel Moolenaar         list_tcs();
686*c243e490SMarcel Moolenaar         errcode = EXIT_SUCCESS;
687*c243e490SMarcel Moolenaar     } else {
688*c243e490SMarcel Moolenaar         if (m_argc == 0)
689*c243e490SMarcel Moolenaar             throw usage_error("Must provide a test case name");
690*c243e490SMarcel Moolenaar         else if (m_argc > 1)
691*c243e490SMarcel Moolenaar             throw usage_error("Cannot provide more than one test case name");
692*c243e490SMarcel Moolenaar         INV(m_argc == 1);
693*c243e490SMarcel Moolenaar 
694*c243e490SMarcel Moolenaar         errcode = run_tc(m_argv[0]);
695*c243e490SMarcel Moolenaar     }
696*c243e490SMarcel Moolenaar 
697*c243e490SMarcel Moolenaar     return errcode;
698*c243e490SMarcel Moolenaar }
699*c243e490SMarcel Moolenaar 
700*c243e490SMarcel Moolenaar namespace atf {
701*c243e490SMarcel Moolenaar     namespace tests {
702*c243e490SMarcel Moolenaar         int run_tp(int, char* const*, void (*)(tp::tc_vector&));
703*c243e490SMarcel Moolenaar     }
704*c243e490SMarcel Moolenaar }
705*c243e490SMarcel Moolenaar 
706*c243e490SMarcel Moolenaar int
707*c243e490SMarcel Moolenaar impl::run_tp(int argc, char* const* argv, void (*add_tcs)(tp::tc_vector&))
708*c243e490SMarcel Moolenaar {
709*c243e490SMarcel Moolenaar     return tp(add_tcs).run(argc, argv);
710*c243e490SMarcel Moolenaar }
711