1 // Copyright (c) 2023 Google LLC.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "tools/util/flags.h"
16
17 #include "gmock/gmock.h"
18
19 #ifdef UTIL_FLAGS_FLAG
20 #undef UTIL_FLAGS_FLAG
21 #define UTIL_FLAGS_FLAG(Type, Prefix, Name, Default, Required, IsShort) \
22 flags::Flag<Type> Name(Default); \
23 flags::FlagRegistration Name##_registration(Name, Prefix #Name, Required, \
24 IsShort)
25 #else
26 #error \
27 "UTIL_FLAGS_FLAG is not defined. Either flags.h is not included of the flag name changed."
28 #endif
29
30 class FlagTest : public ::testing::Test {
31 protected:
SetUp()32 void SetUp() override { flags::FlagList::reset(); }
33 };
34
TEST_F(FlagTest,NoFlags)35 TEST_F(FlagTest, NoFlags) {
36 const char* argv[] = {"binary", nullptr};
37 EXPECT_TRUE(flags::Parse(argv));
38 EXPECT_EQ(flags::positional_arguments.size(), 0);
39 }
40
TEST_F(FlagTest,DashIsPositional)41 TEST_F(FlagTest, DashIsPositional) {
42 const char* argv[] = {"binary", "-", nullptr};
43
44 EXPECT_TRUE(flags::Parse(argv));
45
46 EXPECT_EQ(flags::positional_arguments.size(), 1);
47 EXPECT_EQ(flags::positional_arguments[0], "-");
48 }
49
TEST_F(FlagTest,Positional)50 TEST_F(FlagTest, Positional) {
51 const char* argv[] = {"binary", "A", "BCD", nullptr};
52
53 EXPECT_TRUE(flags::Parse(argv));
54
55 EXPECT_EQ(flags::positional_arguments.size(), 2);
56 EXPECT_EQ(flags::positional_arguments[0], "A");
57 EXPECT_EQ(flags::positional_arguments[1], "BCD");
58 }
59
TEST_F(FlagTest,MissingRequired)60 TEST_F(FlagTest, MissingRequired) {
61 FLAG_SHORT_bool(g, false, true);
62
63 const char* argv[] = {"binary", nullptr};
64 EXPECT_FALSE(flags::Parse(argv));
65 EXPECT_EQ(flags::positional_arguments.size(), 0);
66 }
67
TEST_F(FlagTest,BooleanShortValue)68 TEST_F(FlagTest, BooleanShortValue) {
69 FLAG_SHORT_bool(g, false, false);
70 const char* argv[] = {"binary", "-g", nullptr};
71 EXPECT_FALSE(g.value());
72
73 EXPECT_TRUE(flags::Parse(argv));
74
75 EXPECT_TRUE(g.value());
76 EXPECT_EQ(flags::positional_arguments.size(), 0);
77 }
78
TEST_F(FlagTest,BooleanShortDefaultValue)79 TEST_F(FlagTest, BooleanShortDefaultValue) {
80 FLAG_SHORT_bool(g, true, false);
81 const char* argv[] = {"binary", nullptr};
82 EXPECT_TRUE(g.value());
83
84 EXPECT_TRUE(flags::Parse(argv));
85
86 EXPECT_TRUE(g.value());
87 }
88
TEST_F(FlagTest,BooleanLongValueNotParsed)89 TEST_F(FlagTest, BooleanLongValueNotParsed) {
90 FLAG_SHORT_bool(g, false, false);
91 const char* argv[] = {"binary", "-g", "false", nullptr};
92 EXPECT_FALSE(g.value());
93
94 EXPECT_TRUE(flags::Parse(argv));
95
96 EXPECT_TRUE(g.value());
97 EXPECT_EQ(flags::positional_arguments.size(), 1);
98 EXPECT_EQ(flags::positional_arguments[0], "false");
99 }
100
TEST_F(FlagTest,BooleanLongSplitNotParsed)101 TEST_F(FlagTest, BooleanLongSplitNotParsed) {
102 FLAG_LONG_bool(foo, false, false);
103 const char* argv[] = {"binary", "--foo", "true", nullptr};
104 EXPECT_FALSE(foo.value());
105
106 EXPECT_TRUE(flags::Parse(argv));
107
108 EXPECT_TRUE(foo.value());
109 EXPECT_EQ(flags::positional_arguments.size(), 1);
110 EXPECT_EQ(flags::positional_arguments[0], "true");
111 }
112
TEST_F(FlagTest,BooleanLongExplicitTrue)113 TEST_F(FlagTest, BooleanLongExplicitTrue) {
114 FLAG_LONG_bool(foo, false, false);
115 const char* argv[] = {"binary", "--foo=true", nullptr};
116 EXPECT_FALSE(foo.value());
117
118 EXPECT_TRUE(flags::Parse(argv));
119
120 EXPECT_TRUE(foo.value());
121 EXPECT_EQ(flags::positional_arguments.size(), 0);
122 }
123
TEST_F(FlagTest,BooleanLongExplicitFalse)124 TEST_F(FlagTest, BooleanLongExplicitFalse) {
125 FLAG_LONG_bool(foo, false, false);
126 const char* argv[] = {"binary", "--foo=false", nullptr};
127 EXPECT_FALSE(foo.value());
128
129 EXPECT_TRUE(flags::Parse(argv));
130
131 EXPECT_FALSE(foo.value());
132 EXPECT_EQ(flags::positional_arguments.size(), 0);
133 }
134
TEST_F(FlagTest,BooleanLongDefaultValue)135 TEST_F(FlagTest, BooleanLongDefaultValue) {
136 FLAG_LONG_bool(foo, true, false);
137 const char* argv[] = {"binary", nullptr};
138 EXPECT_TRUE(foo.value());
139
140 EXPECT_TRUE(flags::Parse(argv));
141
142 EXPECT_TRUE(foo.value());
143 EXPECT_EQ(flags::positional_arguments.size(), 0);
144 }
145
TEST_F(FlagTest,BooleanLongDefaultValueCancelled)146 TEST_F(FlagTest, BooleanLongDefaultValueCancelled) {
147 FLAG_LONG_bool(foo, true, false);
148 const char* argv[] = {"binary", "--foo=false", nullptr};
149 EXPECT_TRUE(foo.value());
150
151 EXPECT_TRUE(flags::Parse(argv));
152
153 EXPECT_FALSE(foo.value());
154 EXPECT_EQ(flags::positional_arguments.size(), 0);
155 }
156
TEST_F(FlagTest,StringFlagDefaultValue)157 TEST_F(FlagTest, StringFlagDefaultValue) {
158 FLAG_SHORT_string(f, "default", false);
159 const char* argv[] = {"binary", nullptr};
160 EXPECT_EQ(f.value(), "default");
161
162 EXPECT_TRUE(flags::Parse(argv));
163 EXPECT_EQ(f.value(), "default");
164 EXPECT_EQ(flags::positional_arguments.size(), 0);
165 }
166
TEST_F(FlagTest,StringFlagShortMissingString)167 TEST_F(FlagTest, StringFlagShortMissingString) {
168 FLAG_SHORT_string(f, "default", false);
169 const char* argv[] = {"binary", "-f", nullptr};
170 EXPECT_EQ(f.value(), "default");
171
172 EXPECT_FALSE(flags::Parse(argv));
173 }
174
TEST_F(FlagTest,StringFlagDefault)175 TEST_F(FlagTest, StringFlagDefault) {
176 FLAG_SHORT_string(f, "default", false);
177 const char* argv[] = {"binary", nullptr};
178 EXPECT_EQ(f.value(), "default");
179
180 EXPECT_TRUE(flags::Parse(argv));
181
182 EXPECT_EQ(f.value(), "default");
183 EXPECT_EQ(flags::positional_arguments.size(), 0);
184 }
185
TEST_F(FlagTest,StringFlagSet)186 TEST_F(FlagTest, StringFlagSet) {
187 FLAG_SHORT_string(f, "default", false);
188 const char* argv[] = {"binary", "-f", "toto", nullptr};
189 EXPECT_EQ(f.value(), "default");
190
191 EXPECT_TRUE(flags::Parse(argv));
192
193 EXPECT_EQ(f.value(), "toto");
194 EXPECT_EQ(flags::positional_arguments.size(), 0);
195 }
196
TEST_F(FlagTest,StringLongFlagSetSplit)197 TEST_F(FlagTest, StringLongFlagSetSplit) {
198 FLAG_LONG_string(foo, "default", false);
199 const char* argv[] = {"binary", "--foo", "toto", nullptr};
200 EXPECT_EQ(foo.value(), "default");
201
202 EXPECT_TRUE(flags::Parse(argv));
203
204 EXPECT_EQ(foo.value(), "toto");
205 EXPECT_EQ(flags::positional_arguments.size(), 0);
206 }
207
TEST_F(FlagTest,StringLongFlagSetUnified)208 TEST_F(FlagTest, StringLongFlagSetUnified) {
209 FLAG_LONG_string(foo, "default", false);
210 const char* argv[] = {"binary", "--foo=toto", nullptr};
211 EXPECT_EQ(foo.value(), "default");
212
213 EXPECT_TRUE(flags::Parse(argv));
214
215 EXPECT_EQ(foo.value(), "toto");
216 EXPECT_EQ(flags::positional_arguments.size(), 0);
217 }
218
TEST_F(FlagTest,StringLongFlagSetEmpty)219 TEST_F(FlagTest, StringLongFlagSetEmpty) {
220 FLAG_LONG_string(foo, "default", false);
221 const char* argv[] = {"binary", "--foo=", nullptr};
222 EXPECT_EQ(foo.value(), "default");
223
224 EXPECT_TRUE(flags::Parse(argv));
225
226 EXPECT_EQ(foo.value(), "");
227 EXPECT_EQ(flags::positional_arguments.size(), 0);
228 }
229
TEST_F(FlagTest,AllPositionalAfterDoubleDash)230 TEST_F(FlagTest, AllPositionalAfterDoubleDash) {
231 FLAG_LONG_string(foo, "default", false);
232 const char* argv[] = {"binary", "--", "--foo=toto", nullptr};
233 EXPECT_EQ(foo.value(), "default");
234
235 EXPECT_TRUE(flags::Parse(argv));
236
237 EXPECT_EQ(foo.value(), "default");
238 EXPECT_EQ(flags::positional_arguments.size(), 1);
239 EXPECT_EQ(flags::positional_arguments[0], "--foo=toto");
240 }
241
TEST_F(FlagTest,NothingAfterDoubleDash)242 TEST_F(FlagTest, NothingAfterDoubleDash) {
243 FLAG_LONG_string(foo, "default", false);
244 const char* argv[] = {"binary", "--", nullptr};
245 EXPECT_EQ(foo.value(), "default");
246
247 EXPECT_TRUE(flags::Parse(argv));
248
249 EXPECT_EQ(foo.value(), "default");
250 EXPECT_EQ(flags::positional_arguments.size(), 0);
251 }
252
TEST_F(FlagTest,FlagDoubleSetNotAllowed)253 TEST_F(FlagTest, FlagDoubleSetNotAllowed) {
254 FLAG_LONG_string(foo, "default", false);
255 const char* argv[] = {"binary", "--foo=abc", "--foo=def", nullptr};
256 EXPECT_EQ(foo.value(), "default");
257
258 EXPECT_FALSE(flags::Parse(argv));
259 }
260
TEST_F(FlagTest,MultipleFlags)261 TEST_F(FlagTest, MultipleFlags) {
262 FLAG_LONG_string(foo, "default foo", false);
263 FLAG_LONG_string(bar, "default_bar", false);
264 const char* argv[] = {"binary", "--foo", "abc", "--bar=def", nullptr};
265 EXPECT_EQ(foo.value(), "default foo");
266 EXPECT_EQ(bar.value(), "default_bar");
267
268 EXPECT_TRUE(flags::Parse(argv));
269 EXPECT_EQ(foo.value(), "abc");
270 EXPECT_EQ(bar.value(), "def");
271 }
272
TEST_F(FlagTest,MixedStringAndBool)273 TEST_F(FlagTest, MixedStringAndBool) {
274 FLAG_LONG_string(foo, "default foo", false);
275 FLAG_LONG_string(bar, "default_bar", false);
276 FLAG_SHORT_bool(g, false, false);
277 const char* argv[] = {"binary", "--foo", "abc", "-g", "--bar=def", nullptr};
278 EXPECT_EQ(foo.value(), "default foo");
279 EXPECT_EQ(bar.value(), "default_bar");
280 EXPECT_FALSE(g.value());
281
282 EXPECT_TRUE(flags::Parse(argv));
283 EXPECT_EQ(foo.value(), "abc");
284 EXPECT_EQ(bar.value(), "def");
285 EXPECT_TRUE(g.value());
286 }
287
TEST_F(FlagTest,UintFlagDefaultValue)288 TEST_F(FlagTest, UintFlagDefaultValue) {
289 FLAG_SHORT_uint(f, 18, false);
290 const char* argv[] = {"binary", nullptr};
291 EXPECT_EQ(f.value(), 18);
292
293 EXPECT_TRUE(flags::Parse(argv));
294 EXPECT_EQ(f.value(), 18);
295 EXPECT_EQ(flags::positional_arguments.size(), 0);
296 }
297
TEST_F(FlagTest,UintFlagShortMissingValue)298 TEST_F(FlagTest, UintFlagShortMissingValue) {
299 FLAG_SHORT_uint(f, 19, false);
300 const char* argv[] = {"binary", "-f", nullptr};
301 EXPECT_EQ(f.value(), 19);
302
303 EXPECT_FALSE(flags::Parse(argv));
304 }
305
TEST_F(FlagTest,UintFlagSet)306 TEST_F(FlagTest, UintFlagSet) {
307 FLAG_SHORT_uint(f, 20, false);
308 const char* argv[] = {"binary", "-f", "21", nullptr};
309 EXPECT_EQ(f.value(), 20);
310
311 EXPECT_TRUE(flags::Parse(argv));
312
313 EXPECT_EQ(f.value(), 21);
314 EXPECT_EQ(flags::positional_arguments.size(), 0);
315 }
316
TEST_F(FlagTest,UintLongFlagSetSplit)317 TEST_F(FlagTest, UintLongFlagSetSplit) {
318 FLAG_LONG_uint(foo, 22, false);
319 const char* argv[] = {"binary", "--foo", "23", nullptr};
320 EXPECT_EQ(foo.value(), 22);
321
322 EXPECT_TRUE(flags::Parse(argv));
323
324 EXPECT_EQ(foo.value(), 23);
325 EXPECT_EQ(flags::positional_arguments.size(), 0);
326 }
327
TEST_F(FlagTest,UintLongFlagSetUnified)328 TEST_F(FlagTest, UintLongFlagSetUnified) {
329 FLAG_LONG_uint(foo, 24, false);
330 const char* argv[] = {"binary", "--foo=25", nullptr};
331 EXPECT_EQ(foo.value(), 24);
332
333 EXPECT_TRUE(flags::Parse(argv));
334
335 EXPECT_EQ(foo.value(), 25);
336 EXPECT_EQ(flags::positional_arguments.size(), 0);
337 }
338
TEST_F(FlagTest,UintLongFlagSetEmptyIsWrong)339 TEST_F(FlagTest, UintLongFlagSetEmptyIsWrong) {
340 FLAG_LONG_uint(foo, 26, false);
341 const char* argv[] = {"binary", "--foo=", nullptr};
342 EXPECT_EQ(foo.value(), 26);
343
344 EXPECT_FALSE(flags::Parse(argv));
345 }
346
TEST_F(FlagTest,UintLongFlagSetNegativeFails)347 TEST_F(FlagTest, UintLongFlagSetNegativeFails) {
348 FLAG_LONG_uint(foo, 26, false);
349 const char* argv[] = {"binary", "--foo=-2", nullptr};
350 EXPECT_EQ(foo.value(), 26);
351
352 EXPECT_FALSE(flags::Parse(argv));
353 }
354
TEST_F(FlagTest,UintLongFlagSetOverflowFails)355 TEST_F(FlagTest, UintLongFlagSetOverflowFails) {
356 FLAG_LONG_uint(foo, 27, false);
357 const char* argv[] = {
358 "binary", "--foo=99999999999999999999999999999999999999999999999999999",
359 nullptr};
360 EXPECT_EQ(foo.value(), 27);
361
362 EXPECT_FALSE(flags::Parse(argv));
363 }
364
TEST_F(FlagTest,UintLongFlagSetInvalidCharTrailing)365 TEST_F(FlagTest, UintLongFlagSetInvalidCharTrailing) {
366 FLAG_LONG_uint(foo, 28, false);
367 const char* argv[] = {"binary", "--foo=12A", nullptr};
368 EXPECT_EQ(foo.value(), 28);
369
370 EXPECT_FALSE(flags::Parse(argv));
371 }
372
TEST_F(FlagTest,UintLongFlagSetSpaces)373 TEST_F(FlagTest, UintLongFlagSetSpaces) {
374 FLAG_LONG_uint(foo, 29, false);
375 const char* argv[] = {"binary", "--foo= 12", nullptr};
376 EXPECT_EQ(foo.value(), 29);
377
378 EXPECT_TRUE(flags::Parse(argv));
379 EXPECT_EQ(foo.value(), 12);
380 EXPECT_EQ(flags::positional_arguments.size(), 0);
381 }
382
TEST_F(FlagTest,UintLongFlagSpacesOnly)383 TEST_F(FlagTest, UintLongFlagSpacesOnly) {
384 FLAG_LONG_uint(foo, 30, false);
385 const char* argv[] = {"binary", "--foo= ", nullptr};
386 EXPECT_EQ(foo.value(), 30);
387
388 EXPECT_FALSE(flags::Parse(argv));
389 }
390
TEST_F(FlagTest,UintLongFlagSplitNumber)391 TEST_F(FlagTest, UintLongFlagSplitNumber) {
392 FLAG_LONG_uint(foo, 31, false);
393 const char* argv[] = {"binary", "--foo= 2 2", nullptr};
394 EXPECT_EQ(foo.value(), 31);
395
396 EXPECT_FALSE(flags::Parse(argv));
397 }
398
TEST_F(FlagTest,UintLongFlagHex)399 TEST_F(FlagTest, UintLongFlagHex) {
400 FLAG_LONG_uint(foo, 32, false);
401 const char* argv[] = {"binary", "--foo=0xA", nullptr};
402 EXPECT_EQ(foo.value(), 32);
403
404 EXPECT_FALSE(flags::Parse(argv));
405 }
406
TEST_F(FlagTest,UintLongFlagZeros)407 TEST_F(FlagTest, UintLongFlagZeros) {
408 FLAG_LONG_uint(foo, 33, false);
409 const char* argv[] = {"binary", "--foo=0000", nullptr};
410 EXPECT_EQ(foo.value(), 33);
411
412 EXPECT_TRUE(flags::Parse(argv));
413 EXPECT_EQ(foo.value(), 0);
414 EXPECT_EQ(flags::positional_arguments.size(), 0);
415 }
416