Fix emulator build error
[platform/framework/web/chromium-efl.git] / base / command_line_unittest.cc
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.
4
5 #include "base/command_line.h"
6
7 #include <memory>
8 #include <string>
9 #include <vector>
10
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"
19
20 #if BUILDFLAG(IS_WIN)
21 #include <windows.h>
22
23 #include <shellapi.h>
24
25 #include "base/win/scoped_localalloc.h"
26 #endif  // BUILDFLAG(IS_WIN)
27
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)
34
35 namespace base {
36
37 #if BUILDFLAG(IS_WIN)
38 // To test Windows quoting behavior, we use a string that has some backslashes
39 // and quotes.
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\\\\\\\"");
44 #endif
45
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\\\"");
50
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);
72
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("--"));
82
83   EXPECT_EQ(FilePath(FILE_PATH_LITERAL("program")).value(),
84             cl.GetProgram().value());
85
86   EXPECT_TRUE(cl.HasSwitch("foo"));
87 #if BUILDFLAG(IS_WIN)
88   EXPECT_TRUE(cl.HasSwitch("bar"));
89 #else
90   EXPECT_FALSE(cl.HasSwitch("bar"));
91 #endif
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"));
96
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(
102       "other-switches"));
103   EXPECT_EQ("45--output-rotation", cl.GetSwitchValueASCII("input-translation"));
104
105   const CommandLine::StringVector& args = cl.GetArgs();
106   ASSERT_EQ(8U, args.size());
107
108   auto iter = args.begin();
109   EXPECT_EQ(FILE_PATH_LITERAL("flim"), *iter);
110   ++iter;
111   EXPECT_EQ(FILE_PATH_LITERAL("-"), *iter);
112   ++iter;
113   EXPECT_EQ(FILE_PATH_LITERAL("FLAN"), *iter);
114   ++iter;
115   EXPECT_EQ(FILE_PATH_LITERAL("a"), *iter);
116   ++iter;
117   EXPECT_EQ(FILE_PATH_LITERAL("--"), *iter);
118   ++iter;
119   EXPECT_EQ(FILE_PATH_LITERAL("--not-a-switch"), *iter);
120   ++iter;
121   EXPECT_EQ(FILE_PATH_LITERAL("\"in the time of submarines...\""), *iter);
122   ++iter;
123   EXPECT_EQ(FILE_PATH_LITERAL("unquoted arg-with-space"), *iter);
124   ++iter;
125   EXPECT_TRUE(iter == args.end());
126 }
127
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 "
135       L"--quotes=" +
136       kTrickyQuoted +
137       L" -- -- --not-a-switch \"in the time of submarines...\"");
138
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("--"));
148
149   EXPECT_EQ(FilePath(FILE_PATH_LITERAL("program")).value(),
150             cl.GetProgram().value());
151
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"));
159
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(
165       "other-switches"));
166   EXPECT_EQ("45--output-rotation", cl.GetSwitchValueASCII("input-translation"));
167   EXPECT_EQ(kTricky, cl.GetSwitchValueNative("quotes"));
168
169   const CommandLine::StringVector& args = cl.GetArgs();
170   ASSERT_EQ(5U, args.size());
171
172   std::vector<CommandLine::StringType>::const_iterator iter = args.begin();
173   EXPECT_EQ(FILE_PATH_LITERAL("flim"), *iter);
174   ++iter;
175   EXPECT_EQ(FILE_PATH_LITERAL("FLAN"), *iter);
176   ++iter;
177   EXPECT_EQ(FILE_PATH_LITERAL("--"), *iter);
178   ++iter;
179   EXPECT_EQ(FILE_PATH_LITERAL("--not-a-switch"), *iter);
180   ++iter;
181   EXPECT_EQ(FILE_PATH_LITERAL("in the time of submarines..."), *iter);
182   ++iter;
183   EXPECT_TRUE(iter == args.end());
184
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());
188 #endif
189 }
190
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());
199 #endif
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());
205 }
206
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");
212
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";
217
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);
223
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);
234 #endif
235
236 #if BUILDFLAG(IS_WIN)
237 #define QUOTE_ON_WIN FILE_PATH_LITERAL("\"")
238 #else
239 #define QUOTE_ON_WIN FILE_PATH_LITERAL("")
240 #endif  // BUILDFLAG(IS_WIN)
241
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)
247       .append(kPath1)
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)
254       .append(kPath2)
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());
263 }
264
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;
276
277   CommandLine cl(FilePath(FILE_PATH_LITERAL("Program")));
278
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);
285
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));
295
296 #if BUILDFLAG(IS_WIN)
297   EXPECT_EQ(
298       L"Program "
299       L"--switch1 "
300       L"--switch2=value "
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
304       // switches to argv.
305       L"--quotes=\"\\\"a value with quotes\\\"\" "
306       L"--quotes=\"" +
307           kTrickyQuoted + L"\"",
308       cl.GetCommandLineString());
309 #endif
310 }
311
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);
317
318   cl.AppendSwitch("switch1");
319   cl.AppendSwitchASCII("switch2", "foo");
320
321   cl.AppendArg("--arg2");
322
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]);
332 }
333
334 #if BUILDFLAG(IS_WIN)
335 struct CommandLineQuoteTestCase {
336   const wchar_t* const input_arg;
337   const wchar_t* const expected_output_arg;
338 };
339
340 class CommandLineQuoteTest
341     : public ::testing::TestWithParam<CommandLineQuoteTestCase> {};
342
343 INSTANTIATE_TEST_SUITE_P(
344     CommandLineQuoteTestCases,
345     CommandLineQuoteTest,
346     ::testing::ValuesIn(std::vector<CommandLineQuoteTestCase>{
347         {L"", L""},
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\"")"},
353     }));
354
355 TEST_P(CommandLineQuoteTest, TestCases) {
356   EXPECT_EQ(CommandLine::QuoteForCommandLineToArgvW(GetParam().input_arg),
357             GetParam().expected_output_arg);
358 }
359
360 struct CommandLineQuoteAfterTestCase {
361   const std::vector<std::wstring> input_args;
362   const wchar_t* const expected_output;
363 };
364
365 class CommandLineQuoteAfterTest
366     : public ::testing::TestWithParam<CommandLineQuoteAfterTestCase> {};
367
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"("\\\\" "\\\"")"},
380     }));
381
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" ")});
386   int num_args = 0;
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());
390
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]));
395
396     if (i + 1 < num_args) {
397       recreated_command_line.push_back(L' ');
398     }
399   }
400
401   EXPECT_EQ(recreated_command_line, GetParam().expected_output);
402 }
403
404 TEST(CommandLineTest, GetCommandLineStringForShell) {
405   CommandLine cl = CommandLine::FromString(
406       FILE_PATH_LITERAL("program --switch /switch2 --"));
407   EXPECT_EQ(
408       cl.GetCommandLineStringForShell(),
409       FILE_PATH_LITERAL("program --switch /switch2 -- --single-argument %1"));
410 }
411
412 TEST(CommandLineTest, GetCommandLineStringWithUnsafeInsertSequences) {
413   CommandLine cl(FilePath(FILE_PATH_LITERAL("program")));
414   cl.AppendSwitchASCII("switch", "%1");
415   cl.AppendSwitch("%2");
416   cl.AppendArg("%3");
417   EXPECT_EQ(FILE_PATH_LITERAL("program --switch=%1 --%2 %3"),
418             cl.GetCommandLineStringWithUnsafeInsertSequences());
419 }
420
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());
428 }
429
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
432 // argument.
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());
442 }
443
444 #endif  // BUILDFLAG(IS_WIN)
445
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");
453
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());
458
459   CommandLine c1(FilePath(FILE_PATH_LITERAL("Program1")));
460   c1.AppendSwitch("switch1");
461   CommandLine c2(FilePath(FILE_PATH_LITERAL("Program2")));
462   c2.AppendSwitch("switch2");
463
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"));
468 }
469
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());
480
481   const FilePath kProgramPath(L"Program Path");
482
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());
486
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);
490 }
491 #endif
492
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);
502 }
503
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();
513   initial.reset();
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));
518 }
519
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")));
529
530   CommandLine cl(CommandLine::NO_PROGRAM);
531   EXPECT_THAT(cl.argv(), testing::ElementsAre(FILE_PATH_LITERAL("")));
532
533   cl.CopySwitchesFrom(source, {});
534   EXPECT_THAT(cl.argv(), testing::ElementsAre(FILE_PATH_LITERAL("")));
535
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")));
541 }
542
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"));
548
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]);
556 }
557
558 TEST(CommandLineTest, PrependComplexWrapper) {
559   CommandLine cl(FilePath(FILE_PATH_LITERAL("Program")));
560   cl.AppendSwitch("a");
561   cl.AppendSwitch("b");
562   cl.PrependWrapper(
563       FILE_PATH_LITERAL("wrapper --foo='hello world' --bar=\"let's go\""));
564
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]);
572 }
573
574 TEST(CommandLineTest, RemoveSwitch) {
575   const std::string switch1 = "switch1";
576   const std::string switch2 = "switch2";
577   const std::string value2 = "value";
578
579   CommandLine cl(FilePath(FILE_PATH_LITERAL("Program")));
580
581   cl.AppendSwitch(switch1);
582   cl.AppendSwitchASCII(switch2, value2);
583
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")));
591
592   cl.RemoveSwitch(switch1);
593
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")));
600 }
601
602 TEST(CommandLineTest, RemoveSwitchWithValue) {
603   const std::string switch1 = "switch1";
604   const std::string switch2 = "switch2";
605   const std::string value2 = "value";
606
607   CommandLine cl(FilePath(FILE_PATH_LITERAL("Program")));
608
609   cl.AppendSwitch(switch1);
610   cl.AppendSwitchASCII(switch2, value2);
611
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")));
619
620   cl.RemoveSwitch(switch2);
621
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")));
626 }
627
628 TEST(CommandLineTest, RemoveSwitchDropsMultipleSameSwitches) {
629   const std::string switch1 = "switch1";
630   const std::string value2 = "value2";
631
632   CommandLine cl(FilePath(FILE_PATH_LITERAL("Program")));
633
634   cl.AppendSwitch(switch1);
635   cl.AppendSwitchASCII(switch1, value2);
636
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")));
643
644   cl.RemoveSwitch(switch1);
645
646   EXPECT_FALSE(cl.HasSwitch(switch1));
647   EXPECT_THAT(cl.argv(), testing::ElementsAre(FILE_PATH_LITERAL("Program")));
648 }
649
650 TEST(CommandLineTest, AppendAndRemoveSwitchWithDefaultPrefix) {
651   CommandLine cl(FilePath(FILE_PATH_LITERAL("Program")));
652
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());
657
658   cl.RemoveSwitch("foo");
659   EXPECT_THAT(cl.argv(), testing::ElementsAre(FILE_PATH_LITERAL("Program")));
660   EXPECT_EQ(0u, cl.GetArgs().size());
661 }
662
663 TEST(CommandLineTest, AppendAndRemoveSwitchWithAlternativePrefix) {
664   CommandLine cl(FilePath(FILE_PATH_LITERAL("Program")));
665
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());
670
671   cl.RemoveSwitch("foo");
672   EXPECT_THAT(cl.argv(), testing::ElementsAre(FILE_PATH_LITERAL("Program")));
673   EXPECT_EQ(0u, cl.GetArgs().size());
674 }
675
676 TEST(CommandLineTest, AppendAndRemoveSwitchPreservesOtherSwitchesAndArgs) {
677   CommandLine cl(FilePath(FILE_PATH_LITERAL("Program")));
678
679   cl.AppendSwitch("foo");
680   cl.AppendSwitch("bar");
681   cl.AppendArg("arg");
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")));
687
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")));
693 }
694
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
701   };
702   CommandLine cl(std::size(argv), argv);
703
704   EXPECT_TRUE(cl.HasSwitch("foo"));
705   EXPECT_TRUE(cl.HasSwitch("baz"));
706
707   EXPECT_EQ("two", cl.GetSwitchValueASCII("foo"));
708 }
709
710 // Helper class for the next test case
711 class MergeDuplicateFoosSemicolon : public DuplicateSwitchHandler {
712  public:
713   ~MergeDuplicateFoosSemicolon() override;
714
715   void ResolveDuplicate(base::StringPiece key,
716                         CommandLine::StringPieceType new_value,
717                         CommandLine::StringType& out_value) override;
718 };
719
720 MergeDuplicateFoosSemicolon::~MergeDuplicateFoosSemicolon() = default;
721
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);
728     return;
729   }
730   if (!out_value.empty()) {
731 #if BUILDFLAG(IS_WIN)
732     StrAppend(&out_value, {L";"});
733 #else
734     StrAppend(&out_value, {";"});
735 #endif
736   }
737   StrAppend(&out_value, {new_value});
738 }
739
740 // This flag is an exception to the rule that the second duplicate flag wins
741 // Not thread safe
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
748   };
749   CommandLine::SetDuplicateSwitchHandler(
750       std::make_unique<MergeDuplicateFoosSemicolon>());
751
752   CommandLine cl(std::size(argv), argv);
753
754   EXPECT_TRUE(cl.HasSwitch("mergeable-foo"));
755   EXPECT_TRUE(cl.HasSwitch("baz"));
756
757   EXPECT_EQ("one;two", cl.GetSwitchValueASCII("mergeable-foo"));
758   CommandLine::SetDuplicateSwitchHandler(nullptr);
759 }
760
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\" \""));
766
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\" \"")}));
772
773   CommandLine cl_without_arg =
774       CommandLine::FromString(FILE_PATH_LITERAL("program --single-argument "));
775
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());
780 }
781 #endif  // BUILDFLAG(IS_WIN)
782
783 #if BUILDFLAG(ENABLE_COMMANDLINE_SEQUENCE_CHECKS)
784 TEST(CommandLineDeathTest, ThreadChecks) {
785   test::TaskEnvironment task_environment;
786   RunLoop run_loop;
787   EXPECT_DEATH_IF_SUPPORTED(
788       {
789         ThreadPool::PostTask(FROM_HERE, BindLambdaForTesting([&run_loop]() {
790                                auto* command_line =
791                                    CommandLine::ForCurrentProcess();
792                                command_line->AppendSwitch("test");
793                                run_loop.Quit();
794                              }));
795
796         run_loop.Run();
797       },
798       "");
799 }
800 #endif  // BUILDFLAG(ENABLE_COMMANDLINE_SEQUENCE_CHECKS)
801
802 }  // namespace base