xref: /freebsd/contrib/kyua/cli/cmd_report.cpp (revision b0d29bc4)
1*b0d29bc4SBrooks Davis // Copyright 2011 The Kyua Authors.
2*b0d29bc4SBrooks Davis // All rights reserved.
3*b0d29bc4SBrooks Davis //
4*b0d29bc4SBrooks Davis // Redistribution and use in source and binary forms, with or without
5*b0d29bc4SBrooks Davis // modification, are permitted provided that the following conditions are
6*b0d29bc4SBrooks Davis // met:
7*b0d29bc4SBrooks Davis //
8*b0d29bc4SBrooks Davis // * Redistributions of source code must retain the above copyright
9*b0d29bc4SBrooks Davis //   notice, this list of conditions and the following disclaimer.
10*b0d29bc4SBrooks Davis // * Redistributions in binary form must reproduce the above copyright
11*b0d29bc4SBrooks Davis //   notice, this list of conditions and the following disclaimer in the
12*b0d29bc4SBrooks Davis //   documentation and/or other materials provided with the distribution.
13*b0d29bc4SBrooks Davis // * Neither the name of Google Inc. nor the names of its contributors
14*b0d29bc4SBrooks Davis //   may be used to endorse or promote products derived from this software
15*b0d29bc4SBrooks Davis //   without specific prior written permission.
16*b0d29bc4SBrooks Davis //
17*b0d29bc4SBrooks Davis // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18*b0d29bc4SBrooks Davis // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19*b0d29bc4SBrooks Davis // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20*b0d29bc4SBrooks Davis // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21*b0d29bc4SBrooks Davis // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22*b0d29bc4SBrooks Davis // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23*b0d29bc4SBrooks Davis // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24*b0d29bc4SBrooks Davis // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25*b0d29bc4SBrooks Davis // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26*b0d29bc4SBrooks Davis // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27*b0d29bc4SBrooks Davis // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28*b0d29bc4SBrooks Davis 
29*b0d29bc4SBrooks Davis #include "cli/cmd_report.hpp"
30*b0d29bc4SBrooks Davis 
31*b0d29bc4SBrooks Davis #include <algorithm>
32*b0d29bc4SBrooks Davis #include <cstddef>
33*b0d29bc4SBrooks Davis #include <cstdlib>
34*b0d29bc4SBrooks Davis #include <map>
35*b0d29bc4SBrooks Davis #include <ostream>
36*b0d29bc4SBrooks Davis #include <string>
37*b0d29bc4SBrooks Davis #include <vector>
38*b0d29bc4SBrooks Davis 
39*b0d29bc4SBrooks Davis #include "cli/common.ipp"
40*b0d29bc4SBrooks Davis #include "drivers/scan_results.hpp"
41*b0d29bc4SBrooks Davis #include "model/context.hpp"
42*b0d29bc4SBrooks Davis #include "model/metadata.hpp"
43*b0d29bc4SBrooks Davis #include "model/test_case.hpp"
44*b0d29bc4SBrooks Davis #include "model/test_program.hpp"
45*b0d29bc4SBrooks Davis #include "model/test_result.hpp"
46*b0d29bc4SBrooks Davis #include "model/types.hpp"
47*b0d29bc4SBrooks Davis #include "store/layout.hpp"
48*b0d29bc4SBrooks Davis #include "store/read_transaction.hpp"
49*b0d29bc4SBrooks Davis #include "utils/cmdline/exceptions.hpp"
50*b0d29bc4SBrooks Davis #include "utils/cmdline/options.hpp"
51*b0d29bc4SBrooks Davis #include "utils/cmdline/parser.ipp"
52*b0d29bc4SBrooks Davis #include "utils/cmdline/ui.hpp"
53*b0d29bc4SBrooks Davis #include "utils/datetime.hpp"
54*b0d29bc4SBrooks Davis #include "utils/defs.hpp"
55*b0d29bc4SBrooks Davis #include "utils/format/macros.hpp"
56*b0d29bc4SBrooks Davis #include "utils/fs/path.hpp"
57*b0d29bc4SBrooks Davis #include "utils/optional.ipp"
58*b0d29bc4SBrooks Davis #include "utils/sanity.hpp"
59*b0d29bc4SBrooks Davis #include "utils/stream.hpp"
60*b0d29bc4SBrooks Davis #include "utils/text/operations.ipp"
61*b0d29bc4SBrooks Davis 
62*b0d29bc4SBrooks Davis namespace cmdline = utils::cmdline;
63*b0d29bc4SBrooks Davis namespace config = utils::config;
64*b0d29bc4SBrooks Davis namespace datetime = utils::datetime;
65*b0d29bc4SBrooks Davis namespace fs = utils::fs;
66*b0d29bc4SBrooks Davis namespace layout = store::layout;
67*b0d29bc4SBrooks Davis namespace text = utils::text;
68*b0d29bc4SBrooks Davis 
69*b0d29bc4SBrooks Davis using cli::cmd_report;
70*b0d29bc4SBrooks Davis using utils::optional;
71*b0d29bc4SBrooks Davis 
72*b0d29bc4SBrooks Davis 
73*b0d29bc4SBrooks Davis namespace {
74*b0d29bc4SBrooks Davis 
75*b0d29bc4SBrooks Davis 
76*b0d29bc4SBrooks Davis /// Generates a plain-text report intended to be printed to the console.
77*b0d29bc4SBrooks Davis class report_console_hooks : public drivers::scan_results::base_hooks {
78*b0d29bc4SBrooks Davis     /// Stream to which to write the report.
79*b0d29bc4SBrooks Davis     std::ostream& _output;
80*b0d29bc4SBrooks Davis 
81*b0d29bc4SBrooks Davis     /// Whether to include details in the report or not.
82*b0d29bc4SBrooks Davis     const bool _verbose;
83*b0d29bc4SBrooks Davis 
84*b0d29bc4SBrooks Davis     /// Collection of result types to include in the report.
85*b0d29bc4SBrooks Davis     const cli::result_types& _results_filters;
86*b0d29bc4SBrooks Davis 
87*b0d29bc4SBrooks Davis     /// Path to the results file being read.
88*b0d29bc4SBrooks Davis     const fs::path& _results_file;
89*b0d29bc4SBrooks Davis 
90*b0d29bc4SBrooks Davis     /// The start time of the first test.
91*b0d29bc4SBrooks Davis     optional< utils::datetime::timestamp > _start_time;
92*b0d29bc4SBrooks Davis 
93*b0d29bc4SBrooks Davis     /// The end time of the last test.
94*b0d29bc4SBrooks Davis     optional< utils::datetime::timestamp > _end_time;
95*b0d29bc4SBrooks Davis 
96*b0d29bc4SBrooks Davis     /// The total run time of the tests.  Note that we cannot subtract _end_time
97*b0d29bc4SBrooks Davis     /// from _start_time to compute this due to parallel execution.
98*b0d29bc4SBrooks Davis     utils::datetime::delta _runtime;
99*b0d29bc4SBrooks Davis 
100*b0d29bc4SBrooks Davis     /// Representation of a single result.
101*b0d29bc4SBrooks Davis     struct result_data {
102*b0d29bc4SBrooks Davis         /// The relative path to the test program.
103*b0d29bc4SBrooks Davis         utils::fs::path binary_path;
104*b0d29bc4SBrooks Davis 
105*b0d29bc4SBrooks Davis         /// The name of the test case.
106*b0d29bc4SBrooks Davis         std::string test_case_name;
107*b0d29bc4SBrooks Davis 
108*b0d29bc4SBrooks Davis         /// The result of the test case.
109*b0d29bc4SBrooks Davis         model::test_result result;
110*b0d29bc4SBrooks Davis 
111*b0d29bc4SBrooks Davis         /// The duration of the test case execution.
112*b0d29bc4SBrooks Davis         utils::datetime::delta duration;
113*b0d29bc4SBrooks Davis 
114*b0d29bc4SBrooks Davis         /// Constructs a new results data.
115*b0d29bc4SBrooks Davis         ///
116*b0d29bc4SBrooks Davis         /// \param binary_path_ The relative path to the test program.
117*b0d29bc4SBrooks Davis         /// \param test_case_name_ The name of the test case.
118*b0d29bc4SBrooks Davis         /// \param result_ The result of the test case.
119*b0d29bc4SBrooks Davis         /// \param duration_ The duration of the test case execution.
result_data__anona78b41d90111::report_console_hooks::result_data120*b0d29bc4SBrooks Davis         result_data(const utils::fs::path& binary_path_,
121*b0d29bc4SBrooks Davis                     const std::string& test_case_name_,
122*b0d29bc4SBrooks Davis                     const model::test_result& result_,
123*b0d29bc4SBrooks Davis                     const utils::datetime::delta& duration_) :
124*b0d29bc4SBrooks Davis             binary_path(binary_path_), test_case_name(test_case_name_),
125*b0d29bc4SBrooks Davis             result(result_), duration(duration_)
126*b0d29bc4SBrooks Davis         {
127*b0d29bc4SBrooks Davis         }
128*b0d29bc4SBrooks Davis     };
129*b0d29bc4SBrooks Davis 
130*b0d29bc4SBrooks Davis     /// Results received, broken down by their type.
131*b0d29bc4SBrooks Davis     ///
132*b0d29bc4SBrooks Davis     /// Note that this may not include all results, as keeping the whole list in
133*b0d29bc4SBrooks Davis     /// memory may be too much.
134*b0d29bc4SBrooks Davis     std::map< model::test_result_type, std::vector< result_data > > _results;
135*b0d29bc4SBrooks Davis 
136*b0d29bc4SBrooks Davis     /// Pretty-prints the value of an environment variable.
137*b0d29bc4SBrooks Davis     ///
138*b0d29bc4SBrooks Davis     /// \param indent Prefix for the lines to print.  Continuation lines
139*b0d29bc4SBrooks Davis     ///     use this indentation twice.
140*b0d29bc4SBrooks Davis     /// \param name Name of the variable.
141*b0d29bc4SBrooks Davis     /// \param value Value of the variable.  Can have newlines.
142*b0d29bc4SBrooks Davis     void
print_env_var(const char * indent,const std::string & name,const std::string & value)143*b0d29bc4SBrooks Davis     print_env_var(const char* indent, const std::string& name,
144*b0d29bc4SBrooks Davis                   const std::string& value)
145*b0d29bc4SBrooks Davis     {
146*b0d29bc4SBrooks Davis         const std::vector< std::string > lines = text::split(value, '\n');
147*b0d29bc4SBrooks Davis         if (lines.size() == 0) {
148*b0d29bc4SBrooks Davis             _output << F("%s%s=\n") % indent % name;;
149*b0d29bc4SBrooks Davis         } else {
150*b0d29bc4SBrooks Davis             _output << F("%s%s=%s\n") % indent % name % lines[0];
151*b0d29bc4SBrooks Davis             for (std::vector< std::string >::size_type i = 1;
152*b0d29bc4SBrooks Davis                  i < lines.size(); ++i) {
153*b0d29bc4SBrooks Davis                 _output << F("%s%s%s\n") % indent % indent % lines[i];
154*b0d29bc4SBrooks Davis             }
155*b0d29bc4SBrooks Davis         }
156*b0d29bc4SBrooks Davis     }
157*b0d29bc4SBrooks Davis 
158*b0d29bc4SBrooks Davis     /// Prints the execution context to the output.
159*b0d29bc4SBrooks Davis     ///
160*b0d29bc4SBrooks Davis     /// \param context The context to dump.
161*b0d29bc4SBrooks Davis     void
print_context(const model::context & context)162*b0d29bc4SBrooks Davis     print_context(const model::context& context)
163*b0d29bc4SBrooks Davis     {
164*b0d29bc4SBrooks Davis         _output << "===> Execution context\n";
165*b0d29bc4SBrooks Davis 
166*b0d29bc4SBrooks Davis         _output << F("Current directory: %s\n") % context.cwd();
167*b0d29bc4SBrooks Davis         const std::map< std::string, std::string >& env = context.env();
168*b0d29bc4SBrooks Davis         if (env.empty())
169*b0d29bc4SBrooks Davis             _output << "No environment variables recorded\n";
170*b0d29bc4SBrooks Davis         else {
171*b0d29bc4SBrooks Davis             _output << "Environment variables:\n";
172*b0d29bc4SBrooks Davis             for (std::map< std::string, std::string >::const_iterator
173*b0d29bc4SBrooks Davis                      iter = env.begin(); iter != env.end(); iter++) {
174*b0d29bc4SBrooks Davis                 print_env_var("    ", (*iter).first, (*iter).second);
175*b0d29bc4SBrooks Davis             }
176*b0d29bc4SBrooks Davis         }
177*b0d29bc4SBrooks Davis     }
178*b0d29bc4SBrooks Davis 
179*b0d29bc4SBrooks Davis     /// Dumps a detailed view of the test case.
180*b0d29bc4SBrooks Davis     ///
181*b0d29bc4SBrooks Davis     /// \param result_iter Results iterator pointing at the test case to be
182*b0d29bc4SBrooks Davis     ///     dumped.
183*b0d29bc4SBrooks Davis     void
print_test_case_and_result(const store::results_iterator & result_iter)184*b0d29bc4SBrooks Davis     print_test_case_and_result(const store::results_iterator& result_iter)
185*b0d29bc4SBrooks Davis     {
186*b0d29bc4SBrooks Davis         const model::test_case& test_case =
187*b0d29bc4SBrooks Davis             result_iter.test_program()->find(result_iter.test_case_name());
188*b0d29bc4SBrooks Davis         const model::properties_map props =
189*b0d29bc4SBrooks Davis             test_case.get_metadata().to_properties();
190*b0d29bc4SBrooks Davis 
191*b0d29bc4SBrooks Davis         _output << F("===> %s:%s\n") %
192*b0d29bc4SBrooks Davis             result_iter.test_program()->relative_path() %
193*b0d29bc4SBrooks Davis             result_iter.test_case_name();
194*b0d29bc4SBrooks Davis         _output << F("Result:     %s\n") %
195*b0d29bc4SBrooks Davis             cli::format_result(result_iter.result());
196*b0d29bc4SBrooks Davis         _output << F("Start time: %s\n") %
197*b0d29bc4SBrooks Davis             result_iter.start_time().to_iso8601_in_utc();
198*b0d29bc4SBrooks Davis         _output << F("End time:   %s\n") %
199*b0d29bc4SBrooks Davis             result_iter.end_time().to_iso8601_in_utc();
200*b0d29bc4SBrooks Davis         _output << F("Duration:   %s\n") %
201*b0d29bc4SBrooks Davis             cli::format_delta(result_iter.end_time() -
202*b0d29bc4SBrooks Davis                               result_iter.start_time());
203*b0d29bc4SBrooks Davis 
204*b0d29bc4SBrooks Davis         _output << "\n";
205*b0d29bc4SBrooks Davis         _output << "Metadata:\n";
206*b0d29bc4SBrooks Davis         for (model::properties_map::const_iterator iter = props.begin();
207*b0d29bc4SBrooks Davis              iter != props.end(); ++iter) {
208*b0d29bc4SBrooks Davis             if ((*iter).second.empty()) {
209*b0d29bc4SBrooks Davis                 _output << F("    %s is empty\n") % (*iter).first;
210*b0d29bc4SBrooks Davis             } else {
211*b0d29bc4SBrooks Davis                 _output << F("    %s = %s\n") % (*iter).first % (*iter).second;
212*b0d29bc4SBrooks Davis             }
213*b0d29bc4SBrooks Davis         }
214*b0d29bc4SBrooks Davis 
215*b0d29bc4SBrooks Davis         const std::string stdout_contents = result_iter.stdout_contents();
216*b0d29bc4SBrooks Davis         if (!stdout_contents.empty()) {
217*b0d29bc4SBrooks Davis             _output << "\n"
218*b0d29bc4SBrooks Davis                     << "Standard output:\n"
219*b0d29bc4SBrooks Davis                     << stdout_contents;
220*b0d29bc4SBrooks Davis         }
221*b0d29bc4SBrooks Davis 
222*b0d29bc4SBrooks Davis         const std::string stderr_contents = result_iter.stderr_contents();
223*b0d29bc4SBrooks Davis         if (!stderr_contents.empty()) {
224*b0d29bc4SBrooks Davis             _output << "\n"
225*b0d29bc4SBrooks Davis                     << "Standard error:\n"
226*b0d29bc4SBrooks Davis                     << stderr_contents;
227*b0d29bc4SBrooks Davis         }
228*b0d29bc4SBrooks Davis     }
229*b0d29bc4SBrooks Davis 
230*b0d29bc4SBrooks Davis     /// Counts how many results of a given type have been received.
231*b0d29bc4SBrooks Davis     ///
232*b0d29bc4SBrooks Davis     /// \param type Test result type to count results for.
233*b0d29bc4SBrooks Davis     ///
234*b0d29bc4SBrooks Davis     /// \return The number of test results with \p type.
235*b0d29bc4SBrooks Davis     std::size_t
count_results(const model::test_result_type type)236*b0d29bc4SBrooks Davis     count_results(const model::test_result_type type)
237*b0d29bc4SBrooks Davis     {
238*b0d29bc4SBrooks Davis         const std::map< model::test_result_type,
239*b0d29bc4SBrooks Davis                         std::vector< result_data > >::const_iterator iter =
240*b0d29bc4SBrooks Davis             _results.find(type);
241*b0d29bc4SBrooks Davis         if (iter == _results.end())
242*b0d29bc4SBrooks Davis             return 0;
243*b0d29bc4SBrooks Davis         else
244*b0d29bc4SBrooks Davis             return (*iter).second.size();
245*b0d29bc4SBrooks Davis     }
246*b0d29bc4SBrooks Davis 
247*b0d29bc4SBrooks Davis     /// Prints a set of results.
248*b0d29bc4SBrooks Davis     ///
249*b0d29bc4SBrooks Davis     /// \param type Test result type to print results for.
250*b0d29bc4SBrooks Davis     /// \param title Title used when printing results.
251*b0d29bc4SBrooks Davis     void
print_results(const model::test_result_type type,const char * title)252*b0d29bc4SBrooks Davis     print_results(const model::test_result_type type,
253*b0d29bc4SBrooks Davis                   const char* title)
254*b0d29bc4SBrooks Davis     {
255*b0d29bc4SBrooks Davis         const std::map< model::test_result_type,
256*b0d29bc4SBrooks Davis                         std::vector< result_data > >::const_iterator iter2 =
257*b0d29bc4SBrooks Davis             _results.find(type);
258*b0d29bc4SBrooks Davis         if (iter2 == _results.end())
259*b0d29bc4SBrooks Davis             return;
260*b0d29bc4SBrooks Davis         const std::vector< result_data >& all = (*iter2).second;
261*b0d29bc4SBrooks Davis 
262*b0d29bc4SBrooks Davis         _output << F("===> %s\n") % title;
263*b0d29bc4SBrooks Davis         for (std::vector< result_data >::const_iterator iter = all.begin();
264*b0d29bc4SBrooks Davis              iter != all.end(); iter++) {
265*b0d29bc4SBrooks Davis             _output << F("%s:%s  ->  %s  [%s]\n") % (*iter).binary_path %
266*b0d29bc4SBrooks Davis                 (*iter).test_case_name %
267*b0d29bc4SBrooks Davis                 cli::format_result((*iter).result) %
268*b0d29bc4SBrooks Davis                 cli::format_delta((*iter).duration);
269*b0d29bc4SBrooks Davis         }
270*b0d29bc4SBrooks Davis     }
271*b0d29bc4SBrooks Davis 
272*b0d29bc4SBrooks Davis public:
273*b0d29bc4SBrooks Davis     /// Constructor for the hooks.
274*b0d29bc4SBrooks Davis     ///
275*b0d29bc4SBrooks Davis     /// \param [out] output_ Stream to which to write the report.
276*b0d29bc4SBrooks Davis     /// \param verbose_ Whether to include details in the output or not.
277*b0d29bc4SBrooks Davis     /// \param results_filters_ The result types to include in the report.
278*b0d29bc4SBrooks Davis     ///     Cannot be empty.
279*b0d29bc4SBrooks Davis     /// \param results_file_ Path to the results file being read.
report_console_hooks(std::ostream & output_,const bool verbose_,const cli::result_types & results_filters_,const fs::path & results_file_)280*b0d29bc4SBrooks Davis     report_console_hooks(std::ostream& output_, const bool verbose_,
281*b0d29bc4SBrooks Davis                          const cli::result_types& results_filters_,
282*b0d29bc4SBrooks Davis                          const fs::path& results_file_) :
283*b0d29bc4SBrooks Davis         _output(output_),
284*b0d29bc4SBrooks Davis         _verbose(verbose_),
285*b0d29bc4SBrooks Davis         _results_filters(results_filters_),
286*b0d29bc4SBrooks Davis         _results_file(results_file_)
287*b0d29bc4SBrooks Davis     {
288*b0d29bc4SBrooks Davis         PRE(!results_filters_.empty());
289*b0d29bc4SBrooks Davis     }
290*b0d29bc4SBrooks Davis 
291*b0d29bc4SBrooks Davis     /// Callback executed when the context is loaded.
292*b0d29bc4SBrooks Davis     ///
293*b0d29bc4SBrooks Davis     /// \param context The context loaded from the database.
294*b0d29bc4SBrooks Davis     void
got_context(const model::context & context)295*b0d29bc4SBrooks Davis     got_context(const model::context& context)
296*b0d29bc4SBrooks Davis     {
297*b0d29bc4SBrooks Davis         if (_verbose)
298*b0d29bc4SBrooks Davis             print_context(context);
299*b0d29bc4SBrooks Davis     }
300*b0d29bc4SBrooks Davis 
301*b0d29bc4SBrooks Davis     /// Callback executed when a test results is found.
302*b0d29bc4SBrooks Davis     ///
303*b0d29bc4SBrooks Davis     /// \param iter Container for the test result's data.
304*b0d29bc4SBrooks Davis     void
got_result(store::results_iterator & iter)305*b0d29bc4SBrooks Davis     got_result(store::results_iterator& iter)
306*b0d29bc4SBrooks Davis     {
307*b0d29bc4SBrooks Davis         if (!_start_time || _start_time.get() > iter.start_time())
308*b0d29bc4SBrooks Davis             _start_time = iter.start_time();
309*b0d29bc4SBrooks Davis         if (!_end_time || _end_time.get() < iter.end_time())
310*b0d29bc4SBrooks Davis             _end_time = iter.end_time();
311*b0d29bc4SBrooks Davis 
312*b0d29bc4SBrooks Davis         const datetime::delta duration = iter.end_time() - iter.start_time();
313*b0d29bc4SBrooks Davis 
314*b0d29bc4SBrooks Davis         _runtime += duration;
315*b0d29bc4SBrooks Davis         const model::test_result result = iter.result();
316*b0d29bc4SBrooks Davis         _results[result.type()].push_back(
317*b0d29bc4SBrooks Davis             result_data(iter.test_program()->relative_path(),
318*b0d29bc4SBrooks Davis                         iter.test_case_name(), iter.result(), duration));
319*b0d29bc4SBrooks Davis 
320*b0d29bc4SBrooks Davis         if (_verbose) {
321*b0d29bc4SBrooks Davis             // TODO(jmmv): _results_filters is a list and is small enough for
322*b0d29bc4SBrooks Davis             // std::find to not be an expensive operation here (probably).  But
323*b0d29bc4SBrooks Davis             // we should be using a std::set instead.
324*b0d29bc4SBrooks Davis             if (std::find(_results_filters.begin(), _results_filters.end(),
325*b0d29bc4SBrooks Davis                           iter.result().type()) != _results_filters.end()) {
326*b0d29bc4SBrooks Davis                 print_test_case_and_result(iter);
327*b0d29bc4SBrooks Davis             }
328*b0d29bc4SBrooks Davis         }
329*b0d29bc4SBrooks Davis     }
330*b0d29bc4SBrooks Davis 
331*b0d29bc4SBrooks Davis     /// Prints the tests summary.
332*b0d29bc4SBrooks Davis     void
end(const drivers::scan_results::result &)333*b0d29bc4SBrooks Davis     end(const drivers::scan_results::result& /* r */)
334*b0d29bc4SBrooks Davis     {
335*b0d29bc4SBrooks Davis         typedef std::map< model::test_result_type, const char* > types_map;
336*b0d29bc4SBrooks Davis 
337*b0d29bc4SBrooks Davis         types_map titles;
338*b0d29bc4SBrooks Davis         titles[model::test_result_broken] = "Broken tests";
339*b0d29bc4SBrooks Davis         titles[model::test_result_expected_failure] = "Expected failures";
340*b0d29bc4SBrooks Davis         titles[model::test_result_failed] = "Failed tests";
341*b0d29bc4SBrooks Davis         titles[model::test_result_passed] = "Passed tests";
342*b0d29bc4SBrooks Davis         titles[model::test_result_skipped] = "Skipped tests";
343*b0d29bc4SBrooks Davis 
344*b0d29bc4SBrooks Davis         for (cli::result_types::const_iterator iter = _results_filters.begin();
345*b0d29bc4SBrooks Davis              iter != _results_filters.end(); ++iter) {
346*b0d29bc4SBrooks Davis             const types_map::const_iterator match = titles.find(*iter);
347*b0d29bc4SBrooks Davis             INV_MSG(match != titles.end(), "Conditional does not match user "
348*b0d29bc4SBrooks Davis                     "input validation in parse_types()");
349*b0d29bc4SBrooks Davis             print_results((*match).first, (*match).second);
350*b0d29bc4SBrooks Davis         }
351*b0d29bc4SBrooks Davis 
352*b0d29bc4SBrooks Davis         const std::size_t broken = count_results(model::test_result_broken);
353*b0d29bc4SBrooks Davis         const std::size_t failed = count_results(model::test_result_failed);
354*b0d29bc4SBrooks Davis         const std::size_t passed = count_results(model::test_result_passed);
355*b0d29bc4SBrooks Davis         const std::size_t skipped = count_results(model::test_result_skipped);
356*b0d29bc4SBrooks Davis         const std::size_t xfail = count_results(
357*b0d29bc4SBrooks Davis             model::test_result_expected_failure);
358*b0d29bc4SBrooks Davis         const std::size_t total = broken + failed + passed + skipped + xfail;
359*b0d29bc4SBrooks Davis 
360*b0d29bc4SBrooks Davis         _output << "===> Summary\n";
361*b0d29bc4SBrooks Davis         _output << F("Results read from %s\n") % _results_file;
362*b0d29bc4SBrooks Davis         _output << F("Test cases: %s total, %s skipped, %s expected failures, "
363*b0d29bc4SBrooks Davis                      "%s broken, %s failed\n") %
364*b0d29bc4SBrooks Davis             total % skipped % xfail % broken % failed;
365*b0d29bc4SBrooks Davis         if (_verbose && _start_time) {
366*b0d29bc4SBrooks Davis             INV(_end_time);
367*b0d29bc4SBrooks Davis             _output << F("Start time: %s\n") %
368*b0d29bc4SBrooks Davis                     _start_time.get().to_iso8601_in_utc();
369*b0d29bc4SBrooks Davis             _output << F("End time:   %s\n") %
370*b0d29bc4SBrooks Davis                     _end_time.get().to_iso8601_in_utc();
371*b0d29bc4SBrooks Davis         }
372*b0d29bc4SBrooks Davis         _output << F("Total time: %s\n") % cli::format_delta(_runtime);
373*b0d29bc4SBrooks Davis     }
374*b0d29bc4SBrooks Davis };
375*b0d29bc4SBrooks Davis 
376*b0d29bc4SBrooks Davis 
377*b0d29bc4SBrooks Davis }  // anonymous namespace
378*b0d29bc4SBrooks Davis 
379*b0d29bc4SBrooks Davis 
380*b0d29bc4SBrooks Davis /// Default constructor for cmd_report.
cmd_report(void)381*b0d29bc4SBrooks Davis cmd_report::cmd_report(void) : cli_command(
382*b0d29bc4SBrooks Davis     "report", "", 0, -1,
383*b0d29bc4SBrooks Davis     "Generates a report with the results of a test suite run")
384*b0d29bc4SBrooks Davis {
385*b0d29bc4SBrooks Davis     add_option(results_file_open_option);
386*b0d29bc4SBrooks Davis     add_option(cmdline::bool_option(
387*b0d29bc4SBrooks Davis         "verbose", "Include the execution context and the details of each test "
388*b0d29bc4SBrooks Davis         "case in the report"));
389*b0d29bc4SBrooks Davis     add_option(cmdline::path_option("output", "Path to the output file", "path",
390*b0d29bc4SBrooks Davis                                     "/dev/stdout"));
391*b0d29bc4SBrooks Davis     add_option(results_filter_option);
392*b0d29bc4SBrooks Davis }
393*b0d29bc4SBrooks Davis 
394*b0d29bc4SBrooks Davis 
395*b0d29bc4SBrooks Davis /// Entry point for the "report" subcommand.
396*b0d29bc4SBrooks Davis ///
397*b0d29bc4SBrooks Davis /// \param ui Object to interact with the I/O of the program.
398*b0d29bc4SBrooks Davis /// \param cmdline Representation of the command line to the subcommand.
399*b0d29bc4SBrooks Davis ///
400*b0d29bc4SBrooks Davis /// \return 0 if everything is OK, 1 if the statement is invalid or if there is
401*b0d29bc4SBrooks Davis /// any other problem.
402*b0d29bc4SBrooks Davis int
run(cmdline::ui * ui,const cmdline::parsed_cmdline & cmdline,const config::tree &)403*b0d29bc4SBrooks Davis cmd_report::run(cmdline::ui* ui,
404*b0d29bc4SBrooks Davis                 const cmdline::parsed_cmdline& cmdline,
405*b0d29bc4SBrooks Davis                 const config::tree& /* user_config */)
406*b0d29bc4SBrooks Davis {
407*b0d29bc4SBrooks Davis     std::auto_ptr< std::ostream > output = utils::open_ostream(
408*b0d29bc4SBrooks Davis         cmdline.get_option< cmdline::path_option >("output"));
409*b0d29bc4SBrooks Davis 
410*b0d29bc4SBrooks Davis     const fs::path results_file = layout::find_results(
411*b0d29bc4SBrooks Davis         results_file_open(cmdline));
412*b0d29bc4SBrooks Davis 
413*b0d29bc4SBrooks Davis     const result_types types = get_result_types(cmdline);
414*b0d29bc4SBrooks Davis     report_console_hooks hooks(*output.get(), cmdline.has_option("verbose"),
415*b0d29bc4SBrooks Davis                                types, results_file);
416*b0d29bc4SBrooks Davis     const drivers::scan_results::result result = drivers::scan_results::drive(
417*b0d29bc4SBrooks Davis         results_file, parse_filters(cmdline.arguments()), hooks);
418*b0d29bc4SBrooks Davis 
419*b0d29bc4SBrooks Davis     return report_unused_filters(result.unused_filters, ui) ?
420*b0d29bc4SBrooks Davis         EXIT_FAILURE : EXIT_SUCCESS;
421*b0d29bc4SBrooks Davis }
422