1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <common/libs/utils/flag_parser.h>
18 
19 #include <map>
20 #include <optional>
21 #include <sstream>
22 #include <string>
23 #include <vector>
24 
25 #include <android-base/logging.h>
26 #include <android-base/strings.h>
27 #include <gtest/gtest.h>
28 #include <libxml/parser.h>
29 
30 #include "common/libs/utils/result_matchers.h"
31 #include "gmock/gmock-matchers.h"
32 
33 namespace cuttlefish {
34 
TEST(FlagParser,DuplicateAlias)35 TEST(FlagParser, DuplicateAlias) {
36   FlagAlias alias = {FlagAliasMode::kFlagExact, "--flag"};
37   ASSERT_DEATH({ Flag().Alias(alias).Alias(alias); }, "Duplicate flag alias");
38 }
39 
TEST(FlagParser,ConflictingAlias)40 TEST(FlagParser, ConflictingAlias) {
41   FlagAlias exact_alias = {FlagAliasMode::kFlagExact, "--flag"};
42   FlagAlias following_alias = {FlagAliasMode::kFlagConsumesFollowing, "--flag"};
43   ASSERT_DEATH({ Flag().Alias(exact_alias).Alias(following_alias); },
44                "Overlapping flag aliases");
45 }
46 
TEST(FlagParser,StringFlag)47 TEST(FlagParser, StringFlag) {
48   std::string value;
49   auto flag = GflagsCompatFlag("myflag", value);
50   ASSERT_THAT(flag.Parse({"-myflag=a"}), IsOk());
51   ASSERT_EQ(value, "a");
52   ASSERT_THAT(flag.Parse({"--myflag=b"}), IsOk());
53   ASSERT_EQ(value, "b");
54   ASSERT_THAT(flag.Parse({"-myflag", "c"}), IsOk());
55   ASSERT_EQ(value, "c");
56   ASSERT_THAT(flag.Parse({"--myflag", "d"}), IsOk());
57   ASSERT_EQ(value, "d");
58   ASSERT_THAT(flag.Parse({"--myflag="}), IsOk());
59   ASSERT_EQ(value, "");
60 }
61 
TEST(FlagParser,NormalizedStringFlag)62 TEST(FlagParser, NormalizedStringFlag) {
63   std::string value;
64   auto flag = GflagsCompatFlag("my_flag", value);
65   ASSERT_THAT(flag.Parse({"-my-flag=a"}), IsOk());
66   ASSERT_EQ(value, "a");
67   ASSERT_THAT(flag.Parse({"--my-flag=b"}), IsOk());
68   ASSERT_EQ(value, "b");
69   ASSERT_THAT(flag.Parse({"-my-flag", "c"}), IsOk());
70   ASSERT_EQ(value, "c");
71   ASSERT_THAT(flag.Parse({"--my-flag", "d"}), IsOk());
72   ASSERT_EQ(value, "d");
73   ASSERT_THAT(flag.Parse({"--my-flag="}), IsOk());
74   ASSERT_EQ(value, "");
75 }
76 
flagXml(const Flag & f)77 std::optional<std::map<std::string, std::string>> flagXml(const Flag& f) {
78   std::stringstream xml_stream;
79   if (!f.WriteGflagsCompatXml(xml_stream)) {
80     return {};
81   }
82   auto xml = xml_stream.str();
83   // Holds all memory for the parsed structure.
84   std::unique_ptr<xmlDoc, xmlFreeFunc> doc(
85       xmlReadMemory(xml.c_str(), xml.size(), nullptr, nullptr, 0), xmlFree);
86   if (!doc) {
87     return {};
88   }
89   xmlNodePtr root_element = xmlDocGetRootElement(doc.get());
90   std::map<std::string, std::string> elements_map;
91   for (auto elem = root_element->children; elem != nullptr; elem = elem->next) {
92     if (elem->type != xmlElementType::XML_ELEMENT_NODE) {
93       continue;
94     }
95     elements_map[(char*)elem->name] = "";
96     if (elem->children == nullptr) {
97       continue;
98     }
99     if (elem->children->type != XML_TEXT_NODE) {
100       continue;
101     }
102     elements_map[(char*)elem->name] = (char*)elem->children->content;
103   }
104   return elements_map;
105 }
106 
TEST(FlagParser,GflagsIncompatibleFlag)107 TEST(FlagParser, GflagsIncompatibleFlag) {
108   auto flag = Flag().Alias({FlagAliasMode::kFlagExact, "--flag"});
109   ASSERT_FALSE(flagXml(flag));
110 }
111 
TEST(FlagParser,StringFlagXml)112 TEST(FlagParser, StringFlagXml) {
113   std::string value = "somedefault";
114   auto flag = GflagsCompatFlag("myflag", value).Help("somehelp");
115   auto xml = flagXml(flag);
116   ASSERT_TRUE(xml);
117   ASSERT_NE((*xml)["file"], "");
118   ASSERT_EQ((*xml)["name"], "myflag");
119   ASSERT_EQ((*xml)["meaning"], "somehelp");
120   ASSERT_EQ((*xml)["default"], "somedefault");
121   ASSERT_EQ((*xml)["current"], "somedefault");
122   ASSERT_EQ((*xml)["type"], "string");
123 }
124 
TEST(FlagParser,RepeatedStringFlag)125 TEST(FlagParser, RepeatedStringFlag) {
126   std::string value;
127   auto flag = GflagsCompatFlag("myflag", value);
128   ASSERT_THAT(flag.Parse({"-myflag=a", "--myflag", "b"}), IsOk());
129   ASSERT_EQ(value, "b");
130 }
131 
TEST(FlagParser,RepeatedListFlag)132 TEST(FlagParser, RepeatedListFlag) {
133   std::vector<std::string> elems;
134   auto flag = GflagsCompatFlag("myflag");
135   flag.Setter([&elems](const FlagMatch& match) -> Result<void> {
136     elems.push_back(match.value);
137     return {};
138   });
139   ASSERT_THAT(flag.Parse({"-myflag=a", "--myflag", "b"}), IsOk());
140   ASSERT_EQ(elems, (std::vector<std::string>{"a", "b"}));
141 }
142 
TEST(FlagParser,FlagRemoval)143 TEST(FlagParser, FlagRemoval) {
144   std::string value;
145   auto flag = GflagsCompatFlag("myflag", value);
146   std::vector<std::string> flags = {"-myflag=a", "-otherflag=c"};
147   ASSERT_THAT(flag.Parse(flags), IsOk());
148   ASSERT_EQ(value, "a");
149   ASSERT_EQ(flags, std::vector<std::string>{"-otherflag=c"});
150   flags = {"-otherflag=a", "-myflag=c"};
151   ASSERT_THAT(flag.Parse(flags), IsOk());
152   ASSERT_EQ(value, "c");
153   ASSERT_EQ(flags, std::vector<std::string>{"-otherflag=a"});
154 }
155 
TEST(FlagParser,IntFlag)156 TEST(FlagParser, IntFlag) {
157   std::int32_t value = 0;
158   auto flag = GflagsCompatFlag("myflag", value);
159   ASSERT_THAT(flag.Parse({"-myflag=5"}), IsOk());
160   ASSERT_EQ(value, 5);
161   ASSERT_THAT(flag.Parse({"--myflag=6"}), IsOk());
162   ASSERT_EQ(value, 6);
163   ASSERT_THAT(flag.Parse({"-myflag", "7"}), IsOk());
164   ASSERT_EQ(value, 7);
165   ASSERT_THAT(flag.Parse({"--myflag", "8"}), IsOk());
166   ASSERT_EQ(value, 8);
167 }
168 
TEST(FlagParser,IntFlagXml)169 TEST(FlagParser, IntFlagXml) {
170   int value = 5;
171   auto flag = GflagsCompatFlag("myflag", value).Help("somehelp");
172   auto xml = flagXml(flag);
173   ASSERT_TRUE(xml);
174   ASSERT_NE((*xml)["file"], "");
175   ASSERT_EQ((*xml)["name"], "myflag");
176   ASSERT_EQ((*xml)["meaning"], "somehelp");
177   ASSERT_EQ((*xml)["default"], "5");
178   ASSERT_EQ((*xml)["current"], "5");
179   ASSERT_EQ((*xml)["type"], "string");
180 }
181 
TEST(FlagParser,BoolFlag)182 TEST(FlagParser, BoolFlag) {
183   bool value = false;
184   auto flag = GflagsCompatFlag("myflag", value);
185   ASSERT_THAT(flag.Parse({"-myflag"}), IsOk());
186   ASSERT_TRUE(value);
187 
188   value = false;
189   ASSERT_THAT(flag.Parse({"--myflag"}), IsOk());
190   ASSERT_TRUE(value);
191 
192   value = false;
193   ASSERT_THAT(flag.Parse({"-myflag=true"}), IsOk());
194   ASSERT_TRUE(value);
195 
196   value = false;
197   ASSERT_THAT(flag.Parse({"--myflag=true"}), IsOk());
198   ASSERT_TRUE(value);
199 
200   value = true;
201   ASSERT_THAT(flag.Parse({"-nomyflag"}), IsOk());
202   ASSERT_FALSE(value);
203 
204   value = true;
205   ASSERT_THAT(flag.Parse({"--nomyflag"}), IsOk());
206   ASSERT_FALSE(value);
207 
208   value = true;
209   ASSERT_THAT(flag.Parse({"-myflag=false"}), IsOk());
210   ASSERT_FALSE(value);
211 
212   value = true;
213   ASSERT_THAT(flag.Parse({"--myflag=false"}), IsOk());
214   ASSERT_FALSE(value);
215 
216   ASSERT_THAT(flag.Parse({"--myflag=nonsense"}), IsError());
217 }
218 
TEST(FlagParser,BoolFlagXml)219 TEST(FlagParser, BoolFlagXml) {
220   bool value = true;
221   auto flag = GflagsCompatFlag("myflag", value).Help("somehelp");
222   auto xml = flagXml(flag);
223   ASSERT_TRUE(xml);
224   ASSERT_NE((*xml)["file"], "");
225   ASSERT_EQ((*xml)["name"], "myflag");
226   ASSERT_EQ((*xml)["meaning"], "somehelp");
227   ASSERT_EQ((*xml)["default"], "true");
228   ASSERT_EQ((*xml)["current"], "true");
229   ASSERT_EQ((*xml)["type"], "bool");
230 }
231 
TEST(FlagParser,StringIntFlag)232 TEST(FlagParser, StringIntFlag) {
233   std::int32_t int_value = 0;
234   std::string string_value;
235   auto int_flag = GflagsCompatFlag("int", int_value);
236   auto string_flag = GflagsCompatFlag("string", string_value);
237   std::vector<Flag> flags = {int_flag, string_flag};
238   EXPECT_THAT(ConsumeFlags(flags, {"-int=5", "-string=a"}), IsOk());
239   ASSERT_EQ(int_value, 5);
240   ASSERT_EQ(string_value, "a");
241   EXPECT_THAT(ConsumeFlags(flags, {"--int=6", "--string=b"}), IsOk());
242   ASSERT_EQ(int_value, 6);
243   ASSERT_EQ(string_value, "b");
244   EXPECT_THAT(ConsumeFlags(flags, {"-int", "7", "-string", "c"}), IsOk());
245   ASSERT_EQ(int_value, 7);
246   ASSERT_EQ(string_value, "c");
247   EXPECT_THAT(ConsumeFlags(flags, {"--int", "8", "--string", "d"}), IsOk());
248   ASSERT_EQ(int_value, 8);
249   ASSERT_EQ(string_value, "d");
250 }
251 
TEST(FlagParser,StringVectorFlag)252 TEST(FlagParser, StringVectorFlag) {
253   std::vector<std::string> value;
254   auto flag = GflagsCompatFlag("myflag", value);
255 
256   ASSERT_THAT(flag.Parse({"--myflag="}), IsOk());
257   ASSERT_TRUE(value.empty());
258 
259   ASSERT_THAT(flag.Parse({"--myflag=foo"}), IsOk());
260   ASSERT_EQ(value, std::vector<std::string>({"foo"}));
261 
262   ASSERT_THAT(flag.Parse({"--myflag=foo,bar"}), IsOk());
263   ASSERT_EQ(value, std::vector<std::string>({"foo", "bar"}));
264 
265   ASSERT_THAT(flag.Parse({"--myflag=,bar"}), IsOk());
266   ASSERT_EQ(value, std::vector<std::string>({"", "bar"}));
267 
268   ASSERT_THAT(flag.Parse({"--myflag=foo,"}), IsOk());
269   ASSERT_EQ(value, std::vector<std::string>({"foo", ""}));
270 
271   ASSERT_THAT(flag.Parse({"--myflag=,"}), IsOk());
272   ASSERT_EQ(value, std::vector<std::string>({"", ""}));
273 }
274 
TEST(FlagParser,BoolVectorFlag)275 TEST(FlagParser, BoolVectorFlag) {
276   std::vector<bool> value;
277   bool default_value = true;
278   auto flag = GflagsCompatFlag("myflag", value, default_value);
279 
280   ASSERT_THAT(flag.Parse({"--myflag="}), IsOk());
281   ASSERT_TRUE(value.empty());
282 
283   ASSERT_THAT(flag.Parse({"--myflag=foo"}), IsError());
284   ASSERT_TRUE(value.empty());
285 
286   ASSERT_THAT(flag.Parse({"--myflag=true,bar"}), IsError());
287   ASSERT_TRUE(value.empty());
288 
289   ASSERT_THAT(flag.Parse({"--myflag=true"}), IsOk());
290   ASSERT_EQ(value, std::vector<bool>({true}));
291   ASSERT_TRUE(flagXml(flag));
292   ASSERT_EQ((*flagXml(flag))["default"], "true");
293 
294   ASSERT_THAT(flag.Parse({"--myflag=true,false"}), IsOk());
295   ASSERT_EQ(value, std::vector<bool>({true, false}));
296   ASSERT_TRUE(flagXml(flag));
297   ASSERT_EQ((*flagXml(flag))["default"], "true,false");
298 
299   ASSERT_THAT(flag.Parse({"--myflag=,false"}), IsOk());
300   ASSERT_EQ(value, std::vector<bool>({true, false}));
301   ASSERT_TRUE(flagXml(flag));
302   ASSERT_EQ((*flagXml(flag))["default"], "true,false");
303 
304   ASSERT_THAT(flag.Parse({"--myflag=true,"}), IsOk());
305   ASSERT_EQ(value, std::vector<bool>({true, true}));
306   ASSERT_TRUE(flagXml(flag));
307   ASSERT_EQ((*flagXml(flag))["default"], "true,true");
308 
309   ASSERT_THAT(flag.Parse({"--myflag=,"}), IsOk());
310   ASSERT_EQ(value, std::vector<bool>({true, true}));
311   ASSERT_TRUE(flagXml(flag));
312   ASSERT_EQ((*flagXml(flag))["default"], "true,true");
313 }
314 
TEST(FlagParser,InvalidStringFlag)315 TEST(FlagParser, InvalidStringFlag) {
316   std::string value;
317   auto flag = GflagsCompatFlag("myflag", value);
318   ASSERT_THAT(flag.Parse({"-myflag"}), IsError());
319   ASSERT_THAT(flag.Parse({"--myflag"}), IsError());
320 }
321 
TEST(FlagParser,InvalidIntFlag)322 TEST(FlagParser, InvalidIntFlag) {
323   int value;
324   auto flag = GflagsCompatFlag("myflag", value);
325   ASSERT_THAT(flag.Parse({"-myflag"}), IsError());
326   ASSERT_THAT(flag.Parse({"--myflag"}), IsError());
327   ASSERT_THAT(flag.Parse({"-myflag=abc"}), IsError());
328   ASSERT_THAT(flag.Parse({"--myflag=def"}), IsError());
329   ASSERT_THAT(flag.Parse({"-myflag", "abc"}), IsError());
330   ASSERT_THAT(flag.Parse({"--myflag", "def"}), IsError());
331 }
332 
TEST(FlagParser,VerbosityFlag)333 TEST(FlagParser, VerbosityFlag) {
334   android::base::LogSeverity value = android::base::VERBOSE;
335   auto flag = VerbosityFlag(value);
336   ASSERT_THAT(flag.Parse({"-verbosity=DEBUG"}), IsOk());
337   ASSERT_EQ(value, android::base::DEBUG);
338   ASSERT_THAT(flag.Parse({"--verbosity=INFO"}), IsOk());
339   ASSERT_EQ(value, android::base::INFO);
340   ASSERT_THAT(flag.Parse({"--verbosity=WARNING"}), IsOk());
341   ASSERT_EQ(value, android::base::WARNING);
342   ASSERT_THAT(flag.Parse({"--verbosity=ERROR"}), IsOk());
343   ASSERT_EQ(value, android::base::ERROR);
344   ASSERT_THAT(flag.Parse({"--verbosity=FATAL_WITHOUT_ABORT"}), IsOk());
345   ASSERT_EQ(value, android::base::FATAL_WITHOUT_ABORT);
346   ASSERT_THAT(flag.Parse({"--verbosity=FATAL"}), IsOk());
347   ASSERT_EQ(value, android::base::FATAL);
348   ASSERT_THAT(flag.Parse({"--verbosity=VERBOSE"}), IsOk());
349   ASSERT_EQ(value, android::base::VERBOSE);
350 }
351 
TEST(FlagParser,InvalidVerbosityFlag)352 TEST(FlagParser, InvalidVerbosityFlag) {
353   android::base::LogSeverity value = android::base::VERBOSE;
354   auto flag = VerbosityFlag(value);
355   ASSERT_THAT(flag.Parse({"-verbosity"}), IsError());
356   ASSERT_EQ(value, android::base::VERBOSE);
357   ASSERT_THAT(flag.Parse({"--verbosity"}), IsError());
358   ASSERT_EQ(value, android::base::VERBOSE);
359   ASSERT_THAT(flag.Parse({"-verbosity="}), IsError());
360   ASSERT_EQ(value, android::base::VERBOSE);
361   ASSERT_THAT(flag.Parse({"--verbosity="}), IsError());
362   ASSERT_EQ(value, android::base::VERBOSE);
363   ASSERT_THAT(flag.Parse({"-verbosity=not_a_severity"}), IsError());
364   ASSERT_EQ(value, android::base::VERBOSE);
365   ASSERT_THAT(flag.Parse({"--verbosity=not_a_severity"}), IsError());
366   ASSERT_EQ(value, android::base::VERBOSE);
367   ASSERT_THAT(flag.Parse({"-verbosity", "not_a_severity"}), IsError());
368   ASSERT_EQ(value, android::base::VERBOSE);
369   ASSERT_THAT(flag.Parse({"--verbosity", "not_a_severity"}), IsError());
370   ASSERT_EQ(value, android::base::VERBOSE);
371 }
372 
TEST(FlagParser,InvalidFlagGuard)373 TEST(FlagParser, InvalidFlagGuard) {
374   auto flag = InvalidFlagGuard();
375   ASSERT_THAT(flag.Parse({}), IsOk());
376   ASSERT_THAT(flag.Parse({"positional"}), IsOk());
377   ASSERT_THAT(flag.Parse({"positional", "positional2"}), IsOk());
378   ASSERT_THAT(flag.Parse({"-flag"}), IsError());
379   ASSERT_THAT(flag.Parse({"-"}), IsError());
380 }
381 
TEST(FlagParser,UnexpectedArgumentGuard)382 TEST(FlagParser, UnexpectedArgumentGuard) {
383   auto flag = UnexpectedArgumentGuard();
384   ASSERT_THAT(flag.Parse({}), IsOk());
385   ASSERT_THAT(flag.Parse({"positional"}), IsError());
386   ASSERT_THAT(flag.Parse({"positional", "positional2"}), IsError());
387   ASSERT_THAT(flag.Parse({"-flag"}), IsError());
388   ASSERT_THAT(flag.Parse({"-"}), IsError());
389 }
390 
TEST(FlagParser,EndOfOptionMark)391 TEST(FlagParser, EndOfOptionMark) {
392   std::vector<std::string> args{"-flag", "--", "-invalid_flag"};
393   bool flag = false;
394   std::vector<Flag> flags{GflagsCompatFlag("flag", flag), InvalidFlagGuard()};
395 
396   EXPECT_THAT(ConsumeFlags(flags, args), IsError());
397   EXPECT_THAT(ConsumeFlags(flags, args,
398                            /* recognize_end_of_option_mark */ true),
399               IsOk());
400   ASSERT_TRUE(flag);
401 }
402 
403 class FlagConsumesArbitraryTest : public ::testing::Test {
404  protected:
SetUp()405   void SetUp() override {
406     elems_.clear();
407     flag_ = Flag()
408                 .Alias({FlagAliasMode::kFlagConsumesArbitrary, "--flag"})
409                 .Setter([this](const FlagMatch& match) -> Result<void> {
410                   elems_.push_back(match.value);
411                   return {};
412                 });
413   }
414   Flag flag_;
415   std::vector<std::string> elems_;
416 };
417 
TEST_F(FlagConsumesArbitraryTest,NoValues)418 TEST_F(FlagConsumesArbitraryTest, NoValues) {
419   std::vector<std::string> inputs = {"--flag"};
420   ASSERT_THAT(flag_.Parse(inputs), IsOk());
421   ASSERT_EQ(inputs, (std::vector<std::string>{}));
422   ASSERT_EQ(elems_, (std::vector<std::string>{""}));
423 }
424 
TEST_F(FlagConsumesArbitraryTest,OneValue)425 TEST_F(FlagConsumesArbitraryTest, OneValue) {
426   std::vector<std::string> inputs = {"--flag", "value"};
427   ASSERT_THAT(flag_.Parse(inputs), IsOk());
428   ASSERT_EQ(inputs, (std::vector<std::string>{}));
429   ASSERT_EQ(elems_, (std::vector<std::string>{"value", ""}));
430 }
431 
TEST_F(FlagConsumesArbitraryTest,TwoValues)432 TEST_F(FlagConsumesArbitraryTest, TwoValues) {
433   std::vector<std::string> inputs = {"--flag", "value1", "value2"};
434   ASSERT_THAT(flag_.Parse(inputs), IsOk());
435   ASSERT_EQ(inputs, (std::vector<std::string>{}));
436   ASSERT_EQ(elems_, (std::vector<std::string>{"value1", "value2", ""}));
437 }
438 
TEST_F(FlagConsumesArbitraryTest,NoValuesOtherFlag)439 TEST_F(FlagConsumesArbitraryTest, NoValuesOtherFlag) {
440   std::vector<std::string> inputs = {"--flag", "--otherflag"};
441   ASSERT_THAT(flag_.Parse(inputs), IsOk());
442   ASSERT_EQ(inputs, (std::vector<std::string>{"--otherflag"}));
443   ASSERT_EQ(elems_, (std::vector<std::string>{""}));
444 }
445 
TEST_F(FlagConsumesArbitraryTest,OneValueOtherFlag)446 TEST_F(FlagConsumesArbitraryTest, OneValueOtherFlag) {
447   std::vector<std::string> inputs = {"--flag", "value", "--otherflag"};
448   ASSERT_THAT(flag_.Parse(inputs), IsOk());
449   ASSERT_EQ(inputs, (std::vector<std::string>{"--otherflag"}));
450   ASSERT_EQ(elems_, (std::vector<std::string>{"value", ""}));
451 }
452 
TEST_F(FlagConsumesArbitraryTest,TwoValuesOtherFlag)453 TEST_F(FlagConsumesArbitraryTest, TwoValuesOtherFlag) {
454   std::vector<std::string> inputs = {"--flag", "v1", "v2", "--otherflag"};
455   ASSERT_THAT(flag_.Parse(inputs), IsOk());
456   ASSERT_EQ(inputs, (std::vector<std::string>{"--otherflag"}));
457   ASSERT_EQ(elems_, (std::vector<std::string>{"v1", "v2", ""}));
458 }
459 
460 }  // namespace cuttlefish
461