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