Home | History | Annotate | Line # | Download | only in tools
reader.cpp revision 1.1.1.1
      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 extern "C" {
     31 #include <sys/time.h>
     32 }
     33 
     34 #include <cassert>
     35 #include <cstdlib>
     36 #include <map>
     37 #include <sstream>
     38 #include <utility>
     39 
     40 #include "defs.hpp"
     41 #include "parser.hpp"
     42 #include "reader.hpp"
     43 #include "text.hpp"
     44 
     45 namespace impl = tools::atf_report;
     46 #define IMPL_NAME "tools::atf_report"
     47 
     48 // ------------------------------------------------------------------------
     49 // Auxiliary functions.
     50 // ------------------------------------------------------------------------
     51 
     52 template< typename Type >
     53 Type
     54 string_to_int(const std::string& str)
     55 {
     56     std::istringstream ss(str);
     57     Type s;
     58     ss >> s;
     59 
     60     return s;
     61 }
     62 
     63 // ------------------------------------------------------------------------
     64 // The "atf_tps" auxiliary parser.
     65 // ------------------------------------------------------------------------
     66 
     67 namespace atf_tps {
     68 
     69 static const tools::parser::token_type eof_type = 0;
     70 static const tools::parser::token_type nl_type = 1;
     71 static const tools::parser::token_type text_type = 2;
     72 static const tools::parser::token_type colon_type = 3;
     73 static const tools::parser::token_type comma_type = 4;
     74 static const tools::parser::token_type tps_count_type = 5;
     75 static const tools::parser::token_type tp_start_type = 6;
     76 static const tools::parser::token_type tp_end_type = 7;
     77 static const tools::parser::token_type tc_start_type = 8;
     78 static const tools::parser::token_type tc_so_type = 9;
     79 static const tools::parser::token_type tc_se_type = 10;
     80 static const tools::parser::token_type tc_end_type = 11;
     81 static const tools::parser::token_type passed_type = 12;
     82 static const tools::parser::token_type failed_type = 13;
     83 static const tools::parser::token_type skipped_type = 14;
     84 static const tools::parser::token_type info_type = 16;
     85 static const tools::parser::token_type expected_death_type = 17;
     86 static const tools::parser::token_type expected_exit_type = 18;
     87 static const tools::parser::token_type expected_failure_type = 19;
     88 static const tools::parser::token_type expected_signal_type = 20;
     89 static const tools::parser::token_type expected_timeout_type = 21;
     90 
     91 class tokenizer : public tools::parser::tokenizer< std::istream > {
     92 public:
     93     tokenizer(std::istream& is, size_t curline) :
     94         tools::parser::tokenizer< std::istream >
     95             (is, true, eof_type, nl_type, text_type, curline)
     96     {
     97         add_delim(':', colon_type);
     98         add_delim(',', comma_type);
     99         add_keyword("tps-count", tps_count_type);
    100         add_keyword("tp-start", tp_start_type);
    101         add_keyword("tp-end", tp_end_type);
    102         add_keyword("tc-start", tc_start_type);
    103         add_keyword("tc-so", tc_so_type);
    104         add_keyword("tc-se", tc_se_type);
    105         add_keyword("tc-end", tc_end_type);
    106         add_keyword("passed", passed_type);
    107         add_keyword("failed", failed_type);
    108         add_keyword("skipped", skipped_type);
    109         add_keyword("info", info_type);
    110         add_keyword("expected_death", expected_death_type);
    111         add_keyword("expected_exit", expected_exit_type);
    112         add_keyword("expected_failure", expected_failure_type);
    113         add_keyword("expected_signal", expected_signal_type);
    114         add_keyword("expected_timeout", expected_timeout_type);
    115     }
    116 };
    117 
    118 } // namespace atf_tps
    119 
    120 struct timeval
    121 read_timeval(tools::parser::parser< atf_tps::tokenizer >& parser)
    122 {
    123     using namespace atf_tps;
    124 
    125     tools::parser::token t = parser.expect(text_type, "timestamp");
    126     const std::string::size_type divider = t.text().find('.');
    127     if (divider == std::string::npos || divider == 0 ||
    128         divider == t.text().length() - 1)
    129         throw tools::parser::parse_error(t.lineno(),
    130                                        "Malformed timestamp value " + t.text());
    131 
    132     struct timeval tv;
    133     tv.tv_sec = string_to_int< long >(t.text().substr(0, divider));
    134     tv.tv_usec = string_to_int< long >(t.text().substr(divider + 1));
    135     return tv;
    136 }
    137 
    138 // ------------------------------------------------------------------------
    139 // The "atf_tps_reader" class.
    140 // ------------------------------------------------------------------------
    141 
    142 impl::atf_tps_reader::atf_tps_reader(std::istream& is) :
    143     m_is(is)
    144 {
    145 }
    146 
    147 impl::atf_tps_reader::~atf_tps_reader(void)
    148 {
    149 }
    150 
    151 void
    152 impl::atf_tps_reader::got_info(
    153     const std::string& what ATF_DEFS_ATTRIBUTE_UNUSED,
    154     const std::string& val ATF_DEFS_ATTRIBUTE_UNUSED)
    155 {
    156 }
    157 
    158 void
    159 impl::atf_tps_reader::got_ntps(size_t ntps ATF_DEFS_ATTRIBUTE_UNUSED)
    160 {
    161 }
    162 
    163 void
    164 impl::atf_tps_reader::got_tp_start(
    165     const std::string& tp ATF_DEFS_ATTRIBUTE_UNUSED,
    166     size_t ntcs ATF_DEFS_ATTRIBUTE_UNUSED)
    167 {
    168 }
    169 
    170 void
    171 impl::atf_tps_reader::got_tp_end(
    172     struct timeval* tv ATF_DEFS_ATTRIBUTE_UNUSED,
    173     const std::string& reason ATF_DEFS_ATTRIBUTE_UNUSED)
    174 {
    175 }
    176 
    177 void
    178 impl::atf_tps_reader::got_tc_start(
    179     const std::string& tcname ATF_DEFS_ATTRIBUTE_UNUSED)
    180 {
    181 }
    182 
    183 void
    184 impl::atf_tps_reader::got_tc_stdout_line(
    185     const std::string& line ATF_DEFS_ATTRIBUTE_UNUSED)
    186 {
    187 }
    188 
    189 void
    190 impl::atf_tps_reader::got_tc_stderr_line(
    191     const std::string& line ATF_DEFS_ATTRIBUTE_UNUSED)
    192 {
    193 }
    194 
    195 void
    196 impl::atf_tps_reader::got_tc_end(
    197     const std::string& state ATF_DEFS_ATTRIBUTE_UNUSED,
    198     struct timeval* tv ATF_DEFS_ATTRIBUTE_UNUSED,
    199     const std::string& reason ATF_DEFS_ATTRIBUTE_UNUSED)
    200 {
    201 }
    202 
    203 void
    204 impl::atf_tps_reader::got_eof(void)
    205 {
    206 }
    207 
    208 void
    209 impl::atf_tps_reader::read_info(void* pptr)
    210 {
    211     using tools::parser::parse_error;
    212     using namespace atf_tps;
    213 
    214     tools::parser::parser< tokenizer >& p =
    215         *reinterpret_cast< tools::parser::parser< tokenizer >* >
    216         (pptr);
    217 
    218     (void)p.expect(colon_type, "`:'");
    219 
    220     tools::parser::token t = p.expect(text_type, "info property name");
    221     (void)p.expect(comma_type, "`,'");
    222     got_info(t.text(), tools::text::trim(p.rest_of_line()));
    223 
    224     (void)p.expect(nl_type, "new line");
    225 }
    226 
    227 void
    228 impl::atf_tps_reader::read_tp(void* pptr)
    229 {
    230     using tools::parser::parse_error;
    231     using namespace atf_tps;
    232 
    233     tools::parser::parser< tokenizer >& p =
    234         *reinterpret_cast< tools::parser::parser< tokenizer >* >
    235         (pptr);
    236 
    237     tools::parser::token t = p.expect(tp_start_type,
    238                                     "start of test program");
    239 
    240     t = p.expect(colon_type, "`:'");
    241 
    242     struct timeval s1 = read_timeval(p);
    243 
    244     t = p.expect(comma_type, "`,'");
    245 
    246     t = p.expect(text_type, "test program name");
    247     std::string tpname = t.text();
    248 
    249     t = p.expect(comma_type, "`,'");
    250 
    251     t = p.expect(text_type, "number of test programs");
    252     size_t ntcs = string_to_int< std::size_t >(t.text());
    253 
    254     t = p.expect(nl_type, "new line");
    255 
    256     ATF_PARSER_CALLBACK(p, got_tp_start(tpname, ntcs));
    257 
    258     size_t i = 0;
    259     while (p.good() && i < ntcs) {
    260         try {
    261             read_tc(&p);
    262             i++;
    263         } catch (const parse_error& pe) {
    264             p.add_error(pe);
    265             p.reset(nl_type);
    266         }
    267     }
    268     t = p.expect(tp_end_type, "end of test program");
    269 
    270     t = p.expect(colon_type, "`:'");
    271 
    272     struct timeval s2 = read_timeval(p);
    273 
    274     struct timeval s3;
    275     timersub(&s2, &s1, &s3);
    276 
    277     t = p.expect(comma_type, "`,'");
    278 
    279     t = p.expect(text_type, "test program name");
    280     if (t.text() != tpname)
    281         throw parse_error(t.lineno(), "Test program name used in "
    282                                       "terminator does not match "
    283                                       "opening");
    284 
    285     t = p.expect(nl_type, comma_type,
    286                  "new line or comma_type");
    287     std::string reason;
    288     if (t.type() == comma_type) {
    289         reason = tools::text::trim(p.rest_of_line());
    290         if (reason.empty())
    291             throw parse_error(t.lineno(),
    292                               "Empty reason for failed test program");
    293         t = p.next();
    294     }
    295 
    296     ATF_PARSER_CALLBACK(p, got_tp_end(&s3, reason));
    297 }
    298 
    299 void
    300 impl::atf_tps_reader::read_tc(void* pptr)
    301 {
    302     using tools::parser::parse_error;
    303     using namespace atf_tps;
    304 
    305     tools::parser::parser< tokenizer >& p =
    306         *reinterpret_cast< tools::parser::parser< tokenizer >* >
    307         (pptr);
    308 
    309     tools::parser::token t = p.expect(tc_start_type, "start of test case");
    310 
    311     t = p.expect(colon_type, "`:'");
    312 
    313     struct timeval s1 = read_timeval(p);
    314 
    315     t = p.expect(comma_type, "`,'");
    316 
    317     t = p.expect(text_type, "test case name");
    318     std::string tcname = t.text();
    319 
    320     ATF_PARSER_CALLBACK(p, got_tc_start(tcname));
    321 
    322     t = p.expect(nl_type, "new line");
    323 
    324     t = p.expect(tc_end_type, tc_so_type, tc_se_type,
    325                  "end of test case or test case's stdout/stderr line");
    326     while (t.type() != tc_end_type &&
    327            (t.type() == tc_so_type || t.type() == tc_se_type)) {
    328         tools::parser::token t2 = t;
    329 
    330         t = p.expect(colon_type, "`:'");
    331 
    332         std::string line = p.rest_of_line();
    333 
    334         if (t2.type() == tc_so_type) {
    335             ATF_PARSER_CALLBACK(p, got_tc_stdout_line(line));
    336         } else {
    337             assert(t2.type() == tc_se_type);
    338             ATF_PARSER_CALLBACK(p, got_tc_stderr_line(line));
    339         }
    340 
    341         t = p.expect(nl_type, "new line");
    342 
    343         t = p.expect(tc_end_type, tc_so_type, tc_se_type,
    344                      "end of test case or test case's stdout/stderr line");
    345     }
    346 
    347     t = p.expect(colon_type, "`:'");
    348 
    349     struct timeval s2 = read_timeval(p);
    350 
    351     struct timeval s3;
    352     timersub(&s2, &s1, &s3);
    353 
    354     t = p.expect(comma_type, "`,'");
    355 
    356     t = p.expect(text_type, "test case name");
    357     if (t.text() != tcname)
    358         throw parse_error(t.lineno(),
    359                           "Test case name used in terminator does not "
    360                           "match opening");
    361 
    362     t = p.expect(comma_type, "`,'");
    363 
    364     t = p.expect(expected_death_type, expected_exit_type, expected_failure_type,
    365         expected_signal_type, expected_timeout_type, passed_type, failed_type,
    366         skipped_type, "expected_{death,exit,failure,signal,timeout}, failed, "
    367         "passed or skipped");
    368     if (t.type() == passed_type) {
    369         ATF_PARSER_CALLBACK(p, got_tc_end("passed", &s3, ""));
    370     } else {
    371         std::string state;
    372         if (t.type() == expected_death_type) state = "expected_death";
    373         else if (t.type() == expected_exit_type) state = "expected_exit";
    374         else if (t.type() == expected_failure_type) state = "expected_failure";
    375         else if (t.type() == expected_signal_type) state = "expected_signal";
    376         else if (t.type() == expected_timeout_type) state = "expected_timeout";
    377         else if (t.type() == failed_type) state = "failed";
    378         else if (t.type() == skipped_type) state = "skipped";
    379         else std::abort();
    380 
    381         t = p.expect(comma_type, "`,'");
    382         std::string reason = tools::text::trim(p.rest_of_line());
    383         if (reason.empty())
    384             throw parse_error(t.lineno(), "Empty reason for " + state +
    385                 " test case result");
    386         ATF_PARSER_CALLBACK(p, got_tc_end(state, &s3, reason));
    387     }
    388 
    389     t = p.expect(nl_type, "new line");
    390 }
    391 
    392 void
    393 impl::atf_tps_reader::read(void)
    394 {
    395     using tools::parser::parse_error;
    396     using namespace atf_tps;
    397 
    398     std::pair< size_t, tools::parser::headers_map > hml =
    399         tools::parser::read_headers(m_is, 1);
    400     tools::parser::validate_content_type(hml.second,
    401                                          "application/X-atf-tps", 3);
    402 
    403     tokenizer tkz(m_is, hml.first);
    404     tools::parser::parser< tokenizer > p(tkz);
    405 
    406     try {
    407         tools::parser::token t;
    408 
    409         while ((t = p.expect(tps_count_type, info_type, "tps-count or info "
    410                              "field")).type() == info_type)
    411             read_info(&p);
    412 
    413         t = p.expect(colon_type, "`:'");
    414 
    415         t = p.expect(text_type, "number of test programs");
    416         size_t ntps = string_to_int< std::size_t >(t.text());
    417         ATF_PARSER_CALLBACK(p, got_ntps(ntps));
    418 
    419         t = p.expect(nl_type, "new line");
    420 
    421         size_t i = 0;
    422         while (p.good() && i < ntps) {
    423             try {
    424                 read_tp(&p);
    425                 i++;
    426             } catch (const parse_error& pe) {
    427                 p.add_error(pe);
    428                 p.reset(nl_type);
    429             }
    430         }
    431 
    432         while ((t = p.expect(eof_type, info_type, "end of stream or info "
    433                              "field")).type() == info_type)
    434             read_info(&p);
    435         ATF_PARSER_CALLBACK(p, got_eof());
    436     } catch (const parse_error& pe) {
    437         p.add_error(pe);
    438         p.reset(nl_type);
    439     }
    440 }
    441