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 39 namespace cmdline = utils::cmdline; 40 41 42 namespace { 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(). 50 template< typename Data, Data ExpectedData > 51 class mock_cmd : public cmdline::base_command< Data > { 52 public: 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. 90 class mock_cmd_no_data : public cmdline::base_command_no_data { 91 public: 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(). 126 class parse_cmdline_portal : public cmdline::command_proto { 127 public: 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 153 ATF_TEST_CASE_WITHOUT_HEAD(command_proto__parse_cmdline__ok); 154 ATF_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 165 ATF_TEST_CASE_WITHOUT_HEAD(command_proto__parse_cmdline__parse_fail); 166 ATF_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 176 ATF_TEST_CASE_WITHOUT_HEAD(command_proto__parse_cmdline__args_invalid); 177 ATF_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 194 ATF_TEST_CASE_WITHOUT_HEAD(base_command__getters); 195 ATF_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 206 ATF_TEST_CASE_WITHOUT_HEAD(base_command__main__ok) 207 ATF_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 223 ATF_TEST_CASE_WITHOUT_HEAD(base_command__main__parse_cmdline_fail) 224 ATF_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 238 ATF_TEST_CASE_WITHOUT_HEAD(base_command_no_data__getters); 239 ATF_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 250 ATF_TEST_CASE_WITHOUT_HEAD(base_command_no_data__main__ok) 251 ATF_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 267 ATF_TEST_CASE_WITHOUT_HEAD(base_command_no_data__main__parse_cmdline_fail) 268 ATF_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 282 ATF_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