Flutter Engine
command_line_unittest.cc
Go to the documentation of this file.
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 
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 
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 
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 
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;
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;
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;
138  argv.cbegin(), argv.cend(), &sub_first);
139  EXPECT_EQ(argv.cbegin() + 2, sub_first);
140  }
141 }
142 
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 
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 
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 
258 TEST(CommandLineTest, OddArguments) {
259  {
260  // Except for "arg", these are all options.
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  {
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 
310 TEST(CommandLineTest, MultipleOccurrencesOfOption) {
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.
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 
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 
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 
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 
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 
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
std::vector< std::string > CommandLineToArgv(const CommandLine &command_line)
CommandLine CommandLineFromArgcArgv(int argc, const char *const *argv)
Definition: command_line.h:222
constexpr std::size_t size(T(&array)[N])
Definition: size.h:13
CommandLine CommandLineFromIteratorsFindFirstPositionalArg(InputIterator first, InputIterator last, InputIterator *first_positional_arg)
Definition: command_line.h:181
Definition: ascii_trie.cc:9
uint8_t value
std::vector< std::string_view > GetOptionValues(std::string_view name) const
Definition: command_line.cc:61
TEST(MessageTest, CanEncodeTriviallyCopyableTypes)
CommandLine CommandLineFromInitializerList(std::initializer_list< StringType > argv)
Definition: command_line.h:229
CommandLine CommandLineFromIterators(InputIterator first, InputIterator last)
Definition: command_line.h:201