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 "utils/cmdline/base_command.ipp" 30 31#include <atf-c++.hpp> 32 33#include "utils/cmdline/exceptions.hpp" 34#include "utils/cmdline/options.hpp" 35#include "utils/cmdline/parser.ipp" 36#include "utils/cmdline/ui_mock.hpp" 37#include "utils/defs.hpp" 38 39namespace cmdline = utils::cmdline; 40 41 42namespace { 43 44 45/// Mock command to test the cmdline::base_command base class. 46/// 47/// \param Data The type of the opaque data object passed to main(). 48/// \param ExpectedData The value run() will expect to find in the Data object 49/// passed to main(). 50template< typename Data, Data ExpectedData > 51class mock_cmd : public cmdline::base_command< Data > { 52public: 53 /// Indicates if run() has been called already and executed correctly. 54 bool executed; 55 56 /// Contains the argument of --the_string after run() is executed. 57 std::string optvalue; 58 59 /// Constructs a new mock command. 60 mock_cmd(void) : 61 cmdline::base_command< Data >("mock", "arg1 [arg2 [arg3]]", 1, 3, 62 "Command for testing."), 63 executed(false) 64 { 65 this->add_option(cmdline::string_option("the_string", "Test option", 66 "arg")); 67 } 68 69 /// Executes the command. 70 /// 71 /// \param cmdline Representation of the command line to the subcommand. 72 /// \param data Arbitrary data cookie passed to the command. 73 /// 74 /// \return A hardcoded number for testing purposes. 75 int 76 run(cmdline::ui* /* ui */, 77 const cmdline::parsed_cmdline& cmdline, const Data& data) 78 { 79 if (cmdline.has_option("the_string")) 80 optvalue = cmdline.get_option< cmdline::string_option >( 81 "the_string"); 82 ATF_REQUIRE_EQ(ExpectedData, data); 83 executed = true; 84 return 1234; 85 } 86}; 87 88 89/// Mock command to test the cmdline::base_command_no_data base class. 90class mock_cmd_no_data : public cmdline::base_command_no_data { 91public: 92 /// Indicates if run() has been called already and executed correctly. 93 bool executed; 94 95 /// Contains the argument of --the_string after run() is executed. 96 std::string optvalue; 97 98 /// Constructs a new mock command. 99 mock_cmd_no_data(void) : 100 cmdline::base_command_no_data("mock", "arg1 [arg2 [arg3]]", 1, 3, 101 "Command for testing."), 102 executed(false) 103 { 104 add_option(cmdline::string_option("the_string", "Test option", "arg")); 105 } 106 107 /// Executes the command. 108 /// 109 /// \param cmdline Representation of the command line to the subcommand. 110 /// 111 /// \return A hardcoded number for testing purposes. 112 int 113 run(cmdline::ui* /* ui */, 114 const cmdline::parsed_cmdline& cmdline) 115 { 116 if (cmdline.has_option("the_string")) 117 optvalue = cmdline.get_option< cmdline::string_option >( 118 "the_string"); 119 executed = true; 120 return 1234; 121 } 122}; 123 124 125/// Implementation of a command to get access to parse_cmdline(). 126class parse_cmdline_portal : public cmdline::command_proto { 127public: 128 /// Constructs a new mock command. 129 parse_cmdline_portal(void) : 130 cmdline::command_proto("portal", "arg1 [arg2 [arg3]]", 1, 3, 131 "Command for testing.") 132 { 133 this->add_option(cmdline::string_option("the_string", "Test option", 134 "arg")); 135 } 136 137 /// Delegator for the internal parse_cmdline() method. 138 /// 139 /// \param args The input arguments to be parsed. 140 /// 141 /// \return The parsed command line, split in options and arguments. 142 cmdline::parsed_cmdline 143 operator()(const cmdline::args_vector& args) const 144 { 145 return parse_cmdline(args); 146 } 147}; 148 149 150} // anonymous namespace 151 152 153ATF_TEST_CASE_WITHOUT_HEAD(command_proto__parse_cmdline__ok); 154ATF_TEST_CASE_BODY(command_proto__parse_cmdline__ok) 155{ 156 cmdline::args_vector args; 157 args.push_back("portal"); 158 args.push_back("--the_string=foo bar"); 159 args.push_back("one arg"); 160 args.push_back("another arg"); 161 (void)parse_cmdline_portal()(args); 162} 163 164 165ATF_TEST_CASE_WITHOUT_HEAD(command_proto__parse_cmdline__parse_fail); 166ATF_TEST_CASE_BODY(command_proto__parse_cmdline__parse_fail) 167{ 168 cmdline::args_vector args; 169 args.push_back("portal"); 170 args.push_back("--foo-bar"); 171 ATF_REQUIRE_THROW_RE(cmdline::usage_error, "Unknown.*foo-bar", 172 (void)parse_cmdline_portal()(args)); 173} 174 175 176ATF_TEST_CASE_WITHOUT_HEAD(command_proto__parse_cmdline__args_invalid); 177ATF_TEST_CASE_BODY(command_proto__parse_cmdline__args_invalid) 178{ 179 cmdline::args_vector args; 180 args.push_back("portal"); 181 182 ATF_REQUIRE_THROW_RE(cmdline::usage_error, "Not enough arguments", 183 (void)parse_cmdline_portal()(args)); 184 185 args.push_back("1"); 186 args.push_back("2"); 187 args.push_back("3"); 188 args.push_back("4"); 189 ATF_REQUIRE_THROW_RE(cmdline::usage_error, "Too many arguments", 190 (void)parse_cmdline_portal()(args)); 191} 192 193 194ATF_TEST_CASE_WITHOUT_HEAD(base_command__getters); 195ATF_TEST_CASE_BODY(base_command__getters) 196{ 197 mock_cmd< int, 584 > cmd; 198 ATF_REQUIRE_EQ("mock", cmd.name()); 199 ATF_REQUIRE_EQ("arg1 [arg2 [arg3]]", cmd.arg_list()); 200 ATF_REQUIRE_EQ("Command for testing.", cmd.short_description()); 201 ATF_REQUIRE_EQ(1, cmd.options().size()); 202 ATF_REQUIRE_EQ("the_string", cmd.options()[0]->long_name()); 203} 204 205 206ATF_TEST_CASE_WITHOUT_HEAD(base_command__main__ok) 207ATF_TEST_CASE_BODY(base_command__main__ok) 208{ 209 mock_cmd< int, 584 > cmd; 210 211 cmdline::ui_mock ui; 212 cmdline::args_vector args; 213 args.push_back("mock"); 214 args.push_back("--the_string=foo bar"); 215 args.push_back("one arg"); 216 args.push_back("another arg"); 217 ATF_REQUIRE_EQ(1234, cmd.main(&ui, args, 584)); 218 ATF_REQUIRE(cmd.executed); 219 ATF_REQUIRE_EQ("foo bar", cmd.optvalue); 220} 221 222 223ATF_TEST_CASE_WITHOUT_HEAD(base_command__main__parse_cmdline_fail) 224ATF_TEST_CASE_BODY(base_command__main__parse_cmdline_fail) 225{ 226 mock_cmd< int, 584 > cmd; 227 228 cmdline::ui_mock ui; 229 cmdline::args_vector args; 230 args.push_back("mock"); 231 args.push_back("--foo-bar"); 232 ATF_REQUIRE_THROW_RE(cmdline::usage_error, "Unknown.*foo-bar", 233 cmd.main(&ui, args, 584)); 234 ATF_REQUIRE(!cmd.executed); 235} 236 237 238ATF_TEST_CASE_WITHOUT_HEAD(base_command_no_data__getters); 239ATF_TEST_CASE_BODY(base_command_no_data__getters) 240{ 241 mock_cmd_no_data cmd; 242 ATF_REQUIRE_EQ("mock", cmd.name()); 243 ATF_REQUIRE_EQ("arg1 [arg2 [arg3]]", cmd.arg_list()); 244 ATF_REQUIRE_EQ("Command for testing.", cmd.short_description()); 245 ATF_REQUIRE_EQ(1, cmd.options().size()); 246 ATF_REQUIRE_EQ("the_string", cmd.options()[0]->long_name()); 247} 248 249 250ATF_TEST_CASE_WITHOUT_HEAD(base_command_no_data__main__ok) 251ATF_TEST_CASE_BODY(base_command_no_data__main__ok) 252{ 253 mock_cmd_no_data cmd; 254 255 cmdline::ui_mock ui; 256 cmdline::args_vector args; 257 args.push_back("mock"); 258 args.push_back("--the_string=foo bar"); 259 args.push_back("one arg"); 260 args.push_back("another arg"); 261 ATF_REQUIRE_EQ(1234, cmd.main(&ui, args)); 262 ATF_REQUIRE(cmd.executed); 263 ATF_REQUIRE_EQ("foo bar", cmd.optvalue); 264} 265 266 267ATF_TEST_CASE_WITHOUT_HEAD(base_command_no_data__main__parse_cmdline_fail) 268ATF_TEST_CASE_BODY(base_command_no_data__main__parse_cmdline_fail) 269{ 270 mock_cmd_no_data cmd; 271 272 cmdline::ui_mock ui; 273 cmdline::args_vector args; 274 args.push_back("mock"); 275 args.push_back("--foo-bar"); 276 ATF_REQUIRE_THROW_RE(cmdline::usage_error, "Unknown.*foo-bar", 277 cmd.main(&ui, args)); 278 ATF_REQUIRE(!cmd.executed); 279} 280 281 282ATF_INIT_TEST_CASES(tcs) 283{ 284 ATF_ADD_TEST_CASE(tcs, command_proto__parse_cmdline__ok); 285 ATF_ADD_TEST_CASE(tcs, command_proto__parse_cmdline__parse_fail); 286 ATF_ADD_TEST_CASE(tcs, command_proto__parse_cmdline__args_invalid); 287 288 ATF_ADD_TEST_CASE(tcs, base_command__getters); 289 ATF_ADD_TEST_CASE(tcs, base_command__main__ok); 290 ATF_ADD_TEST_CASE(tcs, base_command__main__parse_cmdline_fail); 291 292 ATF_ADD_TEST_CASE(tcs, base_command_no_data__getters); 293 ATF_ADD_TEST_CASE(tcs, base_command_no_data__main__ok); 294 ATF_ADD_TEST_CASE(tcs, base_command_no_data__main__parse_cmdline_fail); 295} 296