No empty .Rs/.Re
[netbsd-mini2440.git] / external / bsd / atf / dist / tools / atf-report.cpp
blobac6aeb14628e75d548ec1942dd2f623eb4d03c9e
1 //
2 // Automated Testing Framework (atf)
3 //
4 // Copyright (c) 2007, 2008 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.
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.
30 #include <cstdlib>
31 #include <fstream>
32 #include <iostream>
33 #include <memory>
34 #include <utility>
35 #include <vector>
37 #include "atf-c++/application.hpp"
38 #include "atf-c++/fs.hpp"
39 #include "atf-c++/formats.hpp"
40 #include "atf-c++/sanity.hpp"
41 #include "atf-c++/text.hpp"
42 #include "atf-c++/ui.hpp"
44 typedef std::auto_ptr< std::ostream > ostream_ptr;
46 ostream_ptr
47 open_outfile(const atf::fs::path& path)
49 ostream_ptr osp;
50 if (path.str() == "-")
51 osp = ostream_ptr(new std::ofstream("/dev/stdout"));
52 else
53 osp = ostream_ptr(new std::ofstream(path.c_str()));
54 if (!(*osp))
55 throw std::runtime_error("Could not create file " + path.str());
56 return osp;
59 // ------------------------------------------------------------------------
60 // The "writer" interface.
61 // ------------------------------------------------------------------------
63 //!
64 //! \brief A base class that defines an output format.
65 //!
66 //! The writer base class defines a generic interface to output formats.
67 //! This is meant to be subclassed, and each subclass can redefine any
68 //! method to format the information as it wishes.
69 //!
70 //! This class is not tied to a output stream nor a file because, depending
71 //! on the output format, we will want to write to a single file or to
72 //! multiple ones.
73 //!
74 class writer {
75 public:
76 writer(void) {}
77 virtual ~writer(void) {}
79 virtual void write_info(const std::string&, const std::string&) {}
80 virtual void write_ntps(size_t) {}
81 virtual void write_tp_start(const std::string&, size_t) {}
82 virtual void write_tp_end(const std::string&) {}
83 virtual void write_tc_start(const std::string&) {}
84 virtual void write_tc_stdout_line(const std::string&) {}
85 virtual void write_tc_stderr_line(const std::string&) {}
86 virtual void write_tc_end(const atf::tests::tcr&) {}
87 virtual void write_eof(void) {}
90 // ------------------------------------------------------------------------
91 // The "csv_writer" class.
92 // ------------------------------------------------------------------------
94 //!
95 //! \brief A very simple plain-text output format.
96 //!
97 //! The csv_writer class implements a very simple plain-text output
98 //! format that summarizes the results of each executed test case. The
99 //! results are meant to be easily parseable by third-party tools, hence
100 //! they are formatted as a CSV file.
102 class csv_writer : public writer {
103 ostream_ptr m_os;
104 bool m_failed;
106 std::string m_tpname;
107 std::string m_tcname;
109 public:
110 csv_writer(const atf::fs::path& p) :
111 m_os(open_outfile(p))
115 virtual
116 void
117 write_tp_start(const std::string& name, size_t ntcs)
119 m_tpname = name;
120 m_failed = false;
123 virtual
124 void
125 write_tp_end(const std::string& reason)
127 if (!reason.empty())
128 (*m_os) << "tp, " << m_tpname << ", bogus, " << reason
129 << std::endl;
130 else if (m_failed)
131 (*m_os) << "tp, " << m_tpname << ", failed" << std::endl;
132 else
133 (*m_os) << "tp, " << m_tpname << ", passed" << std::endl;
136 virtual
137 void
138 write_tc_start(const std::string& name)
140 m_tcname = name;
143 virtual
144 void
145 write_tc_end(const atf::tests::tcr& tcr)
147 std::string str = "tc, ";
148 if (tcr.get_state() == atf::tests::tcr::passed_state) {
149 str += m_tpname + ", " + m_tcname + ", passed";
150 } else if (tcr.get_state() == atf::tests::tcr::failed_state) {
151 str += m_tpname + ", " + m_tcname + ", failed, " +
152 tcr.get_reason();
153 m_failed = true;
154 } else if (tcr.get_state() == atf::tests::tcr::skipped_state) {
155 str += m_tpname + ", " + m_tcname + ", skipped, " +
156 tcr.get_reason();
157 } else
158 UNREACHABLE;
159 (*m_os) << str << std::endl;
163 // ------------------------------------------------------------------------
164 // The "ticker_writer" class.
165 // ------------------------------------------------------------------------
168 //! \brief A console-friendly output format.
170 //! The ticker_writer class implements a formatter that is user-friendly
171 //! in the sense that it shows the execution of test cases in an easy to
172 //! read format. It is not meant to be parseable and its format can
173 //! freely change across releases.
175 class ticker_writer : public writer {
176 ostream_ptr m_os;
178 size_t m_curtp, m_ntps;
179 size_t m_tcs_passed, m_tcs_failed, m_tcs_skipped;
180 std::string m_tcname, m_tpname;
181 std::vector< std::string > m_failed_tcs;
182 std::vector< std::string > m_failed_tps;
184 void
185 write_info(const std::string& what, const std::string& val)
187 if (what == "tests.root") {
188 (*m_os) << "Tests root: " << val << std::endl
189 << std::endl;
193 void
194 write_ntps(size_t ntps)
196 m_curtp = 1;
197 m_tcs_passed = 0;
198 m_tcs_failed = 0;
199 m_tcs_skipped = 0;
200 m_ntps = ntps;
203 void
204 write_tp_start(const std::string& tp, size_t ntcs)
206 using atf::text::to_string;
207 using atf::ui::format_text;
209 m_tpname = tp;
211 (*m_os) << format_text(tp + " (" + to_string(m_curtp) +
212 "/" + to_string(m_ntps) + "): " +
213 to_string(ntcs) + " test cases")
214 << std::endl;
215 (*m_os).flush();
218 void
219 write_tp_end(const std::string& reason)
221 using atf::ui::format_text_with_tag;
223 m_curtp++;
225 if (!reason.empty()) {
226 (*m_os) << format_text_with_tag("BOGUS TEST PROGRAM: Cannot "
227 "trust its results because "
228 "of `" + reason + "'",
229 m_tpname + ": ", false)
230 << std::endl;
231 m_failed_tps.push_back(m_tpname);
233 (*m_os) << std::endl;
234 (*m_os).flush();
236 m_tpname.clear();
239 void
240 write_tc_start(const std::string& tcname)
242 m_tcname = tcname;
244 (*m_os) << " " + tcname + ": ";
245 (*m_os).flush();
248 void
249 write_tc_end(const atf::tests::tcr& tcr)
251 std::string str;
253 atf::tests::tcr::state s = tcr.get_state();
254 if (s == atf::tests::tcr::passed_state) {
255 str = "Passed.";
256 m_tcs_passed++;
257 } else if (s == atf::tests::tcr::failed_state) {
258 str = "Failed: " + tcr.get_reason();
259 m_tcs_failed++;
260 m_failed_tcs.push_back(m_tpname + ":" + m_tcname);
261 } else if (s == atf::tests::tcr::skipped_state) {
262 str = "Skipped: " + tcr.get_reason();
263 m_tcs_skipped++;
264 } else
265 UNREACHABLE;
267 // XXX Wrap text. format_text_with_tag does not currently allow
268 // to specify the current column, which is needed because we have
269 // already printed the tc's name.
270 (*m_os) << str << std::endl;
272 m_tcname = "";
275 void
276 write_eof(void)
278 using atf::text::join;
279 using atf::text::to_string;
280 using atf::ui::format_text;
281 using atf::ui::format_text_with_tag;
283 if (!m_failed_tps.empty()) {
284 (*m_os) << format_text("Failed (bogus) test programs:")
285 << std::endl;
286 (*m_os) << format_text_with_tag(join(m_failed_tps, ", "),
287 " ", false) << std::endl
288 << std::endl;
291 if (!m_failed_tcs.empty()) {
292 (*m_os) << format_text("Failed test cases:") << std::endl;
293 (*m_os) << format_text_with_tag(join(m_failed_tcs, ", "),
294 " ", false) << std::endl
295 << std::endl;
298 (*m_os) << format_text("Summary for " + to_string(m_ntps) +
299 " test programs:")
300 << std::endl;
301 (*m_os) << format_text_with_tag(to_string(m_tcs_passed) +
302 " passed test cases.",
303 " ", false)
304 << std::endl;
305 (*m_os) << format_text_with_tag(to_string(m_tcs_failed) +
306 " failed test cases.",
307 " ", false)
308 << std::endl;
309 (*m_os) << format_text_with_tag(to_string(m_tcs_skipped) +
310 " skipped test cases.",
311 " ", false)
312 << std::endl;
315 public:
316 ticker_writer(const atf::fs::path& p) :
317 m_os(open_outfile(p))
322 // ------------------------------------------------------------------------
323 // The "xml" class.
324 // ------------------------------------------------------------------------
327 //! \brief A single-file XML output format.
329 //! The xml_writer class implements a formatter that prints the results
330 //! of test cases in an XML format easily parseable later on by other
331 //! utilities.
333 class xml_writer : public writer {
334 ostream_ptr m_os;
336 size_t m_curtp, m_ntps;
337 size_t m_tcs_passed, m_tcs_failed, m_tcs_skipped;
338 std::string m_tcname, m_tpname;
339 std::vector< std::string > m_failed_tcs;
340 std::vector< std::string > m_failed_tps;
342 static
343 std::string
344 attrval(const std::string& str)
346 return str;
349 static
350 std::string
351 elemval(const std::string& str)
353 std::string ostr;
354 for (std::string::const_iterator iter = str.begin();
355 iter != str.end(); iter++) {
356 switch (*iter) {
357 case '&': ostr += "&amp;"; break;
358 case '<': ostr += "&lt;"; break;
359 case '>': ostr += "&gt;"; break;
360 default: ostr += *iter;
363 return ostr;
366 void
367 write_info(const std::string& what, const std::string& val)
369 (*m_os) << "<info class=\"" << what << "\">" << val << "</info>"
370 << std::endl;
373 void
374 write_tp_start(const std::string& tp, size_t ntcs)
376 (*m_os) << "<tp id=\"" << attrval(tp) << "\">" << std::endl;
379 void
380 write_tp_end(const std::string& reason)
382 if (!reason.empty())
383 (*m_os) << "<failed>" << elemval(reason) << "</failed>"
384 << std::endl;
385 (*m_os) << "</tp>" << std::endl;
388 void
389 write_tc_start(const std::string& tcname)
391 (*m_os) << "<tc id=\"" << attrval(tcname) << "\">" << std::endl;
394 void
395 write_tc_stdout_line(const std::string& line)
397 (*m_os) << "<so>" << elemval(line) << "</so>" << std::endl;
400 void
401 write_tc_stderr_line(const std::string& line)
403 (*m_os) << "<se>" << elemval(line) << "</se>" << std::endl;
406 void
407 write_tc_end(const atf::tests::tcr& tcr)
409 std::string str;
411 atf::tests::tcr::state s = tcr.get_state();
412 if (s == atf::tests::tcr::passed_state) {
413 (*m_os) << "<passed />" << std::endl;
414 } else if (s == atf::tests::tcr::failed_state) {
415 (*m_os) << "<failed>" << elemval(tcr.get_reason())
416 << "</failed>" << std::endl;
417 } else if (s == atf::tests::tcr::skipped_state) {
418 (*m_os) << "<skipped>" << elemval(tcr.get_reason())
419 << "</skipped>" << std::endl;
420 } else
421 UNREACHABLE;
422 (*m_os) << "</tc>" << std::endl;
425 void
426 write_eof(void)
428 (*m_os) << "</tests-results>" << std::endl;
431 public:
432 xml_writer(const atf::fs::path& p) :
433 m_os(open_outfile(p))
435 (*m_os) << "<?xml version=\"1.0\"?>" << std::endl
436 << "<!DOCTYPE tests-results PUBLIC "
437 "\"-//NetBSD//DTD ATF Tests Results 0.1//EN\" "
438 "\"http://www.NetBSD.org/XML/atf/tests-results.dtd\">"
439 << std::endl
440 << std::endl
441 << "<tests-results>" << std::endl;
445 // ------------------------------------------------------------------------
446 // The "converter" class.
447 // ------------------------------------------------------------------------
450 //! \brief A reader that redirects events to multiple writers.
452 //! The converter class implements an atf_tps_reader that, for each event
453 //! raised by the parser, redirects it to multiple writers so that they
454 //! can reformat it according to their output rules.
456 class converter : public atf::formats::atf_tps_reader {
457 typedef std::vector< writer* > outs_vector;
458 outs_vector m_outs;
460 void
461 got_info(const std::string& what, const std::string& val)
463 for (outs_vector::iterator iter = m_outs.begin();
464 iter != m_outs.end(); iter++)
465 (*iter)->write_info(what, val);
468 void
469 got_ntps(size_t ntps)
471 for (outs_vector::iterator iter = m_outs.begin();
472 iter != m_outs.end(); iter++)
473 (*iter)->write_ntps(ntps);
476 void
477 got_tp_start(const std::string& tp, size_t ntcs)
479 for (outs_vector::iterator iter = m_outs.begin();
480 iter != m_outs.end(); iter++)
481 (*iter)->write_tp_start(tp, ntcs);
484 void
485 got_tp_end(const std::string& reason)
487 for (outs_vector::iterator iter = m_outs.begin();
488 iter != m_outs.end(); iter++)
489 (*iter)->write_tp_end(reason);
492 void
493 got_tc_start(const std::string& tcname)
495 for (outs_vector::iterator iter = m_outs.begin();
496 iter != m_outs.end(); iter++)
497 (*iter)->write_tc_start(tcname);
500 void
501 got_tc_stdout_line(const std::string& line)
503 for (outs_vector::iterator iter = m_outs.begin();
504 iter != m_outs.end(); iter++)
505 (*iter)->write_tc_stdout_line(line);
508 void
509 got_tc_stderr_line(const std::string& line)
511 for (outs_vector::iterator iter = m_outs.begin();
512 iter != m_outs.end(); iter++)
513 (*iter)->write_tc_stderr_line(line);
516 void
517 got_tc_end(const atf::tests::tcr& tcr)
519 for (outs_vector::iterator iter = m_outs.begin();
520 iter != m_outs.end(); iter++)
521 (*iter)->write_tc_end(tcr);
524 void
525 got_eof(void)
527 for (outs_vector::iterator iter = m_outs.begin();
528 iter != m_outs.end(); iter++)
529 (*iter)->write_eof();
532 public:
533 converter(std::istream& is) :
534 atf::formats::atf_tps_reader(is)
538 ~converter(void)
540 for (outs_vector::iterator iter = m_outs.begin();
541 iter != m_outs.end(); iter++)
542 delete *iter;
545 void
546 add_output(const std::string& fmt, const atf::fs::path& p)
548 if (fmt == "csv") {
549 m_outs.push_back(new csv_writer(p));
550 } else if (fmt == "ticker") {
551 m_outs.push_back(new ticker_writer(p));
552 } else if (fmt == "xml") {
553 m_outs.push_back(new xml_writer(p));
554 } else
555 throw std::runtime_error("Unknown format `" + fmt + "'");
559 // ------------------------------------------------------------------------
560 // The "atf_report" class.
561 // ------------------------------------------------------------------------
563 class atf_report : public atf::application::app {
564 static const char* m_description;
566 typedef std::pair< std::string, atf::fs::path > fmt_path_pair;
567 std::vector< fmt_path_pair > m_oflags;
569 void process_option(int, const char*);
570 options_set specific_options(void) const;
572 public:
573 atf_report(void);
575 int main(void);
578 const char* atf_report::m_description =
579 "atf-report is a tool that parses the output of atf-run and "
580 "generates user-friendly reports in multiple different formats.";
582 atf_report::atf_report(void) :
583 app(m_description, "atf-report(1)", "atf(7)")
587 void
588 atf_report::process_option(int ch, const char* arg)
590 switch (ch) {
591 case 'o':
593 std::string str(arg);
594 std::string::size_type pos = str.find(':');
595 if (pos == std::string::npos)
596 throw std::runtime_error("Syntax error in -o option");
597 else {
598 std::string fmt = str.substr(0, pos);
599 atf::fs::path path = atf::fs::path(str.substr(pos + 1));
600 m_oflags.push_back(fmt_path_pair(fmt, path));
603 break;
605 default:
606 UNREACHABLE;
610 atf_report::options_set
611 atf_report::specific_options(void)
612 const
614 using atf::application::option;
615 options_set opts;
616 opts.insert(option('o', "fmt:path", "Adds a new output file; multiple "
617 "ones can be specified, and a - "
618 "path means stdout"));
619 return opts;
623 atf_report::main(void)
625 if (m_oflags.empty())
626 m_oflags.push_back(fmt_path_pair("ticker", atf::fs::path("-")));
628 // Look for path duplicates.
629 std::set< atf::fs::path > paths;
630 for (std::vector< fmt_path_pair >::const_iterator iter = m_oflags.begin();
631 iter != m_oflags.end(); iter++) {
632 atf::fs::path p = (*iter).second;
633 if (p == atf::fs::path("/dev/stdout"))
634 p = atf::fs::path("-");
635 if (paths.find(p) != paths.end())
636 throw std::runtime_error("The file `" + p.str() + "' was "
637 "specified more than once");
638 paths.insert((*iter).second);
641 // Generate the output files.
642 converter cnv(std::cin);
643 for (std::vector< fmt_path_pair >::const_iterator iter = m_oflags.begin();
644 iter != m_oflags.end(); iter++)
645 cnv.add_output((*iter).first, (*iter).second);
646 cnv.read();
648 return EXIT_SUCCESS;
652 main(int argc, char* const* argv)
654 return atf_report().run(argc, argv);