1*b0d29bc4SBrooks Davis // Copyright 2010 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_test.hpp" 30*b0d29bc4SBrooks Davis 31*b0d29bc4SBrooks Davis #include <cstdlib> 32*b0d29bc4SBrooks Davis 33*b0d29bc4SBrooks Davis #include "cli/common.ipp" 34*b0d29bc4SBrooks Davis #include "drivers/run_tests.hpp" 35*b0d29bc4SBrooks Davis #include "model/test_program.hpp" 36*b0d29bc4SBrooks Davis #include "model/test_result.hpp" 37*b0d29bc4SBrooks Davis #include "store/layout.hpp" 38*b0d29bc4SBrooks Davis #include "utils/cmdline/options.hpp" 39*b0d29bc4SBrooks Davis #include "utils/cmdline/parser.ipp" 40*b0d29bc4SBrooks Davis #include "utils/cmdline/ui.hpp" 41*b0d29bc4SBrooks Davis #include "utils/config/tree.ipp" 42*b0d29bc4SBrooks Davis #include "utils/datetime.hpp" 43*b0d29bc4SBrooks Davis #include "utils/format/macros.hpp" 44*b0d29bc4SBrooks Davis #include "utils/fs/path.hpp" 45*b0d29bc4SBrooks Davis 46*b0d29bc4SBrooks Davis namespace cmdline = utils::cmdline; 47*b0d29bc4SBrooks Davis namespace config = utils::config; 48*b0d29bc4SBrooks Davis namespace datetime = utils::datetime; 49*b0d29bc4SBrooks Davis namespace fs = utils::fs; 50*b0d29bc4SBrooks Davis namespace layout = store::layout; 51*b0d29bc4SBrooks Davis 52*b0d29bc4SBrooks Davis using cli::cmd_test; 53*b0d29bc4SBrooks Davis 54*b0d29bc4SBrooks Davis 55*b0d29bc4SBrooks Davis namespace { 56*b0d29bc4SBrooks Davis 57*b0d29bc4SBrooks Davis 58*b0d29bc4SBrooks Davis /// Hooks to print a progress report of the execution of the tests. 59*b0d29bc4SBrooks Davis class print_hooks : public drivers::run_tests::base_hooks { 60*b0d29bc4SBrooks Davis /// Object to interact with the I/O of the program. 61*b0d29bc4SBrooks Davis cmdline::ui* _ui; 62*b0d29bc4SBrooks Davis 63*b0d29bc4SBrooks Davis /// Whether the tests are executed in parallel or not. 64*b0d29bc4SBrooks Davis bool _parallel; 65*b0d29bc4SBrooks Davis 66*b0d29bc4SBrooks Davis public: 67*b0d29bc4SBrooks Davis /// The amount of positive test results found so far. 68*b0d29bc4SBrooks Davis unsigned long good_count; 69*b0d29bc4SBrooks Davis 70*b0d29bc4SBrooks Davis /// The amount of negative test results found so far. 71*b0d29bc4SBrooks Davis unsigned long bad_count; 72*b0d29bc4SBrooks Davis 73*b0d29bc4SBrooks Davis /// Constructor for the hooks. 74*b0d29bc4SBrooks Davis /// 75*b0d29bc4SBrooks Davis /// \param ui_ Object to interact with the I/O of the program. 76*b0d29bc4SBrooks Davis /// \param parallel_ True if we are executing more than one test at once. 77*b0d29bc4SBrooks Davis print_hooks(cmdline::ui* ui_, const bool parallel_) : 78*b0d29bc4SBrooks Davis _ui(ui_), 79*b0d29bc4SBrooks Davis _parallel(parallel_), 80*b0d29bc4SBrooks Davis good_count(0), 81*b0d29bc4SBrooks Davis bad_count(0) 82*b0d29bc4SBrooks Davis { 83*b0d29bc4SBrooks Davis } 84*b0d29bc4SBrooks Davis 85*b0d29bc4SBrooks Davis /// Called when the processing of a test case begins. 86*b0d29bc4SBrooks Davis /// 87*b0d29bc4SBrooks Davis /// \param test_program The test program containing the test case. 88*b0d29bc4SBrooks Davis /// \param test_case_name The name of the test case being executed. 89*b0d29bc4SBrooks Davis virtual void 90*b0d29bc4SBrooks Davis got_test_case(const model::test_program& test_program, 91*b0d29bc4SBrooks Davis const std::string& test_case_name) 92*b0d29bc4SBrooks Davis { 93*b0d29bc4SBrooks Davis if (!_parallel) { 94*b0d29bc4SBrooks Davis _ui->out(F("%s -> ") % 95*b0d29bc4SBrooks Davis cli::format_test_case_id(test_program, test_case_name), 96*b0d29bc4SBrooks Davis false); 97*b0d29bc4SBrooks Davis } 98*b0d29bc4SBrooks Davis } 99*b0d29bc4SBrooks Davis 100*b0d29bc4SBrooks Davis /// Called when a result of a test case becomes available. 101*b0d29bc4SBrooks Davis /// 102*b0d29bc4SBrooks Davis /// \param test_program The test program containing the test case. 103*b0d29bc4SBrooks Davis /// \param test_case_name The name of the test case being executed. 104*b0d29bc4SBrooks Davis /// \param result The result of the execution of the test case. 105*b0d29bc4SBrooks Davis /// \param duration The time it took to run the test. 106*b0d29bc4SBrooks Davis virtual void 107*b0d29bc4SBrooks Davis got_result(const model::test_program& test_program, 108*b0d29bc4SBrooks Davis const std::string& test_case_name, 109*b0d29bc4SBrooks Davis const model::test_result& result, 110*b0d29bc4SBrooks Davis const datetime::delta& duration) 111*b0d29bc4SBrooks Davis { 112*b0d29bc4SBrooks Davis if (_parallel) { 113*b0d29bc4SBrooks Davis _ui->out(F("%s -> ") % 114*b0d29bc4SBrooks Davis cli::format_test_case_id(test_program, test_case_name), 115*b0d29bc4SBrooks Davis false); 116*b0d29bc4SBrooks Davis } 117*b0d29bc4SBrooks Davis _ui->out(F("%s [%s]") % cli::format_result(result) % 118*b0d29bc4SBrooks Davis cli::format_delta(duration)); 119*b0d29bc4SBrooks Davis if (result.good()) 120*b0d29bc4SBrooks Davis good_count++; 121*b0d29bc4SBrooks Davis else 122*b0d29bc4SBrooks Davis bad_count++; 123*b0d29bc4SBrooks Davis } 124*b0d29bc4SBrooks Davis }; 125*b0d29bc4SBrooks Davis 126*b0d29bc4SBrooks Davis 127*b0d29bc4SBrooks Davis } // anonymous namespace 128*b0d29bc4SBrooks Davis 129*b0d29bc4SBrooks Davis 130*b0d29bc4SBrooks Davis /// Default constructor for cmd_test. 131*b0d29bc4SBrooks Davis cmd_test::cmd_test(void) : cli_command( 132*b0d29bc4SBrooks Davis "test", "[test-program ...]", 0, -1, "Run tests") 133*b0d29bc4SBrooks Davis { 134*b0d29bc4SBrooks Davis add_option(build_root_option); 135*b0d29bc4SBrooks Davis add_option(kyuafile_option); 136*b0d29bc4SBrooks Davis add_option(results_file_create_option); 137*b0d29bc4SBrooks Davis } 138*b0d29bc4SBrooks Davis 139*b0d29bc4SBrooks Davis 140*b0d29bc4SBrooks Davis /// Entry point for the "test" subcommand. 141*b0d29bc4SBrooks Davis /// 142*b0d29bc4SBrooks Davis /// \param ui Object to interact with the I/O of the program. 143*b0d29bc4SBrooks Davis /// \param cmdline Representation of the command line to the subcommand. 144*b0d29bc4SBrooks Davis /// \param user_config The runtime configuration of the program. 145*b0d29bc4SBrooks Davis /// 146*b0d29bc4SBrooks Davis /// \return 0 if all tests passed, 1 otherwise. 147*b0d29bc4SBrooks Davis int 148*b0d29bc4SBrooks Davis cmd_test::run(cmdline::ui* ui, const cmdline::parsed_cmdline& cmdline, 149*b0d29bc4SBrooks Davis const config::tree& user_config) 150*b0d29bc4SBrooks Davis { 151*b0d29bc4SBrooks Davis const layout::results_id_file_pair results = layout::new_db( 152*b0d29bc4SBrooks Davis results_file_create(cmdline), kyuafile_path(cmdline).branch_path()); 153*b0d29bc4SBrooks Davis 154*b0d29bc4SBrooks Davis const bool parallel = (user_config.lookup< config::positive_int_node >( 155*b0d29bc4SBrooks Davis "parallelism") > 1); 156*b0d29bc4SBrooks Davis 157*b0d29bc4SBrooks Davis print_hooks hooks(ui, parallel); 158*b0d29bc4SBrooks Davis const drivers::run_tests::result result = drivers::run_tests::drive( 159*b0d29bc4SBrooks Davis kyuafile_path(cmdline), build_root_path(cmdline), results.second, 160*b0d29bc4SBrooks Davis parse_filters(cmdline.arguments()), user_config, hooks); 161*b0d29bc4SBrooks Davis 162*b0d29bc4SBrooks Davis int exit_code; 163*b0d29bc4SBrooks Davis if (hooks.good_count > 0 || hooks.bad_count > 0) { 164*b0d29bc4SBrooks Davis ui->out(""); 165*b0d29bc4SBrooks Davis if (!results.first.empty()) { 166*b0d29bc4SBrooks Davis ui->out(F("Results file id is %s") % results.first); 167*b0d29bc4SBrooks Davis } 168*b0d29bc4SBrooks Davis ui->out(F("Results saved to %s") % results.second); 169*b0d29bc4SBrooks Davis ui->out(""); 170*b0d29bc4SBrooks Davis 171*b0d29bc4SBrooks Davis ui->out(F("%s/%s passed (%s failed)") % hooks.good_count % 172*b0d29bc4SBrooks Davis (hooks.good_count + hooks.bad_count) % hooks.bad_count); 173*b0d29bc4SBrooks Davis 174*b0d29bc4SBrooks Davis exit_code = (hooks.bad_count == 0 ? EXIT_SUCCESS : EXIT_FAILURE); 175*b0d29bc4SBrooks Davis } else { 176*b0d29bc4SBrooks Davis // TODO(jmmv): Delete created empty file; it's useless! 177*b0d29bc4SBrooks Davis if (!results.first.empty()) { 178*b0d29bc4SBrooks Davis ui->out(F("Results file id is %s") % results.first); 179*b0d29bc4SBrooks Davis } 180*b0d29bc4SBrooks Davis ui->out(F("Results saved to %s") % results.second); 181*b0d29bc4SBrooks Davis exit_code = EXIT_SUCCESS; 182*b0d29bc4SBrooks Davis } 183*b0d29bc4SBrooks Davis 184*b0d29bc4SBrooks Davis return report_unused_filters(result.unused_filters, ui) ? 185*b0d29bc4SBrooks Davis EXIT_FAILURE : exit_code; 186*b0d29bc4SBrooks Davis } 187