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