1b0d29bc4SBrooks Davis // Copyright 2010 The Kyua Authors. 2b0d29bc4SBrooks Davis // All rights reserved. 3b0d29bc4SBrooks Davis // 4b0d29bc4SBrooks Davis // Redistribution and use in source and binary forms, with or without 5b0d29bc4SBrooks Davis // modification, are permitted provided that the following conditions are 6b0d29bc4SBrooks Davis // met: 7b0d29bc4SBrooks Davis // 8b0d29bc4SBrooks Davis // * Redistributions of source code must retain the above copyright 9b0d29bc4SBrooks Davis // notice, this list of conditions and the following disclaimer. 10b0d29bc4SBrooks Davis // * Redistributions in binary form must reproduce the above copyright 11b0d29bc4SBrooks Davis // notice, this list of conditions and the following disclaimer in the 12b0d29bc4SBrooks Davis // documentation and/or other materials provided with the distribution. 13b0d29bc4SBrooks Davis // * Neither the name of Google Inc. nor the names of its contributors 14b0d29bc4SBrooks Davis // may be used to endorse or promote products derived from this software 15b0d29bc4SBrooks Davis // without specific prior written permission. 16b0d29bc4SBrooks Davis // 17b0d29bc4SBrooks Davis // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18b0d29bc4SBrooks Davis // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19b0d29bc4SBrooks Davis // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20b0d29bc4SBrooks Davis // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21b0d29bc4SBrooks Davis // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22b0d29bc4SBrooks Davis // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23b0d29bc4SBrooks Davis // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24b0d29bc4SBrooks Davis // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25b0d29bc4SBrooks Davis // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26b0d29bc4SBrooks Davis // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27b0d29bc4SBrooks Davis // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28b0d29bc4SBrooks Davis 29b0d29bc4SBrooks Davis #include "cli/cmd_test.hpp" 30b0d29bc4SBrooks Davis 31b0d29bc4SBrooks Davis #include <cstdlib> 32b0d29bc4SBrooks Davis 33b0d29bc4SBrooks Davis #include "cli/common.ipp" 34b0d29bc4SBrooks Davis #include "drivers/run_tests.hpp" 35b0d29bc4SBrooks Davis #include "model/test_program.hpp" 36b0d29bc4SBrooks Davis #include "model/test_result.hpp" 37b0d29bc4SBrooks Davis #include "store/layout.hpp" 38b0d29bc4SBrooks Davis #include "utils/cmdline/options.hpp" 39b0d29bc4SBrooks Davis #include "utils/cmdline/parser.ipp" 40b0d29bc4SBrooks Davis #include "utils/cmdline/ui.hpp" 41b0d29bc4SBrooks Davis #include "utils/config/tree.ipp" 42b0d29bc4SBrooks Davis #include "utils/datetime.hpp" 43b0d29bc4SBrooks Davis #include "utils/format/macros.hpp" 44b0d29bc4SBrooks Davis #include "utils/fs/path.hpp" 45b0d29bc4SBrooks Davis 46b0d29bc4SBrooks Davis namespace cmdline = utils::cmdline; 47b0d29bc4SBrooks Davis namespace config = utils::config; 48b0d29bc4SBrooks Davis namespace datetime = utils::datetime; 49b0d29bc4SBrooks Davis namespace fs = utils::fs; 50b0d29bc4SBrooks Davis namespace layout = store::layout; 51b0d29bc4SBrooks Davis 52b0d29bc4SBrooks Davis using cli::cmd_test; 53b0d29bc4SBrooks Davis 54b0d29bc4SBrooks Davis 55b0d29bc4SBrooks Davis namespace { 56b0d29bc4SBrooks Davis 57b0d29bc4SBrooks Davis 58b0d29bc4SBrooks Davis /// Hooks to print a progress report of the execution of the tests. 59b0d29bc4SBrooks Davis class print_hooks : public drivers::run_tests::base_hooks { 60b0d29bc4SBrooks Davis /// Object to interact with the I/O of the program. 61b0d29bc4SBrooks Davis cmdline::ui* _ui; 62b0d29bc4SBrooks Davis 63b0d29bc4SBrooks Davis /// Whether the tests are executed in parallel or not. 64b0d29bc4SBrooks Davis bool _parallel; 65b0d29bc4SBrooks Davis 66b0d29bc4SBrooks Davis public: 67*99689201SIgor Ostapenko /// The amount of test results per type. 68*99689201SIgor Ostapenko std::map<enum model::test_result_type, unsigned long> type_count; 69b0d29bc4SBrooks Davis 70b0d29bc4SBrooks Davis /// Constructor for the hooks. 71b0d29bc4SBrooks Davis /// 72b0d29bc4SBrooks Davis /// \param ui_ Object to interact with the I/O of the program. 73b0d29bc4SBrooks Davis /// \param parallel_ True if we are executing more than one test at once. 74b0d29bc4SBrooks Davis print_hooks(cmdline::ui* ui_, const bool parallel_) : 75b0d29bc4SBrooks Davis _ui(ui_), 76*99689201SIgor Ostapenko _parallel(parallel_) 77b0d29bc4SBrooks Davis { 78*99689201SIgor Ostapenko for (const auto& pair : model::test_result_types) 79*99689201SIgor Ostapenko type_count[pair.first] = 0; 80b0d29bc4SBrooks Davis } 81b0d29bc4SBrooks Davis 82b0d29bc4SBrooks Davis /// Called when the processing of a test case begins. 83b0d29bc4SBrooks Davis /// 84b0d29bc4SBrooks Davis /// \param test_program The test program containing the test case. 85b0d29bc4SBrooks Davis /// \param test_case_name The name of the test case being executed. 86b0d29bc4SBrooks Davis virtual void 87b0d29bc4SBrooks Davis got_test_case(const model::test_program& test_program, 88b0d29bc4SBrooks Davis const std::string& test_case_name) 89b0d29bc4SBrooks Davis { 90b0d29bc4SBrooks Davis if (!_parallel) { 91b0d29bc4SBrooks Davis _ui->out(F("%s -> ") % 92b0d29bc4SBrooks Davis cli::format_test_case_id(test_program, test_case_name), 93b0d29bc4SBrooks Davis false); 94b0d29bc4SBrooks Davis } 95b0d29bc4SBrooks Davis } 96b0d29bc4SBrooks Davis 97b0d29bc4SBrooks Davis /// Called when a result of a test case becomes available. 98b0d29bc4SBrooks Davis /// 99b0d29bc4SBrooks Davis /// \param test_program The test program containing the test case. 100b0d29bc4SBrooks Davis /// \param test_case_name The name of the test case being executed. 101b0d29bc4SBrooks Davis /// \param result The result of the execution of the test case. 102b0d29bc4SBrooks Davis /// \param duration The time it took to run the test. 103b0d29bc4SBrooks Davis virtual void 104b0d29bc4SBrooks Davis got_result(const model::test_program& test_program, 105b0d29bc4SBrooks Davis const std::string& test_case_name, 106b0d29bc4SBrooks Davis const model::test_result& result, 107b0d29bc4SBrooks Davis const datetime::delta& duration) 108b0d29bc4SBrooks Davis { 109b0d29bc4SBrooks Davis if (_parallel) { 110b0d29bc4SBrooks Davis _ui->out(F("%s -> ") % 111b0d29bc4SBrooks Davis cli::format_test_case_id(test_program, test_case_name), 112b0d29bc4SBrooks Davis false); 113b0d29bc4SBrooks Davis } 114b0d29bc4SBrooks Davis _ui->out(F("%s [%s]") % cli::format_result(result) % 115b0d29bc4SBrooks Davis cli::format_delta(duration)); 116*99689201SIgor Ostapenko 117*99689201SIgor Ostapenko type_count[result.type()]++; 118b0d29bc4SBrooks Davis } 119b0d29bc4SBrooks Davis }; 120b0d29bc4SBrooks Davis 121b0d29bc4SBrooks Davis 122b0d29bc4SBrooks Davis } // anonymous namespace 123b0d29bc4SBrooks Davis 124b0d29bc4SBrooks Davis 125b0d29bc4SBrooks Davis /// Default constructor for cmd_test. 126b0d29bc4SBrooks Davis cmd_test::cmd_test(void) : cli_command( 127b0d29bc4SBrooks Davis "test", "[test-program ...]", 0, -1, "Run tests") 128b0d29bc4SBrooks Davis { 129b0d29bc4SBrooks Davis add_option(build_root_option); 130b0d29bc4SBrooks Davis add_option(kyuafile_option); 131b0d29bc4SBrooks Davis add_option(results_file_create_option); 132b0d29bc4SBrooks Davis } 133b0d29bc4SBrooks Davis 134b0d29bc4SBrooks Davis 135b0d29bc4SBrooks Davis /// Entry point for the "test" subcommand. 136b0d29bc4SBrooks Davis /// 137b0d29bc4SBrooks Davis /// \param ui Object to interact with the I/O of the program. 138b0d29bc4SBrooks Davis /// \param cmdline Representation of the command line to the subcommand. 139b0d29bc4SBrooks Davis /// \param user_config The runtime configuration of the program. 140b0d29bc4SBrooks Davis /// 141b0d29bc4SBrooks Davis /// \return 0 if all tests passed, 1 otherwise. 142b0d29bc4SBrooks Davis int 143b0d29bc4SBrooks Davis cmd_test::run(cmdline::ui* ui, const cmdline::parsed_cmdline& cmdline, 144b0d29bc4SBrooks Davis const config::tree& user_config) 145b0d29bc4SBrooks Davis { 146b0d29bc4SBrooks Davis const layout::results_id_file_pair results = layout::new_db( 147b0d29bc4SBrooks Davis results_file_create(cmdline), kyuafile_path(cmdline).branch_path()); 148b0d29bc4SBrooks Davis 149b0d29bc4SBrooks Davis const bool parallel = (user_config.lookup< config::positive_int_node >( 150b0d29bc4SBrooks Davis "parallelism") > 1); 151b0d29bc4SBrooks Davis 152b0d29bc4SBrooks Davis print_hooks hooks(ui, parallel); 153b0d29bc4SBrooks Davis const drivers::run_tests::result result = drivers::run_tests::drive( 154b0d29bc4SBrooks Davis kyuafile_path(cmdline), build_root_path(cmdline), results.second, 155b0d29bc4SBrooks Davis parse_filters(cmdline.arguments()), user_config, hooks); 156b0d29bc4SBrooks Davis 157*99689201SIgor Ostapenko unsigned long total = 0; 158*99689201SIgor Ostapenko unsigned long good = 0; 159*99689201SIgor Ostapenko unsigned long bad = 0; 160*99689201SIgor Ostapenko for (const auto& pair : model::test_result_types) { 161*99689201SIgor Ostapenko const auto& type = pair.second; 162*99689201SIgor Ostapenko const auto count = hooks.type_count[type.id]; 163*99689201SIgor Ostapenko total += count; 164*99689201SIgor Ostapenko if (type.is_run && type.is_good) 165*99689201SIgor Ostapenko good += count; 166*99689201SIgor Ostapenko if (!type.is_good) 167*99689201SIgor Ostapenko bad += count; 168*99689201SIgor Ostapenko } 169*99689201SIgor Ostapenko 170b0d29bc4SBrooks Davis int exit_code; 171*99689201SIgor Ostapenko if (total > 0) { 172b0d29bc4SBrooks Davis ui->out(""); 173b0d29bc4SBrooks Davis if (!results.first.empty()) { 174b0d29bc4SBrooks Davis ui->out(F("Results file id is %s") % results.first); 175b0d29bc4SBrooks Davis } 176b0d29bc4SBrooks Davis ui->out(F("Results saved to %s") % results.second); 177b0d29bc4SBrooks Davis ui->out(""); 178b0d29bc4SBrooks Davis 179*99689201SIgor Ostapenko ui->out(F("%s/%s passed (") % good % total, false); 180*99689201SIgor Ostapenko const auto& types = model::test_result_types; 181*99689201SIgor Ostapenko for (auto it = types.begin(); it != types.end(); it++) { 182*99689201SIgor Ostapenko const auto& type = it->second; 183*99689201SIgor Ostapenko if (!type.is_run || !type.is_good) { 184*99689201SIgor Ostapenko if (it != types.begin()) 185*99689201SIgor Ostapenko ui->out(", ", false); 186*99689201SIgor Ostapenko ui->out(F("%s %s") % hooks.type_count[type.id] % type.name, 187*99689201SIgor Ostapenko false); 188*99689201SIgor Ostapenko } 189*99689201SIgor Ostapenko } 190*99689201SIgor Ostapenko ui->out(")"); 191b0d29bc4SBrooks Davis 192*99689201SIgor Ostapenko exit_code = (bad == 0 ? EXIT_SUCCESS : EXIT_FAILURE); 193b0d29bc4SBrooks Davis } else { 194b0d29bc4SBrooks Davis // TODO(jmmv): Delete created empty file; it's useless! 195b0d29bc4SBrooks Davis if (!results.first.empty()) { 196b0d29bc4SBrooks Davis ui->out(F("Results file id is %s") % results.first); 197b0d29bc4SBrooks Davis } 198b0d29bc4SBrooks Davis ui->out(F("Results saved to %s") % results.second); 199b0d29bc4SBrooks Davis exit_code = EXIT_SUCCESS; 200b0d29bc4SBrooks Davis } 201b0d29bc4SBrooks Davis 202b0d29bc4SBrooks Davis return report_unused_filters(result.unused_filters, ui) ? 203b0d29bc4SBrooks Davis EXIT_FAILURE : exit_code; 204b0d29bc4SBrooks Davis } 205