xref: /src/contrib/kyua/cli/cmd_report.cpp (revision b392a90ba4e5ea07d8a88a834fd102191d1967bf)
108334c51SBrooks Davis // Copyright 2011 The Kyua Authors.
208334c51SBrooks Davis // All rights reserved.
308334c51SBrooks Davis //
408334c51SBrooks Davis // Redistribution and use in source and binary forms, with or without
508334c51SBrooks Davis // modification, are permitted provided that the following conditions are
608334c51SBrooks Davis // met:
708334c51SBrooks Davis //
808334c51SBrooks Davis // * Redistributions of source code must retain the above copyright
908334c51SBrooks Davis //   notice, this list of conditions and the following disclaimer.
1008334c51SBrooks Davis // * Redistributions in binary form must reproduce the above copyright
1108334c51SBrooks Davis //   notice, this list of conditions and the following disclaimer in the
1208334c51SBrooks Davis //   documentation and/or other materials provided with the distribution.
1308334c51SBrooks Davis // * Neither the name of Google Inc. nor the names of its contributors
1408334c51SBrooks Davis //   may be used to endorse or promote products derived from this software
1508334c51SBrooks Davis //   without specific prior written permission.
1608334c51SBrooks Davis //
1708334c51SBrooks Davis // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1808334c51SBrooks Davis // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1908334c51SBrooks Davis // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
2008334c51SBrooks Davis // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2108334c51SBrooks Davis // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2208334c51SBrooks Davis // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2308334c51SBrooks Davis // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2408334c51SBrooks Davis // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2508334c51SBrooks Davis // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2608334c51SBrooks Davis // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2708334c51SBrooks Davis // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2808334c51SBrooks Davis 
2908334c51SBrooks Davis #include "cli/cmd_report.hpp"
3008334c51SBrooks Davis 
3108334c51SBrooks Davis #include <algorithm>
3208334c51SBrooks Davis #include <cstddef>
3308334c51SBrooks Davis #include <cstdlib>
3408334c51SBrooks Davis #include <map>
3508334c51SBrooks Davis #include <ostream>
3608334c51SBrooks Davis #include <string>
3708334c51SBrooks Davis #include <vector>
3808334c51SBrooks Davis 
3908334c51SBrooks Davis #include "cli/common.ipp"
4008334c51SBrooks Davis #include "drivers/scan_results.hpp"
4108334c51SBrooks Davis #include "model/context.hpp"
4208334c51SBrooks Davis #include "model/metadata.hpp"
4308334c51SBrooks Davis #include "model/test_case.hpp"
4408334c51SBrooks Davis #include "model/test_program.hpp"
4508334c51SBrooks Davis #include "model/test_result.hpp"
4608334c51SBrooks Davis #include "model/types.hpp"
4708334c51SBrooks Davis #include "store/layout.hpp"
4808334c51SBrooks Davis #include "store/read_transaction.hpp"
4908334c51SBrooks Davis #include "utils/cmdline/exceptions.hpp"
5008334c51SBrooks Davis #include "utils/cmdline/options.hpp"
5108334c51SBrooks Davis #include "utils/cmdline/parser.ipp"
5208334c51SBrooks Davis #include "utils/cmdline/ui.hpp"
5308334c51SBrooks Davis #include "utils/datetime.hpp"
5408334c51SBrooks Davis #include "utils/defs.hpp"
5508334c51SBrooks Davis #include "utils/format/macros.hpp"
5608334c51SBrooks Davis #include "utils/fs/path.hpp"
5708334c51SBrooks Davis #include "utils/optional.ipp"
5808334c51SBrooks Davis #include "utils/sanity.hpp"
5908334c51SBrooks Davis #include "utils/stream.hpp"
6008334c51SBrooks Davis #include "utils/text/operations.ipp"
6108334c51SBrooks Davis 
6208334c51SBrooks Davis namespace cmdline = utils::cmdline;
6308334c51SBrooks Davis namespace config = utils::config;
6408334c51SBrooks Davis namespace datetime = utils::datetime;
6508334c51SBrooks Davis namespace fs = utils::fs;
6608334c51SBrooks Davis namespace layout = store::layout;
6708334c51SBrooks Davis namespace text = utils::text;
6808334c51SBrooks Davis 
6908334c51SBrooks Davis using cli::cmd_report;
7008334c51SBrooks Davis using utils::optional;
7108334c51SBrooks Davis 
7208334c51SBrooks Davis 
7308334c51SBrooks Davis namespace {
7408334c51SBrooks Davis 
7508334c51SBrooks Davis 
7608334c51SBrooks Davis /// Generates a plain-text report intended to be printed to the console.
7708334c51SBrooks Davis class report_console_hooks : public drivers::scan_results::base_hooks {
7808334c51SBrooks Davis     /// Stream to which to write the report.
7908334c51SBrooks Davis     std::ostream& _output;
8008334c51SBrooks Davis 
8108334c51SBrooks Davis     /// Whether to include details in the report or not.
8208334c51SBrooks Davis     const bool _verbose;
8308334c51SBrooks Davis 
8408334c51SBrooks Davis     /// Collection of result types to include in the report.
8508334c51SBrooks Davis     const cli::result_types& _results_filters;
8608334c51SBrooks Davis 
8708334c51SBrooks Davis     /// Path to the results file being read.
8808334c51SBrooks Davis     const fs::path& _results_file;
8908334c51SBrooks Davis 
9008334c51SBrooks Davis     /// The start time of the first test.
9108334c51SBrooks Davis     optional< utils::datetime::timestamp > _start_time;
9208334c51SBrooks Davis 
9308334c51SBrooks Davis     /// The end time of the last test.
9408334c51SBrooks Davis     optional< utils::datetime::timestamp > _end_time;
9508334c51SBrooks Davis 
9608334c51SBrooks Davis     /// The total run time of the tests.  Note that we cannot subtract _end_time
9708334c51SBrooks Davis     /// from _start_time to compute this due to parallel execution.
9808334c51SBrooks Davis     utils::datetime::delta _runtime;
9908334c51SBrooks Davis 
10008334c51SBrooks Davis     /// Representation of a single result.
10108334c51SBrooks Davis     struct result_data {
10208334c51SBrooks Davis         /// The relative path to the test program.
10308334c51SBrooks Davis         utils::fs::path binary_path;
10408334c51SBrooks Davis 
10508334c51SBrooks Davis         /// The name of the test case.
10608334c51SBrooks Davis         std::string test_case_name;
10708334c51SBrooks Davis 
10808334c51SBrooks Davis         /// The result of the test case.
10908334c51SBrooks Davis         model::test_result result;
11008334c51SBrooks Davis 
11108334c51SBrooks Davis         /// The duration of the test case execution.
11208334c51SBrooks Davis         utils::datetime::delta duration;
11308334c51SBrooks Davis 
11408334c51SBrooks Davis         /// Constructs a new results data.
11508334c51SBrooks Davis         ///
11608334c51SBrooks Davis         /// \param binary_path_ The relative path to the test program.
11708334c51SBrooks Davis         /// \param test_case_name_ The name of the test case.
11808334c51SBrooks Davis         /// \param result_ The result of the test case.
11908334c51SBrooks Davis         /// \param duration_ The duration of the test case execution.
result_data__anondf48ccc60111::report_console_hooks::result_data12008334c51SBrooks Davis         result_data(const utils::fs::path& binary_path_,
12108334c51SBrooks Davis                     const std::string& test_case_name_,
12208334c51SBrooks Davis                     const model::test_result& result_,
12308334c51SBrooks Davis                     const utils::datetime::delta& duration_) :
12408334c51SBrooks Davis             binary_path(binary_path_), test_case_name(test_case_name_),
12508334c51SBrooks Davis             result(result_), duration(duration_)
12608334c51SBrooks Davis         {
12708334c51SBrooks Davis         }
12808334c51SBrooks Davis     };
12908334c51SBrooks Davis 
13008334c51SBrooks Davis     /// Results received, broken down by their type.
13108334c51SBrooks Davis     ///
13208334c51SBrooks Davis     /// Note that this may not include all results, as keeping the whole list in
13308334c51SBrooks Davis     /// memory may be too much.
13408334c51SBrooks Davis     std::map< model::test_result_type, std::vector< result_data > > _results;
13508334c51SBrooks Davis 
13608334c51SBrooks Davis     /// Pretty-prints the value of an environment variable.
13708334c51SBrooks Davis     ///
13808334c51SBrooks Davis     /// \param indent Prefix for the lines to print.  Continuation lines
13908334c51SBrooks Davis     ///     use this indentation twice.
14008334c51SBrooks Davis     /// \param name Name of the variable.
14108334c51SBrooks Davis     /// \param value Value of the variable.  Can have newlines.
14208334c51SBrooks Davis     void
print_env_var(const char * indent,const std::string & name,const std::string & value)14308334c51SBrooks Davis     print_env_var(const char* indent, const std::string& name,
14408334c51SBrooks Davis                   const std::string& value)
14508334c51SBrooks Davis     {
14608334c51SBrooks Davis         const std::vector< std::string > lines = text::split(value, '\n');
14708334c51SBrooks Davis         if (lines.size() == 0) {
14808334c51SBrooks Davis             _output << F("%s%s=\n") % indent % name;;
14908334c51SBrooks Davis         } else {
15008334c51SBrooks Davis             _output << F("%s%s=%s\n") % indent % name % lines[0];
15108334c51SBrooks Davis             for (std::vector< std::string >::size_type i = 1;
15208334c51SBrooks Davis                  i < lines.size(); ++i) {
15308334c51SBrooks Davis                 _output << F("%s%s%s\n") % indent % indent % lines[i];
15408334c51SBrooks Davis             }
15508334c51SBrooks Davis         }
15608334c51SBrooks Davis     }
15708334c51SBrooks Davis 
15808334c51SBrooks Davis     /// Prints the execution context to the output.
15908334c51SBrooks Davis     ///
16008334c51SBrooks Davis     /// \param context The context to dump.
16108334c51SBrooks Davis     void
print_context(const model::context & context)16208334c51SBrooks Davis     print_context(const model::context& context)
16308334c51SBrooks Davis     {
16408334c51SBrooks Davis         _output << "===> Execution context\n";
16508334c51SBrooks Davis 
16608334c51SBrooks Davis         _output << F("Current directory: %s\n") % context.cwd();
16708334c51SBrooks Davis         const std::map< std::string, std::string >& env = context.env();
16808334c51SBrooks Davis         if (env.empty())
16908334c51SBrooks Davis             _output << "No environment variables recorded\n";
17008334c51SBrooks Davis         else {
17108334c51SBrooks Davis             _output << "Environment variables:\n";
17208334c51SBrooks Davis             for (std::map< std::string, std::string >::const_iterator
17308334c51SBrooks Davis                      iter = env.begin(); iter != env.end(); iter++) {
17408334c51SBrooks Davis                 print_env_var("    ", (*iter).first, (*iter).second);
17508334c51SBrooks Davis             }
17608334c51SBrooks Davis         }
17708334c51SBrooks Davis     }
17808334c51SBrooks Davis 
17908334c51SBrooks Davis     /// Dumps a detailed view of the test case.
18008334c51SBrooks Davis     ///
18108334c51SBrooks Davis     /// \param result_iter Results iterator pointing at the test case to be
18208334c51SBrooks Davis     ///     dumped.
18308334c51SBrooks Davis     void
print_test_case_and_result(const store::results_iterator & result_iter)18408334c51SBrooks Davis     print_test_case_and_result(const store::results_iterator& result_iter)
18508334c51SBrooks Davis     {
18608334c51SBrooks Davis         const model::test_case& test_case =
18708334c51SBrooks Davis             result_iter.test_program()->find(result_iter.test_case_name());
18808334c51SBrooks Davis         const model::properties_map props =
18908334c51SBrooks Davis             test_case.get_metadata().to_properties();
19008334c51SBrooks Davis 
19108334c51SBrooks Davis         _output << F("===> %s:%s\n") %
19208334c51SBrooks Davis             result_iter.test_program()->relative_path() %
19308334c51SBrooks Davis             result_iter.test_case_name();
19408334c51SBrooks Davis         _output << F("Result:     %s\n") %
19508334c51SBrooks Davis             cli::format_result(result_iter.result());
19608334c51SBrooks Davis         _output << F("Start time: %s\n") %
19708334c51SBrooks Davis             result_iter.start_time().to_iso8601_in_utc();
19808334c51SBrooks Davis         _output << F("End time:   %s\n") %
19908334c51SBrooks Davis             result_iter.end_time().to_iso8601_in_utc();
20008334c51SBrooks Davis         _output << F("Duration:   %s\n") %
20108334c51SBrooks Davis             cli::format_delta(result_iter.end_time() -
20208334c51SBrooks Davis                               result_iter.start_time());
20308334c51SBrooks Davis 
20408334c51SBrooks Davis         _output << "\n";
20508334c51SBrooks Davis         _output << "Metadata:\n";
20608334c51SBrooks Davis         for (model::properties_map::const_iterator iter = props.begin();
20708334c51SBrooks Davis              iter != props.end(); ++iter) {
20808334c51SBrooks Davis             if ((*iter).second.empty()) {
20908334c51SBrooks Davis                 _output << F("    %s is empty\n") % (*iter).first;
21008334c51SBrooks Davis             } else {
21108334c51SBrooks Davis                 _output << F("    %s = %s\n") % (*iter).first % (*iter).second;
21208334c51SBrooks Davis             }
21308334c51SBrooks Davis         }
21408334c51SBrooks Davis 
21508334c51SBrooks Davis         const std::string stdout_contents = result_iter.stdout_contents();
21608334c51SBrooks Davis         if (!stdout_contents.empty()) {
21708334c51SBrooks Davis             _output << "\n"
21808334c51SBrooks Davis                     << "Standard output:\n"
21908334c51SBrooks Davis                     << stdout_contents;
22008334c51SBrooks Davis         }
22108334c51SBrooks Davis 
22208334c51SBrooks Davis         const std::string stderr_contents = result_iter.stderr_contents();
22308334c51SBrooks Davis         if (!stderr_contents.empty()) {
22408334c51SBrooks Davis             _output << "\n"
22508334c51SBrooks Davis                     << "Standard error:\n"
22608334c51SBrooks Davis                     << stderr_contents;
22708334c51SBrooks Davis         }
22808334c51SBrooks Davis     }
22908334c51SBrooks Davis 
23008334c51SBrooks Davis     /// Counts how many results of a given type have been received.
23108334c51SBrooks Davis     ///
23208334c51SBrooks Davis     /// \param type Test result type to count results for.
23308334c51SBrooks Davis     ///
23408334c51SBrooks Davis     /// \return The number of test results with \p type.
23508334c51SBrooks Davis     std::size_t
count_results(const model::test_result_type type)23608334c51SBrooks Davis     count_results(const model::test_result_type type)
23708334c51SBrooks Davis     {
23808334c51SBrooks Davis         const std::map< model::test_result_type,
23908334c51SBrooks Davis                         std::vector< result_data > >::const_iterator iter =
24008334c51SBrooks Davis             _results.find(type);
24108334c51SBrooks Davis         if (iter == _results.end())
24208334c51SBrooks Davis             return 0;
24308334c51SBrooks Davis         else
24408334c51SBrooks Davis             return (*iter).second.size();
24508334c51SBrooks Davis     }
24608334c51SBrooks Davis 
24708334c51SBrooks Davis     /// Prints a set of results.
24808334c51SBrooks Davis     ///
24908334c51SBrooks Davis     /// \param type Test result type to print results for.
25008334c51SBrooks Davis     /// \param title Title used when printing results.
25108334c51SBrooks Davis     void
print_results(const model::test_result_type type,const char * title)25208334c51SBrooks Davis     print_results(const model::test_result_type type,
25308334c51SBrooks Davis                   const char* title)
25408334c51SBrooks Davis     {
25508334c51SBrooks Davis         const std::map< model::test_result_type,
25608334c51SBrooks Davis                         std::vector< result_data > >::const_iterator iter2 =
25708334c51SBrooks Davis             _results.find(type);
25808334c51SBrooks Davis         if (iter2 == _results.end())
25908334c51SBrooks Davis             return;
26008334c51SBrooks Davis         const std::vector< result_data >& all = (*iter2).second;
26108334c51SBrooks Davis 
26208334c51SBrooks Davis         _output << F("===> %s\n") % title;
26308334c51SBrooks Davis         for (std::vector< result_data >::const_iterator iter = all.begin();
26408334c51SBrooks Davis              iter != all.end(); iter++) {
26508334c51SBrooks Davis             _output << F("%s:%s  ->  %s  [%s]\n") % (*iter).binary_path %
26608334c51SBrooks Davis                 (*iter).test_case_name %
26708334c51SBrooks Davis                 cli::format_result((*iter).result) %
26808334c51SBrooks Davis                 cli::format_delta((*iter).duration);
26908334c51SBrooks Davis         }
27008334c51SBrooks Davis     }
27108334c51SBrooks Davis 
27208334c51SBrooks Davis public:
27308334c51SBrooks Davis     /// Constructor for the hooks.
27408334c51SBrooks Davis     ///
27508334c51SBrooks Davis     /// \param [out] output_ Stream to which to write the report.
27608334c51SBrooks Davis     /// \param verbose_ Whether to include details in the output or not.
27708334c51SBrooks Davis     /// \param results_filters_ The result types to include in the report.
27808334c51SBrooks Davis     ///     Cannot be empty.
27908334c51SBrooks 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_)28008334c51SBrooks Davis     report_console_hooks(std::ostream& output_, const bool verbose_,
28108334c51SBrooks Davis                          const cli::result_types& results_filters_,
28208334c51SBrooks Davis                          const fs::path& results_file_) :
28308334c51SBrooks Davis         _output(output_),
28408334c51SBrooks Davis         _verbose(verbose_),
28508334c51SBrooks Davis         _results_filters(results_filters_),
28608334c51SBrooks Davis         _results_file(results_file_)
28708334c51SBrooks Davis     {
28808334c51SBrooks Davis         PRE(!results_filters_.empty());
28908334c51SBrooks Davis     }
29008334c51SBrooks Davis 
29108334c51SBrooks Davis     /// Callback executed when the context is loaded.
29208334c51SBrooks Davis     ///
29308334c51SBrooks Davis     /// \param context The context loaded from the database.
29408334c51SBrooks Davis     void
got_context(const model::context & context)29508334c51SBrooks Davis     got_context(const model::context& context)
29608334c51SBrooks Davis     {
29708334c51SBrooks Davis         if (_verbose)
29808334c51SBrooks Davis             print_context(context);
29908334c51SBrooks Davis     }
30008334c51SBrooks Davis 
30108334c51SBrooks Davis     /// Callback executed when a test results is found.
30208334c51SBrooks Davis     ///
30308334c51SBrooks Davis     /// \param iter Container for the test result's data.
30408334c51SBrooks Davis     void
got_result(store::results_iterator & iter)30508334c51SBrooks Davis     got_result(store::results_iterator& iter)
30608334c51SBrooks Davis     {
30708334c51SBrooks Davis         if (!_start_time || _start_time.get() > iter.start_time())
30808334c51SBrooks Davis             _start_time = iter.start_time();
30908334c51SBrooks Davis         if (!_end_time || _end_time.get() < iter.end_time())
31008334c51SBrooks Davis             _end_time = iter.end_time();
31108334c51SBrooks Davis 
31208334c51SBrooks Davis         const datetime::delta duration = iter.end_time() - iter.start_time();
31308334c51SBrooks Davis 
31408334c51SBrooks Davis         _runtime += duration;
31508334c51SBrooks Davis         const model::test_result result = iter.result();
31608334c51SBrooks Davis         _results[result.type()].push_back(
31708334c51SBrooks Davis             result_data(iter.test_program()->relative_path(),
31808334c51SBrooks Davis                         iter.test_case_name(), iter.result(), duration));
31908334c51SBrooks Davis 
32008334c51SBrooks Davis         if (_verbose) {
32108334c51SBrooks Davis             // TODO(jmmv): _results_filters is a list and is small enough for
32208334c51SBrooks Davis             // std::find to not be an expensive operation here (probably).  But
32308334c51SBrooks Davis             // we should be using a std::set instead.
32408334c51SBrooks Davis             if (std::find(_results_filters.begin(), _results_filters.end(),
32508334c51SBrooks Davis                           iter.result().type()) != _results_filters.end()) {
32608334c51SBrooks Davis                 print_test_case_and_result(iter);
32708334c51SBrooks Davis             }
32808334c51SBrooks Davis         }
32908334c51SBrooks Davis     }
33008334c51SBrooks Davis 
33108334c51SBrooks Davis     /// Prints the tests summary.
33208334c51SBrooks Davis     void
end(const drivers::scan_results::result &)33308334c51SBrooks Davis     end(const drivers::scan_results::result& /* r */)
33408334c51SBrooks Davis     {
33508334c51SBrooks Davis         typedef std::map< model::test_result_type, const char* > types_map;
33608334c51SBrooks Davis 
33708334c51SBrooks Davis         types_map titles;
33808334c51SBrooks Davis         titles[model::test_result_broken] = "Broken tests";
33908334c51SBrooks Davis         titles[model::test_result_expected_failure] = "Expected failures";
34008334c51SBrooks Davis         titles[model::test_result_failed] = "Failed tests";
34108334c51SBrooks Davis         titles[model::test_result_passed] = "Passed tests";
34208334c51SBrooks Davis         titles[model::test_result_skipped] = "Skipped tests";
34308334c51SBrooks Davis 
34408334c51SBrooks Davis         for (cli::result_types::const_iterator iter = _results_filters.begin();
34508334c51SBrooks Davis              iter != _results_filters.end(); ++iter) {
34608334c51SBrooks Davis             const types_map::const_iterator match = titles.find(*iter);
34708334c51SBrooks Davis             INV_MSG(match != titles.end(), "Conditional does not match user "
34808334c51SBrooks Davis                     "input validation in parse_types()");
34908334c51SBrooks Davis             print_results((*match).first, (*match).second);
35008334c51SBrooks Davis         }
35108334c51SBrooks Davis 
35208334c51SBrooks Davis         const std::size_t broken = count_results(model::test_result_broken);
35308334c51SBrooks Davis         const std::size_t failed = count_results(model::test_result_failed);
35408334c51SBrooks Davis         const std::size_t passed = count_results(model::test_result_passed);
35508334c51SBrooks Davis         const std::size_t skipped = count_results(model::test_result_skipped);
35608334c51SBrooks Davis         const std::size_t xfail = count_results(
35708334c51SBrooks Davis             model::test_result_expected_failure);
35808334c51SBrooks Davis         const std::size_t total = broken + failed + passed + skipped + xfail;
35908334c51SBrooks Davis 
36008334c51SBrooks Davis         _output << "===> Summary\n";
36108334c51SBrooks Davis         _output << F("Results read from %s\n") % _results_file;
36208334c51SBrooks Davis         _output << F("Test cases: %s total, %s skipped, %s expected failures, "
36308334c51SBrooks Davis                      "%s broken, %s failed\n") %
36408334c51SBrooks Davis             total % skipped % xfail % broken % failed;
36508334c51SBrooks Davis         if (_verbose && _start_time) {
36608334c51SBrooks Davis             INV(_end_time);
36708334c51SBrooks Davis             _output << F("Start time: %s\n") %
36808334c51SBrooks Davis                     _start_time.get().to_iso8601_in_utc();
36908334c51SBrooks Davis             _output << F("End time:   %s\n") %
37008334c51SBrooks Davis                     _end_time.get().to_iso8601_in_utc();
37108334c51SBrooks Davis         }
37208334c51SBrooks Davis         _output << F("Total time: %s\n") % cli::format_delta(_runtime);
37308334c51SBrooks Davis     }
37408334c51SBrooks Davis };
37508334c51SBrooks Davis 
37608334c51SBrooks Davis 
37708334c51SBrooks Davis }  // anonymous namespace
37808334c51SBrooks Davis 
37908334c51SBrooks Davis 
38008334c51SBrooks Davis /// Default constructor for cmd_report.
cmd_report(void)38108334c51SBrooks Davis cmd_report::cmd_report(void) : cli_command(
38208334c51SBrooks Davis     "report", "", 0, -1,
38308334c51SBrooks Davis     "Generates a report with the results of a test suite run")
38408334c51SBrooks Davis {
38508334c51SBrooks Davis     add_option(results_file_open_option);
38608334c51SBrooks Davis     add_option(cmdline::bool_option(
38708334c51SBrooks Davis         "verbose", "Include the execution context and the details of each test "
38808334c51SBrooks Davis         "case in the report"));
38908334c51SBrooks Davis     add_option(cmdline::path_option("output", "Path to the output file", "path",
39008334c51SBrooks Davis                                     "/dev/stdout"));
39108334c51SBrooks Davis     add_option(results_filter_option);
39208334c51SBrooks Davis }
39308334c51SBrooks Davis 
39408334c51SBrooks Davis 
39508334c51SBrooks Davis /// Entry point for the "report" subcommand.
39608334c51SBrooks Davis ///
39708334c51SBrooks Davis /// \param ui Object to interact with the I/O of the program.
39808334c51SBrooks Davis /// \param cmdline Representation of the command line to the subcommand.
39908334c51SBrooks Davis ///
40008334c51SBrooks Davis /// \return 0 if everything is OK, 1 if the statement is invalid or if there is
40108334c51SBrooks Davis /// any other problem.
40208334c51SBrooks Davis int
run(cmdline::ui * ui,const cmdline::parsed_cmdline & cmdline,const config::tree &)40308334c51SBrooks Davis cmd_report::run(cmdline::ui* ui,
40408334c51SBrooks Davis                 const cmdline::parsed_cmdline& cmdline,
40508334c51SBrooks Davis                 const config::tree& /* user_config */)
40608334c51SBrooks Davis {
407b392a90bSJohn Baldwin     std::unique_ptr< std::ostream > output = utils::open_ostream(
40808334c51SBrooks Davis         cmdline.get_option< cmdline::path_option >("output"));
40908334c51SBrooks Davis 
41008334c51SBrooks Davis     const fs::path results_file = layout::find_results(
41108334c51SBrooks Davis         results_file_open(cmdline));
41208334c51SBrooks Davis 
41308334c51SBrooks Davis     const result_types types = get_result_types(cmdline);
41408334c51SBrooks Davis     report_console_hooks hooks(*output.get(), cmdline.has_option("verbose"),
41508334c51SBrooks Davis                                types, results_file);
41608334c51SBrooks Davis     const drivers::scan_results::result result = drivers::scan_results::drive(
41708334c51SBrooks Davis         results_file, parse_filters(cmdline.arguments()), hooks);
41808334c51SBrooks Davis 
41908334c51SBrooks Davis     return report_unused_filters(result.unused_filters, ui) ?
42008334c51SBrooks Davis         EXIT_FAILURE : EXIT_SUCCESS;
42108334c51SBrooks Davis }
422