Home | History | Annotate | Line # | Download | only in tools
      1 //
      2 // Automated Testing Framework (atf)
      3 //
      4 // Copyright (c) 2007 The NetBSD Foundation, Inc.
      5 // All rights reserved.
      6 //
      7 // Redistribution and use in source and binary forms, with or without
      8 // modification, are permitted provided that the following conditions
      9 // are met:
     10 // 1. Redistributions of source code must retain the above copyright
     11 //    notice, this list of conditions and the following disclaimer.
     12 // 2. Redistributions in binary form must reproduce the above copyright
     13 //    notice, this list of conditions and the following disclaimer in the
     14 //    documentation and/or other materials provided with the distribution.
     15 //
     16 // THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
     17 // CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
     18 // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     19 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     20 // IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
     21 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     22 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
     23 // GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
     25 // IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     26 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
     27 // IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     28 //
     29 
     30 #include <cassert>
     31 #include <cstdlib>
     32 #include <fstream>
     33 
     34 #include "atffile.hpp"
     35 #include "exceptions.hpp"
     36 #include "expand.hpp"
     37 #include "parser.hpp"
     38 
     39 namespace impl = tools;
     40 namespace detail = tools::detail;
     41 
     42 namespace {
     43 
     44 typedef std::map< std::string, std::string > vars_map;
     45 
     46 } // anonymous namespace
     47 
     48 // ------------------------------------------------------------------------
     49 // The "atf_atffile" auxiliary parser.
     50 // ------------------------------------------------------------------------
     51 
     52 namespace atf_atffile {
     53 
     54 static const tools::parser::token_type eof_type = 0;
     55 static const tools::parser::token_type nl_type = 1;
     56 static const tools::parser::token_type text_type = 2;
     57 static const tools::parser::token_type colon_type = 3;
     58 static const tools::parser::token_type conf_type = 4;
     59 static const tools::parser::token_type dblquote_type = 5;
     60 static const tools::parser::token_type equal_type = 6;
     61 static const tools::parser::token_type hash_type = 7;
     62 static const tools::parser::token_type prop_type = 8;
     63 static const tools::parser::token_type tp_type = 9;
     64 static const tools::parser::token_type tp_glob_type = 10;
     65 
     66 class tokenizer : public tools::parser::tokenizer< std::istream > {
     67 public:
     68     tokenizer(std::istream& is, size_t curline) :
     69         tools::parser::tokenizer< std::istream >
     70             (is, true, eof_type, nl_type, text_type, curline)
     71     {
     72         add_delim(':', colon_type);
     73         add_delim('=', equal_type);
     74         add_delim('#', hash_type);
     75         add_quote('"', dblquote_type);
     76         add_keyword("conf", conf_type);
     77         add_keyword("prop", prop_type);
     78         add_keyword("tp", tp_type);
     79         add_keyword("tp-glob", tp_glob_type);
     80     }
     81 };
     82 
     83 } // namespace atf_atffile
     84 
     85 // ------------------------------------------------------------------------
     86 // The "atf_atffile_reader" class.
     87 // ------------------------------------------------------------------------
     88 
     89 detail::atf_atffile_reader::atf_atffile_reader(std::istream& is) :
     90     m_is(is)
     91 {
     92 }
     93 
     94 detail::atf_atffile_reader::~atf_atffile_reader(void)
     95 {
     96 }
     97 
     98 void
     99 detail::atf_atffile_reader::got_conf(
    100     const std::string& name __attribute__((__unused__)),
    101     const std::string& val __attribute__((__unused__)))
    102 {
    103 }
    104 
    105 void
    106 detail::atf_atffile_reader::got_prop(
    107     const std::string& name __attribute__((__unused__)),
    108     const std::string& val __attribute__((__unused__)))
    109 {
    110 }
    111 
    112 void
    113 detail::atf_atffile_reader::got_tp(
    114     const std::string& name __attribute__((__unused__)),
    115     bool isglob __attribute__((__unused__)))
    116 {
    117 }
    118 
    119 void
    120 detail::atf_atffile_reader::got_eof(void)
    121 {
    122 }
    123 
    124 void
    125 detail::atf_atffile_reader::read(void)
    126 {
    127     using tools::parser::parse_error;
    128     using namespace atf_atffile;
    129 
    130     std::pair< size_t, tools::parser::headers_map > hml =
    131         tools::parser::read_headers(m_is, 1);
    132     tools::parser::validate_content_type(hml.second,
    133         "application/X-atf-atffile", 1);
    134 
    135     tokenizer tkz(m_is, hml.first);
    136     tools::parser::parser< tokenizer > p(tkz);
    137 
    138     for (;;) {
    139         try {
    140             tools::parser::token t =
    141                 p.expect(conf_type, hash_type, prop_type, tp_type,
    142                          tp_glob_type, nl_type, eof_type,
    143                          "conf, #, prop, tp, tp-glob, a new line or eof");
    144             if (t.type() == eof_type)
    145                 break;
    146 
    147             if (t.type() == conf_type) {
    148                 t = p.expect(colon_type, "`:'");
    149 
    150                 t = p.expect(text_type, "variable name");
    151                 std::string var = t.text();
    152 
    153                 t = p.expect(equal_type, "equal sign");
    154 
    155                 t = p.expect(text_type, "word or quoted string");
    156                 ATF_PARSER_CALLBACK(p, got_conf(var, t.text()));
    157             } else if (t.type() == hash_type) {
    158                 (void)p.rest_of_line();
    159             } else if (t.type() == prop_type) {
    160                 t = p.expect(colon_type, "`:'");
    161 
    162                 t = p.expect(text_type, "property name");
    163                 std::string name = t.text();
    164 
    165                 t = p.expect(equal_type, "equale sign");
    166 
    167                 t = p.expect(text_type, "word or quoted string");
    168                 ATF_PARSER_CALLBACK(p, got_prop(name, t.text()));
    169             } else if (t.type() == tp_type) {
    170                 t = p.expect(colon_type, "`:'");
    171 
    172                 t = p.expect(text_type, "word or quoted string");
    173                 ATF_PARSER_CALLBACK(p, got_tp(t.text(), false));
    174             } else if (t.type() == tp_glob_type) {
    175                 t = p.expect(colon_type, "`:'");
    176 
    177                 t = p.expect(text_type, "word or quoted string");
    178                 ATF_PARSER_CALLBACK(p, got_tp(t.text(), true));
    179             } else if (t.type() == nl_type) {
    180                 continue;
    181             } else
    182                 std::abort();
    183 
    184             t = p.expect(nl_type, hash_type, eof_type,
    185                          "new line or comment");
    186             if (t.type() == hash_type) {
    187                 (void)p.rest_of_line();
    188                 t = p.next();
    189             } else if (t.type() == eof_type)
    190                 break;
    191         } catch (const parse_error& pe) {
    192             p.add_error(pe);
    193             p.reset(nl_type);
    194         }
    195     }
    196 
    197     ATF_PARSER_CALLBACK(p, got_eof());
    198 }
    199 
    200 // ------------------------------------------------------------------------
    201 // The "reader" helper class.
    202 // ------------------------------------------------------------------------
    203 
    204 class reader : public detail::atf_atffile_reader {
    205     const tools::fs::directory& m_dir;
    206     vars_map m_conf, m_props;
    207     std::vector< std::string > m_tps;
    208 
    209     void
    210     got_tp(const std::string& name, bool isglob)
    211     {
    212         if (isglob) {
    213             std::vector< std::string > ms =
    214                 tools::expand::expand_glob(name, m_dir.names());
    215             // Cannot use m_tps.insert(iterator, begin, end) here because it
    216             // does not work under Solaris.
    217             for (std::vector< std::string >::const_iterator iter = ms.begin();
    218                  iter != ms.end(); iter++)
    219                 m_tps.push_back(*iter);
    220         } else {
    221             if (m_dir.find(name) == m_dir.end())
    222                 throw tools::not_found_error< tools::fs::path >
    223                     ("Cannot locate the " + name + " file",
    224                      tools::fs::path(name));
    225             m_tps.push_back(name);
    226         }
    227     }
    228 
    229     void
    230     got_prop(const std::string& name, const std::string& val)
    231     {
    232         m_props[name] = val;
    233     }
    234 
    235     void
    236     got_conf(const std::string& var, const std::string& val)
    237     {
    238         m_conf[var] = val;
    239     }
    240 
    241 public:
    242     reader(std::istream& is, const tools::fs::directory& dir) :
    243         detail::atf_atffile_reader(is),
    244         m_dir(dir)
    245     {
    246     }
    247 
    248     const vars_map&
    249     conf(void)
    250         const
    251     {
    252         return m_conf;
    253     }
    254 
    255     const vars_map&
    256     props(void)
    257         const
    258     {
    259         return m_props;
    260     }
    261 
    262     const std::vector< std::string >&
    263     tps(void)
    264         const
    265     {
    266         return m_tps;
    267     }
    268 };
    269 
    270 // ------------------------------------------------------------------------
    271 // The "atffile" class.
    272 // ------------------------------------------------------------------------
    273 
    274 impl::atffile::atffile(const vars_map& config_vars,
    275                        const std::vector< std::string >& test_program_names,
    276                        const vars_map& properties) :
    277     m_conf(config_vars),
    278     m_tps(test_program_names),
    279     m_props(properties)
    280 {
    281     assert(properties.find("test-suite") != properties.end());
    282 }
    283 
    284 const std::vector< std::string >&
    285 impl::atffile::tps(void)
    286     const
    287 {
    288     return m_tps;
    289 }
    290 
    291 const vars_map&
    292 impl::atffile::conf(void)
    293     const
    294 {
    295     return m_conf;
    296 }
    297 
    298 const vars_map&
    299 impl::atffile::props(void)
    300     const
    301 {
    302     return m_props;
    303 }
    304 
    305 // ------------------------------------------------------------------------
    306 // Free functions.
    307 // ------------------------------------------------------------------------
    308 
    309 // XXX Glob expansion and file existance checks certainly do not belong in
    310 // a *parser*.  This needs to be taken out...
    311 impl::atffile
    312 impl::read_atffile(const tools::fs::path& filename)
    313 {
    314     // Scan the directory where the atffile lives in to gather a list of
    315     // all possible test programs in it.
    316     tools::fs::directory dir(filename.branch_path());
    317     dir.erase(filename.leaf_name());
    318     tools::fs::directory::iterator iter = dir.begin();
    319     while (iter != dir.end()) {
    320         const std::string& name = (*iter).first;
    321         const tools::fs::file_info& fi = (*iter).second;
    322 
    323         // Discard hidden files and non-executable ones so that they are
    324         // not candidates for glob matching.
    325         if (name[0] == '.' || (!fi.is_owner_executable() &&
    326                                !fi.is_group_executable()))
    327             dir.erase(iter++);
    328         else
    329             iter++;
    330     }
    331 
    332     // Parse the atffile.
    333     std::ifstream is(filename.c_str());
    334     if (!is)
    335         throw tools::not_found_error< tools::fs::path >
    336             ("Cannot open Atffile", filename);
    337     reader r(is, dir);
    338     r.read();
    339     is.close();
    340 
    341     // Sanity checks.
    342     if (r.props().find("test-suite") == r.props().end())
    343         throw tools::not_found_error< std::string >
    344             ("Undefined property `test-suite'", "test-suite");
    345 
    346     return atffile(r.conf(), r.tps(), r.props());
    347 }
    348