// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/command_line.h" #include #include #include #include "base/files/file_path.h" #include "base/strings/strcat.h" #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" namespace base { // To test Windows quoting behavior, we use a string that has some backslashes // and quotes. // Consider the command-line argument: q\"bs1\bs2\\bs3q\\\" // Here it is with C-style escapes. static const CommandLine::StringType kTrickyQuoted = FILE_PATH_LITERAL("q\\\"bs1\\bs2\\\\bs3q\\\\\\\""); // It should be parsed by Windows as: q"bs1\bs2\\bs3q\" // Here that is with C-style escapes. static const CommandLine::StringType kTricky = FILE_PATH_LITERAL("q\"bs1\\bs2\\\\bs3q\\\""); TEST(CommandLineTest, CommandLineConstructor) { const CommandLine::CharType* argv[] = { FILE_PATH_LITERAL("program"), FILE_PATH_LITERAL("--foo="), FILE_PATH_LITERAL("-bAr"), FILE_PATH_LITERAL("-spaetzel=pierogi"), FILE_PATH_LITERAL("-baz"), FILE_PATH_LITERAL("flim"), FILE_PATH_LITERAL("--other-switches=--dog=canine --cat=feline"), FILE_PATH_LITERAL("-spaetzle=Crepe"), FILE_PATH_LITERAL("-=loosevalue"), FILE_PATH_LITERAL("-"), FILE_PATH_LITERAL("FLAN"), FILE_PATH_LITERAL("a"), FILE_PATH_LITERAL("--input-translation=45--output-rotation"), FILE_PATH_LITERAL("--"), FILE_PATH_LITERAL("--"), FILE_PATH_LITERAL("--not-a-switch"), FILE_PATH_LITERAL("\"in the time of submarines...\""), FILE_PATH_LITERAL("unquoted arg-with-space")}; CommandLine cl(std::size(argv), argv); EXPECT_FALSE(cl.GetCommandLineString().empty()); EXPECT_FALSE(cl.HasSwitch("cruller")); EXPECT_FALSE(cl.HasSwitch("flim")); EXPECT_FALSE(cl.HasSwitch("program")); EXPECT_FALSE(cl.HasSwitch("dog")); EXPECT_FALSE(cl.HasSwitch("cat")); EXPECT_FALSE(cl.HasSwitch("output-rotation")); EXPECT_FALSE(cl.HasSwitch("not-a-switch")); EXPECT_FALSE(cl.HasSwitch("--")); EXPECT_EQ(FilePath(FILE_PATH_LITERAL("program")).value(), cl.GetProgram().value()); EXPECT_TRUE(cl.HasSwitch("foo")); #if BUILDFLAG(IS_WIN) EXPECT_TRUE(cl.HasSwitch("bar")); #else EXPECT_FALSE(cl.HasSwitch("bar")); #endif EXPECT_TRUE(cl.HasSwitch("baz")); EXPECT_TRUE(cl.HasSwitch("spaetzle")); EXPECT_TRUE(cl.HasSwitch("other-switches")); EXPECT_TRUE(cl.HasSwitch("input-translation")); EXPECT_EQ("Crepe", cl.GetSwitchValueASCII("spaetzle")); EXPECT_EQ("", cl.GetSwitchValueASCII("foo")); EXPECT_EQ("", cl.GetSwitchValueASCII("bar")); EXPECT_EQ("", cl.GetSwitchValueASCII("cruller")); EXPECT_EQ("--dog=canine --cat=feline", cl.GetSwitchValueASCII( "other-switches")); EXPECT_EQ("45--output-rotation", cl.GetSwitchValueASCII("input-translation")); const CommandLine::StringVector& args = cl.GetArgs(); ASSERT_EQ(8U, args.size()); auto iter = args.begin(); EXPECT_EQ(FILE_PATH_LITERAL("flim"), *iter); ++iter; EXPECT_EQ(FILE_PATH_LITERAL("-"), *iter); ++iter; EXPECT_EQ(FILE_PATH_LITERAL("FLAN"), *iter); ++iter; EXPECT_EQ(FILE_PATH_LITERAL("a"), *iter); ++iter; EXPECT_EQ(FILE_PATH_LITERAL("--"), *iter); ++iter; EXPECT_EQ(FILE_PATH_LITERAL("--not-a-switch"), *iter); ++iter; EXPECT_EQ(FILE_PATH_LITERAL("\"in the time of submarines...\""), *iter); ++iter; EXPECT_EQ(FILE_PATH_LITERAL("unquoted arg-with-space"), *iter); ++iter; EXPECT_TRUE(iter == args.end()); } TEST(CommandLineTest, CommandLineFromString) { #if BUILDFLAG(IS_WIN) CommandLine cl = CommandLine::FromString( L"program --foo= -bAr /Spaetzel=pierogi /Baz flim " L"--other-switches=\"--dog=canine --cat=feline\" " L"-spaetzle=Crepe -=loosevalue FLAN " L"--input-translation=\"45\"--output-rotation " L"--quotes=" + kTrickyQuoted + L" -- -- --not-a-switch \"in the time of submarines...\""); EXPECT_FALSE(cl.GetCommandLineString().empty()); EXPECT_FALSE(cl.HasSwitch("cruller")); EXPECT_FALSE(cl.HasSwitch("flim")); EXPECT_FALSE(cl.HasSwitch("program")); EXPECT_FALSE(cl.HasSwitch("dog")); EXPECT_FALSE(cl.HasSwitch("cat")); EXPECT_FALSE(cl.HasSwitch("output-rotation")); EXPECT_FALSE(cl.HasSwitch("not-a-switch")); EXPECT_FALSE(cl.HasSwitch("--")); EXPECT_EQ(FilePath(FILE_PATH_LITERAL("program")).value(), cl.GetProgram().value()); EXPECT_TRUE(cl.HasSwitch("foo")); EXPECT_TRUE(cl.HasSwitch("bar")); EXPECT_TRUE(cl.HasSwitch("baz")); EXPECT_TRUE(cl.HasSwitch("spaetzle")); EXPECT_TRUE(cl.HasSwitch("other-switches")); EXPECT_TRUE(cl.HasSwitch("input-translation")); EXPECT_TRUE(cl.HasSwitch("quotes")); EXPECT_EQ("Crepe", cl.GetSwitchValueASCII("spaetzle")); EXPECT_EQ("", cl.GetSwitchValueASCII("foo")); EXPECT_EQ("", cl.GetSwitchValueASCII("bar")); EXPECT_EQ("", cl.GetSwitchValueASCII("cruller")); EXPECT_EQ("--dog=canine --cat=feline", cl.GetSwitchValueASCII( "other-switches")); EXPECT_EQ("45--output-rotation", cl.GetSwitchValueASCII("input-translation")); EXPECT_EQ(kTricky, cl.GetSwitchValueNative("quotes")); const CommandLine::StringVector& args = cl.GetArgs(); ASSERT_EQ(5U, args.size()); std::vector::const_iterator iter = args.begin(); EXPECT_EQ(FILE_PATH_LITERAL("flim"), *iter); ++iter; EXPECT_EQ(FILE_PATH_LITERAL("FLAN"), *iter); ++iter; EXPECT_EQ(FILE_PATH_LITERAL("--"), *iter); ++iter; EXPECT_EQ(FILE_PATH_LITERAL("--not-a-switch"), *iter); ++iter; EXPECT_EQ(FILE_PATH_LITERAL("in the time of submarines..."), *iter); ++iter; EXPECT_TRUE(iter == args.end()); // Check that a generated string produces an equivalent command line. CommandLine cl_duplicate = CommandLine::FromString(cl.GetCommandLineString()); EXPECT_EQ(cl.GetCommandLineString(), cl_duplicate.GetCommandLineString()); #endif } // Tests behavior with an empty input string. TEST(CommandLineTest, EmptyString) { #if BUILDFLAG(IS_WIN) CommandLine cl_from_string = CommandLine::FromString(std::wstring()); EXPECT_TRUE(cl_from_string.GetCommandLineString().empty()); EXPECT_TRUE(cl_from_string.GetProgram().empty()); EXPECT_EQ(1U, cl_from_string.argv().size()); EXPECT_TRUE(cl_from_string.GetArgs().empty()); #endif CommandLine cl_from_argv(0, nullptr); EXPECT_TRUE(cl_from_argv.GetCommandLineString().empty()); EXPECT_TRUE(cl_from_argv.GetProgram().empty()); EXPECT_EQ(1U, cl_from_argv.argv().size()); EXPECT_TRUE(cl_from_argv.GetArgs().empty()); } TEST(CommandLineTest, GetArgumentsString) { static const FilePath::CharType kPath1[] = FILE_PATH_LITERAL("C:\\Some File\\With Spaces.ggg"); static const FilePath::CharType kPath2[] = FILE_PATH_LITERAL("C:\\no\\spaces.ggg"); static const char kFirstArgName[] = "first-arg"; static const char kSecondArgName[] = "arg2"; static const char kThirdArgName[] = "arg with space"; static const char kFourthArgName[] = "nospace"; CommandLine cl(CommandLine::NO_PROGRAM); cl.AppendSwitchPath(kFirstArgName, FilePath(kPath1)); cl.AppendSwitchPath(kSecondArgName, FilePath(kPath2)); cl.AppendArg(kThirdArgName); cl.AppendArg(kFourthArgName); #if BUILDFLAG(IS_WIN) CommandLine::StringType expected_first_arg(UTF8ToWide(kFirstArgName)); CommandLine::StringType expected_second_arg(UTF8ToWide(kSecondArgName)); CommandLine::StringType expected_third_arg(UTF8ToWide(kThirdArgName)); CommandLine::StringType expected_fourth_arg(UTF8ToWide(kFourthArgName)); #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) CommandLine::StringType expected_first_arg(kFirstArgName); CommandLine::StringType expected_second_arg(kSecondArgName); CommandLine::StringType expected_third_arg(kThirdArgName); CommandLine::StringType expected_fourth_arg(kFourthArgName); #endif #if BUILDFLAG(IS_WIN) #define QUOTE_ON_WIN FILE_PATH_LITERAL("\"") #else #define QUOTE_ON_WIN FILE_PATH_LITERAL("") #endif // BUILDFLAG(IS_WIN) CommandLine::StringType expected_str; expected_str.append(FILE_PATH_LITERAL("--")) .append(expected_first_arg) .append(FILE_PATH_LITERAL("=")) .append(QUOTE_ON_WIN) .append(kPath1) .append(QUOTE_ON_WIN) .append(FILE_PATH_LITERAL(" ")) .append(FILE_PATH_LITERAL("--")) .append(expected_second_arg) .append(FILE_PATH_LITERAL("=")) .append(QUOTE_ON_WIN) .append(kPath2) .append(QUOTE_ON_WIN) .append(FILE_PATH_LITERAL(" ")) .append(QUOTE_ON_WIN) .append(expected_third_arg) .append(QUOTE_ON_WIN) .append(FILE_PATH_LITERAL(" ")) .append(expected_fourth_arg); EXPECT_EQ(expected_str, cl.GetArgumentsString()); } // Test methods for appending switches to a command line. TEST(CommandLineTest, AppendSwitches) { std::string switch1 = "switch1"; std::string switch2 = "switch2"; std::string value2 = "value"; std::string switch3 = "switch3"; std::string value3 = "a value with spaces"; std::string switch4 = "switch4"; std::string value4 = "\"a value with quotes\""; std::string switch5 = "quotes"; CommandLine::StringType value5 = kTricky; CommandLine cl(FilePath(FILE_PATH_LITERAL("Program"))); cl.AppendSwitch(switch1); cl.AppendSwitchASCII(switch2, value2); cl.AppendSwitchASCII(switch3, value3); cl.AppendSwitchASCII(switch4, value4); cl.AppendSwitchASCII(switch5, value4); cl.AppendSwitchNative(switch5, value5); EXPECT_TRUE(cl.HasSwitch(switch1)); EXPECT_TRUE(cl.HasSwitch(switch2)); EXPECT_EQ(value2, cl.GetSwitchValueASCII(switch2)); EXPECT_TRUE(cl.HasSwitch(switch3)); EXPECT_EQ(value3, cl.GetSwitchValueASCII(switch3)); EXPECT_TRUE(cl.HasSwitch(switch4)); EXPECT_EQ(value4, cl.GetSwitchValueASCII(switch4)); EXPECT_TRUE(cl.HasSwitch(switch5)); EXPECT_EQ(value5, cl.GetSwitchValueNative(switch5)); #if BUILDFLAG(IS_WIN) EXPECT_EQ( L"Program " L"--switch1 " L"--switch2=value " L"--switch3=\"a value with spaces\" " L"--switch4=\"\\\"a value with quotes\\\"\" " // Even though the switches are unique, appending can add repeat // switches to argv. L"--quotes=\"\\\"a value with quotes\\\"\" " L"--quotes=\"" + kTrickyQuoted + L"\"", cl.GetCommandLineString()); #endif } TEST(CommandLineTest, AppendSwitchesDashDash) { const CommandLine::CharType* raw_argv[] = { FILE_PATH_LITERAL("prog"), FILE_PATH_LITERAL("--"), FILE_PATH_LITERAL("--arg1") }; CommandLine cl(std::size(raw_argv), raw_argv); cl.AppendSwitch("switch1"); cl.AppendSwitchASCII("switch2", "foo"); cl.AppendArg("--arg2"); EXPECT_EQ(FILE_PATH_LITERAL("prog --switch1 --switch2=foo -- --arg1 --arg2"), cl.GetCommandLineString()); CommandLine::StringVector cl_argv = cl.argv(); EXPECT_EQ(FILE_PATH_LITERAL("prog"), cl_argv[0]); EXPECT_EQ(FILE_PATH_LITERAL("--switch1"), cl_argv[1]); EXPECT_EQ(FILE_PATH_LITERAL("--switch2=foo"), cl_argv[2]); EXPECT_EQ(FILE_PATH_LITERAL("--"), cl_argv[3]); EXPECT_EQ(FILE_PATH_LITERAL("--arg1"), cl_argv[4]); EXPECT_EQ(FILE_PATH_LITERAL("--arg2"), cl_argv[5]); } #if BUILDFLAG(IS_WIN) TEST(CommandLineTest, GetCommandLineStringForShell) { CommandLine cl = CommandLine::FromString( FILE_PATH_LITERAL("program --switch /switch2 --")); EXPECT_EQ( cl.GetCommandLineStringForShell(), FILE_PATH_LITERAL("program --switch /switch2 -- --single-argument %1")); } TEST(CommandLineTest, GetCommandLineStringWithUnsafeInsertSequences) { CommandLine cl(FilePath(FILE_PATH_LITERAL("program"))); cl.AppendSwitchASCII("switch", "%1"); cl.AppendSwitch("%2"); cl.AppendArg("%3"); EXPECT_EQ(FILE_PATH_LITERAL("program --switch=%1 --%2 %3"), cl.GetCommandLineStringWithUnsafeInsertSequences()); } #endif // BUILDFLAG(IS_WIN) // Tests that when AppendArguments is called that the program is set correctly // on the target CommandLine object and the switches from the source // CommandLine are added to the target. TEST(CommandLineTest, AppendArguments) { CommandLine cl1(FilePath(FILE_PATH_LITERAL("Program"))); cl1.AppendSwitch("switch1"); cl1.AppendSwitchASCII("switch2", "foo"); CommandLine cl2(CommandLine::NO_PROGRAM); cl2.AppendArguments(cl1, true); EXPECT_EQ(cl1.GetProgram().value(), cl2.GetProgram().value()); EXPECT_EQ(cl1.GetCommandLineString(), cl2.GetCommandLineString()); CommandLine c1(FilePath(FILE_PATH_LITERAL("Program1"))); c1.AppendSwitch("switch1"); CommandLine c2(FilePath(FILE_PATH_LITERAL("Program2"))); c2.AppendSwitch("switch2"); c1.AppendArguments(c2, true); EXPECT_EQ(c1.GetProgram().value(), c2.GetProgram().value()); EXPECT_TRUE(c1.HasSwitch("switch1")); EXPECT_TRUE(c1.HasSwitch("switch2")); } #if BUILDFLAG(IS_WIN) // Make sure that the command line string program paths are quoted as necessary. // This only makes sense on Windows and the test is basically here to guard // against regressions. TEST(CommandLineTest, ProgramQuotes) { // Check that quotes are not added for paths without spaces. const FilePath kProgram(L"Program"); CommandLine cl_program(kProgram); EXPECT_EQ(kProgram.value(), cl_program.GetProgram().value()); EXPECT_EQ(kProgram.value(), cl_program.GetCommandLineString()); const FilePath kProgramPath(L"Program Path"); // Check that quotes are not returned from GetProgram(). CommandLine cl_program_path(kProgramPath); EXPECT_EQ(kProgramPath.value(), cl_program_path.GetProgram().value()); // Check that quotes are added to command line string paths containing spaces. CommandLine::StringType cmd_string(cl_program_path.GetCommandLineString()); EXPECT_EQ(L"\"Program Path\"", cmd_string); } #endif // Calling Init multiple times should not modify the previous CommandLine. TEST(CommandLineTest, Init) { // Call Init without checking output once so we know it's been called // whether or not the test runner does so. CommandLine::Init(0, nullptr); CommandLine* initial = CommandLine::ForCurrentProcess(); EXPECT_FALSE(CommandLine::Init(0, nullptr)); CommandLine* current = CommandLine::ForCurrentProcess(); EXPECT_EQ(initial, current); } // Test that copies of CommandLine have a valid StringPiece map. TEST(CommandLineTest, Copy) { std::unique_ptr initial( new CommandLine(CommandLine::NO_PROGRAM)); initial->AppendSwitch("a"); initial->AppendSwitch("bbbbbbbbbbbbbbb"); initial->AppendSwitch("c"); CommandLine copy_constructed(*initial); CommandLine assigned = *initial; CommandLine::SwitchMap switch_map = initial->GetSwitches(); initial.reset(); for (const auto& pair : switch_map) EXPECT_TRUE(copy_constructed.HasSwitch(pair.first)); for (const auto& pair : switch_map) EXPECT_TRUE(assigned.HasSwitch(pair.first)); } TEST(CommandLineTest, PrependSimpleWrapper) { CommandLine cl(FilePath(FILE_PATH_LITERAL("Program"))); cl.AppendSwitch("a"); cl.AppendSwitch("b"); cl.PrependWrapper(FILE_PATH_LITERAL("wrapper --foo --bar")); EXPECT_EQ(6u, cl.argv().size()); EXPECT_EQ(FILE_PATH_LITERAL("wrapper"), cl.argv()[0]); EXPECT_EQ(FILE_PATH_LITERAL("--foo"), cl.argv()[1]); EXPECT_EQ(FILE_PATH_LITERAL("--bar"), cl.argv()[2]); EXPECT_EQ(FILE_PATH_LITERAL("Program"), cl.argv()[3]); EXPECT_EQ(FILE_PATH_LITERAL("--a"), cl.argv()[4]); EXPECT_EQ(FILE_PATH_LITERAL("--b"), cl.argv()[5]); } TEST(CommandLineTest, PrependComplexWrapper) { CommandLine cl(FilePath(FILE_PATH_LITERAL("Program"))); cl.AppendSwitch("a"); cl.AppendSwitch("b"); cl.PrependWrapper( FILE_PATH_LITERAL("wrapper --foo='hello world' --bar=\"let's go\"")); EXPECT_EQ(6u, cl.argv().size()); EXPECT_EQ(FILE_PATH_LITERAL("wrapper"), cl.argv()[0]); EXPECT_EQ(FILE_PATH_LITERAL("--foo='hello world'"), cl.argv()[1]); EXPECT_EQ(FILE_PATH_LITERAL("--bar=\"let's go\""), cl.argv()[2]); EXPECT_EQ(FILE_PATH_LITERAL("Program"), cl.argv()[3]); EXPECT_EQ(FILE_PATH_LITERAL("--a"), cl.argv()[4]); EXPECT_EQ(FILE_PATH_LITERAL("--b"), cl.argv()[5]); } TEST(CommandLineTest, RemoveSwitch) { const std::string switch1 = "switch1"; const std::string switch2 = "switch2"; const std::string value2 = "value"; CommandLine cl(FilePath(FILE_PATH_LITERAL("Program"))); cl.AppendSwitch(switch1); cl.AppendSwitchASCII(switch2, value2); EXPECT_TRUE(cl.HasSwitch(switch1)); EXPECT_TRUE(cl.HasSwitch(switch2)); EXPECT_EQ(value2, cl.GetSwitchValueASCII(switch2)); EXPECT_THAT(cl.argv(), testing::ElementsAre(FILE_PATH_LITERAL("Program"), FILE_PATH_LITERAL("--switch1"), FILE_PATH_LITERAL("--switch2=value"))); cl.RemoveSwitch(switch1); EXPECT_FALSE(cl.HasSwitch(switch1)); EXPECT_TRUE(cl.HasSwitch(switch2)); EXPECT_EQ(value2, cl.GetSwitchValueASCII(switch2)); EXPECT_THAT(cl.argv(), testing::ElementsAre(FILE_PATH_LITERAL("Program"), FILE_PATH_LITERAL("--switch2=value"))); } TEST(CommandLineTest, RemoveSwitchWithValue) { const std::string switch1 = "switch1"; const std::string switch2 = "switch2"; const std::string value2 = "value"; CommandLine cl(FilePath(FILE_PATH_LITERAL("Program"))); cl.AppendSwitch(switch1); cl.AppendSwitchASCII(switch2, value2); EXPECT_TRUE(cl.HasSwitch(switch1)); EXPECT_TRUE(cl.HasSwitch(switch2)); EXPECT_EQ(value2, cl.GetSwitchValueASCII(switch2)); EXPECT_THAT(cl.argv(), testing::ElementsAre(FILE_PATH_LITERAL("Program"), FILE_PATH_LITERAL("--switch1"), FILE_PATH_LITERAL("--switch2=value"))); cl.RemoveSwitch(switch2); EXPECT_TRUE(cl.HasSwitch(switch1)); EXPECT_FALSE(cl.HasSwitch(switch2)); EXPECT_THAT(cl.argv(), testing::ElementsAre(FILE_PATH_LITERAL("Program"), FILE_PATH_LITERAL("--switch1"))); } TEST(CommandLineTest, RemoveSwitchDropsMultipleSameSwitches) { const std::string switch1 = "switch1"; const std::string value2 = "value2"; CommandLine cl(FilePath(FILE_PATH_LITERAL("Program"))); cl.AppendSwitch(switch1); cl.AppendSwitchASCII(switch1, value2); EXPECT_TRUE(cl.HasSwitch(switch1)); EXPECT_EQ(value2, cl.GetSwitchValueASCII(switch1)); EXPECT_THAT(cl.argv(), testing::ElementsAre(FILE_PATH_LITERAL("Program"), FILE_PATH_LITERAL("--switch1"), FILE_PATH_LITERAL("--switch1=value2"))); cl.RemoveSwitch(switch1); EXPECT_FALSE(cl.HasSwitch(switch1)); EXPECT_THAT(cl.argv(), testing::ElementsAre(FILE_PATH_LITERAL("Program"))); } TEST(CommandLineTest, AppendAndRemoveSwitchWithDefaultPrefix) { CommandLine cl(FilePath(FILE_PATH_LITERAL("Program"))); cl.AppendSwitch("foo"); EXPECT_THAT(cl.argv(), testing::ElementsAre(FILE_PATH_LITERAL("Program"), FILE_PATH_LITERAL("--foo"))); EXPECT_EQ(0u, cl.GetArgs().size()); cl.RemoveSwitch("foo"); EXPECT_THAT(cl.argv(), testing::ElementsAre(FILE_PATH_LITERAL("Program"))); EXPECT_EQ(0u, cl.GetArgs().size()); } TEST(CommandLineTest, AppendAndRemoveSwitchWithAlternativePrefix) { CommandLine cl(FilePath(FILE_PATH_LITERAL("Program"))); cl.AppendSwitch("-foo"); EXPECT_THAT(cl.argv(), testing::ElementsAre(FILE_PATH_LITERAL("Program"), FILE_PATH_LITERAL("-foo"))); EXPECT_EQ(0u, cl.GetArgs().size()); cl.RemoveSwitch("foo"); EXPECT_THAT(cl.argv(), testing::ElementsAre(FILE_PATH_LITERAL("Program"))); EXPECT_EQ(0u, cl.GetArgs().size()); } TEST(CommandLineTest, AppendAndRemoveSwitchPreservesOtherSwitchesAndArgs) { CommandLine cl(FilePath(FILE_PATH_LITERAL("Program"))); cl.AppendSwitch("foo"); cl.AppendSwitch("bar"); cl.AppendArg("arg"); EXPECT_THAT(cl.argv(), testing::ElementsAre(FILE_PATH_LITERAL("Program"), FILE_PATH_LITERAL("--foo"), FILE_PATH_LITERAL("--bar"), FILE_PATH_LITERAL("arg"))); EXPECT_THAT(cl.GetArgs(), testing::ElementsAre(FILE_PATH_LITERAL("arg"))); cl.RemoveSwitch("foo"); EXPECT_THAT(cl.argv(), testing::ElementsAre(FILE_PATH_LITERAL("Program"), FILE_PATH_LITERAL("--bar"), FILE_PATH_LITERAL("arg"))); EXPECT_THAT(cl.GetArgs(), testing::ElementsAre(FILE_PATH_LITERAL("arg"))); } TEST(CommandLineTest, MultipleSameSwitch) { const CommandLine::CharType* argv[] = { FILE_PATH_LITERAL("program"), FILE_PATH_LITERAL("--foo=one"), // --foo first time FILE_PATH_LITERAL("-baz"), FILE_PATH_LITERAL("--foo=two") // --foo second time }; CommandLine cl(std::size(argv), argv); EXPECT_TRUE(cl.HasSwitch("foo")); EXPECT_TRUE(cl.HasSwitch("baz")); EXPECT_EQ("two", cl.GetSwitchValueASCII("foo")); } // Helper class for the next test case class MergeDuplicateFoosSemicolon : public DuplicateSwitchHandler { public: ~MergeDuplicateFoosSemicolon() override; void ResolveDuplicate(base::StringPiece key, CommandLine::StringPieceType new_value, CommandLine::StringType& out_value) override; }; MergeDuplicateFoosSemicolon::~MergeDuplicateFoosSemicolon() = default; void MergeDuplicateFoosSemicolon::ResolveDuplicate( base::StringPiece key, CommandLine::StringPieceType new_value, CommandLine::StringType& out_value) { if (key != "mergeable-foo") { out_value = CommandLine::StringType(new_value); return; } if (!out_value.empty()) { #if BUILDFLAG(IS_WIN) StrAppend(&out_value, {L";"}); #else StrAppend(&out_value, {";"}); #endif } StrAppend(&out_value, {new_value}); } // This flag is an exception to the rule that the second duplicate flag wins // Not thread safe TEST(CommandLineTest, MultipleFilterFileSwitch) { const CommandLine::CharType* const argv[] = { FILE_PATH_LITERAL("program"), FILE_PATH_LITERAL("--mergeable-foo=one"), // --first time FILE_PATH_LITERAL("-baz"), FILE_PATH_LITERAL("--mergeable-foo=two") // --second time }; CommandLine::SetDuplicateSwitchHandler( std::make_unique()); CommandLine cl(std::size(argv), argv); EXPECT_TRUE(cl.HasSwitch("mergeable-foo")); EXPECT_TRUE(cl.HasSwitch("baz")); EXPECT_EQ("one;two", cl.GetSwitchValueASCII("mergeable-foo")); CommandLine::SetDuplicateSwitchHandler(nullptr); } #if BUILDFLAG(IS_WIN) TEST(CommandLineTest, ParseAsSingleArgument) { CommandLine cl = CommandLine::FromString( FILE_PATH_LITERAL("program --switch_before arg_before " "--single-argument arg with spaces \"and quotes\" \"")); EXPECT_FALSE(cl.GetCommandLineString().empty()); EXPECT_EQ(FilePath(FILE_PATH_LITERAL("program")), cl.GetProgram()); EXPECT_TRUE(cl.HasSwitch("switch_before")); EXPECT_EQ(cl.GetArgs(), CommandLine::StringVector({FILE_PATH_LITERAL( "arg with spaces \"and quotes\" \"")})); CommandLine cl_without_arg = CommandLine::FromString(FILE_PATH_LITERAL("program --single-argument ")); EXPECT_FALSE(cl_without_arg.GetCommandLineString().empty()); EXPECT_EQ(FilePath(FILE_PATH_LITERAL("program")), cl_without_arg.GetProgram()); EXPECT_TRUE(cl_without_arg.GetArgs().empty()); } #endif // BUILDFLAG(IS_WIN) } // namespace base