1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include <string> 6 #include <vector> 7 8 #include "base/command_line.h" 9 #include "base/files/file_path.h" 10 #include "base/macros.h" 11 #include "base/memory/scoped_ptr.h" 12 #include "base/strings/utf_string_conversions.h" 13 #include "build/build_config.h" 14 #include "testing/gtest/include/gtest/gtest.h" 15 16 namespace base { 17 18 // To test Windows quoting behavior, we use a string that has some backslashes 19 // and quotes. 20 // Consider the command-line argument: q\"bs1\bs2\\bs3q\\\" 21 // Here it is with C-style escapes. 22 static const CommandLine::StringType kTrickyQuoted = 23 FILE_PATH_LITERAL("q\\\"bs1\\bs2\\\\bs3q\\\\\\\""); 24 // It should be parsed by Windows as: q"bs1\bs2\\bs3q\" 25 // Here that is with C-style escapes. 26 static const CommandLine::StringType kTricky = 27 FILE_PATH_LITERAL("q\"bs1\\bs2\\\\bs3q\\\""); 28 29 TEST(CommandLineTest, CommandLineConstructor) { 30 const CommandLine::CharType* argv[] = { 31 FILE_PATH_LITERAL("program"), 32 FILE_PATH_LITERAL("--foo="), 33 FILE_PATH_LITERAL("-bAr"), 34 FILE_PATH_LITERAL("-spaetzel=pierogi"), 35 FILE_PATH_LITERAL("-baz"), 36 FILE_PATH_LITERAL("flim"), 37 FILE_PATH_LITERAL("--other-switches=--dog=canine --cat=feline"), 38 FILE_PATH_LITERAL("-spaetzle=Crepe"), 39 FILE_PATH_LITERAL("-=loosevalue"), 40 FILE_PATH_LITERAL("-"), 41 FILE_PATH_LITERAL("FLAN"), 42 FILE_PATH_LITERAL("a"), 43 FILE_PATH_LITERAL("--input-translation=45--output-rotation"), 44 FILE_PATH_LITERAL("--"), 45 FILE_PATH_LITERAL("--"), 46 FILE_PATH_LITERAL("--not-a-switch"), 47 FILE_PATH_LITERAL("\"in the time of submarines...\""), 48 FILE_PATH_LITERAL("unquoted arg-with-space")}; 49 CommandLine cl(arraysize(argv), argv); 50 51 EXPECT_FALSE(cl.GetCommandLineString().empty()); 52 EXPECT_FALSE(cl.HasSwitch("cruller")); 53 EXPECT_FALSE(cl.HasSwitch("flim")); 54 EXPECT_FALSE(cl.HasSwitch("program")); 55 EXPECT_FALSE(cl.HasSwitch("dog")); 56 EXPECT_FALSE(cl.HasSwitch("cat")); 57 EXPECT_FALSE(cl.HasSwitch("output-rotation")); 58 EXPECT_FALSE(cl.HasSwitch("not-a-switch")); 59 EXPECT_FALSE(cl.HasSwitch("--")); 60 61 EXPECT_EQ(FilePath(FILE_PATH_LITERAL("program")).value(), 62 cl.GetProgram().value()); 63 64 EXPECT_TRUE(cl.HasSwitch("foo")); 65 #if defined(OS_WIN) 66 EXPECT_TRUE(cl.HasSwitch("bar")); 67 #else 68 EXPECT_FALSE(cl.HasSwitch("bar")); 69 #endif 70 EXPECT_TRUE(cl.HasSwitch("baz")); 71 EXPECT_TRUE(cl.HasSwitch("spaetzle")); 72 EXPECT_TRUE(cl.HasSwitch("other-switches")); 73 EXPECT_TRUE(cl.HasSwitch("input-translation")); 74 75 EXPECT_EQ("Crepe", cl.GetSwitchValueASCII("spaetzle")); 76 EXPECT_EQ("", cl.GetSwitchValueASCII("foo")); 77 EXPECT_EQ("", cl.GetSwitchValueASCII("bar")); 78 EXPECT_EQ("", cl.GetSwitchValueASCII("cruller")); 79 EXPECT_EQ("--dog=canine --cat=feline", cl.GetSwitchValueASCII( 80 "other-switches")); 81 EXPECT_EQ("45--output-rotation", cl.GetSwitchValueASCII("input-translation")); 82 83 const CommandLine::StringVector& args = cl.GetArgs(); 84 ASSERT_EQ(8U, args.size()); 85 86 std::vector<CommandLine::StringType>::const_iterator iter = args.begin(); 87 EXPECT_EQ(FILE_PATH_LITERAL("flim"), *iter); 88 ++iter; 89 EXPECT_EQ(FILE_PATH_LITERAL("-"), *iter); 90 ++iter; 91 EXPECT_EQ(FILE_PATH_LITERAL("FLAN"), *iter); 92 ++iter; 93 EXPECT_EQ(FILE_PATH_LITERAL("a"), *iter); 94 ++iter; 95 EXPECT_EQ(FILE_PATH_LITERAL("--"), *iter); 96 ++iter; 97 EXPECT_EQ(FILE_PATH_LITERAL("--not-a-switch"), *iter); 98 ++iter; 99 EXPECT_EQ(FILE_PATH_LITERAL("\"in the time of submarines...\""), *iter); 100 ++iter; 101 EXPECT_EQ(FILE_PATH_LITERAL("unquoted arg-with-space"), *iter); 102 ++iter; 103 EXPECT_TRUE(iter == args.end()); 104 } 105 106 TEST(CommandLineTest, CommandLineFromString) { 107 #if defined(OS_WIN) 108 CommandLine cl = CommandLine::FromString( 109 L"program --foo= -bAr /Spaetzel=pierogi /Baz flim " 110 L"--other-switches=\"--dog=canine --cat=feline\" " 111 L"-spaetzle=Crepe -=loosevalue FLAN " 112 L"--input-translation=\"45\"--output-rotation " 113 L"--quotes=" + kTrickyQuoted + L" " 114 L"-- -- --not-a-switch " 115 L"\"in the time of submarines...\""); 116 117 EXPECT_FALSE(cl.GetCommandLineString().empty()); 118 EXPECT_FALSE(cl.HasSwitch("cruller")); 119 EXPECT_FALSE(cl.HasSwitch("flim")); 120 EXPECT_FALSE(cl.HasSwitch("program")); 121 EXPECT_FALSE(cl.HasSwitch("dog")); 122 EXPECT_FALSE(cl.HasSwitch("cat")); 123 EXPECT_FALSE(cl.HasSwitch("output-rotation")); 124 EXPECT_FALSE(cl.HasSwitch("not-a-switch")); 125 EXPECT_FALSE(cl.HasSwitch("--")); 126 127 EXPECT_EQ(FilePath(FILE_PATH_LITERAL("program")).value(), 128 cl.GetProgram().value()); 129 130 EXPECT_TRUE(cl.HasSwitch("foo")); 131 EXPECT_TRUE(cl.HasSwitch("bar")); 132 EXPECT_TRUE(cl.HasSwitch("baz")); 133 EXPECT_TRUE(cl.HasSwitch("spaetzle")); 134 EXPECT_TRUE(cl.HasSwitch("other-switches")); 135 EXPECT_TRUE(cl.HasSwitch("input-translation")); 136 EXPECT_TRUE(cl.HasSwitch("quotes")); 137 138 EXPECT_EQ("Crepe", cl.GetSwitchValueASCII("spaetzle")); 139 EXPECT_EQ("", cl.GetSwitchValueASCII("foo")); 140 EXPECT_EQ("", cl.GetSwitchValueASCII("bar")); 141 EXPECT_EQ("", cl.GetSwitchValueASCII("cruller")); 142 EXPECT_EQ("--dog=canine --cat=feline", cl.GetSwitchValueASCII( 143 "other-switches")); 144 EXPECT_EQ("45--output-rotation", cl.GetSwitchValueASCII("input-translation")); 145 EXPECT_EQ(kTricky, cl.GetSwitchValueNative("quotes")); 146 147 const CommandLine::StringVector& args = cl.GetArgs(); 148 ASSERT_EQ(5U, args.size()); 149 150 std::vector<CommandLine::StringType>::const_iterator iter = args.begin(); 151 EXPECT_EQ(FILE_PATH_LITERAL("flim"), *iter); 152 ++iter; 153 EXPECT_EQ(FILE_PATH_LITERAL("FLAN"), *iter); 154 ++iter; 155 EXPECT_EQ(FILE_PATH_LITERAL("--"), *iter); 156 ++iter; 157 EXPECT_EQ(FILE_PATH_LITERAL("--not-a-switch"), *iter); 158 ++iter; 159 EXPECT_EQ(FILE_PATH_LITERAL("in the time of submarines..."), *iter); 160 ++iter; 161 EXPECT_TRUE(iter == args.end()); 162 163 // Check that a generated string produces an equivalent command line. 164 CommandLine cl_duplicate = CommandLine::FromString(cl.GetCommandLineString()); 165 EXPECT_EQ(cl.GetCommandLineString(), cl_duplicate.GetCommandLineString()); 166 #endif 167 } 168 169 // Tests behavior with an empty input string. 170 TEST(CommandLineTest, EmptyString) { 171 #if defined(OS_WIN) 172 CommandLine cl_from_string = CommandLine::FromString(L""); 173 EXPECT_TRUE(cl_from_string.GetCommandLineString().empty()); 174 EXPECT_TRUE(cl_from_string.GetProgram().empty()); 175 EXPECT_EQ(1U, cl_from_string.argv().size()); 176 EXPECT_TRUE(cl_from_string.GetArgs().empty()); 177 #endif 178 CommandLine cl_from_argv(0, NULL); 179 EXPECT_TRUE(cl_from_argv.GetCommandLineString().empty()); 180 EXPECT_TRUE(cl_from_argv.GetProgram().empty()); 181 EXPECT_EQ(1U, cl_from_argv.argv().size()); 182 EXPECT_TRUE(cl_from_argv.GetArgs().empty()); 183 } 184 185 TEST(CommandLineTest, GetArgumentsString) { 186 static const FilePath::CharType kPath1[] = 187 FILE_PATH_LITERAL("C:\\Some File\\With Spaces.ggg"); 188 static const FilePath::CharType kPath2[] = 189 FILE_PATH_LITERAL("C:\\no\\spaces.ggg"); 190 191 static const char kFirstArgName[] = "first-arg"; 192 static const char kSecondArgName[] = "arg2"; 193 static const char kThirdArgName[] = "arg with space"; 194 static const char kFourthArgName[] = "nospace"; 195 static const char kFifthArgName[] = "%1"; 196 197 CommandLine cl(CommandLine::NO_PROGRAM); 198 cl.AppendSwitchPath(kFirstArgName, FilePath(kPath1)); 199 cl.AppendSwitchPath(kSecondArgName, FilePath(kPath2)); 200 cl.AppendArg(kThirdArgName); 201 cl.AppendArg(kFourthArgName); 202 cl.AppendArg(kFifthArgName); 203 204 #if defined(OS_WIN) 205 CommandLine::StringType expected_first_arg(UTF8ToUTF16(kFirstArgName)); 206 CommandLine::StringType expected_second_arg(UTF8ToUTF16(kSecondArgName)); 207 CommandLine::StringType expected_third_arg(UTF8ToUTF16(kThirdArgName)); 208 CommandLine::StringType expected_fourth_arg(UTF8ToUTF16(kFourthArgName)); 209 CommandLine::StringType expected_fifth_arg(UTF8ToUTF16(kFifthArgName)); 210 #elif defined(OS_POSIX) 211 CommandLine::StringType expected_first_arg(kFirstArgName); 212 CommandLine::StringType expected_second_arg(kSecondArgName); 213 CommandLine::StringType expected_third_arg(kThirdArgName); 214 CommandLine::StringType expected_fourth_arg(kFourthArgName); 215 CommandLine::StringType expected_fifth_arg(kFifthArgName); 216 #endif 217 218 #if defined(OS_WIN) 219 #define QUOTE_ON_WIN FILE_PATH_LITERAL("\"") 220 #else 221 #define QUOTE_ON_WIN FILE_PATH_LITERAL("") 222 #endif // OS_WIN 223 224 CommandLine::StringType expected_str; 225 expected_str.append(FILE_PATH_LITERAL("--")) 226 .append(expected_first_arg) 227 .append(FILE_PATH_LITERAL("=")) 228 .append(QUOTE_ON_WIN) 229 .append(kPath1) 230 .append(QUOTE_ON_WIN) 231 .append(FILE_PATH_LITERAL(" ")) 232 .append(FILE_PATH_LITERAL("--")) 233 .append(expected_second_arg) 234 .append(FILE_PATH_LITERAL("=")) 235 .append(QUOTE_ON_WIN) 236 .append(kPath2) 237 .append(QUOTE_ON_WIN) 238 .append(FILE_PATH_LITERAL(" ")) 239 .append(QUOTE_ON_WIN) 240 .append(expected_third_arg) 241 .append(QUOTE_ON_WIN) 242 .append(FILE_PATH_LITERAL(" ")) 243 .append(expected_fourth_arg) 244 .append(FILE_PATH_LITERAL(" ")); 245 246 CommandLine::StringType expected_str_no_quote_placeholders(expected_str); 247 expected_str_no_quote_placeholders.append(expected_fifth_arg); 248 EXPECT_EQ(expected_str_no_quote_placeholders, cl.GetArgumentsString()); 249 250 #if defined(OS_WIN) 251 CommandLine::StringType expected_str_quote_placeholders(expected_str); 252 expected_str_quote_placeholders.append(QUOTE_ON_WIN) 253 .append(expected_fifth_arg) 254 .append(QUOTE_ON_WIN); 255 EXPECT_EQ(expected_str_quote_placeholders, 256 cl.GetArgumentsStringWithPlaceholders()); 257 #endif 258 } 259 260 // Test methods for appending switches to a command line. 261 TEST(CommandLineTest, AppendSwitches) { 262 std::string switch1 = "switch1"; 263 std::string switch2 = "switch2"; 264 std::string value2 = "value"; 265 std::string switch3 = "switch3"; 266 std::string value3 = "a value with spaces"; 267 std::string switch4 = "switch4"; 268 std::string value4 = "\"a value with quotes\""; 269 std::string switch5 = "quotes"; 270 CommandLine::StringType value5 = kTricky; 271 272 CommandLine cl(FilePath(FILE_PATH_LITERAL("Program"))); 273 274 cl.AppendSwitch(switch1); 275 cl.AppendSwitchASCII(switch2, value2); 276 cl.AppendSwitchASCII(switch3, value3); 277 cl.AppendSwitchASCII(switch4, value4); 278 cl.AppendSwitchASCII(switch5, value4); 279 cl.AppendSwitchNative(switch5, value5); 280 281 EXPECT_TRUE(cl.HasSwitch(switch1)); 282 EXPECT_TRUE(cl.HasSwitch(switch2)); 283 EXPECT_EQ(value2, cl.GetSwitchValueASCII(switch2)); 284 EXPECT_TRUE(cl.HasSwitch(switch3)); 285 EXPECT_EQ(value3, cl.GetSwitchValueASCII(switch3)); 286 EXPECT_TRUE(cl.HasSwitch(switch4)); 287 EXPECT_EQ(value4, cl.GetSwitchValueASCII(switch4)); 288 EXPECT_TRUE(cl.HasSwitch(switch5)); 289 EXPECT_EQ(value5, cl.GetSwitchValueNative(switch5)); 290 291 #if defined(OS_WIN) 292 EXPECT_EQ(L"Program " 293 L"--switch1 " 294 L"--switch2=value " 295 L"--switch3=\"a value with spaces\" " 296 L"--switch4=\"\\\"a value with quotes\\\"\" " 297 // Even though the switches are unique, appending can add repeat 298 // switches to argv. 299 L"--quotes=\"\\\"a value with quotes\\\"\" " 300 L"--quotes=\"" + kTrickyQuoted + L"\"", 301 cl.GetCommandLineString()); 302 #endif 303 } 304 305 TEST(CommandLineTest, AppendSwitchesDashDash) { 306 const CommandLine::CharType* raw_argv[] = { FILE_PATH_LITERAL("prog"), 307 FILE_PATH_LITERAL("--"), 308 FILE_PATH_LITERAL("--arg1") }; 309 CommandLine cl(arraysize(raw_argv), raw_argv); 310 311 cl.AppendSwitch("switch1"); 312 cl.AppendSwitchASCII("switch2", "foo"); 313 314 cl.AppendArg("--arg2"); 315 316 EXPECT_EQ(FILE_PATH_LITERAL("prog --switch1 --switch2=foo -- --arg1 --arg2"), 317 cl.GetCommandLineString()); 318 CommandLine::StringVector cl_argv = cl.argv(); 319 EXPECT_EQ(FILE_PATH_LITERAL("prog"), cl_argv[0]); 320 EXPECT_EQ(FILE_PATH_LITERAL("--switch1"), cl_argv[1]); 321 EXPECT_EQ(FILE_PATH_LITERAL("--switch2=foo"), cl_argv[2]); 322 EXPECT_EQ(FILE_PATH_LITERAL("--"), cl_argv[3]); 323 EXPECT_EQ(FILE_PATH_LITERAL("--arg1"), cl_argv[4]); 324 EXPECT_EQ(FILE_PATH_LITERAL("--arg2"), cl_argv[5]); 325 } 326 327 // Tests that when AppendArguments is called that the program is set correctly 328 // on the target CommandLine object and the switches from the source 329 // CommandLine are added to the target. 330 TEST(CommandLineTest, AppendArguments) { 331 CommandLine cl1(FilePath(FILE_PATH_LITERAL("Program"))); 332 cl1.AppendSwitch("switch1"); 333 cl1.AppendSwitchASCII("switch2", "foo"); 334 335 CommandLine cl2(CommandLine::NO_PROGRAM); 336 cl2.AppendArguments(cl1, true); 337 EXPECT_EQ(cl1.GetProgram().value(), cl2.GetProgram().value()); 338 EXPECT_EQ(cl1.GetCommandLineString(), cl2.GetCommandLineString()); 339 340 CommandLine c1(FilePath(FILE_PATH_LITERAL("Program1"))); 341 c1.AppendSwitch("switch1"); 342 CommandLine c2(FilePath(FILE_PATH_LITERAL("Program2"))); 343 c2.AppendSwitch("switch2"); 344 345 c1.AppendArguments(c2, true); 346 EXPECT_EQ(c1.GetProgram().value(), c2.GetProgram().value()); 347 EXPECT_TRUE(c1.HasSwitch("switch1")); 348 EXPECT_TRUE(c1.HasSwitch("switch2")); 349 } 350 351 #if defined(OS_WIN) 352 // Make sure that the command line string program paths are quoted as necessary. 353 // This only makes sense on Windows and the test is basically here to guard 354 // against regressions. 355 TEST(CommandLineTest, ProgramQuotes) { 356 // Check that quotes are not added for paths without spaces. 357 const FilePath kProgram(L"Program"); 358 CommandLine cl_program(kProgram); 359 EXPECT_EQ(kProgram.value(), cl_program.GetProgram().value()); 360 EXPECT_EQ(kProgram.value(), cl_program.GetCommandLineString()); 361 362 const FilePath kProgramPath(L"Program Path"); 363 364 // Check that quotes are not returned from GetProgram(). 365 CommandLine cl_program_path(kProgramPath); 366 EXPECT_EQ(kProgramPath.value(), cl_program_path.GetProgram().value()); 367 368 // Check that quotes are added to command line string paths containing spaces. 369 CommandLine::StringType cmd_string(cl_program_path.GetCommandLineString()); 370 EXPECT_EQ(L"\"Program Path\"", cmd_string); 371 372 // Check the optional quoting of placeholders in programs. 373 CommandLine cl_quote_placeholder(FilePath(L"%1")); 374 EXPECT_EQ(L"%1", cl_quote_placeholder.GetCommandLineString()); 375 EXPECT_EQ(L"\"%1\"", 376 cl_quote_placeholder.GetCommandLineStringWithPlaceholders()); 377 } 378 #endif 379 380 // Calling Init multiple times should not modify the previous CommandLine. 381 TEST(CommandLineTest, Init) { 382 // Call Init without checking output once so we know it's been called 383 // whether or not the test runner does so. 384 CommandLine::Init(0, NULL); 385 CommandLine* initial = CommandLine::ForCurrentProcess(); 386 EXPECT_FALSE(CommandLine::Init(0, NULL)); 387 CommandLine* current = CommandLine::ForCurrentProcess(); 388 EXPECT_EQ(initial, current); 389 } 390 391 // Test that copies of CommandLine have a valid StringPiece map. 392 TEST(CommandLineTest, Copy) { 393 scoped_ptr<CommandLine> initial(new CommandLine(CommandLine::NO_PROGRAM)); 394 initial->AppendSwitch("a"); 395 initial->AppendSwitch("bbbbbbbbbbbbbbb"); 396 initial->AppendSwitch("c"); 397 CommandLine copy_constructed(*initial); 398 CommandLine assigned = *initial; 399 CommandLine::SwitchMap switch_map = initial->GetSwitches(); 400 initial.reset(); 401 for (const auto& pair : switch_map) 402 EXPECT_TRUE(copy_constructed.HasSwitch(pair.first)); 403 for (const auto& pair : switch_map) 404 EXPECT_TRUE(assigned.HasSwitch(pair.first)); 405 } 406 407 } // namespace base 408