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. 120*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 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 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 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 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 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. 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 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 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 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. 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 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