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