1// Copyright 2010 The Kyua Authors. 2// All rights reserved. 3// 4// Redistribution and use in source and binary forms, with or without 5// modification, are permitted provided that the following conditions are 6// met: 7// 8// * Redistributions of source code must retain the above copyright 9// notice, this list of conditions and the following disclaimer. 10// * Redistributions in binary form must reproduce the above copyright 11// notice, this list of conditions and the following disclaimer in the 12// documentation and/or other materials provided with the distribution. 13// * Neither the name of Google Inc. nor the names of its contributors 14// may be used to endorse or promote products derived from this software 15// without specific prior written permission. 16// 17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29#include "cli/cmd_help.hpp" 30 31#include <algorithm> 32#include <cstdlib> 33#include <iterator> 34 35#include <atf-c++.hpp> 36 37#include "cli/common.ipp" 38#include "engine/config.hpp" 39#include "utils/cmdline/commands_map.ipp" 40#include "utils/cmdline/exceptions.hpp" 41#include "utils/cmdline/globals.hpp" 42#include "utils/cmdline/options.hpp" 43#include "utils/cmdline/parser.hpp" 44#include "utils/cmdline/ui_mock.hpp" 45#include "utils/config/tree.ipp" 46#include "utils/defs.hpp" 47#include "utils/sanity.hpp" 48 49#if defined(HAVE_CONFIG_H) 50# include "config.h" 51#endif 52 53namespace cmdline = utils::cmdline; 54namespace config = utils::config; 55 56using cli::cmd_help; 57 58 59namespace { 60 61 62/// Mock command with a simple definition (no options, no arguments). 63/// 64/// Attempting to run this command will result in a crash. It is only provided 65/// to validate the generation of interactive help. 66class cmd_mock_simple : public cli::cli_command { 67public: 68 /// Constructs a new mock command. 69 /// 70 /// \param name_ The name of the command to create. 71 cmd_mock_simple(const char* name_) : cli::cli_command( 72 name_, "", 0, 0, "Simple command") 73 { 74 } 75 76 /// Runs the mock command. 77 /// 78 /// \return Nothing because this function is never called. 79 int 80 run(cmdline::ui* /* ui */, 81 const cmdline::parsed_cmdline& /* cmdline */, 82 const config::tree& /* user_config */) 83 { 84 UNREACHABLE; 85 } 86}; 87 88 89/// Mock command with a complex definition (some options, some arguments). 90/// 91/// Attempting to run this command will result in a crash. It is only provided 92/// to validate the generation of interactive help. 93class cmd_mock_complex : public cli::cli_command { 94public: 95 /// Constructs a new mock command. 96 /// 97 /// \param name_ The name of the command to create. 98 cmd_mock_complex(const char* name_) : cli::cli_command( 99 name_, "[arg1 .. argN]", 0, 2, "Complex command") 100 { 101 add_option(cmdline::bool_option("flag_a", "Flag A")); 102 add_option(cmdline::bool_option('b', "flag_b", "Flag B")); 103 add_option(cmdline::string_option('c', "flag_c", "Flag C", "c_arg")); 104 add_option(cmdline::string_option("flag_d", "Flag D", "d_arg", "foo")); 105 } 106 107 /// Runs the mock command. 108 /// 109 /// \return Nothing because this function is never called. 110 int 111 run(cmdline::ui* /* ui */, 112 const cmdline::parsed_cmdline& /* cmdline */, 113 const config::tree& /* user_config */) 114 { 115 UNREACHABLE; 116 } 117}; 118 119 120/// Initializes the cmdline library and generates the set of test commands. 121/// 122/// \param [out] commands A mapping that is updated to contain the commands to 123/// use for testing. 124static void 125setup(cmdline::commands_map< cli::cli_command >& commands) 126{ 127 cmdline::init("progname"); 128 129 commands.insert(new cmd_mock_simple("mock_simple")); 130 commands.insert(new cmd_mock_complex("mock_complex")); 131 132 commands.insert(new cmd_mock_simple("mock_simple_2"), "First"); 133 commands.insert(new cmd_mock_complex("mock_complex_2"), "First"); 134 135 commands.insert(new cmd_mock_simple("mock_simple_3"), "Second"); 136} 137 138 139/// Performs a test on the global help (not that of a subcommand). 140/// 141/// \param general_options The genral options supported by the tool, if any. 142/// \param expected_options Expected lines of help output documenting the 143/// options in general_options. 144/// \param ui The cmdline::mock_ui object to which to write the output. 145static void 146global_test(const cmdline::options_vector& general_options, 147 const std::vector< std::string >& expected_options, 148 cmdline::ui_mock& ui) 149{ 150 cmdline::commands_map< cli::cli_command > mock_commands; 151 setup(mock_commands); 152 153 cmdline::args_vector args; 154 args.push_back("help"); 155 156 cmd_help cmd(&general_options, &mock_commands); 157 ATF_REQUIRE_EQ(EXIT_SUCCESS, cmd.main(&ui, args, engine::default_config())); 158 159 std::vector< std::string > expected; 160 161 expected.push_back(PACKAGE " (" PACKAGE_NAME ") " PACKAGE_VERSION); 162 expected.push_back(""); 163 expected.push_back("Usage: progname [general_options] command " 164 "[command_options] [args]"); 165 if (!general_options.empty()) { 166 expected.push_back(""); 167 expected.push_back("Available general options:"); 168 std::copy(expected_options.begin(), expected_options.end(), 169 std::back_inserter(expected)); 170 } 171 expected.push_back(""); 172 expected.push_back("Generic commands:"); 173 expected.push_back(" mock_complex Complex command."); 174 expected.push_back(" mock_simple Simple command."); 175 expected.push_back(""); 176 expected.push_back("First commands:"); 177 expected.push_back(" mock_complex_2 Complex command."); 178 expected.push_back(" mock_simple_2 Simple command."); 179 expected.push_back(""); 180 expected.push_back("Second commands:"); 181 expected.push_back(" mock_simple_3 Simple command."); 182 expected.push_back(""); 183 expected.push_back("See kyua(1) for more details."); 184 185 ATF_REQUIRE(expected == ui.out_log()); 186 ATF_REQUIRE(ui.err_log().empty()); 187} 188 189 190} // anonymous namespace 191 192 193ATF_TEST_CASE_WITHOUT_HEAD(global__no_options); 194ATF_TEST_CASE_BODY(global__no_options) 195{ 196 cmdline::ui_mock ui; 197 198 cmdline::options_vector general_options; 199 200 global_test(general_options, std::vector< std::string >(), ui); 201} 202 203 204ATF_TEST_CASE_WITHOUT_HEAD(global__some_options); 205ATF_TEST_CASE_BODY(global__some_options) 206{ 207 cmdline::ui_mock ui; 208 209 cmdline::options_vector general_options; 210 const cmdline::bool_option flag_a("flag_a", "Flag A"); 211 general_options.push_back(&flag_a); 212 const cmdline::string_option flag_c('c', "lc", "Flag C", "X"); 213 general_options.push_back(&flag_c); 214 215 std::vector< std::string > expected; 216 expected.push_back(" --flag_a Flag A."); 217 expected.push_back(" -c X, --lc=X Flag C."); 218 219 global_test(general_options, expected, ui); 220} 221 222 223ATF_TEST_CASE_WITHOUT_HEAD(subcommand__simple); 224ATF_TEST_CASE_BODY(subcommand__simple) 225{ 226 cmdline::options_vector general_options; 227 228 cmdline::commands_map< cli::cli_command > mock_commands; 229 setup(mock_commands); 230 231 cmdline::args_vector args; 232 args.push_back("help"); 233 args.push_back("mock_simple"); 234 235 cmd_help cmd(&general_options, &mock_commands); 236 cmdline::ui_mock ui; 237 ATF_REQUIRE_EQ(EXIT_SUCCESS, cmd.main(&ui, args, engine::default_config())); 238 ATF_REQUIRE(atf::utils::grep_collection( 239 "^kyua.*" PACKAGE_VERSION, ui.out_log())); 240 ATF_REQUIRE(atf::utils::grep_collection( 241 "^Usage: progname \\[general_options\\] mock_simple$", ui.out_log())); 242 ATF_REQUIRE(!atf::utils::grep_collection( 243 "Available.*options", ui.out_log())); 244 ATF_REQUIRE(atf::utils::grep_collection( 245 "^See kyua-mock_simple\\(1\\) for more details.", ui.out_log())); 246 ATF_REQUIRE(ui.err_log().empty()); 247} 248 249 250ATF_TEST_CASE_WITHOUT_HEAD(subcommand__complex); 251ATF_TEST_CASE_BODY(subcommand__complex) 252{ 253 cmdline::options_vector general_options; 254 const cmdline::bool_option global_a("global_a", "Global A"); 255 general_options.push_back(&global_a); 256 const cmdline::string_option global_c('c', "global_c", "Global C", 257 "c_global"); 258 general_options.push_back(&global_c); 259 260 cmdline::commands_map< cli::cli_command > mock_commands; 261 setup(mock_commands); 262 263 cmdline::args_vector args; 264 args.push_back("help"); 265 args.push_back("mock_complex"); 266 267 cmd_help cmd(&general_options, &mock_commands); 268 cmdline::ui_mock ui; 269 ATF_REQUIRE_EQ(EXIT_SUCCESS, cmd.main(&ui, args, engine::default_config())); 270 ATF_REQUIRE(atf::utils::grep_collection( 271 "^kyua.*" PACKAGE_VERSION, ui.out_log())); 272 ATF_REQUIRE(atf::utils::grep_collection( 273 "^Usage: progname \\[general_options\\] mock_complex " 274 "\\[command_options\\] \\[arg1 .. argN\\]$", ui.out_log())); 275 ATF_REQUIRE(atf::utils::grep_collection("Available general options", 276 ui.out_log())); 277 ATF_REQUIRE(atf::utils::grep_collection("--global_a", ui.out_log())); 278 ATF_REQUIRE(atf::utils::grep_collection("--global_c=c_global", 279 ui.out_log())); 280 ATF_REQUIRE(atf::utils::grep_collection("Available command options", 281 ui.out_log())); 282 ATF_REQUIRE(atf::utils::grep_collection("--flag_a *Flag A", 283 ui.out_log())); 284 ATF_REQUIRE(atf::utils::grep_collection("-b.*--flag_b *Flag B", 285 ui.out_log())); 286 ATF_REQUIRE(atf::utils::grep_collection( 287 "-c c_arg.*--flag_c=c_arg *Flag C", ui.out_log())); 288 ATF_REQUIRE(atf::utils::grep_collection( 289 "--flag_d=d_arg *Flag D.*default.*foo", ui.out_log())); 290 ATF_REQUIRE(atf::utils::grep_collection( 291 "^See kyua-mock_complex\\(1\\) for more details.", ui.out_log())); 292 ATF_REQUIRE(ui.err_log().empty()); 293} 294 295 296ATF_TEST_CASE_WITHOUT_HEAD(subcommand__unknown); 297ATF_TEST_CASE_BODY(subcommand__unknown) 298{ 299 cmdline::options_vector general_options; 300 301 cmdline::commands_map< cli::cli_command > mock_commands; 302 setup(mock_commands); 303 304 cmdline::args_vector args; 305 args.push_back("help"); 306 args.push_back("foobar"); 307 308 cmd_help cmd(&general_options, &mock_commands); 309 cmdline::ui_mock ui; 310 ATF_REQUIRE_THROW_RE(cmdline::usage_error, "command foobar.*not exist", 311 cmd.main(&ui, args, engine::default_config())); 312 ATF_REQUIRE(ui.out_log().empty()); 313 ATF_REQUIRE(ui.err_log().empty()); 314} 315 316 317ATF_TEST_CASE_WITHOUT_HEAD(invalid_args); 318ATF_TEST_CASE_BODY(invalid_args) 319{ 320 cmdline::options_vector general_options; 321 322 cmdline::commands_map< cli::cli_command > mock_commands; 323 setup(mock_commands); 324 325 cmdline::args_vector args; 326 args.push_back("help"); 327 args.push_back("mock_simple"); 328 args.push_back("mock_complex"); 329 330 cmd_help cmd(&general_options, &mock_commands); 331 cmdline::ui_mock ui; 332 ATF_REQUIRE_THROW_RE(cmdline::usage_error, "Too many arguments", 333 cmd.main(&ui, args, engine::default_config())); 334 ATF_REQUIRE(ui.out_log().empty()); 335 ATF_REQUIRE(ui.err_log().empty()); 336} 337 338 339ATF_INIT_TEST_CASES(tcs) 340{ 341 ATF_ADD_TEST_CASE(tcs, global__no_options); 342 ATF_ADD_TEST_CASE(tcs, global__some_options); 343 ATF_ADD_TEST_CASE(tcs, subcommand__simple); 344 ATF_ADD_TEST_CASE(tcs, subcommand__complex); 345 ATF_ADD_TEST_CASE(tcs, subcommand__unknown); 346 ATF_ADD_TEST_CASE(tcs, invalid_args); 347} 348