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