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 <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