1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/command_line.h"
11 #include "base/debug/debugging_buildflags.h"
12 #include "base/files/file_path.h"
13 #include "base/strings/strcat.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "build/build_config.h"
17 #include "testing/gmock/include/gmock/gmock.h"
18 #include "testing/gtest/include/gtest/gtest.h"
25 #include "base/win/scoped_localalloc.h"
26 #endif // BUILDFLAG(IS_WIN)
28 #if BUILDFLAG(ENABLE_COMMANDLINE_SEQUENCE_CHECKS)
29 #include "base/run_loop.h"
30 #include "base/task/thread_pool.h"
31 #include "base/test/bind.h"
32 #include "base/test/task_environment.h"
33 #endif // BUILDFLAG(ENABLE_COMMANDLINE_SEQUENCE_CHECKS)
38 // To test Windows quoting behavior, we use a string that has some backslashes
40 // Consider the command-line argument: q\"bs1\bs2\\bs3q\\\"
41 // Here it is with C-style escapes.
42 static const CommandLine::StringType kTrickyQuoted =
43 FILE_PATH_LITERAL("q\\\"bs1\\bs2\\\\bs3q\\\\\\\"");
46 // It should be parsed by Windows as: q"bs1\bs2\\bs3q\"
47 // Here that is with C-style escapes.
48 static const CommandLine::StringType kTricky =
49 FILE_PATH_LITERAL("q\"bs1\\bs2\\\\bs3q\\\"");
51 TEST(CommandLineTest, CommandLineConstructor) {
52 const CommandLine::CharType* argv[] = {
53 FILE_PATH_LITERAL("program"),
54 FILE_PATH_LITERAL("--foo="),
55 FILE_PATH_LITERAL("-bAr"),
56 FILE_PATH_LITERAL("-spaetzel=pierogi"),
57 FILE_PATH_LITERAL("-baz"),
58 FILE_PATH_LITERAL("flim"),
59 FILE_PATH_LITERAL("--other-switches=--dog=canine --cat=feline"),
60 FILE_PATH_LITERAL("-spaetzle=Crepe"),
61 FILE_PATH_LITERAL("-=loosevalue"),
62 FILE_PATH_LITERAL("-"),
63 FILE_PATH_LITERAL("FLAN"),
64 FILE_PATH_LITERAL("a"),
65 FILE_PATH_LITERAL("--input-translation=45--output-rotation"),
66 FILE_PATH_LITERAL("--"),
67 FILE_PATH_LITERAL("--"),
68 FILE_PATH_LITERAL("--not-a-switch"),
69 FILE_PATH_LITERAL("\"in the time of submarines...\""),
70 FILE_PATH_LITERAL("unquoted arg-with-space")};
71 CommandLine cl(std::size(argv), argv);
73 EXPECT_FALSE(cl.GetCommandLineString().empty());
74 EXPECT_FALSE(cl.HasSwitch("cruller"));
75 EXPECT_FALSE(cl.HasSwitch("flim"));
76 EXPECT_FALSE(cl.HasSwitch("program"));
77 EXPECT_FALSE(cl.HasSwitch("dog"));
78 EXPECT_FALSE(cl.HasSwitch("cat"));
79 EXPECT_FALSE(cl.HasSwitch("output-rotation"));
80 EXPECT_FALSE(cl.HasSwitch("not-a-switch"));
81 EXPECT_FALSE(cl.HasSwitch("--"));
83 EXPECT_EQ(FilePath(FILE_PATH_LITERAL("program")).value(),
84 cl.GetProgram().value());
86 EXPECT_TRUE(cl.HasSwitch("foo"));
88 EXPECT_TRUE(cl.HasSwitch("bar"));
90 EXPECT_FALSE(cl.HasSwitch("bar"));
92 EXPECT_TRUE(cl.HasSwitch("baz"));
93 EXPECT_TRUE(cl.HasSwitch("spaetzle"));
94 EXPECT_TRUE(cl.HasSwitch("other-switches"));
95 EXPECT_TRUE(cl.HasSwitch("input-translation"));
97 EXPECT_EQ("Crepe", cl.GetSwitchValueASCII("spaetzle"));
98 EXPECT_EQ("", cl.GetSwitchValueASCII("foo"));
99 EXPECT_EQ("", cl.GetSwitchValueASCII("bar"));
100 EXPECT_EQ("", cl.GetSwitchValueASCII("cruller"));
101 EXPECT_EQ("--dog=canine --cat=feline", cl.GetSwitchValueASCII(
103 EXPECT_EQ("45--output-rotation", cl.GetSwitchValueASCII("input-translation"));
105 const CommandLine::StringVector& args = cl.GetArgs();
106 ASSERT_EQ(8U, args.size());
108 auto iter = args.begin();
109 EXPECT_EQ(FILE_PATH_LITERAL("flim"), *iter);
111 EXPECT_EQ(FILE_PATH_LITERAL("-"), *iter);
113 EXPECT_EQ(FILE_PATH_LITERAL("FLAN"), *iter);
115 EXPECT_EQ(FILE_PATH_LITERAL("a"), *iter);
117 EXPECT_EQ(FILE_PATH_LITERAL("--"), *iter);
119 EXPECT_EQ(FILE_PATH_LITERAL("--not-a-switch"), *iter);
121 EXPECT_EQ(FILE_PATH_LITERAL("\"in the time of submarines...\""), *iter);
123 EXPECT_EQ(FILE_PATH_LITERAL("unquoted arg-with-space"), *iter);
125 EXPECT_TRUE(iter == args.end());
128 TEST(CommandLineTest, CommandLineFromString) {
129 #if BUILDFLAG(IS_WIN)
130 CommandLine cl = CommandLine::FromString(
131 L"program --foo= -bAr /Spaetzel=pierogi /Baz flim "
132 L"--other-switches=\"--dog=canine --cat=feline\" "
133 L"-spaetzle=Crepe -=loosevalue FLAN "
134 L"--input-translation=\"45\"--output-rotation "
137 L" -- -- --not-a-switch \"in the time of submarines...\"");
139 EXPECT_FALSE(cl.GetCommandLineString().empty());
140 EXPECT_FALSE(cl.HasSwitch("cruller"));
141 EXPECT_FALSE(cl.HasSwitch("flim"));
142 EXPECT_FALSE(cl.HasSwitch("program"));
143 EXPECT_FALSE(cl.HasSwitch("dog"));
144 EXPECT_FALSE(cl.HasSwitch("cat"));
145 EXPECT_FALSE(cl.HasSwitch("output-rotation"));
146 EXPECT_FALSE(cl.HasSwitch("not-a-switch"));
147 EXPECT_FALSE(cl.HasSwitch("--"));
149 EXPECT_EQ(FilePath(FILE_PATH_LITERAL("program")).value(),
150 cl.GetProgram().value());
152 EXPECT_TRUE(cl.HasSwitch("foo"));
153 EXPECT_TRUE(cl.HasSwitch("bar"));
154 EXPECT_TRUE(cl.HasSwitch("baz"));
155 EXPECT_TRUE(cl.HasSwitch("spaetzle"));
156 EXPECT_TRUE(cl.HasSwitch("other-switches"));
157 EXPECT_TRUE(cl.HasSwitch("input-translation"));
158 EXPECT_TRUE(cl.HasSwitch("quotes"));
160 EXPECT_EQ("Crepe", cl.GetSwitchValueASCII("spaetzle"));
161 EXPECT_EQ("", cl.GetSwitchValueASCII("foo"));
162 EXPECT_EQ("", cl.GetSwitchValueASCII("bar"));
163 EXPECT_EQ("", cl.GetSwitchValueASCII("cruller"));
164 EXPECT_EQ("--dog=canine --cat=feline", cl.GetSwitchValueASCII(
166 EXPECT_EQ("45--output-rotation", cl.GetSwitchValueASCII("input-translation"));
167 EXPECT_EQ(kTricky, cl.GetSwitchValueNative("quotes"));
169 const CommandLine::StringVector& args = cl.GetArgs();
170 ASSERT_EQ(5U, args.size());
172 std::vector<CommandLine::StringType>::const_iterator iter = args.begin();
173 EXPECT_EQ(FILE_PATH_LITERAL("flim"), *iter);
175 EXPECT_EQ(FILE_PATH_LITERAL("FLAN"), *iter);
177 EXPECT_EQ(FILE_PATH_LITERAL("--"), *iter);
179 EXPECT_EQ(FILE_PATH_LITERAL("--not-a-switch"), *iter);
181 EXPECT_EQ(FILE_PATH_LITERAL("in the time of submarines..."), *iter);
183 EXPECT_TRUE(iter == args.end());
185 // Check that a generated string produces an equivalent command line.
186 CommandLine cl_duplicate = CommandLine::FromString(cl.GetCommandLineString());
187 EXPECT_EQ(cl.GetCommandLineString(), cl_duplicate.GetCommandLineString());
191 // Tests behavior with an empty input string.
192 TEST(CommandLineTest, EmptyString) {
193 #if BUILDFLAG(IS_WIN)
194 CommandLine cl_from_string = CommandLine::FromString(std::wstring());
195 EXPECT_TRUE(cl_from_string.GetCommandLineString().empty());
196 EXPECT_TRUE(cl_from_string.GetProgram().empty());
197 EXPECT_EQ(1U, cl_from_string.argv().size());
198 EXPECT_TRUE(cl_from_string.GetArgs().empty());
200 CommandLine cl_from_argv(0, nullptr);
201 EXPECT_TRUE(cl_from_argv.GetCommandLineString().empty());
202 EXPECT_TRUE(cl_from_argv.GetProgram().empty());
203 EXPECT_EQ(1U, cl_from_argv.argv().size());
204 EXPECT_TRUE(cl_from_argv.GetArgs().empty());
207 TEST(CommandLineTest, GetArgumentsString) {
208 static const FilePath::CharType kPath1[] =
209 FILE_PATH_LITERAL("C:\\Some File\\With Spaces.ggg");
210 static const FilePath::CharType kPath2[] =
211 FILE_PATH_LITERAL("C:\\no\\spaces.ggg");
213 static const char kFirstArgName[] = "first-arg";
214 static const char kSecondArgName[] = "arg2";
215 static const char kThirdArgName[] = "arg with space";
216 static const char kFourthArgName[] = "nospace";
218 CommandLine cl(CommandLine::NO_PROGRAM);
219 cl.AppendSwitchPath(kFirstArgName, FilePath(kPath1));
220 cl.AppendSwitchPath(kSecondArgName, FilePath(kPath2));
221 cl.AppendArg(kThirdArgName);
222 cl.AppendArg(kFourthArgName);
224 #if BUILDFLAG(IS_WIN)
225 CommandLine::StringType expected_first_arg(UTF8ToWide(kFirstArgName));
226 CommandLine::StringType expected_second_arg(UTF8ToWide(kSecondArgName));
227 CommandLine::StringType expected_third_arg(UTF8ToWide(kThirdArgName));
228 CommandLine::StringType expected_fourth_arg(UTF8ToWide(kFourthArgName));
229 #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
230 CommandLine::StringType expected_first_arg(kFirstArgName);
231 CommandLine::StringType expected_second_arg(kSecondArgName);
232 CommandLine::StringType expected_third_arg(kThirdArgName);
233 CommandLine::StringType expected_fourth_arg(kFourthArgName);
236 #if BUILDFLAG(IS_WIN)
237 #define QUOTE_ON_WIN FILE_PATH_LITERAL("\"")
239 #define QUOTE_ON_WIN FILE_PATH_LITERAL("")
240 #endif // BUILDFLAG(IS_WIN)
242 CommandLine::StringType expected_str;
243 expected_str.append(FILE_PATH_LITERAL("--"))
244 .append(expected_first_arg)
245 .append(FILE_PATH_LITERAL("="))
246 .append(QUOTE_ON_WIN)
248 .append(QUOTE_ON_WIN)
249 .append(FILE_PATH_LITERAL(" "))
250 .append(FILE_PATH_LITERAL("--"))
251 .append(expected_second_arg)
252 .append(FILE_PATH_LITERAL("="))
253 .append(QUOTE_ON_WIN)
255 .append(QUOTE_ON_WIN)
256 .append(FILE_PATH_LITERAL(" "))
257 .append(QUOTE_ON_WIN)
258 .append(expected_third_arg)
259 .append(QUOTE_ON_WIN)
260 .append(FILE_PATH_LITERAL(" "))
261 .append(expected_fourth_arg);
262 EXPECT_EQ(expected_str, cl.GetArgumentsString());
265 // Test methods for appending switches to a command line.
266 TEST(CommandLineTest, AppendSwitches) {
267 std::string switch1 = "switch1";
268 std::string switch2 = "switch2";
269 std::string value2 = "value";
270 std::string switch3 = "switch3";
271 std::string value3 = "a value with spaces";
272 std::string switch4 = "switch4";
273 std::string value4 = "\"a value with quotes\"";
274 std::string switch5 = "quotes";
275 CommandLine::StringType value5 = kTricky;
277 CommandLine cl(FilePath(FILE_PATH_LITERAL("Program")));
279 cl.AppendSwitch(switch1);
280 cl.AppendSwitchASCII(switch2, value2);
281 cl.AppendSwitchASCII(switch3, value3);
282 cl.AppendSwitchASCII(switch4, value4);
283 cl.AppendSwitchASCII(switch5, value4);
284 cl.AppendSwitchNative(switch5, value5);
286 EXPECT_TRUE(cl.HasSwitch(switch1));
287 EXPECT_TRUE(cl.HasSwitch(switch2));
288 EXPECT_EQ(value2, cl.GetSwitchValueASCII(switch2));
289 EXPECT_TRUE(cl.HasSwitch(switch3));
290 EXPECT_EQ(value3, cl.GetSwitchValueASCII(switch3));
291 EXPECT_TRUE(cl.HasSwitch(switch4));
292 EXPECT_EQ(value4, cl.GetSwitchValueASCII(switch4));
293 EXPECT_TRUE(cl.HasSwitch(switch5));
294 EXPECT_EQ(value5, cl.GetSwitchValueNative(switch5));
296 #if BUILDFLAG(IS_WIN)
301 L"--switch3=\"a value with spaces\" "
302 L"--switch4=\"\\\"a value with quotes\\\"\" "
303 // Even though the switches are unique, appending can add repeat
305 L"--quotes=\"\\\"a value with quotes\\\"\" "
307 kTrickyQuoted + L"\"",
308 cl.GetCommandLineString());
312 TEST(CommandLineTest, AppendSwitchesDashDash) {
313 const CommandLine::CharType* const raw_argv[] = {FILE_PATH_LITERAL("prog"),
314 FILE_PATH_LITERAL("--"),
315 FILE_PATH_LITERAL("--arg1")};
316 CommandLine cl(std::size(raw_argv), raw_argv);
318 cl.AppendSwitch("switch1");
319 cl.AppendSwitchASCII("switch2", "foo");
321 cl.AppendArg("--arg2");
323 EXPECT_EQ(FILE_PATH_LITERAL("prog --switch1 --switch2=foo -- --arg1 --arg2"),
324 cl.GetCommandLineString());
325 CommandLine::StringVector cl_argv = cl.argv();
326 EXPECT_EQ(FILE_PATH_LITERAL("prog"), cl_argv[0]);
327 EXPECT_EQ(FILE_PATH_LITERAL("--switch1"), cl_argv[1]);
328 EXPECT_EQ(FILE_PATH_LITERAL("--switch2=foo"), cl_argv[2]);
329 EXPECT_EQ(FILE_PATH_LITERAL("--"), cl_argv[3]);
330 EXPECT_EQ(FILE_PATH_LITERAL("--arg1"), cl_argv[4]);
331 EXPECT_EQ(FILE_PATH_LITERAL("--arg2"), cl_argv[5]);
334 #if BUILDFLAG(IS_WIN)
335 struct CommandLineQuoteTestCase {
336 const wchar_t* const input_arg;
337 const wchar_t* const expected_output_arg;
340 class CommandLineQuoteTest
341 : public ::testing::TestWithParam<CommandLineQuoteTestCase> {};
343 INSTANTIATE_TEST_SUITE_P(
344 CommandLineQuoteTestCases,
345 CommandLineQuoteTest,
346 ::testing::ValuesIn(std::vector<CommandLineQuoteTestCase>{
348 {L"abc = xyz", LR"("abc = xyz")"},
349 {LR"(C:\AppData\Local\setup.exe)", LR"("C:\AppData\Local\setup.exe")"},
350 {LR"(C:\Program Files\setup.exe)", LR"("C:\Program Files\setup.exe")"},
351 {LR"("C:\Program Files\setup.exe")",
352 LR"("\"C:\Program Files\setup.exe\"")"},
355 TEST_P(CommandLineQuoteTest, TestCases) {
356 EXPECT_EQ(CommandLine::QuoteForCommandLineToArgvW(GetParam().input_arg),
357 GetParam().expected_output_arg);
360 struct CommandLineQuoteAfterTestCase {
361 const std::vector<std::wstring> input_args;
362 const wchar_t* const expected_output;
365 class CommandLineQuoteAfterTest
366 : public ::testing::TestWithParam<CommandLineQuoteAfterTestCase> {};
368 INSTANTIATE_TEST_SUITE_P(
369 CommandLineQuoteAfterTestCases,
370 CommandLineQuoteAfterTest,
371 ::testing::ValuesIn(std::vector<CommandLineQuoteAfterTestCase>{
372 {{L"abc=1"}, L"abc=1"},
373 {{L"abc=1", L"xyz=2"}, L"abc=1 xyz=2"},
374 {{L"abc=1", L"xyz=2", L"q"}, L"abc=1 xyz=2 q"},
375 {{L" abc=1 ", L" xyz=2", L"q "}, L"abc=1 xyz=2 q"},
376 {{LR"("abc = 1")"}, LR"("abc = 1")"},
377 {{LR"(abc" = "1)", L"xyz=2"}, LR"("abc = 1" xyz=2)"},
378 {{LR"(abc" = "1)"}, LR"("abc = 1")"},
379 {{LR"(\\)", LR"(\\\")"}, LR"("\\\\" "\\\"")"},
382 TEST_P(CommandLineQuoteAfterTest, TestCases) {
383 std::wstring input_command_line =
384 base::StrCat({LR"(c:\test\process.exe )",
385 base::JoinString(GetParam().input_args, L" ")});
387 base::win::ScopedLocalAllocTyped<wchar_t*> argv(
388 ::CommandLineToArgvW(&input_command_line[0], &num_args));
389 ASSERT_EQ(num_args - 1U, GetParam().input_args.size());
391 std::wstring recreated_command_line;
392 for (int i = 1; i < num_args; ++i) {
393 recreated_command_line.append(
394 CommandLine::QuoteForCommandLineToArgvW(argv.get()[i]));
396 if (i + 1 < num_args) {
397 recreated_command_line.push_back(L' ');
401 EXPECT_EQ(recreated_command_line, GetParam().expected_output);
404 TEST(CommandLineTest, GetCommandLineStringForShell) {
405 CommandLine cl = CommandLine::FromString(
406 FILE_PATH_LITERAL("program --switch /switch2 --"));
408 cl.GetCommandLineStringForShell(),
409 FILE_PATH_LITERAL("program --switch /switch2 -- --single-argument %1"));
412 TEST(CommandLineTest, GetCommandLineStringWithUnsafeInsertSequences) {
413 CommandLine cl(FilePath(FILE_PATH_LITERAL("program")));
414 cl.AppendSwitchASCII("switch", "%1");
415 cl.AppendSwitch("%2");
417 EXPECT_EQ(FILE_PATH_LITERAL("program --switch=%1 --%2 %3"),
418 cl.GetCommandLineStringWithUnsafeInsertSequences());
421 TEST(CommandLineTest, HasSingleArgument) {
422 CommandLine cl(FilePath(FILE_PATH_LITERAL("Program")));
423 cl.AppendSwitchASCII("switch2", "foo");
424 EXPECT_FALSE(cl.HasSingleArgumentSwitch());
425 CommandLine cl_for_shell(
426 CommandLine::FromString(cl.GetCommandLineStringForShell()));
427 EXPECT_TRUE(cl_for_shell.HasSingleArgumentSwitch());
430 // Test that creating a new command line from the string version of a single
431 // argument command line maintains the single argument switch, and the
433 TEST(CommandLineTest, MaintainSingleArgument) {
434 // Putting a space in the file name will force escaping of the argument.
435 static const CommandLine::StringType kCommandLine =
436 FILE_PATH_LITERAL("program --switch --single-argument foo bar.html");
437 CommandLine cl = CommandLine::FromString(kCommandLine);
438 CommandLine cl_for_shell = CommandLine::FromString(cl.GetCommandLineString());
439 EXPECT_TRUE(cl_for_shell.HasSingleArgumentSwitch());
440 // Verify that we command line survives the round trip with an escaped arg.
441 EXPECT_EQ(kCommandLine, cl_for_shell.GetCommandLineString());
444 #endif // BUILDFLAG(IS_WIN)
446 // Tests that when AppendArguments is called that the program is set correctly
447 // on the target CommandLine object and the switches from the source
448 // CommandLine are added to the target.
449 TEST(CommandLineTest, AppendArguments) {
450 CommandLine cl1(FilePath(FILE_PATH_LITERAL("Program")));
451 cl1.AppendSwitch("switch1");
452 cl1.AppendSwitchASCII("switch2", "foo");
454 CommandLine cl2(CommandLine::NO_PROGRAM);
455 cl2.AppendArguments(cl1, true);
456 EXPECT_EQ(cl1.GetProgram().value(), cl2.GetProgram().value());
457 EXPECT_EQ(cl1.GetCommandLineString(), cl2.GetCommandLineString());
459 CommandLine c1(FilePath(FILE_PATH_LITERAL("Program1")));
460 c1.AppendSwitch("switch1");
461 CommandLine c2(FilePath(FILE_PATH_LITERAL("Program2")));
462 c2.AppendSwitch("switch2");
464 c1.AppendArguments(c2, true);
465 EXPECT_EQ(c1.GetProgram().value(), c2.GetProgram().value());
466 EXPECT_TRUE(c1.HasSwitch("switch1"));
467 EXPECT_TRUE(c1.HasSwitch("switch2"));
470 #if BUILDFLAG(IS_WIN)
471 // Make sure that the command line string program paths are quoted as necessary.
472 // This only makes sense on Windows and the test is basically here to guard
473 // against regressions.
474 TEST(CommandLineTest, ProgramQuotes) {
475 // Check that quotes are not added for paths without spaces.
476 const FilePath kProgram(L"Program");
477 CommandLine cl_program(kProgram);
478 EXPECT_EQ(kProgram.value(), cl_program.GetProgram().value());
479 EXPECT_EQ(kProgram.value(), cl_program.GetCommandLineString());
481 const FilePath kProgramPath(L"Program Path");
483 // Check that quotes are not returned from GetProgram().
484 CommandLine cl_program_path(kProgramPath);
485 EXPECT_EQ(kProgramPath.value(), cl_program_path.GetProgram().value());
487 // Check that quotes are added to command line string paths containing spaces.
488 CommandLine::StringType cmd_string(cl_program_path.GetCommandLineString());
489 EXPECT_EQ(L"\"Program Path\"", cmd_string);
493 // Calling Init multiple times should not modify the previous CommandLine.
494 TEST(CommandLineTest, Init) {
495 // Call Init without checking output once so we know it's been called
496 // whether or not the test runner does so.
497 CommandLine::Init(0, nullptr);
498 CommandLine* initial = CommandLine::ForCurrentProcess();
499 EXPECT_FALSE(CommandLine::Init(0, nullptr));
500 CommandLine* current = CommandLine::ForCurrentProcess();
501 EXPECT_EQ(initial, current);
504 // Test that copies of CommandLine have a valid StringPiece map.
505 TEST(CommandLineTest, Copy) {
506 auto initial = std::make_unique<CommandLine>(CommandLine::NO_PROGRAM);
507 initial->AppendSwitch("a");
508 initial->AppendSwitch("bbbbbbbbbbbbbbb");
509 initial->AppendSwitch("c");
510 CommandLine copy_constructed(*initial);
511 CommandLine assigned = *initial;
512 CommandLine::SwitchMap switch_map = initial->GetSwitches();
514 for (const auto& pair : switch_map)
515 EXPECT_TRUE(copy_constructed.HasSwitch(pair.first));
516 for (const auto& pair : switch_map)
517 EXPECT_TRUE(assigned.HasSwitch(pair.first));
520 TEST(CommandLineTest, CopySwitches) {
521 CommandLine source(CommandLine::NO_PROGRAM);
522 source.AppendSwitch("a");
523 source.AppendSwitch("bbbb");
524 source.AppendSwitch("c");
525 EXPECT_THAT(source.argv(), testing::ElementsAre(FILE_PATH_LITERAL(""),
526 FILE_PATH_LITERAL("--a"),
527 FILE_PATH_LITERAL("--bbbb"),
528 FILE_PATH_LITERAL("--c")));
530 CommandLine cl(CommandLine::NO_PROGRAM);
531 EXPECT_THAT(cl.argv(), testing::ElementsAre(FILE_PATH_LITERAL("")));
533 cl.CopySwitchesFrom(source, {});
534 EXPECT_THAT(cl.argv(), testing::ElementsAre(FILE_PATH_LITERAL("")));
536 static const char* const kSwitchesToCopy[] = {"a", "nosuch", "c"};
537 cl.CopySwitchesFrom(source, kSwitchesToCopy);
538 EXPECT_THAT(cl.argv(), testing::ElementsAre(FILE_PATH_LITERAL(""),
539 FILE_PATH_LITERAL("--a"),
540 FILE_PATH_LITERAL("--c")));
543 TEST(CommandLineTest, PrependSimpleWrapper) {
544 CommandLine cl(FilePath(FILE_PATH_LITERAL("Program")));
545 cl.AppendSwitch("a");
546 cl.AppendSwitch("b");
547 cl.PrependWrapper(FILE_PATH_LITERAL("wrapper --foo --bar"));
549 EXPECT_EQ(6u, cl.argv().size());
550 EXPECT_EQ(FILE_PATH_LITERAL("wrapper"), cl.argv()[0]);
551 EXPECT_EQ(FILE_PATH_LITERAL("--foo"), cl.argv()[1]);
552 EXPECT_EQ(FILE_PATH_LITERAL("--bar"), cl.argv()[2]);
553 EXPECT_EQ(FILE_PATH_LITERAL("Program"), cl.argv()[3]);
554 EXPECT_EQ(FILE_PATH_LITERAL("--a"), cl.argv()[4]);
555 EXPECT_EQ(FILE_PATH_LITERAL("--b"), cl.argv()[5]);
558 TEST(CommandLineTest, PrependComplexWrapper) {
559 CommandLine cl(FilePath(FILE_PATH_LITERAL("Program")));
560 cl.AppendSwitch("a");
561 cl.AppendSwitch("b");
563 FILE_PATH_LITERAL("wrapper --foo='hello world' --bar=\"let's go\""));
565 EXPECT_EQ(6u, cl.argv().size());
566 EXPECT_EQ(FILE_PATH_LITERAL("wrapper"), cl.argv()[0]);
567 EXPECT_EQ(FILE_PATH_LITERAL("--foo='hello world'"), cl.argv()[1]);
568 EXPECT_EQ(FILE_PATH_LITERAL("--bar=\"let's go\""), cl.argv()[2]);
569 EXPECT_EQ(FILE_PATH_LITERAL("Program"), cl.argv()[3]);
570 EXPECT_EQ(FILE_PATH_LITERAL("--a"), cl.argv()[4]);
571 EXPECT_EQ(FILE_PATH_LITERAL("--b"), cl.argv()[5]);
574 TEST(CommandLineTest, RemoveSwitch) {
575 const std::string switch1 = "switch1";
576 const std::string switch2 = "switch2";
577 const std::string value2 = "value";
579 CommandLine cl(FilePath(FILE_PATH_LITERAL("Program")));
581 cl.AppendSwitch(switch1);
582 cl.AppendSwitchASCII(switch2, value2);
584 EXPECT_TRUE(cl.HasSwitch(switch1));
585 EXPECT_TRUE(cl.HasSwitch(switch2));
586 EXPECT_EQ(value2, cl.GetSwitchValueASCII(switch2));
587 EXPECT_THAT(cl.argv(),
588 testing::ElementsAre(FILE_PATH_LITERAL("Program"),
589 FILE_PATH_LITERAL("--switch1"),
590 FILE_PATH_LITERAL("--switch2=value")));
592 cl.RemoveSwitch(switch1);
594 EXPECT_FALSE(cl.HasSwitch(switch1));
595 EXPECT_TRUE(cl.HasSwitch(switch2));
596 EXPECT_EQ(value2, cl.GetSwitchValueASCII(switch2));
597 EXPECT_THAT(cl.argv(),
598 testing::ElementsAre(FILE_PATH_LITERAL("Program"),
599 FILE_PATH_LITERAL("--switch2=value")));
602 TEST(CommandLineTest, RemoveSwitchWithValue) {
603 const std::string switch1 = "switch1";
604 const std::string switch2 = "switch2";
605 const std::string value2 = "value";
607 CommandLine cl(FilePath(FILE_PATH_LITERAL("Program")));
609 cl.AppendSwitch(switch1);
610 cl.AppendSwitchASCII(switch2, value2);
612 EXPECT_TRUE(cl.HasSwitch(switch1));
613 EXPECT_TRUE(cl.HasSwitch(switch2));
614 EXPECT_EQ(value2, cl.GetSwitchValueASCII(switch2));
615 EXPECT_THAT(cl.argv(),
616 testing::ElementsAre(FILE_PATH_LITERAL("Program"),
617 FILE_PATH_LITERAL("--switch1"),
618 FILE_PATH_LITERAL("--switch2=value")));
620 cl.RemoveSwitch(switch2);
622 EXPECT_TRUE(cl.HasSwitch(switch1));
623 EXPECT_FALSE(cl.HasSwitch(switch2));
624 EXPECT_THAT(cl.argv(), testing::ElementsAre(FILE_PATH_LITERAL("Program"),
625 FILE_PATH_LITERAL("--switch1")));
628 TEST(CommandLineTest, RemoveSwitchDropsMultipleSameSwitches) {
629 const std::string switch1 = "switch1";
630 const std::string value2 = "value2";
632 CommandLine cl(FilePath(FILE_PATH_LITERAL("Program")));
634 cl.AppendSwitch(switch1);
635 cl.AppendSwitchASCII(switch1, value2);
637 EXPECT_TRUE(cl.HasSwitch(switch1));
638 EXPECT_EQ(value2, cl.GetSwitchValueASCII(switch1));
639 EXPECT_THAT(cl.argv(),
640 testing::ElementsAre(FILE_PATH_LITERAL("Program"),
641 FILE_PATH_LITERAL("--switch1"),
642 FILE_PATH_LITERAL("--switch1=value2")));
644 cl.RemoveSwitch(switch1);
646 EXPECT_FALSE(cl.HasSwitch(switch1));
647 EXPECT_THAT(cl.argv(), testing::ElementsAre(FILE_PATH_LITERAL("Program")));
650 TEST(CommandLineTest, AppendAndRemoveSwitchWithDefaultPrefix) {
651 CommandLine cl(FilePath(FILE_PATH_LITERAL("Program")));
653 cl.AppendSwitch("foo");
654 EXPECT_THAT(cl.argv(), testing::ElementsAre(FILE_PATH_LITERAL("Program"),
655 FILE_PATH_LITERAL("--foo")));
656 EXPECT_EQ(0u, cl.GetArgs().size());
658 cl.RemoveSwitch("foo");
659 EXPECT_THAT(cl.argv(), testing::ElementsAre(FILE_PATH_LITERAL("Program")));
660 EXPECT_EQ(0u, cl.GetArgs().size());
663 TEST(CommandLineTest, AppendAndRemoveSwitchWithAlternativePrefix) {
664 CommandLine cl(FilePath(FILE_PATH_LITERAL("Program")));
666 cl.AppendSwitch("-foo");
667 EXPECT_THAT(cl.argv(), testing::ElementsAre(FILE_PATH_LITERAL("Program"),
668 FILE_PATH_LITERAL("-foo")));
669 EXPECT_EQ(0u, cl.GetArgs().size());
671 cl.RemoveSwitch("foo");
672 EXPECT_THAT(cl.argv(), testing::ElementsAre(FILE_PATH_LITERAL("Program")));
673 EXPECT_EQ(0u, cl.GetArgs().size());
676 TEST(CommandLineTest, AppendAndRemoveSwitchPreservesOtherSwitchesAndArgs) {
677 CommandLine cl(FilePath(FILE_PATH_LITERAL("Program")));
679 cl.AppendSwitch("foo");
680 cl.AppendSwitch("bar");
682 EXPECT_THAT(cl.argv(), testing::ElementsAre(FILE_PATH_LITERAL("Program"),
683 FILE_PATH_LITERAL("--foo"),
684 FILE_PATH_LITERAL("--bar"),
685 FILE_PATH_LITERAL("arg")));
686 EXPECT_THAT(cl.GetArgs(), testing::ElementsAre(FILE_PATH_LITERAL("arg")));
688 cl.RemoveSwitch("foo");
689 EXPECT_THAT(cl.argv(), testing::ElementsAre(FILE_PATH_LITERAL("Program"),
690 FILE_PATH_LITERAL("--bar"),
691 FILE_PATH_LITERAL("arg")));
692 EXPECT_THAT(cl.GetArgs(), testing::ElementsAre(FILE_PATH_LITERAL("arg")));
695 TEST(CommandLineTest, MultipleSameSwitch) {
696 const CommandLine::CharType* argv[] = {
697 FILE_PATH_LITERAL("program"),
698 FILE_PATH_LITERAL("--foo=one"), // --foo first time
699 FILE_PATH_LITERAL("-baz"),
700 FILE_PATH_LITERAL("--foo=two") // --foo second time
702 CommandLine cl(std::size(argv), argv);
704 EXPECT_TRUE(cl.HasSwitch("foo"));
705 EXPECT_TRUE(cl.HasSwitch("baz"));
707 EXPECT_EQ("two", cl.GetSwitchValueASCII("foo"));
710 // Helper class for the next test case
711 class MergeDuplicateFoosSemicolon : public DuplicateSwitchHandler {
713 ~MergeDuplicateFoosSemicolon() override;
715 void ResolveDuplicate(base::StringPiece key,
716 CommandLine::StringPieceType new_value,
717 CommandLine::StringType& out_value) override;
720 MergeDuplicateFoosSemicolon::~MergeDuplicateFoosSemicolon() = default;
722 void MergeDuplicateFoosSemicolon::ResolveDuplicate(
723 base::StringPiece key,
724 CommandLine::StringPieceType new_value,
725 CommandLine::StringType& out_value) {
726 if (key != "mergeable-foo") {
727 out_value = CommandLine::StringType(new_value);
730 if (!out_value.empty()) {
731 #if BUILDFLAG(IS_WIN)
732 StrAppend(&out_value, {L";"});
734 StrAppend(&out_value, {";"});
737 StrAppend(&out_value, {new_value});
740 // This flag is an exception to the rule that the second duplicate flag wins
742 TEST(CommandLineTest, MultipleFilterFileSwitch) {
743 const CommandLine::CharType* const argv[] = {
744 FILE_PATH_LITERAL("program"),
745 FILE_PATH_LITERAL("--mergeable-foo=one"), // --first time
746 FILE_PATH_LITERAL("-baz"),
747 FILE_PATH_LITERAL("--mergeable-foo=two") // --second time
749 CommandLine::SetDuplicateSwitchHandler(
750 std::make_unique<MergeDuplicateFoosSemicolon>());
752 CommandLine cl(std::size(argv), argv);
754 EXPECT_TRUE(cl.HasSwitch("mergeable-foo"));
755 EXPECT_TRUE(cl.HasSwitch("baz"));
757 EXPECT_EQ("one;two", cl.GetSwitchValueASCII("mergeable-foo"));
758 CommandLine::SetDuplicateSwitchHandler(nullptr);
761 #if BUILDFLAG(IS_WIN)
762 TEST(CommandLineTest, ParseAsSingleArgument) {
763 CommandLine cl = CommandLine::FromString(
764 FILE_PATH_LITERAL("program --switch_before arg_before "
765 "--single-argument arg with spaces \"and quotes\" \""));
767 EXPECT_FALSE(cl.GetCommandLineString().empty());
768 EXPECT_EQ(FilePath(FILE_PATH_LITERAL("program")), cl.GetProgram());
769 EXPECT_TRUE(cl.HasSwitch("switch_before"));
770 EXPECT_EQ(cl.GetArgs(), CommandLine::StringVector({FILE_PATH_LITERAL(
771 "arg with spaces \"and quotes\" \"")}));
773 CommandLine cl_without_arg =
774 CommandLine::FromString(FILE_PATH_LITERAL("program --single-argument "));
776 EXPECT_FALSE(cl_without_arg.GetCommandLineString().empty());
777 EXPECT_EQ(FilePath(FILE_PATH_LITERAL("program")),
778 cl_without_arg.GetProgram());
779 EXPECT_TRUE(cl_without_arg.GetArgs().empty());
781 #endif // BUILDFLAG(IS_WIN)
783 #if BUILDFLAG(ENABLE_COMMANDLINE_SEQUENCE_CHECKS)
784 TEST(CommandLineDeathTest, ThreadChecks) {
785 test::TaskEnvironment task_environment;
787 EXPECT_DEATH_IF_SUPPORTED(
789 ThreadPool::PostTask(FROM_HERE, BindLambdaForTesting([&run_loop]() {
791 CommandLine::ForCurrentProcess();
792 command_line->AppendSwitch("test");
800 #endif // BUILDFLAG(ENABLE_COMMANDLINE_SEQUENCE_CHECKS)