[M108 Migration][VD] Support set time and time zone offset
[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/files/file_path.h"
12 #include "base/strings/strcat.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "build/build_config.h"
15 #include "testing/gmock/include/gmock/gmock.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17
18 namespace base {
19
20 // To test Windows quoting behavior, we use a string that has some backslashes
21 // and quotes.
22 // Consider the command-line argument: q\"bs1\bs2\\bs3q\\\"
23 // Here it is with C-style escapes.
24 static const CommandLine::StringType kTrickyQuoted =
25     FILE_PATH_LITERAL("q\\\"bs1\\bs2\\\\bs3q\\\\\\\"");
26 // It should be parsed by Windows as: q"bs1\bs2\\bs3q\"
27 // Here that is with C-style escapes.
28 static const CommandLine::StringType kTricky =
29     FILE_PATH_LITERAL("q\"bs1\\bs2\\\\bs3q\\\"");
30
31 TEST(CommandLineTest, CommandLineConstructor) {
32   const CommandLine::CharType* argv[] = {
33       FILE_PATH_LITERAL("program"),
34       FILE_PATH_LITERAL("--foo="),
35       FILE_PATH_LITERAL("-bAr"),
36       FILE_PATH_LITERAL("-spaetzel=pierogi"),
37       FILE_PATH_LITERAL("-baz"),
38       FILE_PATH_LITERAL("flim"),
39       FILE_PATH_LITERAL("--other-switches=--dog=canine --cat=feline"),
40       FILE_PATH_LITERAL("-spaetzle=Crepe"),
41       FILE_PATH_LITERAL("-=loosevalue"),
42       FILE_PATH_LITERAL("-"),
43       FILE_PATH_LITERAL("FLAN"),
44       FILE_PATH_LITERAL("a"),
45       FILE_PATH_LITERAL("--input-translation=45--output-rotation"),
46       FILE_PATH_LITERAL("--"),
47       FILE_PATH_LITERAL("--"),
48       FILE_PATH_LITERAL("--not-a-switch"),
49       FILE_PATH_LITERAL("\"in the time of submarines...\""),
50       FILE_PATH_LITERAL("unquoted arg-with-space")};
51   CommandLine cl(std::size(argv), argv);
52
53   EXPECT_FALSE(cl.GetCommandLineString().empty());
54   EXPECT_FALSE(cl.HasSwitch("cruller"));
55   EXPECT_FALSE(cl.HasSwitch("flim"));
56   EXPECT_FALSE(cl.HasSwitch("program"));
57   EXPECT_FALSE(cl.HasSwitch("dog"));
58   EXPECT_FALSE(cl.HasSwitch("cat"));
59   EXPECT_FALSE(cl.HasSwitch("output-rotation"));
60   EXPECT_FALSE(cl.HasSwitch("not-a-switch"));
61   EXPECT_FALSE(cl.HasSwitch("--"));
62
63   EXPECT_EQ(FilePath(FILE_PATH_LITERAL("program")).value(),
64             cl.GetProgram().value());
65
66   EXPECT_TRUE(cl.HasSwitch("foo"));
67 #if BUILDFLAG(IS_WIN)
68   EXPECT_TRUE(cl.HasSwitch("bar"));
69 #else
70   EXPECT_FALSE(cl.HasSwitch("bar"));
71 #endif
72   EXPECT_TRUE(cl.HasSwitch("baz"));
73   EXPECT_TRUE(cl.HasSwitch("spaetzle"));
74   EXPECT_TRUE(cl.HasSwitch("other-switches"));
75   EXPECT_TRUE(cl.HasSwitch("input-translation"));
76
77   EXPECT_EQ("Crepe", cl.GetSwitchValueASCII("spaetzle"));
78   EXPECT_EQ("", cl.GetSwitchValueASCII("foo"));
79   EXPECT_EQ("", cl.GetSwitchValueASCII("bar"));
80   EXPECT_EQ("", cl.GetSwitchValueASCII("cruller"));
81   EXPECT_EQ("--dog=canine --cat=feline", cl.GetSwitchValueASCII(
82       "other-switches"));
83   EXPECT_EQ("45--output-rotation", cl.GetSwitchValueASCII("input-translation"));
84
85   const CommandLine::StringVector& args = cl.GetArgs();
86   ASSERT_EQ(8U, args.size());
87
88   auto iter = args.begin();
89   EXPECT_EQ(FILE_PATH_LITERAL("flim"), *iter);
90   ++iter;
91   EXPECT_EQ(FILE_PATH_LITERAL("-"), *iter);
92   ++iter;
93   EXPECT_EQ(FILE_PATH_LITERAL("FLAN"), *iter);
94   ++iter;
95   EXPECT_EQ(FILE_PATH_LITERAL("a"), *iter);
96   ++iter;
97   EXPECT_EQ(FILE_PATH_LITERAL("--"), *iter);
98   ++iter;
99   EXPECT_EQ(FILE_PATH_LITERAL("--not-a-switch"), *iter);
100   ++iter;
101   EXPECT_EQ(FILE_PATH_LITERAL("\"in the time of submarines...\""), *iter);
102   ++iter;
103   EXPECT_EQ(FILE_PATH_LITERAL("unquoted arg-with-space"), *iter);
104   ++iter;
105   EXPECT_TRUE(iter == args.end());
106 }
107
108 TEST(CommandLineTest, CommandLineFromString) {
109 #if BUILDFLAG(IS_WIN)
110   CommandLine cl = CommandLine::FromString(
111       L"program --foo= -bAr  /Spaetzel=pierogi /Baz flim "
112       L"--other-switches=\"--dog=canine --cat=feline\" "
113       L"-spaetzle=Crepe   -=loosevalue  FLAN "
114       L"--input-translation=\"45\"--output-rotation "
115       L"--quotes=" +
116       kTrickyQuoted +
117       L" -- -- --not-a-switch \"in the time of submarines...\"");
118
119   EXPECT_FALSE(cl.GetCommandLineString().empty());
120   EXPECT_FALSE(cl.HasSwitch("cruller"));
121   EXPECT_FALSE(cl.HasSwitch("flim"));
122   EXPECT_FALSE(cl.HasSwitch("program"));
123   EXPECT_FALSE(cl.HasSwitch("dog"));
124   EXPECT_FALSE(cl.HasSwitch("cat"));
125   EXPECT_FALSE(cl.HasSwitch("output-rotation"));
126   EXPECT_FALSE(cl.HasSwitch("not-a-switch"));
127   EXPECT_FALSE(cl.HasSwitch("--"));
128
129   EXPECT_EQ(FilePath(FILE_PATH_LITERAL("program")).value(),
130             cl.GetProgram().value());
131
132   EXPECT_TRUE(cl.HasSwitch("foo"));
133   EXPECT_TRUE(cl.HasSwitch("bar"));
134   EXPECT_TRUE(cl.HasSwitch("baz"));
135   EXPECT_TRUE(cl.HasSwitch("spaetzle"));
136   EXPECT_TRUE(cl.HasSwitch("other-switches"));
137   EXPECT_TRUE(cl.HasSwitch("input-translation"));
138   EXPECT_TRUE(cl.HasSwitch("quotes"));
139
140   EXPECT_EQ("Crepe", cl.GetSwitchValueASCII("spaetzle"));
141   EXPECT_EQ("", cl.GetSwitchValueASCII("foo"));
142   EXPECT_EQ("", cl.GetSwitchValueASCII("bar"));
143   EXPECT_EQ("", cl.GetSwitchValueASCII("cruller"));
144   EXPECT_EQ("--dog=canine --cat=feline", cl.GetSwitchValueASCII(
145       "other-switches"));
146   EXPECT_EQ("45--output-rotation", cl.GetSwitchValueASCII("input-translation"));
147   EXPECT_EQ(kTricky, cl.GetSwitchValueNative("quotes"));
148
149   const CommandLine::StringVector& args = cl.GetArgs();
150   ASSERT_EQ(5U, args.size());
151
152   std::vector<CommandLine::StringType>::const_iterator iter = args.begin();
153   EXPECT_EQ(FILE_PATH_LITERAL("flim"), *iter);
154   ++iter;
155   EXPECT_EQ(FILE_PATH_LITERAL("FLAN"), *iter);
156   ++iter;
157   EXPECT_EQ(FILE_PATH_LITERAL("--"), *iter);
158   ++iter;
159   EXPECT_EQ(FILE_PATH_LITERAL("--not-a-switch"), *iter);
160   ++iter;
161   EXPECT_EQ(FILE_PATH_LITERAL("in the time of submarines..."), *iter);
162   ++iter;
163   EXPECT_TRUE(iter == args.end());
164
165   // Check that a generated string produces an equivalent command line.
166   CommandLine cl_duplicate = CommandLine::FromString(cl.GetCommandLineString());
167   EXPECT_EQ(cl.GetCommandLineString(), cl_duplicate.GetCommandLineString());
168 #endif
169 }
170
171 // Tests behavior with an empty input string.
172 TEST(CommandLineTest, EmptyString) {
173 #if BUILDFLAG(IS_WIN)
174   CommandLine cl_from_string = CommandLine::FromString(std::wstring());
175   EXPECT_TRUE(cl_from_string.GetCommandLineString().empty());
176   EXPECT_TRUE(cl_from_string.GetProgram().empty());
177   EXPECT_EQ(1U, cl_from_string.argv().size());
178   EXPECT_TRUE(cl_from_string.GetArgs().empty());
179 #endif
180   CommandLine cl_from_argv(0, nullptr);
181   EXPECT_TRUE(cl_from_argv.GetCommandLineString().empty());
182   EXPECT_TRUE(cl_from_argv.GetProgram().empty());
183   EXPECT_EQ(1U, cl_from_argv.argv().size());
184   EXPECT_TRUE(cl_from_argv.GetArgs().empty());
185 }
186
187 TEST(CommandLineTest, GetArgumentsString) {
188   static const FilePath::CharType kPath1[] =
189       FILE_PATH_LITERAL("C:\\Some File\\With Spaces.ggg");
190   static const FilePath::CharType kPath2[] =
191       FILE_PATH_LITERAL("C:\\no\\spaces.ggg");
192
193   static const char kFirstArgName[] = "first-arg";
194   static const char kSecondArgName[] = "arg2";
195   static const char kThirdArgName[] = "arg with space";
196   static const char kFourthArgName[] = "nospace";
197
198   CommandLine cl(CommandLine::NO_PROGRAM);
199   cl.AppendSwitchPath(kFirstArgName, FilePath(kPath1));
200   cl.AppendSwitchPath(kSecondArgName, FilePath(kPath2));
201   cl.AppendArg(kThirdArgName);
202   cl.AppendArg(kFourthArgName);
203
204 #if BUILDFLAG(IS_WIN)
205   CommandLine::StringType expected_first_arg(UTF8ToWide(kFirstArgName));
206   CommandLine::StringType expected_second_arg(UTF8ToWide(kSecondArgName));
207   CommandLine::StringType expected_third_arg(UTF8ToWide(kThirdArgName));
208   CommandLine::StringType expected_fourth_arg(UTF8ToWide(kFourthArgName));
209 #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
210   CommandLine::StringType expected_first_arg(kFirstArgName);
211   CommandLine::StringType expected_second_arg(kSecondArgName);
212   CommandLine::StringType expected_third_arg(kThirdArgName);
213   CommandLine::StringType expected_fourth_arg(kFourthArgName);
214 #endif
215
216 #if BUILDFLAG(IS_WIN)
217 #define QUOTE_ON_WIN FILE_PATH_LITERAL("\"")
218 #else
219 #define QUOTE_ON_WIN FILE_PATH_LITERAL("")
220 #endif  // BUILDFLAG(IS_WIN)
221
222   CommandLine::StringType expected_str;
223   expected_str.append(FILE_PATH_LITERAL("--"))
224       .append(expected_first_arg)
225       .append(FILE_PATH_LITERAL("="))
226       .append(QUOTE_ON_WIN)
227       .append(kPath1)
228       .append(QUOTE_ON_WIN)
229       .append(FILE_PATH_LITERAL(" "))
230       .append(FILE_PATH_LITERAL("--"))
231       .append(expected_second_arg)
232       .append(FILE_PATH_LITERAL("="))
233       .append(QUOTE_ON_WIN)
234       .append(kPath2)
235       .append(QUOTE_ON_WIN)
236       .append(FILE_PATH_LITERAL(" "))
237       .append(QUOTE_ON_WIN)
238       .append(expected_third_arg)
239       .append(QUOTE_ON_WIN)
240       .append(FILE_PATH_LITERAL(" "))
241       .append(expected_fourth_arg);
242   EXPECT_EQ(expected_str, cl.GetArgumentsString());
243 }
244
245 // Test methods for appending switches to a command line.
246 TEST(CommandLineTest, AppendSwitches) {
247   std::string switch1 = "switch1";
248   std::string switch2 = "switch2";
249   std::string value2 = "value";
250   std::string switch3 = "switch3";
251   std::string value3 = "a value with spaces";
252   std::string switch4 = "switch4";
253   std::string value4 = "\"a value with quotes\"";
254   std::string switch5 = "quotes";
255   CommandLine::StringType value5 = kTricky;
256
257   CommandLine cl(FilePath(FILE_PATH_LITERAL("Program")));
258
259   cl.AppendSwitch(switch1);
260   cl.AppendSwitchASCII(switch2, value2);
261   cl.AppendSwitchASCII(switch3, value3);
262   cl.AppendSwitchASCII(switch4, value4);
263   cl.AppendSwitchASCII(switch5, value4);
264   cl.AppendSwitchNative(switch5, value5);
265
266   EXPECT_TRUE(cl.HasSwitch(switch1));
267   EXPECT_TRUE(cl.HasSwitch(switch2));
268   EXPECT_EQ(value2, cl.GetSwitchValueASCII(switch2));
269   EXPECT_TRUE(cl.HasSwitch(switch3));
270   EXPECT_EQ(value3, cl.GetSwitchValueASCII(switch3));
271   EXPECT_TRUE(cl.HasSwitch(switch4));
272   EXPECT_EQ(value4, cl.GetSwitchValueASCII(switch4));
273   EXPECT_TRUE(cl.HasSwitch(switch5));
274   EXPECT_EQ(value5, cl.GetSwitchValueNative(switch5));
275
276 #if BUILDFLAG(IS_WIN)
277   EXPECT_EQ(
278       L"Program "
279       L"--switch1 "
280       L"--switch2=value "
281       L"--switch3=\"a value with spaces\" "
282       L"--switch4=\"\\\"a value with quotes\\\"\" "
283       // Even though the switches are unique, appending can add repeat
284       // switches to argv.
285       L"--quotes=\"\\\"a value with quotes\\\"\" "
286       L"--quotes=\"" +
287           kTrickyQuoted + L"\"",
288       cl.GetCommandLineString());
289 #endif
290 }
291
292 TEST(CommandLineTest, AppendSwitchesDashDash) {
293  const CommandLine::CharType* raw_argv[] = { FILE_PATH_LITERAL("prog"),
294                                              FILE_PATH_LITERAL("--"),
295                                              FILE_PATH_LITERAL("--arg1") };
296  CommandLine cl(std::size(raw_argv), raw_argv);
297
298  cl.AppendSwitch("switch1");
299  cl.AppendSwitchASCII("switch2", "foo");
300
301  cl.AppendArg("--arg2");
302
303  EXPECT_EQ(FILE_PATH_LITERAL("prog --switch1 --switch2=foo -- --arg1 --arg2"),
304            cl.GetCommandLineString());
305  CommandLine::StringVector cl_argv = cl.argv();
306  EXPECT_EQ(FILE_PATH_LITERAL("prog"), cl_argv[0]);
307  EXPECT_EQ(FILE_PATH_LITERAL("--switch1"), cl_argv[1]);
308  EXPECT_EQ(FILE_PATH_LITERAL("--switch2=foo"), cl_argv[2]);
309  EXPECT_EQ(FILE_PATH_LITERAL("--"), cl_argv[3]);
310  EXPECT_EQ(FILE_PATH_LITERAL("--arg1"), cl_argv[4]);
311  EXPECT_EQ(FILE_PATH_LITERAL("--arg2"), cl_argv[5]);
312 }
313
314 #if BUILDFLAG(IS_WIN)
315 TEST(CommandLineTest, GetCommandLineStringForShell) {
316   CommandLine cl = CommandLine::FromString(
317       FILE_PATH_LITERAL("program --switch /switch2 --"));
318   EXPECT_EQ(
319       cl.GetCommandLineStringForShell(),
320       FILE_PATH_LITERAL("program --switch /switch2 -- --single-argument %1"));
321 }
322
323 TEST(CommandLineTest, GetCommandLineStringWithUnsafeInsertSequences) {
324   CommandLine cl(FilePath(FILE_PATH_LITERAL("program")));
325   cl.AppendSwitchASCII("switch", "%1");
326   cl.AppendSwitch("%2");
327   cl.AppendArg("%3");
328   EXPECT_EQ(FILE_PATH_LITERAL("program --switch=%1 --%2 %3"),
329             cl.GetCommandLineStringWithUnsafeInsertSequences());
330 }
331
332 TEST(CommandLineTest, HasSingleArgument) {
333   CommandLine cl(FilePath(FILE_PATH_LITERAL("Program")));
334   cl.AppendSwitchASCII("switch2", "foo");
335   EXPECT_FALSE(cl.HasSingleArgumentSwitch());
336   CommandLine cl_for_shell(
337       CommandLine::FromString(cl.GetCommandLineStringForShell()));
338   EXPECT_TRUE(cl_for_shell.HasSingleArgumentSwitch());
339 }
340
341 #endif  // BUILDFLAG(IS_WIN)
342
343 // Tests that when AppendArguments is called that the program is set correctly
344 // on the target CommandLine object and the switches from the source
345 // CommandLine are added to the target.
346 TEST(CommandLineTest, AppendArguments) {
347   CommandLine cl1(FilePath(FILE_PATH_LITERAL("Program")));
348   cl1.AppendSwitch("switch1");
349   cl1.AppendSwitchASCII("switch2", "foo");
350
351   CommandLine cl2(CommandLine::NO_PROGRAM);
352   cl2.AppendArguments(cl1, true);
353   EXPECT_EQ(cl1.GetProgram().value(), cl2.GetProgram().value());
354   EXPECT_EQ(cl1.GetCommandLineString(), cl2.GetCommandLineString());
355
356   CommandLine c1(FilePath(FILE_PATH_LITERAL("Program1")));
357   c1.AppendSwitch("switch1");
358   CommandLine c2(FilePath(FILE_PATH_LITERAL("Program2")));
359   c2.AppendSwitch("switch2");
360
361   c1.AppendArguments(c2, true);
362   EXPECT_EQ(c1.GetProgram().value(), c2.GetProgram().value());
363   EXPECT_TRUE(c1.HasSwitch("switch1"));
364   EXPECT_TRUE(c1.HasSwitch("switch2"));
365 }
366
367 #if BUILDFLAG(IS_WIN)
368 // Make sure that the command line string program paths are quoted as necessary.
369 // This only makes sense on Windows and the test is basically here to guard
370 // against regressions.
371 TEST(CommandLineTest, ProgramQuotes) {
372   // Check that quotes are not added for paths without spaces.
373   const FilePath kProgram(L"Program");
374   CommandLine cl_program(kProgram);
375   EXPECT_EQ(kProgram.value(), cl_program.GetProgram().value());
376   EXPECT_EQ(kProgram.value(), cl_program.GetCommandLineString());
377
378   const FilePath kProgramPath(L"Program Path");
379
380   // Check that quotes are not returned from GetProgram().
381   CommandLine cl_program_path(kProgramPath);
382   EXPECT_EQ(kProgramPath.value(), cl_program_path.GetProgram().value());
383
384   // Check that quotes are added to command line string paths containing spaces.
385   CommandLine::StringType cmd_string(cl_program_path.GetCommandLineString());
386   EXPECT_EQ(L"\"Program Path\"", cmd_string);
387 }
388 #endif
389
390 // Calling Init multiple times should not modify the previous CommandLine.
391 TEST(CommandLineTest, Init) {
392   // Call Init without checking output once so we know it's been called
393   // whether or not the test runner does so.
394   CommandLine::Init(0, nullptr);
395   CommandLine* initial = CommandLine::ForCurrentProcess();
396   EXPECT_FALSE(CommandLine::Init(0, nullptr));
397   CommandLine* current = CommandLine::ForCurrentProcess();
398   EXPECT_EQ(initial, current);
399 }
400
401 // Test that copies of CommandLine have a valid StringPiece map.
402 TEST(CommandLineTest, Copy) {
403   std::unique_ptr<CommandLine> initial(
404       new CommandLine(CommandLine::NO_PROGRAM));
405   initial->AppendSwitch("a");
406   initial->AppendSwitch("bbbbbbbbbbbbbbb");
407   initial->AppendSwitch("c");
408   CommandLine copy_constructed(*initial);
409   CommandLine assigned = *initial;
410   CommandLine::SwitchMap switch_map = initial->GetSwitches();
411   initial.reset();
412   for (const auto& pair : switch_map)
413     EXPECT_TRUE(copy_constructed.HasSwitch(pair.first));
414   for (const auto& pair : switch_map)
415     EXPECT_TRUE(assigned.HasSwitch(pair.first));
416 }
417
418 TEST(CommandLineTest, PrependSimpleWrapper) {
419   CommandLine cl(FilePath(FILE_PATH_LITERAL("Program")));
420   cl.AppendSwitch("a");
421   cl.AppendSwitch("b");
422   cl.PrependWrapper(FILE_PATH_LITERAL("wrapper --foo --bar"));
423
424   EXPECT_EQ(6u, cl.argv().size());
425   EXPECT_EQ(FILE_PATH_LITERAL("wrapper"), cl.argv()[0]);
426   EXPECT_EQ(FILE_PATH_LITERAL("--foo"), cl.argv()[1]);
427   EXPECT_EQ(FILE_PATH_LITERAL("--bar"), cl.argv()[2]);
428   EXPECT_EQ(FILE_PATH_LITERAL("Program"), cl.argv()[3]);
429   EXPECT_EQ(FILE_PATH_LITERAL("--a"), cl.argv()[4]);
430   EXPECT_EQ(FILE_PATH_LITERAL("--b"), cl.argv()[5]);
431 }
432
433 TEST(CommandLineTest, PrependComplexWrapper) {
434   CommandLine cl(FilePath(FILE_PATH_LITERAL("Program")));
435   cl.AppendSwitch("a");
436   cl.AppendSwitch("b");
437   cl.PrependWrapper(
438       FILE_PATH_LITERAL("wrapper --foo='hello world' --bar=\"let's go\""));
439
440   EXPECT_EQ(6u, cl.argv().size());
441   EXPECT_EQ(FILE_PATH_LITERAL("wrapper"), cl.argv()[0]);
442   EXPECT_EQ(FILE_PATH_LITERAL("--foo='hello world'"), cl.argv()[1]);
443   EXPECT_EQ(FILE_PATH_LITERAL("--bar=\"let's go\""), cl.argv()[2]);
444   EXPECT_EQ(FILE_PATH_LITERAL("Program"), cl.argv()[3]);
445   EXPECT_EQ(FILE_PATH_LITERAL("--a"), cl.argv()[4]);
446   EXPECT_EQ(FILE_PATH_LITERAL("--b"), cl.argv()[5]);
447 }
448
449 TEST(CommandLineTest, RemoveSwitch) {
450   const std::string switch1 = "switch1";
451   const std::string switch2 = "switch2";
452   const std::string value2 = "value";
453
454   CommandLine cl(FilePath(FILE_PATH_LITERAL("Program")));
455
456   cl.AppendSwitch(switch1);
457   cl.AppendSwitchASCII(switch2, value2);
458
459   EXPECT_TRUE(cl.HasSwitch(switch1));
460   EXPECT_TRUE(cl.HasSwitch(switch2));
461   EXPECT_EQ(value2, cl.GetSwitchValueASCII(switch2));
462   EXPECT_THAT(cl.argv(),
463               testing::ElementsAre(FILE_PATH_LITERAL("Program"),
464                                    FILE_PATH_LITERAL("--switch1"),
465                                    FILE_PATH_LITERAL("--switch2=value")));
466
467   cl.RemoveSwitch(switch1);
468
469   EXPECT_FALSE(cl.HasSwitch(switch1));
470   EXPECT_TRUE(cl.HasSwitch(switch2));
471   EXPECT_EQ(value2, cl.GetSwitchValueASCII(switch2));
472   EXPECT_THAT(cl.argv(),
473               testing::ElementsAre(FILE_PATH_LITERAL("Program"),
474                                    FILE_PATH_LITERAL("--switch2=value")));
475 }
476
477 TEST(CommandLineTest, RemoveSwitchWithValue) {
478   const std::string switch1 = "switch1";
479   const std::string switch2 = "switch2";
480   const std::string value2 = "value";
481
482   CommandLine cl(FilePath(FILE_PATH_LITERAL("Program")));
483
484   cl.AppendSwitch(switch1);
485   cl.AppendSwitchASCII(switch2, value2);
486
487   EXPECT_TRUE(cl.HasSwitch(switch1));
488   EXPECT_TRUE(cl.HasSwitch(switch2));
489   EXPECT_EQ(value2, cl.GetSwitchValueASCII(switch2));
490   EXPECT_THAT(cl.argv(),
491               testing::ElementsAre(FILE_PATH_LITERAL("Program"),
492                                    FILE_PATH_LITERAL("--switch1"),
493                                    FILE_PATH_LITERAL("--switch2=value")));
494
495   cl.RemoveSwitch(switch2);
496
497   EXPECT_TRUE(cl.HasSwitch(switch1));
498   EXPECT_FALSE(cl.HasSwitch(switch2));
499   EXPECT_THAT(cl.argv(), testing::ElementsAre(FILE_PATH_LITERAL("Program"),
500                                               FILE_PATH_LITERAL("--switch1")));
501 }
502
503 TEST(CommandLineTest, RemoveSwitchDropsMultipleSameSwitches) {
504   const std::string switch1 = "switch1";
505   const std::string value2 = "value2";
506
507   CommandLine cl(FilePath(FILE_PATH_LITERAL("Program")));
508
509   cl.AppendSwitch(switch1);
510   cl.AppendSwitchASCII(switch1, value2);
511
512   EXPECT_TRUE(cl.HasSwitch(switch1));
513   EXPECT_EQ(value2, cl.GetSwitchValueASCII(switch1));
514   EXPECT_THAT(cl.argv(),
515               testing::ElementsAre(FILE_PATH_LITERAL("Program"),
516                                    FILE_PATH_LITERAL("--switch1"),
517                                    FILE_PATH_LITERAL("--switch1=value2")));
518
519   cl.RemoveSwitch(switch1);
520
521   EXPECT_FALSE(cl.HasSwitch(switch1));
522   EXPECT_THAT(cl.argv(), testing::ElementsAre(FILE_PATH_LITERAL("Program")));
523 }
524
525 TEST(CommandLineTest, AppendAndRemoveSwitchWithDefaultPrefix) {
526   CommandLine cl(FilePath(FILE_PATH_LITERAL("Program")));
527
528   cl.AppendSwitch("foo");
529   EXPECT_THAT(cl.argv(), testing::ElementsAre(FILE_PATH_LITERAL("Program"),
530                                               FILE_PATH_LITERAL("--foo")));
531   EXPECT_EQ(0u, cl.GetArgs().size());
532
533   cl.RemoveSwitch("foo");
534   EXPECT_THAT(cl.argv(), testing::ElementsAre(FILE_PATH_LITERAL("Program")));
535   EXPECT_EQ(0u, cl.GetArgs().size());
536 }
537
538 TEST(CommandLineTest, AppendAndRemoveSwitchWithAlternativePrefix) {
539   CommandLine cl(FilePath(FILE_PATH_LITERAL("Program")));
540
541   cl.AppendSwitch("-foo");
542   EXPECT_THAT(cl.argv(), testing::ElementsAre(FILE_PATH_LITERAL("Program"),
543                                               FILE_PATH_LITERAL("-foo")));
544   EXPECT_EQ(0u, cl.GetArgs().size());
545
546   cl.RemoveSwitch("foo");
547   EXPECT_THAT(cl.argv(), testing::ElementsAre(FILE_PATH_LITERAL("Program")));
548   EXPECT_EQ(0u, cl.GetArgs().size());
549 }
550
551 TEST(CommandLineTest, AppendAndRemoveSwitchPreservesOtherSwitchesAndArgs) {
552   CommandLine cl(FilePath(FILE_PATH_LITERAL("Program")));
553
554   cl.AppendSwitch("foo");
555   cl.AppendSwitch("bar");
556   cl.AppendArg("arg");
557   EXPECT_THAT(cl.argv(), testing::ElementsAre(FILE_PATH_LITERAL("Program"),
558                                               FILE_PATH_LITERAL("--foo"),
559                                               FILE_PATH_LITERAL("--bar"),
560                                               FILE_PATH_LITERAL("arg")));
561   EXPECT_THAT(cl.GetArgs(), testing::ElementsAre(FILE_PATH_LITERAL("arg")));
562
563   cl.RemoveSwitch("foo");
564   EXPECT_THAT(cl.argv(), testing::ElementsAre(FILE_PATH_LITERAL("Program"),
565                                               FILE_PATH_LITERAL("--bar"),
566                                               FILE_PATH_LITERAL("arg")));
567   EXPECT_THAT(cl.GetArgs(), testing::ElementsAre(FILE_PATH_LITERAL("arg")));
568 }
569
570 TEST(CommandLineTest, MultipleSameSwitch) {
571   const CommandLine::CharType* argv[] = {
572       FILE_PATH_LITERAL("program"),
573       FILE_PATH_LITERAL("--foo=one"),  // --foo first time
574       FILE_PATH_LITERAL("-baz"),
575       FILE_PATH_LITERAL("--foo=two")  // --foo second time
576   };
577   CommandLine cl(std::size(argv), argv);
578
579   EXPECT_TRUE(cl.HasSwitch("foo"));
580   EXPECT_TRUE(cl.HasSwitch("baz"));
581
582   EXPECT_EQ("two", cl.GetSwitchValueASCII("foo"));
583 }
584
585 // Helper class for the next test case
586 class MergeDuplicateFoosSemicolon : public DuplicateSwitchHandler {
587  public:
588   ~MergeDuplicateFoosSemicolon() override;
589
590   void ResolveDuplicate(base::StringPiece key,
591                         CommandLine::StringPieceType new_value,
592                         CommandLine::StringType& out_value) override;
593 };
594
595 MergeDuplicateFoosSemicolon::~MergeDuplicateFoosSemicolon() = default;
596
597 void MergeDuplicateFoosSemicolon::ResolveDuplicate(
598     base::StringPiece key,
599     CommandLine::StringPieceType new_value,
600     CommandLine::StringType& out_value) {
601   if (key != "mergeable-foo") {
602     out_value = CommandLine::StringType(new_value);
603     return;
604   }
605   if (!out_value.empty()) {
606 #if BUILDFLAG(IS_WIN)
607     StrAppend(&out_value, {L";"});
608 #else
609     StrAppend(&out_value, {";"});
610 #endif
611   }
612   StrAppend(&out_value, {new_value});
613 }
614
615 // This flag is an exception to the rule that the second duplicate flag wins
616 // Not thread safe
617 TEST(CommandLineTest, MultipleFilterFileSwitch) {
618   const CommandLine::CharType* const argv[] = {
619       FILE_PATH_LITERAL("program"),
620       FILE_PATH_LITERAL("--mergeable-foo=one"),  // --first time
621       FILE_PATH_LITERAL("-baz"),
622       FILE_PATH_LITERAL("--mergeable-foo=two")  // --second time
623   };
624   CommandLine::SetDuplicateSwitchHandler(
625       std::make_unique<MergeDuplicateFoosSemicolon>());
626
627   CommandLine cl(std::size(argv), argv);
628
629   EXPECT_TRUE(cl.HasSwitch("mergeable-foo"));
630   EXPECT_TRUE(cl.HasSwitch("baz"));
631
632   EXPECT_EQ("one;two", cl.GetSwitchValueASCII("mergeable-foo"));
633   CommandLine::SetDuplicateSwitchHandler(nullptr);
634 }
635
636 #if BUILDFLAG(IS_WIN)
637 TEST(CommandLineTest, ParseAsSingleArgument) {
638   CommandLine cl = CommandLine::FromString(
639       FILE_PATH_LITERAL("program --switch_before arg_before "
640                         "--single-argument arg with spaces \"and quotes\" \""));
641
642   EXPECT_FALSE(cl.GetCommandLineString().empty());
643   EXPECT_EQ(FilePath(FILE_PATH_LITERAL("program")), cl.GetProgram());
644   EXPECT_TRUE(cl.HasSwitch("switch_before"));
645   EXPECT_EQ(cl.GetArgs(), CommandLine::StringVector({FILE_PATH_LITERAL(
646                               "arg with spaces \"and quotes\" \"")}));
647
648   CommandLine cl_without_arg =
649       CommandLine::FromString(FILE_PATH_LITERAL("program --single-argument "));
650
651   EXPECT_FALSE(cl_without_arg.GetCommandLineString().empty());
652   EXPECT_EQ(FilePath(FILE_PATH_LITERAL("program")),
653             cl_without_arg.GetProgram());
654   EXPECT_TRUE(cl_without_arg.GetArgs().empty());
655 }
656 #endif  // BUILDFLAG(IS_WIN)
657
658 } // namespace base