1235680Sadrian//
2235680Sadrian// Automated Testing Framework (atf)
3235680Sadrian//
4235680Sadrian// Copyright (c) 2007 The NetBSD Foundation, Inc.
5235680Sadrian// All rights reserved.
6235680Sadrian//
7235680Sadrian// Redistribution and use in source and binary forms, with or without
8235680Sadrian// modification, are permitted provided that the following conditions
9235680Sadrian// are met:
10235680Sadrian// 1. Redistributions of source code must retain the above copyright
11235680Sadrian//    notice, this list of conditions and the following disclaimer.
12235680Sadrian// 2. Redistributions in binary form must reproduce the above copyright
13235680Sadrian//    notice, this list of conditions and the following disclaimer in the
14235680Sadrian//    documentation and/or other materials provided with the distribution.
15235680Sadrian//
16235680Sadrian// THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
17235680Sadrian// CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
18235680Sadrian// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19235680Sadrian// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20235680Sadrian// IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
21235680Sadrian// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22235680Sadrian// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23235680Sadrian// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24235680Sadrian// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25235680Sadrian// IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26235680Sadrian// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27235680Sadrian// IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28235680Sadrian//
29235680Sadrian
30235680Sadrianextern "C" {
31235680Sadrian#include <sys/time.h>
32235680Sadrian}
33235680Sadrian
34235680Sadrian#include <cstdlib>
35235680Sadrian#include <fstream>
36235680Sadrian#include <iomanip>
37235680Sadrian#include <iostream>
38235680Sadrian#include <memory>
39235680Sadrian#include <sstream>
40235680Sadrian#include <utility>
41235680Sadrian#include <vector>
42235680Sadrian
43235680Sadrian#include "atf-c/defs.h"
44235680Sadrian
45235680Sadrian#include "atf-c++/detail/application.hpp"
46235680Sadrian#include "atf-c++/detail/fs.hpp"
47235680Sadrian#include "atf-c++/detail/sanity.hpp"
48235680Sadrian#include "atf-c++/detail/text.hpp"
49235680Sadrian#include "atf-c++/detail/ui.hpp"
50235680Sadrian
51235680Sadrian#include "reader.hpp"
52235680Sadrian
53235680Sadriantypedef std::auto_ptr< std::ostream > ostream_ptr;
54235680Sadrian
55235680Sadrianstatic ostream_ptr
56235680Sadrianopen_outfile(const atf::fs::path& path)
57235680Sadrian{
58235680Sadrian    ostream_ptr osp;
59235680Sadrian    if (path.str() == "-")
60235680Sadrian        osp = ostream_ptr(new std::ofstream("/dev/stdout"));
61235680Sadrian    else
62235680Sadrian        osp = ostream_ptr(new std::ofstream(path.c_str()));
63235680Sadrian    if (!(*osp))
64235680Sadrian        throw std::runtime_error("Could not create file " + path.str());
65235680Sadrian    return osp;
66235680Sadrian}
67235680Sadrian
68235680Sadrianstatic std::string
69235680Sadrianformat_tv(struct timeval* tv)
70235680Sadrian{
71235680Sadrian    std::ostringstream output;
72235680Sadrian    output << static_cast< long >(tv->tv_sec) << '.'
73235680Sadrian           << std::setfill('0') << std::setw(6)
74235680Sadrian           << static_cast< long >(tv->tv_usec);
75235680Sadrian    return output.str();
76235680Sadrian}
77235680Sadrian
78235680Sadrian// ------------------------------------------------------------------------
79235680Sadrian// The "writer" interface.
80235680Sadrian// ------------------------------------------------------------------------
81235680Sadrian
82235680Sadrian//!
83235680Sadrian//! \brief A base class that defines an output format.
84235680Sadrian//!
85235680Sadrian//! The writer base class defines a generic interface to output formats.
86235680Sadrian//! This is meant to be subclassed, and each subclass can redefine any
87235680Sadrian//! method to format the information as it wishes.
88235680Sadrian//!
89235680Sadrian//! This class is not tied to a output stream nor a file because, depending
90235680Sadrian//! on the output format, we will want to write to a single file or to
91235680Sadrian//! multiple ones.
92235680Sadrian//!
93235680Sadrianclass writer {
94235680Sadrianpublic:
95235680Sadrian    writer(void) {}
96235680Sadrian    virtual ~writer(void) {}
97235680Sadrian
98235680Sadrian    virtual void write_info(const std::string&, const std::string&) {}
99235680Sadrian    virtual void write_ntps(size_t) {}
100235680Sadrian    virtual void write_tp_start(const std::string&, size_t) {}
101235680Sadrian    virtual void write_tp_end(struct timeval*, const std::string&) {}
102235680Sadrian    virtual void write_tc_start(const std::string&) {}
103235680Sadrian    virtual void write_tc_stdout_line(const std::string&) {}
104235680Sadrian    virtual void write_tc_stderr_line(const std::string&) {}
105235680Sadrian    virtual void write_tc_end(const std::string&, struct timeval*,
106235680Sadrian                              const std::string&) {}
107235680Sadrian    virtual void write_eof(void) {}
108235680Sadrian};
109235680Sadrian
110235680Sadrian// ------------------------------------------------------------------------
111239201Sadrian// The "csv_writer" class.
112235680Sadrian// ------------------------------------------------------------------------
113239201Sadrian
114235680Sadrian//!
115235680Sadrian//! \brief A very simple plain-text output format.
116235680Sadrian//!
117235680Sadrian//! The csv_writer class implements a very simple plain-text output
118235680Sadrian//! format that summarizes the results of each executed test case.  The
119235680Sadrian//! results are meant to be easily parseable by third-party tools, hence
120235680Sadrian//! they are formatted as a CSV file.
121235680Sadrian//!
122239201Sadrianclass csv_writer : public writer {
123239201Sadrian    ostream_ptr m_os;
124239201Sadrian    bool m_failed;
125239201Sadrian
126235680Sadrian    std::string m_tpname;
127235680Sadrian    std::string m_tcname;
128235680Sadrian
129235680Sadrianpublic:
130235680Sadrian    csv_writer(const atf::fs::path& p) :
131235680Sadrian        m_os(open_outfile(p))
132235680Sadrian    {
133235680Sadrian    }
134235680Sadrian
135235680Sadrian    virtual
136235680Sadrian    void
137235680Sadrian    write_tp_start(const std::string& name,
138235680Sadrian                   size_t ntcs ATF_DEFS_ATTRIBUTE_UNUSED)
139235680Sadrian    {
140235680Sadrian        m_tpname = name;
141235680Sadrian        m_failed = false;
142235680Sadrian    }
143235680Sadrian
144235680Sadrian    virtual
145235680Sadrian    void
146235680Sadrian    write_tp_end(struct timeval* tv, const std::string& reason)
147235680Sadrian    {
148235680Sadrian        const std::string timestamp = format_tv(tv);
149235680Sadrian
150235680Sadrian        if (!reason.empty())
151235680Sadrian            (*m_os) << "tp, " << timestamp << ", " << m_tpname << ", bogus, "
152235680Sadrian                    << reason << "\n";
153235680Sadrian        else if (m_failed)
154235680Sadrian            (*m_os) << "tp, " << timestamp << ", "<< m_tpname << ", failed\n";
155235680Sadrian        else
156235680Sadrian            (*m_os) << "tp, " << timestamp << ", "<< m_tpname << ", passed\n";
157235680Sadrian    }
158235680Sadrian
159235680Sadrian    virtual
160235680Sadrian    void
161235680Sadrian    write_tc_start(const std::string& name)
162235680Sadrian    {
163235680Sadrian        m_tcname = name;
164235680Sadrian    }
165235680Sadrian
166235680Sadrian    virtual
167235680Sadrian    void
168235680Sadrian    write_tc_end(const std::string& state, struct timeval* tv,
169235680Sadrian                 const std::string& reason)
170235680Sadrian    {
171235680Sadrian        std::string str = m_tpname + ", " + m_tcname + ", " + state;
172235680Sadrian        if (!reason.empty())
173235680Sadrian            str += ", " + reason;
174235680Sadrian        (*m_os) << "tc, " << format_tv(tv) << ", " << str << "\n";
175235680Sadrian
176235680Sadrian        if (state == "failed")
177235680Sadrian            m_failed = true;
178235680Sadrian    }
179235680Sadrian};
180235680Sadrian
181235680Sadrian// ------------------------------------------------------------------------
182235680Sadrian// The "ticker_writer" class.
183235680Sadrian// ------------------------------------------------------------------------
184235680Sadrian
185235680Sadrian//!
186235680Sadrian//! \brief A console-friendly output format.
187235680Sadrian//!
188235680Sadrian//! The ticker_writer class implements a formatter that is user-friendly
189235680Sadrian//! in the sense that it shows the execution of test cases in an easy to
190235680Sadrian//! read format.  It is not meant to be parseable and its format can
191235680Sadrian//! freely change across releases.
192235680Sadrian//!
193235680Sadrianclass ticker_writer : public writer {
194235680Sadrian    ostream_ptr m_os;
195235680Sadrian
196235680Sadrian    size_t m_curtp, m_ntps;
197235680Sadrian    size_t m_tcs_passed, m_tcs_failed, m_tcs_skipped, m_tcs_expected_failures;
198235680Sadrian    std::string m_tcname, m_tpname;
199235680Sadrian    std::vector< std::string > m_failed_tcs;
200235680Sadrian    std::map< std::string, std::string > m_expected_failures_tcs;
201235680Sadrian    std::vector< std::string > m_failed_tps;
202235680Sadrian
203235680Sadrian    void
204235680Sadrian    write_info(const std::string& what, const std::string& val)
205235680Sadrian    {
206235680Sadrian        if (what == "tests.root") {
207235680Sadrian            (*m_os) << "Tests root: " << val << "\n\n";
208235680Sadrian        }
209235680Sadrian    }
210235680Sadrian
211235680Sadrian    void
212235680Sadrian    write_ntps(size_t ntps)
213235680Sadrian    {
214235680Sadrian        m_curtp = 1;
215235680Sadrian        m_tcs_passed = 0;
216235680Sadrian        m_tcs_failed = 0;
217235680Sadrian        m_tcs_skipped = 0;
218235680Sadrian        m_tcs_expected_failures = 0;
219235680Sadrian        m_ntps = ntps;
220235680Sadrian    }
221235680Sadrian
222235680Sadrian    void
223235680Sadrian    write_tp_start(const std::string& tp, size_t ntcs)
224235680Sadrian    {
225235680Sadrian        using atf::text::to_string;
226235680Sadrian        using atf::ui::format_text;
227235680Sadrian
228235680Sadrian        m_tpname = tp;
229235680Sadrian
230235680Sadrian        (*m_os) << format_text(tp + " (" + to_string(m_curtp) +
231235680Sadrian                               "/" + to_string(m_ntps) + "): " +
232235680Sadrian                               to_string(ntcs) + " test cases")
233235680Sadrian                << "\n";
234235680Sadrian        (*m_os).flush();
235235680Sadrian    }
236235680Sadrian
237235680Sadrian    void
238235680Sadrian    write_tp_end(struct timeval* tv, const std::string& reason)
239235680Sadrian    {
240235680Sadrian        using atf::ui::format_text_with_tag;
241235680Sadrian
242235680Sadrian        m_curtp++;
243235680Sadrian
244235680Sadrian        if (!reason.empty()) {
245235680Sadrian            (*m_os) << format_text_with_tag("BOGUS TEST PROGRAM: Cannot "
246235680Sadrian                                            "trust its results because "
247235680Sadrian                                            "of `" + reason + "'",
248235680Sadrian                                            m_tpname + ": ", false)
249235680Sadrian                    << "\n";
250235680Sadrian            m_failed_tps.push_back(m_tpname);
251235680Sadrian        }
252235680Sadrian        (*m_os) << "[" << format_tv(tv) << "s]\n\n";
253235680Sadrian        (*m_os).flush();
254235680Sadrian
255235680Sadrian        m_tpname.clear();
256235680Sadrian    }
257235680Sadrian
258235680Sadrian    void
259235680Sadrian    write_tc_start(const std::string& tcname)
260235680Sadrian    {
261235680Sadrian        m_tcname = tcname;
262235680Sadrian
263235680Sadrian        (*m_os) << "    " + tcname + ": ";
264235680Sadrian        (*m_os).flush();
265235680Sadrian    }
266235680Sadrian
267235680Sadrian    void
268235680Sadrian    write_tc_end(const std::string& state, struct timeval* tv,
269235680Sadrian                 const std::string& reason)
270235680Sadrian    {
271235680Sadrian        std::string str;
272235680Sadrian
273235680Sadrian        (*m_os) << "[" << format_tv(tv) << "s] ";
274239051Sadrian
275239051Sadrian        if (state == "expected_death" || state == "expected_exit" ||
276239201Sadrian            state == "expected_failure" || state == "expected_signal" ||
277235680Sadrian            state == "expected_timeout") {
278235680Sadrian            str = "Expected failure: " + reason;
279235680Sadrian            m_tcs_expected_failures++;
280235680Sadrian            m_expected_failures_tcs[m_tpname + ":" + m_tcname] = reason;
281235680Sadrian        } else if (state == "failed") {
282235680Sadrian            str = "Failed: " + reason;
283235680Sadrian            m_tcs_failed++;
284235680Sadrian            m_failed_tcs.push_back(m_tpname + ":" + m_tcname);
285235680Sadrian        } else if (state == "passed") {
286235680Sadrian            str = "Passed.";
287235680Sadrian            m_tcs_passed++;
288238609Sadrian        } else if (state == "skipped") {
289238609Sadrian            str = "Skipped: " + reason;
290235680Sadrian            m_tcs_skipped++;
291235680Sadrian        } else
292235680Sadrian            UNREACHABLE;
293235680Sadrian
294235680Sadrian        // XXX Wrap text.  format_text_with_tag does not currently allow
295235680Sadrian        // to specify the current column, which is needed because we have
296238609Sadrian        // already printed the tc's name.
297235680Sadrian        (*m_os) << str << '\n';
298235680Sadrian
299235680Sadrian        m_tcname = "";
300235680Sadrian    }
301235680Sadrian
302235680Sadrian    static void
303235680Sadrian    write_expected_failures(const std::map< std::string, std::string >& xfails,
304235680Sadrian                            std::ostream& os)
305235680Sadrian    {
306235680Sadrian        using atf::ui::format_text;
307235680Sadrian        using atf::ui::format_text_with_tag;
308235680Sadrian
309235680Sadrian        os << format_text("Test cases for known bugs:") << "\n";
310235680Sadrian
311239051Sadrian        for (std::map< std::string, std::string >::const_iterator iter =
312235680Sadrian             xfails.begin(); iter != xfails.end(); iter++) {
313235680Sadrian            const std::string& name = (*iter).first;
314235680Sadrian            const std::string& reason = (*iter).second;
315235680Sadrian
316235680Sadrian            os << format_text_with_tag(reason, "    " + name + ": ", false)
317235680Sadrian               << "\n";
318235680Sadrian        }
319235680Sadrian    }
320235680Sadrian
321235680Sadrian    void
322235680Sadrian    write_eof(void)
323235680Sadrian    {
324235680Sadrian        using atf::text::join;
325249569Sadrian        using atf::text::to_string;
326235680Sadrian        using atf::ui::format_text;
327235680Sadrian        using atf::ui::format_text_with_tag;
328235680Sadrian
329235680Sadrian        if (!m_failed_tps.empty()) {
330235680Sadrian            (*m_os) << format_text("Failed (bogus) test programs:")
331235680Sadrian                    << "\n";
332235680Sadrian            (*m_os) << format_text_with_tag(join(m_failed_tps, ", "),
333239201Sadrian                                            "    ", false) << "\n\n";
334239201Sadrian        }
335239201Sadrian
336239201Sadrian        if (!m_expected_failures_tcs.empty()) {
337239201Sadrian            write_expected_failures(m_expected_failures_tcs, *m_os);
338239201Sadrian            (*m_os) << "\n";
339239201Sadrian        }
340239201Sadrian
341239201Sadrian        if (!m_failed_tcs.empty()) {
342239201Sadrian            (*m_os) << format_text("Failed test cases:") << "\n";
343239201Sadrian            (*m_os) << format_text_with_tag(join(m_failed_tcs, ", "),
344239201Sadrian                                            "    ", false) << "\n\n";
345239201Sadrian        }
346239201Sadrian
347239201Sadrian        (*m_os) << format_text("Summary for " + to_string(m_ntps) +
348239201Sadrian                               " test programs:") << "\n";
349239201Sadrian        (*m_os) << format_text_with_tag(to_string(m_tcs_passed) +
350239201Sadrian                                        " passed test cases.",
351239201Sadrian                                        "    ", false) << "\n";
352239201Sadrian        (*m_os) << format_text_with_tag(to_string(m_tcs_failed) +
353235680Sadrian                                        " failed test cases.",
354239051Sadrian                                        "    ", false) << "\n";
355239051Sadrian        (*m_os) << format_text_with_tag(to_string(m_tcs_expected_failures) +
356239051Sadrian                                        " expected failed test cases.",
357239051Sadrian                                        "    ", false) << "\n";
358235680Sadrian        (*m_os) << format_text_with_tag(to_string(m_tcs_skipped) +
359239051Sadrian                                        " skipped test cases.",
360239051Sadrian                                        "    ", false) << "\n";
361239051Sadrian    }
362239051Sadrian
363235680Sadrianpublic:
364235680Sadrian    ticker_writer(const atf::fs::path& p) :
365235680Sadrian        m_os(open_outfile(p))
366235680Sadrian    {
367235680Sadrian    }
368235680Sadrian};
369235680Sadrian
370235680Sadrian// ------------------------------------------------------------------------
371235680Sadrian// The "xml" class.
372235680Sadrian// ------------------------------------------------------------------------
373235680Sadrian
374235680Sadrian//!
375235680Sadrian//! \brief A single-file XML output format.
376235680Sadrian//!
377235680Sadrian//! The xml_writer class implements a formatter that prints the results
378235680Sadrian//! of test cases in an XML format easily parseable later on by other
379235680Sadrian//! utilities.
380235680Sadrian//!
381235680Sadrianclass xml_writer : public writer {
382248671Sadrian    ostream_ptr m_os;
383248671Sadrian
384248671Sadrian    size_t m_curtp, m_ntps;
385248671Sadrian    std::string m_tcname, m_tpname;
386248671Sadrian
387248671Sadrian    static
388248671Sadrian    std::string
389248671Sadrian    attrval(const std::string& str)
390248671Sadrian    {
391248671Sadrian        return str;
392248671Sadrian    }
393248671Sadrian
394248671Sadrian    static
395248671Sadrian    std::string
396248671Sadrian    elemval(const std::string& str)
397248671Sadrian    {
398248671Sadrian        std::string ostr;
399248671Sadrian        for (std::string::const_iterator iter = str.begin();
400248671Sadrian             iter != str.end(); iter++) {
401248671Sadrian            switch (*iter) {
402250619Sadrian            case '&': ostr += "&amp;"; break;
403250619Sadrian            case '<': ostr += "&lt;"; break;
404250619Sadrian            case '>': ostr += "&gt;"; break;
405250619Sadrian            default:  ostr += *iter;
406250619Sadrian            }
407248671Sadrian        }
408248671Sadrian        return ostr;
409248671Sadrian    }
410248671Sadrian
411248671Sadrian    void
412248671Sadrian    write_info(const std::string& what, const std::string& val)
413248671Sadrian    {
414248671Sadrian        (*m_os) << "<info class=\"" << what << "\">" << val << "</info>\n";
415248671Sadrian    }
416248671Sadrian
417248671Sadrian    void
418248671Sadrian    write_tp_start(const std::string& tp,
419248671Sadrian                   size_t ntcs ATF_DEFS_ATTRIBUTE_UNUSED)
420235680Sadrian    {
421235680Sadrian        (*m_os) << "<tp id=\"" << attrval(tp) << "\">\n";
422235680Sadrian    }
423235680Sadrian
424235680Sadrian    void
425235680Sadrian    write_tp_end(struct timeval* tv, const std::string& reason)
426235680Sadrian    {
427235680Sadrian        if (!reason.empty())
428235680Sadrian            (*m_os) << "<failed>" << elemval(reason) << "</failed>\n";
429235680Sadrian        (*m_os) << "<tp-time>" << format_tv(tv) << "</tp-time>";
430235680Sadrian        (*m_os) << "</tp>\n";
431235680Sadrian    }
432235680Sadrian
433235680Sadrian    void
434235680Sadrian    write_tc_start(const std::string& tcname)
435235680Sadrian    {
436235680Sadrian        (*m_os) << "<tc id=\"" << attrval(tcname) << "\">\n";
437235680Sadrian    }
438235680Sadrian
439235680Sadrian    void
440235680Sadrian    write_tc_stdout_line(const std::string& line)
441235680Sadrian    {
442235680Sadrian        (*m_os) << "<so>" << elemval(line) << "</so>\n";
443235680Sadrian    }
444235680Sadrian
445235680Sadrian    void
446248671Sadrian    write_tc_stderr_line(const std::string& line)
447235680Sadrian    {
448235680Sadrian        (*m_os) << "<se>" << elemval(line) << "</se>\n";
449235680Sadrian    }
450235680Sadrian
451235680Sadrian    void
452235680Sadrian    write_tc_end(const std::string& state, struct timeval* tv,
453235680Sadrian                 const std::string& reason)
454235680Sadrian    {
455235680Sadrian        std::string str;
456235680Sadrian
457235680Sadrian        if (state == "expected_death" || state == "expected_exit" ||
458235680Sadrian            state == "expected_failure" || state == "expected_signal" ||
459250619Sadrian            state == "expected_timeout") {
460250619Sadrian            (*m_os) << "<" << state << ">" << elemval(reason)
461250619Sadrian                    << "</" << state << ">\n";
462250619Sadrian        } else if (state == "passed") {
463235680Sadrian            (*m_os) << "<passed />\n";
464235680Sadrian        } else if (state == "failed") {
465235680Sadrian            (*m_os) << "<failed>" << elemval(reason) << "</failed>\n";
466235680Sadrian        } else if (state == "skipped") {
467235680Sadrian            (*m_os) << "<skipped>" << elemval(reason) << "</skipped>\n";
468235680Sadrian        } else
469235680Sadrian            UNREACHABLE;
470235680Sadrian        (*m_os) << "<tc-time>" << format_tv(tv) << "</tc-time>";
471235680Sadrian        (*m_os) << "</tc>\n";
472235680Sadrian    }
473235680Sadrian
474235680Sadrian    void
475235680Sadrian    write_eof(void)
476235680Sadrian    {
477235680Sadrian        (*m_os) << "</tests-results>\n";
478235680Sadrian    }
479235680Sadrian
480235680Sadrianpublic:
481235680Sadrian    xml_writer(const atf::fs::path& p) :
482235680Sadrian        m_os(open_outfile(p))
483235680Sadrian    {
484235680Sadrian        (*m_os) << "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n"
485235680Sadrian                << "<!DOCTYPE tests-results PUBLIC "
486248750Sadrian                   "\"-//NetBSD//DTD ATF Tests Results 0.1//EN\" "
487248750Sadrian                   "\"http://www.NetBSD.org/XML/atf/tests-results.dtd\">\n\n"
488248750Sadrian                   "<tests-results>\n";
489248750Sadrian    }
490235680Sadrian};
491238609Sadrian
492235680Sadrian// ------------------------------------------------------------------------
493238609Sadrian// The "converter" class.
494238609Sadrian// ------------------------------------------------------------------------
495235680Sadrian
496235680Sadrian//!
497235680Sadrian//! \brief A reader that redirects events to multiple writers.
498248750Sadrian//!
499248750Sadrian//! The converter class implements an atf_tps_reader that, for each event
500248750Sadrian//! raised by the parser, redirects it to multiple writers so that they
501248750Sadrian//! can reformat it according to their output rules.
502235680Sadrian//!
503235680Sadrianclass converter : public atf::atf_report::atf_tps_reader {
504235680Sadrian    typedef std::vector< writer* > outs_vector;
505235680Sadrian    outs_vector m_outs;
506235680Sadrian
507235680Sadrian    void
508235680Sadrian    got_info(const std::string& what, const std::string& val)
509235680Sadrian    {
510235680Sadrian        for (outs_vector::iterator iter = m_outs.begin();
511235680Sadrian             iter != m_outs.end(); iter++)
512235680Sadrian            (*iter)->write_info(what, val);
513235680Sadrian    }
514235680Sadrian
515235680Sadrian    void
516235680Sadrian    got_ntps(size_t ntps)
517235680Sadrian    {
518235680Sadrian        for (outs_vector::iterator iter = m_outs.begin();
519235680Sadrian             iter != m_outs.end(); iter++)
520235680Sadrian            (*iter)->write_ntps(ntps);
521235680Sadrian    }
522235680Sadrian
523235680Sadrian    void
524235680Sadrian    got_tp_start(const std::string& tp, size_t ntcs)
525235680Sadrian    {
526235680Sadrian        for (outs_vector::iterator iter = m_outs.begin();
527235680Sadrian             iter != m_outs.end(); iter++)
528235680Sadrian            (*iter)->write_tp_start(tp, ntcs);
529235680Sadrian    }
530235680Sadrian
531235680Sadrian    void
532248671Sadrian    got_tp_end(struct timeval* tv, const std::string& reason)
533248671Sadrian    {
534248671Sadrian        for (outs_vector::iterator iter = m_outs.begin();
535248671Sadrian             iter != m_outs.end(); iter++)
536248671Sadrian            (*iter)->write_tp_end(tv, reason);
537248671Sadrian    }
538235680Sadrian
539235680Sadrian    void
540235680Sadrian    got_tc_start(const std::string& tcname)
541235680Sadrian    {
542235680Sadrian        for (outs_vector::iterator iter = m_outs.begin();
543235680Sadrian             iter != m_outs.end(); iter++)
544239201Sadrian            (*iter)->write_tc_start(tcname);
545239201Sadrian    }
546239201Sadrian
547239201Sadrian    void
548239201Sadrian    got_tc_stdout_line(const std::string& line)
549239201Sadrian    {
550235680Sadrian        for (outs_vector::iterator iter = m_outs.begin();
551235680Sadrian             iter != m_outs.end(); iter++)
552239201Sadrian            (*iter)->write_tc_stdout_line(line);
553235680Sadrian    }
554235680Sadrian
555235680Sadrian    void
556235680Sadrian    got_tc_stderr_line(const std::string& line)
557235680Sadrian    {
558235680Sadrian        for (outs_vector::iterator iter = m_outs.begin();
559235680Sadrian             iter != m_outs.end(); iter++)
560248750Sadrian            (*iter)->write_tc_stderr_line(line);
561248750Sadrian    }
562248671Sadrian
563248750Sadrian    void
564248750Sadrian    got_tc_end(const std::string& state, struct timeval* tv,
565248750Sadrian               const std::string& reason)
566248750Sadrian    {
567248750Sadrian        for (outs_vector::iterator iter = m_outs.begin();
568248750Sadrian             iter != m_outs.end(); iter++)
569248750Sadrian            (*iter)->write_tc_end(state, tv, reason);
570248750Sadrian    }
571248750Sadrian
572248750Sadrian    void
573248750Sadrian    got_eof(void)
574248750Sadrian    {
575248750Sadrian        for (outs_vector::iterator iter = m_outs.begin();
576248750Sadrian             iter != m_outs.end(); iter++)
577248750Sadrian            (*iter)->write_eof();
578248750Sadrian    }
579248750Sadrian
580248750Sadrianpublic:
581248750Sadrian    converter(std::istream& is) :
582248750Sadrian        atf::atf_report::atf_tps_reader(is)
583248750Sadrian    {
584248750Sadrian    }
585248750Sadrian
586248750Sadrian    ~converter(void)
587248750Sadrian    {
588248750Sadrian        for (outs_vector::iterator iter = m_outs.begin();
589248750Sadrian             iter != m_outs.end(); iter++)
590248750Sadrian            delete *iter;
591248750Sadrian    }
592248750Sadrian
593248750Sadrian    void
594248750Sadrian    add_output(const std::string& fmt, const atf::fs::path& p)
595248750Sadrian    {
596248750Sadrian        if (fmt == "csv") {
597248750Sadrian            m_outs.push_back(new csv_writer(p));
598248750Sadrian        } else if (fmt == "ticker") {
599248750Sadrian            m_outs.push_back(new ticker_writer(p));
600248750Sadrian        } else if (fmt == "xml") {
601248750Sadrian            m_outs.push_back(new xml_writer(p));
602248750Sadrian        } else
603248750Sadrian            throw std::runtime_error("Unknown format `" + fmt + "'");
604248750Sadrian    }
605248750Sadrian};
606248750Sadrian
607248750Sadrian// ------------------------------------------------------------------------
608248750Sadrian// The "atf_report" class.
609248750Sadrian// ------------------------------------------------------------------------
610248750Sadrian
611248750Sadrianclass atf_report : public atf::application::app {
612248750Sadrian    static const char* m_description;
613248750Sadrian
614248750Sadrian    typedef std::pair< std::string, atf::fs::path > fmt_path_pair;
615248750Sadrian    std::vector< fmt_path_pair > m_oflags;
616248750Sadrian
617248750Sadrian    void process_option(int, const char*);
618248750Sadrian    options_set specific_options(void) const;
619248750Sadrian
620248750Sadrianpublic:
621248750Sadrian    atf_report(void);
622248750Sadrian
623248750Sadrian    int main(void);
624248750Sadrian};
625248750Sadrian
626248750Sadrianconst char* atf_report::m_description =
627248750Sadrian    "atf-report is a tool that parses the output of atf-run and "
628248750Sadrian    "generates user-friendly reports in multiple different formats.";
629248750Sadrian
630248750Sadrianatf_report::atf_report(void) :
631248750Sadrian    app(m_description, "atf-report(1)", "atf(7)")
632248750Sadrian{
633248750Sadrian}
634248750Sadrian
635248750Sadrianvoid
636248750Sadrianatf_report::process_option(int ch, const char* arg)
637248750Sadrian{
638248750Sadrian    switch (ch) {
639248750Sadrian    case 'o':
640248750Sadrian        {
641248750Sadrian            std::string str(arg);
642248750Sadrian            std::string::size_type pos = str.find(':');
643248750Sadrian            if (pos == std::string::npos)
644248750Sadrian                throw std::runtime_error("Syntax error in -o option");
645250783Sadrian            else {
646248750Sadrian                std::string fmt = str.substr(0, pos);
647248750Sadrian                atf::fs::path path = atf::fs::path(str.substr(pos + 1));
648248750Sadrian                m_oflags.push_back(fmt_path_pair(fmt, path));
649248750Sadrian            }
650248750Sadrian        }
651248750Sadrian        break;
652248750Sadrian
653248750Sadrian    default:
654248750Sadrian        UNREACHABLE;
655248671Sadrian    }
656248671Sadrian}
657248671Sadrian
658248671Sadrianatf_report::options_set
659248671Sadrianatf_report::specific_options(void)
660248671Sadrian    const
661248671Sadrian{
662248671Sadrian    using atf::application::option;
663248671Sadrian    options_set opts;
664248671Sadrian    opts.insert(option('o', "fmt:path", "Adds a new output file; multiple "
665250783Sadrian                                        "ones can be specified, and a - "
666248671Sadrian                                        "path means stdout"));
667248671Sadrian    return opts;
668248671Sadrian}
669248671Sadrian
670248671Sadrianint
671248750Sadrianatf_report::main(void)
672248750Sadrian{
673248750Sadrian    if (m_argc > 0)
674248750Sadrian        throw std::runtime_error("No arguments allowed");
675248750Sadrian
676248750Sadrian    if (m_oflags.empty())
677248750Sadrian        m_oflags.push_back(fmt_path_pair("ticker", atf::fs::path("-")));
678248750Sadrian
679248750Sadrian    // Look for path duplicates.
680248750Sadrian    std::set< atf::fs::path > paths;
681248750Sadrian    for (std::vector< fmt_path_pair >::const_iterator iter = m_oflags.begin();
682248750Sadrian         iter != m_oflags.end(); iter++) {
683248750Sadrian        atf::fs::path p = (*iter).second;
684248750Sadrian        if (p == atf::fs::path("/dev/stdout"))
685248750Sadrian            p = atf::fs::path("-");
686248750Sadrian        if (paths.find(p) != paths.end())
687248750Sadrian            throw std::runtime_error("The file `" + p.str() + "' was "
688248750Sadrian                                     "specified more than once");
689248750Sadrian        paths.insert((*iter).second);
690248750Sadrian    }
691235680Sadrian
692235680Sadrian    // Generate the output files.
693235680Sadrian    converter cnv(std::cin);
694235680Sadrian    for (std::vector< fmt_path_pair >::const_iterator iter = m_oflags.begin();
695235680Sadrian         iter != m_oflags.end(); iter++)
696235680Sadrian        cnv.add_output((*iter).first, (*iter).second);
697235680Sadrian    cnv.read();
698235680Sadrian
699235680Sadrian    return EXIT_SUCCESS;
700235680Sadrian}
701235680Sadrian
702235680Sadrianint
703235680Sadrianmain(int argc, char* const* argv)
704235680Sadrian{
705235680Sadrian    return atf_report().run(argc, argv);
706235680Sadrian}
707235680Sadrian