• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "flutter/fml/command_line.h"
6 
7 #include <string_view>
8 #include <utility>
9 
10 #include "flutter/fml/macros.h"
11 #include "flutter/fml/size.h"
12 #include "gtest/gtest.h"
13 
14 namespace fml {
15 namespace {
16 
TEST(CommandLineTest,Basic)17 TEST(CommandLineTest, Basic) {
18   // Making this const verifies that the methods called are const.
19   const auto cl = CommandLineFromInitializerList(
20       {"my_program", "--flag1", "--flag2=value2", "arg1", "arg2", "arg3"});
21 
22   EXPECT_TRUE(cl.has_argv0());
23   EXPECT_EQ("my_program", cl.argv0());
24 
25   EXPECT_EQ(2u, cl.options().size());
26   EXPECT_EQ("flag1", cl.options()[0].name);
27   EXPECT_EQ(std::string(), cl.options()[0].value);
28   EXPECT_EQ("flag2", cl.options()[1].name);
29   EXPECT_EQ("value2", cl.options()[1].value);
30 
31   EXPECT_EQ(3u, cl.positional_args().size());
32   EXPECT_EQ("arg1", cl.positional_args()[0]);
33   EXPECT_EQ("arg2", cl.positional_args()[1]);
34   EXPECT_EQ("arg3", cl.positional_args()[2]);
35 
36   EXPECT_TRUE(cl.HasOption("flag1"));
37   EXPECT_TRUE(cl.HasOption("flag1", nullptr));
38   size_t index = static_cast<size_t>(-1);
39   EXPECT_TRUE(cl.HasOption("flag2", &index));
40   EXPECT_EQ(1u, index);
41   EXPECT_FALSE(cl.HasOption("flag3"));
42   EXPECT_FALSE(cl.HasOption("flag3", nullptr));
43 
44   std::string value = "nonempty";
45   EXPECT_TRUE(cl.GetOptionValue("flag1", &value));
46   EXPECT_EQ(std::string(), value);
47   EXPECT_TRUE(cl.GetOptionValue("flag2", &value));
48   EXPECT_EQ("value2", value);
49   EXPECT_FALSE(cl.GetOptionValue("flag3", &value));
50 
51   EXPECT_EQ(std::string(), cl.GetOptionValueWithDefault("flag1", "nope"));
52   EXPECT_EQ("value2", cl.GetOptionValueWithDefault("flag2", "nope"));
53   EXPECT_EQ("nope", cl.GetOptionValueWithDefault("flag3", "nope"));
54 }
55 
TEST(CommandLineTest,DefaultConstructor)56 TEST(CommandLineTest, DefaultConstructor) {
57   CommandLine cl;
58   EXPECT_FALSE(cl.has_argv0());
59   EXPECT_EQ(std::string(), cl.argv0());
60   EXPECT_EQ(std::vector<CommandLine::Option>(), cl.options());
61   EXPECT_EQ(std::vector<std::string>(), cl.positional_args());
62 }
63 
TEST(CommandLineTest,ComponentConstructor)64 TEST(CommandLineTest, ComponentConstructor) {
65   const std::string argv0 = "my_program";
66   const std::vector<CommandLine::Option> options = {
67       CommandLine::Option("flag", "value")};
68   const std::vector<std::string> positional_args = {"arg"};
69 
70   CommandLine cl(argv0, options, positional_args);
71   EXPECT_TRUE(cl.has_argv0());
72   EXPECT_EQ(argv0, cl.argv0());
73   EXPECT_EQ(options, cl.options());
74   EXPECT_EQ(positional_args, cl.positional_args());
75   EXPECT_EQ("value", cl.GetOptionValueWithDefault("flag", "nope"));
76 }
77 
TEST(CommandLineTest,CommandLineFromIteratorsFindFirstPositionalArg)78 TEST(CommandLineTest, CommandLineFromIteratorsFindFirstPositionalArg) {
79   // This shows how one might process subcommands.
80   {
81     static std::vector<std::string> argv = {"my_program", "--flag1",
82                                             "--flag2",    "subcommand",
83                                             "--subflag",  "subarg"};
84     auto first = argv.cbegin();
85     auto last = argv.cend();
86     std::vector<std::string>::const_iterator sub_first;
87     auto cl =
88         CommandLineFromIteratorsFindFirstPositionalArg(first, last, &sub_first);
89     EXPECT_TRUE(cl.has_argv0());
90     EXPECT_EQ(argv[0], cl.argv0());
91     std::vector<CommandLine::Option> expected_options = {
92         CommandLine::Option("flag1"), CommandLine::Option("flag2")};
93     EXPECT_EQ(expected_options, cl.options());
94     std::vector<std::string> expected_positional_args = {argv[3], argv[4],
95                                                          argv[5]};
96     EXPECT_EQ(expected_positional_args, cl.positional_args());
97     EXPECT_TRUE(cl.HasOption("flag1", nullptr));
98     EXPECT_TRUE(cl.HasOption("flag2", nullptr));
99     EXPECT_FALSE(cl.HasOption("subflag", nullptr));
100 
101     EXPECT_EQ(first + 3, sub_first);
102     auto sub_cl = CommandLineFromIterators(sub_first, last);
103     EXPECT_TRUE(sub_cl.has_argv0());
104     EXPECT_EQ(argv[3], sub_cl.argv0());
105     std::vector<CommandLine::Option> expected_sub_options = {
106         CommandLine::Option("subflag")};
107     EXPECT_EQ(expected_sub_options, sub_cl.options());
108     std::vector<std::string> expected_sub_positional_args = {argv[5]};
109     EXPECT_EQ(expected_sub_positional_args, sub_cl.positional_args());
110     EXPECT_FALSE(sub_cl.HasOption("flag1", nullptr));
111     EXPECT_FALSE(sub_cl.HasOption("flag2", nullptr));
112     EXPECT_TRUE(sub_cl.HasOption("subflag", nullptr));
113   }
114 
115   // No positional argument.
116   {
117     static std::vector<std::string> argv = {"my_program", "--flag"};
118     std::vector<std::string>::const_iterator sub_first;
119     auto cl = CommandLineFromIteratorsFindFirstPositionalArg(
120         argv.cbegin(), argv.cend(), &sub_first);
121     EXPECT_EQ(argv.cend(), sub_first);
122   }
123 
124   // Multiple positional arguments.
125   {
126     static std::vector<std::string> argv = {"my_program", "arg1", "arg2"};
127     std::vector<std::string>::const_iterator sub_first;
128     auto cl = CommandLineFromIteratorsFindFirstPositionalArg(
129         argv.cbegin(), argv.cend(), &sub_first);
130     EXPECT_EQ(argv.cbegin() + 1, sub_first);
131   }
132 
133   // "--".
134   {
135     static std::vector<std::string> argv = {"my_program", "--", "--arg"};
136     std::vector<std::string>::const_iterator sub_first;
137     auto cl = CommandLineFromIteratorsFindFirstPositionalArg(
138         argv.cbegin(), argv.cend(), &sub_first);
139     EXPECT_EQ(argv.cbegin() + 2, sub_first);
140   }
141 }
142 
TEST(CommandLineTest,CommmandLineFromIterators)143 TEST(CommandLineTest, CommmandLineFromIterators) {
144   {
145     // Note (here and below): The |const| ensures that the factory method can
146     // accept const iterators.
147     const std::vector<std::string> argv = {"my_program", "--flag=value", "arg"};
148 
149     auto cl = CommandLineFromIterators(argv.begin(), argv.end());
150     EXPECT_TRUE(cl.has_argv0());
151     EXPECT_EQ(argv[0], cl.argv0());
152     std::vector<CommandLine::Option> expected_options = {
153         CommandLine::Option("flag", "value")};
154     EXPECT_EQ(expected_options, cl.options());
155     std::vector<std::string> expected_positional_args = {argv[2]};
156     EXPECT_EQ(expected_positional_args, cl.positional_args());
157     EXPECT_EQ("value", cl.GetOptionValueWithDefault("flag", "nope"));
158   }
159 
160   // Can handle empty argv.
161   {
162     const std::vector<std::string> argv;
163 
164     auto cl = CommandLineFromIterators(argv.begin(), argv.end());
165     EXPECT_FALSE(cl.has_argv0());
166     EXPECT_EQ(std::string(), cl.argv0());
167     EXPECT_EQ(std::vector<CommandLine::Option>(), cl.options());
168     EXPECT_EQ(std::vector<std::string>(), cl.positional_args());
169   }
170 
171   // Can handle empty |argv[0]|.
172   {
173     const std::vector<std::string> argv = {""};
174 
175     auto cl = CommandLineFromIterators(argv.begin(), argv.end());
176     EXPECT_TRUE(cl.has_argv0());
177     EXPECT_EQ(std::string(), cl.argv0());
178     EXPECT_EQ(std::vector<CommandLine::Option>(), cl.options());
179     EXPECT_EQ(std::vector<std::string>(), cl.positional_args());
180   }
181 
182   // Can also take a vector of |const char*|s.
183   {
184     const std::vector<const char*> argv = {"my_program", "--flag=value", "arg"};
185 
186     auto cl = CommandLineFromIterators(argv.begin(), argv.end());
187     EXPECT_TRUE(cl.has_argv0());
188     EXPECT_EQ(argv[0], cl.argv0());
189     std::vector<CommandLine::Option> expected_options = {
190         CommandLine::Option("flag", "value")};
191     EXPECT_EQ(expected_options, cl.options());
192     std::vector<std::string> expected_positional_args = {argv[2]};
193     EXPECT_EQ(expected_positional_args, cl.positional_args());
194     EXPECT_EQ("value", cl.GetOptionValueWithDefault("flag", "nope"));
195   }
196 
197   // Or a plain old array.
198   {
199     static const char* const argv[] = {"my_program", "--flag=value", "arg"};
200 
201     auto cl = CommandLineFromIterators(argv, argv + fml::size(argv));
202     EXPECT_TRUE(cl.has_argv0());
203     EXPECT_EQ(argv[0], cl.argv0());
204     std::vector<CommandLine::Option> expected_options = {
205         CommandLine::Option("flag", "value")};
206     EXPECT_EQ(expected_options, cl.options());
207     std::vector<std::string> expected_positional_args = {argv[2]};
208     EXPECT_EQ(expected_positional_args, cl.positional_args());
209     EXPECT_EQ("value", cl.GetOptionValueWithDefault("flag", "nope"));
210   }
211 }
212 
TEST(CommandLineTest,CommandLineFromArgcArgv)213 TEST(CommandLineTest, CommandLineFromArgcArgv) {
214   static const char* const argv[] = {"my_program", "--flag=value", "arg"};
215   const int argc = static_cast<int>(fml::size(argv));
216 
217   auto cl = CommandLineFromArgcArgv(argc, argv);
218   EXPECT_TRUE(cl.has_argv0());
219   EXPECT_EQ(argv[0], cl.argv0());
220   std::vector<CommandLine::Option> expected_options = {
221       CommandLine::Option("flag", "value")};
222   EXPECT_EQ(expected_options, cl.options());
223   std::vector<std::string> expected_positional_args = {argv[2]};
224   EXPECT_EQ(expected_positional_args, cl.positional_args());
225   EXPECT_EQ("value", cl.GetOptionValueWithDefault("flag", "nope"));
226 }
227 
TEST(CommandLineTest,CommandLineFromInitializerList)228 TEST(CommandLineTest, CommandLineFromInitializerList) {
229   {
230     std::initializer_list<const char*> il = {"my_program", "--flag=value",
231                                              "arg"};
232     auto cl = CommandLineFromInitializerList(il);
233     EXPECT_TRUE(cl.has_argv0());
234     EXPECT_EQ("my_program", cl.argv0());
235     std::vector<CommandLine::Option> expected_options = {
236         CommandLine::Option("flag", "value")};
237     EXPECT_EQ(expected_options, cl.options());
238     std::vector<std::string> expected_positional_args = {"arg"};
239     EXPECT_EQ(expected_positional_args, cl.positional_args());
240     EXPECT_EQ("value", cl.GetOptionValueWithDefault("flag", "nope"));
241   }
242 
243   {
244     std::initializer_list<std::string> il = {"my_program", "--flag=value",
245                                              "arg"};
246     auto cl = CommandLineFromInitializerList(il);
247     EXPECT_TRUE(cl.has_argv0());
248     EXPECT_EQ("my_program", cl.argv0());
249     std::vector<CommandLine::Option> expected_options = {
250         CommandLine::Option("flag", "value")};
251     EXPECT_EQ(expected_options, cl.options());
252     std::vector<std::string> expected_positional_args = {"arg"};
253     EXPECT_EQ(expected_positional_args, cl.positional_args());
254     EXPECT_EQ("value", cl.GetOptionValueWithDefault("flag", "nope"));
255   }
256 }
257 
TEST(CommandLineTest,OddArguments)258 TEST(CommandLineTest, OddArguments) {
259   {
260     // Except for "arg", these are all options.
261     auto cl = CommandLineFromInitializerList(
262         {"my_program", "--=", "--=foo", "--bar=", "--==", "--===", "--==x",
263          "arg"});
264     EXPECT_TRUE(cl.has_argv0());
265     EXPECT_EQ("my_program", cl.argv0());
266     std::vector<CommandLine::Option> expected_options = {
267         CommandLine::Option("="),      CommandLine::Option("=foo"),
268         CommandLine::Option("bar"),    CommandLine::Option("="),
269         CommandLine::Option("=", "="), CommandLine::Option("=", "x")};
270     EXPECT_EQ(expected_options, cl.options());
271     std::vector<std::string> expected_positional_args = {"arg"};
272     EXPECT_EQ(expected_positional_args, cl.positional_args());
273   }
274 
275   // "-x" is an argument, not an options.
276   {
277     auto cl = CommandLineFromInitializerList({"", "-x"});
278     EXPECT_TRUE(cl.has_argv0());
279     EXPECT_EQ(std::string(), cl.argv0());
280     EXPECT_EQ(std::vector<CommandLine::Option>(), cl.options());
281     std::vector<std::string> expected_positional_args = {"-x"};
282     EXPECT_EQ(expected_positional_args, cl.positional_args());
283   }
284 
285   // Ditto for "-".
286   {
287     auto cl = CommandLineFromInitializerList({"", "-"});
288     EXPECT_TRUE(cl.has_argv0());
289     EXPECT_EQ(std::string(), cl.argv0());
290     EXPECT_EQ(std::vector<CommandLine::Option>(), cl.options());
291     std::vector<std::string> expected_positional_args = {"-"};
292     EXPECT_EQ(expected_positional_args, cl.positional_args());
293   }
294 
295   // "--" terminates option processing, but isn't an argument in the first
296   // occurrence.
297   {
298     auto cl = CommandLineFromInitializerList(
299         {"", "--flag=value", "--", "--not-a-flag", "arg", "--"});
300     EXPECT_TRUE(cl.has_argv0());
301     EXPECT_EQ(std::string(), cl.argv0());
302     std::vector<CommandLine::Option> expected_options = {
303         CommandLine::Option("flag", "value")};
304     std::vector<std::string> expected_positional_args = {"--not-a-flag", "arg",
305                                                          "--"};
306     EXPECT_EQ(expected_positional_args, cl.positional_args());
307   }
308 }
309 
TEST(CommandLineTest,MultipleOccurrencesOfOption)310 TEST(CommandLineTest, MultipleOccurrencesOfOption) {
311   auto cl = CommandLineFromInitializerList(
312       {"my_program", "--flag1=value1", "--flag2=value2", "--flag1=value3"});
313   std::vector<CommandLine::Option> expected_options = {
314       CommandLine::Option("flag1", "value1"),
315       CommandLine::Option("flag2", "value2"),
316       CommandLine::Option("flag1", "value3")};
317   EXPECT_EQ("value3", cl.GetOptionValueWithDefault("flag1", "nope"));
318   EXPECT_EQ("value2", cl.GetOptionValueWithDefault("flag2", "nope"));
319   std::vector<std::string_view> values = cl.GetOptionValues("flag1");
320   ASSERT_EQ(2u, values.size());
321   EXPECT_EQ("value1", values[0]);
322   EXPECT_EQ("value3", values[1]);
323 }
324 
325 // |cl1| and |cl2| should be not equal.
ExpectNotEqual(const char * message,std::initializer_list<std::string> c1,std::initializer_list<std::string> c2)326 void ExpectNotEqual(const char* message,
327                     std::initializer_list<std::string> c1,
328                     std::initializer_list<std::string> c2) {
329   SCOPED_TRACE(message);
330 
331   const auto cl1 = CommandLineFromInitializerList(c1);
332   const auto cl2 = CommandLineFromInitializerList(c2);
333 
334   // These are tautological.
335   EXPECT_TRUE(cl1 == cl1);
336   EXPECT_FALSE(cl1 != cl1);
337   EXPECT_TRUE(cl2 == cl2);
338   EXPECT_FALSE(cl2 != cl2);
339 
340   // These rely on |cl1| not being equal to |cl2|.
341   EXPECT_FALSE(cl1 == cl2);
342   EXPECT_TRUE(cl1 != cl2);
343   EXPECT_FALSE(cl2 == cl1);
344   EXPECT_TRUE(cl2 != cl1);
345 }
346 
ExpectEqual(const char * message,std::initializer_list<std::string> c1,std::initializer_list<std::string> c2)347 void ExpectEqual(const char* message,
348                  std::initializer_list<std::string> c1,
349                  std::initializer_list<std::string> c2) {
350   SCOPED_TRACE(message);
351 
352   const auto cl1 = CommandLineFromInitializerList(c1);
353   const auto cl2 = CommandLineFromInitializerList(c2);
354 
355   // These are tautological.
356   EXPECT_TRUE(cl1 == cl1);
357   EXPECT_FALSE(cl1 != cl1);
358   EXPECT_TRUE(cl2 == cl2);
359   EXPECT_FALSE(cl2 != cl2);
360 
361   // These rely on |cl1| being equal to |cl2|.
362   EXPECT_TRUE(cl1 == cl2);
363   EXPECT_FALSE(cl1 != cl2);
364   EXPECT_TRUE(cl2 == cl1);
365   EXPECT_FALSE(cl2 != cl1);
366 }
367 
TEST(CommandLineTest,ComparisonOperators)368 TEST(CommandLineTest, ComparisonOperators) {
369   ExpectNotEqual("1", {}, {""});
370   ExpectNotEqual("2", {"abc"}, {"def"});
371   ExpectNotEqual("3", {"abc", "--flag"}, {"abc"});
372   ExpectNotEqual("4", {"abc", "--flag1"}, {"abc", "--flag2"});
373   ExpectNotEqual("5", {"abc", "--flag1", "--flag2"}, {"abc", "--flag1"});
374   ExpectNotEqual("6", {"abc", "arg"}, {"abc"});
375   ExpectNotEqual("7", {"abc", "arg1"}, {"abc", "arg2"});
376   ExpectNotEqual("8", {"abc", "arg1", "arg2"}, {"abc", "arg1"});
377   ExpectNotEqual("9", {"abc", "--flag", "arg1"}, {"abc", "--flag", "arg2"});
378 
379   // However, the presence of an unnecessary "--" shouldn't affect what's
380   // constructed.
381   ExpectEqual("10", {"abc", "--flag", "arg"}, {"abc", "--flag", "--", "arg"});
382 }
383 
TEST(CommandLineTest,MoveAndCopy)384 TEST(CommandLineTest, MoveAndCopy) {
385   const auto cl = CommandLineFromInitializerList(
386       {"my_program", "--flag1=value1", "--flag2", "arg"});
387 
388   // Copy constructor.
389   CommandLine cl2(cl);
390   EXPECT_EQ(cl, cl2);
391   // Check that |option_index_| gets copied too.
392   EXPECT_EQ("value1", cl2.GetOptionValueWithDefault("flag1", "nope"));
393 
394   // Move constructor.
395   CommandLine cl3(std::move(cl2));
396   EXPECT_EQ(cl, cl3);
397   EXPECT_EQ("value1", cl3.GetOptionValueWithDefault("flag1", "nope"));
398 
399   // Copy assignment.
400   CommandLine cl4;
401   EXPECT_NE(cl, cl4);
402   cl4 = cl;
403   EXPECT_EQ(cl, cl4);
404   EXPECT_EQ("value1", cl4.GetOptionValueWithDefault("flag1", "nope"));
405 
406   // Move assignment.
407   CommandLine cl5;
408   EXPECT_NE(cl, cl5);
409   cl5 = std::move(cl4);
410   EXPECT_EQ(cl, cl5);
411   EXPECT_EQ("value1", cl5.GetOptionValueWithDefault("flag1", "nope"));
412 }
413 
ToArgvHelper(const char * message,std::initializer_list<std::string> c)414 void ToArgvHelper(const char* message, std::initializer_list<std::string> c) {
415   SCOPED_TRACE(message);
416   std::vector<std::string> argv = c;
417   auto cl = CommandLineFromInitializerList(c);
418   EXPECT_EQ(argv, CommandLineToArgv(cl));
419 }
420 
TEST(CommandLineTest,CommandLineToArgv)421 TEST(CommandLineTest, CommandLineToArgv) {
422   ToArgvHelper("1", {});
423   ToArgvHelper("2", {""});
424   ToArgvHelper("3", {"my_program"});
425   ToArgvHelper("4", {"my_program", "--flag"});
426   ToArgvHelper("5", {"my_program", "--flag1", "--flag2=value"});
427   ToArgvHelper("6", {"my_program", "arg"});
428   ToArgvHelper("7", {"my_program", "arg1", "arg2"});
429   ToArgvHelper("8", {"my_program", "--flag1", "--flag2=value", "arg1", "arg2"});
430   ToArgvHelper("9", {"my_program", "--flag", "--", "--not-a-flag"});
431   ToArgvHelper("10", {"my_program", "--flag", "arg", "--"});
432 
433   // However, |CommandLineToArgv()| will "strip" an unneeded "--".
434   {
435     auto cl = CommandLineFromInitializerList({"my_program", "--"});
436     std::vector<std::string> argv = {"my_program"};
437     EXPECT_EQ(argv, CommandLineToArgv(cl));
438   }
439   {
440     auto cl =
441         CommandLineFromInitializerList({"my_program", "--flag", "--", "arg"});
442     std::vector<std::string> argv = {"my_program", "--flag", "arg"};
443     EXPECT_EQ(argv, CommandLineToArgv(cl));
444   }
445 }
446 
447 }  // namespace
448 }  // namespace fml
449