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