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