// Copyright (c) 2023 Google LLC. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "tools/util/flags.h" #include "gmock/gmock.h" #ifdef UTIL_FLAGS_FLAG #undef UTIL_FLAGS_FLAG #define UTIL_FLAGS_FLAG(Type, Prefix, Name, Default, Required, IsShort) \ flags::Flag Name(Default); \ flags::FlagRegistration Name##_registration(Name, Prefix #Name, Required, \ IsShort) #else #error \ "UTIL_FLAGS_FLAG is not defined. Either flags.h is not included of the flag name changed." #endif class FlagTest : public ::testing::Test { protected: void SetUp() override { flags::FlagList::reset(); } }; TEST_F(FlagTest, NoFlags) { const char* argv[] = {"binary", nullptr}; EXPECT_TRUE(flags::Parse(argv)); EXPECT_EQ(flags::positional_arguments.size(), 0); } TEST_F(FlagTest, DashIsPositional) { const char* argv[] = {"binary", "-", nullptr}; EXPECT_TRUE(flags::Parse(argv)); EXPECT_EQ(flags::positional_arguments.size(), 1); EXPECT_EQ(flags::positional_arguments[0], "-"); } TEST_F(FlagTest, Positional) { const char* argv[] = {"binary", "A", "BCD", nullptr}; EXPECT_TRUE(flags::Parse(argv)); EXPECT_EQ(flags::positional_arguments.size(), 2); EXPECT_EQ(flags::positional_arguments[0], "A"); EXPECT_EQ(flags::positional_arguments[1], "BCD"); } TEST_F(FlagTest, MissingRequired) { FLAG_SHORT_bool(g, false, true); const char* argv[] = {"binary", nullptr}; EXPECT_FALSE(flags::Parse(argv)); EXPECT_EQ(flags::positional_arguments.size(), 0); } TEST_F(FlagTest, BooleanShortValue) { FLAG_SHORT_bool(g, false, false); const char* argv[] = {"binary", "-g", nullptr}; EXPECT_FALSE(g.value()); EXPECT_TRUE(flags::Parse(argv)); EXPECT_TRUE(g.value()); EXPECT_EQ(flags::positional_arguments.size(), 0); } TEST_F(FlagTest, BooleanShortDefaultValue) { FLAG_SHORT_bool(g, true, false); const char* argv[] = {"binary", nullptr}; EXPECT_TRUE(g.value()); EXPECT_TRUE(flags::Parse(argv)); EXPECT_TRUE(g.value()); } TEST_F(FlagTest, BooleanLongValueNotParsed) { FLAG_SHORT_bool(g, false, false); const char* argv[] = {"binary", "-g", "false", nullptr}; EXPECT_FALSE(g.value()); EXPECT_TRUE(flags::Parse(argv)); EXPECT_TRUE(g.value()); EXPECT_EQ(flags::positional_arguments.size(), 1); EXPECT_EQ(flags::positional_arguments[0], "false"); } TEST_F(FlagTest, BooleanLongSplitNotParsed) { FLAG_LONG_bool(foo, false, false); const char* argv[] = {"binary", "--foo", "true", nullptr}; EXPECT_FALSE(foo.value()); EXPECT_TRUE(flags::Parse(argv)); EXPECT_TRUE(foo.value()); EXPECT_EQ(flags::positional_arguments.size(), 1); EXPECT_EQ(flags::positional_arguments[0], "true"); } TEST_F(FlagTest, BooleanLongExplicitTrue) { FLAG_LONG_bool(foo, false, false); const char* argv[] = {"binary", "--foo=true", nullptr}; EXPECT_FALSE(foo.value()); EXPECT_TRUE(flags::Parse(argv)); EXPECT_TRUE(foo.value()); EXPECT_EQ(flags::positional_arguments.size(), 0); } TEST_F(FlagTest, BooleanLongExplicitFalse) { FLAG_LONG_bool(foo, false, false); const char* argv[] = {"binary", "--foo=false", nullptr}; EXPECT_FALSE(foo.value()); EXPECT_TRUE(flags::Parse(argv)); EXPECT_FALSE(foo.value()); EXPECT_EQ(flags::positional_arguments.size(), 0); } TEST_F(FlagTest, BooleanLongDefaultValue) { FLAG_LONG_bool(foo, true, false); const char* argv[] = {"binary", nullptr}; EXPECT_TRUE(foo.value()); EXPECT_TRUE(flags::Parse(argv)); EXPECT_TRUE(foo.value()); EXPECT_EQ(flags::positional_arguments.size(), 0); } TEST_F(FlagTest, BooleanLongDefaultValueCancelled) { FLAG_LONG_bool(foo, true, false); const char* argv[] = {"binary", "--foo=false", nullptr}; EXPECT_TRUE(foo.value()); EXPECT_TRUE(flags::Parse(argv)); EXPECT_FALSE(foo.value()); EXPECT_EQ(flags::positional_arguments.size(), 0); } TEST_F(FlagTest, StringFlagDefaultValue) { FLAG_SHORT_string(f, "default", false); const char* argv[] = {"binary", nullptr}; EXPECT_EQ(f.value(), "default"); EXPECT_TRUE(flags::Parse(argv)); EXPECT_EQ(f.value(), "default"); EXPECT_EQ(flags::positional_arguments.size(), 0); } TEST_F(FlagTest, StringFlagShortMissingString) { FLAG_SHORT_string(f, "default", false); const char* argv[] = {"binary", "-f", nullptr}; EXPECT_EQ(f.value(), "default"); EXPECT_FALSE(flags::Parse(argv)); } TEST_F(FlagTest, StringFlagDefault) { FLAG_SHORT_string(f, "default", false); const char* argv[] = {"binary", nullptr}; EXPECT_EQ(f.value(), "default"); EXPECT_TRUE(flags::Parse(argv)); EXPECT_EQ(f.value(), "default"); EXPECT_EQ(flags::positional_arguments.size(), 0); } TEST_F(FlagTest, StringFlagSet) { FLAG_SHORT_string(f, "default", false); const char* argv[] = {"binary", "-f", "toto", nullptr}; EXPECT_EQ(f.value(), "default"); EXPECT_TRUE(flags::Parse(argv)); EXPECT_EQ(f.value(), "toto"); EXPECT_EQ(flags::positional_arguments.size(), 0); } TEST_F(FlagTest, StringLongFlagSetSplit) { FLAG_LONG_string(foo, "default", false); const char* argv[] = {"binary", "--foo", "toto", nullptr}; EXPECT_EQ(foo.value(), "default"); EXPECT_TRUE(flags::Parse(argv)); EXPECT_EQ(foo.value(), "toto"); EXPECT_EQ(flags::positional_arguments.size(), 0); } TEST_F(FlagTest, StringLongFlagSetUnified) { FLAG_LONG_string(foo, "default", false); const char* argv[] = {"binary", "--foo=toto", nullptr}; EXPECT_EQ(foo.value(), "default"); EXPECT_TRUE(flags::Parse(argv)); EXPECT_EQ(foo.value(), "toto"); EXPECT_EQ(flags::positional_arguments.size(), 0); } TEST_F(FlagTest, StringLongFlagSetEmpty) { FLAG_LONG_string(foo, "default", false); const char* argv[] = {"binary", "--foo=", nullptr}; EXPECT_EQ(foo.value(), "default"); EXPECT_TRUE(flags::Parse(argv)); EXPECT_EQ(foo.value(), ""); EXPECT_EQ(flags::positional_arguments.size(), 0); } TEST_F(FlagTest, AllPositionalAfterDoubleDash) { FLAG_LONG_string(foo, "default", false); const char* argv[] = {"binary", "--", "--foo=toto", nullptr}; EXPECT_EQ(foo.value(), "default"); EXPECT_TRUE(flags::Parse(argv)); EXPECT_EQ(foo.value(), "default"); EXPECT_EQ(flags::positional_arguments.size(), 1); EXPECT_EQ(flags::positional_arguments[0], "--foo=toto"); } TEST_F(FlagTest, NothingAfterDoubleDash) { FLAG_LONG_string(foo, "default", false); const char* argv[] = {"binary", "--", nullptr}; EXPECT_EQ(foo.value(), "default"); EXPECT_TRUE(flags::Parse(argv)); EXPECT_EQ(foo.value(), "default"); EXPECT_EQ(flags::positional_arguments.size(), 0); } TEST_F(FlagTest, FlagDoubleSetNotAllowed) { FLAG_LONG_string(foo, "default", false); const char* argv[] = {"binary", "--foo=abc", "--foo=def", nullptr}; EXPECT_EQ(foo.value(), "default"); EXPECT_FALSE(flags::Parse(argv)); } TEST_F(FlagTest, MultipleFlags) { FLAG_LONG_string(foo, "default foo", false); FLAG_LONG_string(bar, "default_bar", false); const char* argv[] = {"binary", "--foo", "abc", "--bar=def", nullptr}; EXPECT_EQ(foo.value(), "default foo"); EXPECT_EQ(bar.value(), "default_bar"); EXPECT_TRUE(flags::Parse(argv)); EXPECT_EQ(foo.value(), "abc"); EXPECT_EQ(bar.value(), "def"); } TEST_F(FlagTest, MixedStringAndBool) { FLAG_LONG_string(foo, "default foo", false); FLAG_LONG_string(bar, "default_bar", false); FLAG_SHORT_bool(g, false, false); const char* argv[] = {"binary", "--foo", "abc", "-g", "--bar=def", nullptr}; EXPECT_EQ(foo.value(), "default foo"); EXPECT_EQ(bar.value(), "default_bar"); EXPECT_FALSE(g.value()); EXPECT_TRUE(flags::Parse(argv)); EXPECT_EQ(foo.value(), "abc"); EXPECT_EQ(bar.value(), "def"); EXPECT_TRUE(g.value()); } TEST_F(FlagTest, UintFlagDefaultValue) { FLAG_SHORT_uint(f, 18, false); const char* argv[] = {"binary", nullptr}; EXPECT_EQ(f.value(), 18); EXPECT_TRUE(flags::Parse(argv)); EXPECT_EQ(f.value(), 18); EXPECT_EQ(flags::positional_arguments.size(), 0); } TEST_F(FlagTest, UintFlagShortMissingValue) { FLAG_SHORT_uint(f, 19, false); const char* argv[] = {"binary", "-f", nullptr}; EXPECT_EQ(f.value(), 19); EXPECT_FALSE(flags::Parse(argv)); } TEST_F(FlagTest, UintFlagSet) { FLAG_SHORT_uint(f, 20, false); const char* argv[] = {"binary", "-f", "21", nullptr}; EXPECT_EQ(f.value(), 20); EXPECT_TRUE(flags::Parse(argv)); EXPECT_EQ(f.value(), 21); EXPECT_EQ(flags::positional_arguments.size(), 0); } TEST_F(FlagTest, UintLongFlagSetSplit) { FLAG_LONG_uint(foo, 22, false); const char* argv[] = {"binary", "--foo", "23", nullptr}; EXPECT_EQ(foo.value(), 22); EXPECT_TRUE(flags::Parse(argv)); EXPECT_EQ(foo.value(), 23); EXPECT_EQ(flags::positional_arguments.size(), 0); } TEST_F(FlagTest, UintLongFlagSetUnified) { FLAG_LONG_uint(foo, 24, false); const char* argv[] = {"binary", "--foo=25", nullptr}; EXPECT_EQ(foo.value(), 24); EXPECT_TRUE(flags::Parse(argv)); EXPECT_EQ(foo.value(), 25); EXPECT_EQ(flags::positional_arguments.size(), 0); } TEST_F(FlagTest, UintLongFlagSetEmptyIsWrong) { FLAG_LONG_uint(foo, 26, false); const char* argv[] = {"binary", "--foo=", nullptr}; EXPECT_EQ(foo.value(), 26); EXPECT_FALSE(flags::Parse(argv)); } TEST_F(FlagTest, UintLongFlagSetNegativeFails) { FLAG_LONG_uint(foo, 26, false); const char* argv[] = {"binary", "--foo=-2", nullptr}; EXPECT_EQ(foo.value(), 26); EXPECT_FALSE(flags::Parse(argv)); } TEST_F(FlagTest, UintLongFlagSetOverflowFails) { FLAG_LONG_uint(foo, 27, false); const char* argv[] = { "binary", "--foo=99999999999999999999999999999999999999999999999999999", nullptr}; EXPECT_EQ(foo.value(), 27); EXPECT_FALSE(flags::Parse(argv)); } TEST_F(FlagTest, UintLongFlagSetInvalidCharTrailing) { FLAG_LONG_uint(foo, 28, false); const char* argv[] = {"binary", "--foo=12A", nullptr}; EXPECT_EQ(foo.value(), 28); EXPECT_FALSE(flags::Parse(argv)); } TEST_F(FlagTest, UintLongFlagSetSpaces) { FLAG_LONG_uint(foo, 29, false); const char* argv[] = {"binary", "--foo= 12", nullptr}; EXPECT_EQ(foo.value(), 29); EXPECT_TRUE(flags::Parse(argv)); EXPECT_EQ(foo.value(), 12); EXPECT_EQ(flags::positional_arguments.size(), 0); } TEST_F(FlagTest, UintLongFlagSpacesOnly) { FLAG_LONG_uint(foo, 30, false); const char* argv[] = {"binary", "--foo= ", nullptr}; EXPECT_EQ(foo.value(), 30); EXPECT_FALSE(flags::Parse(argv)); } TEST_F(FlagTest, UintLongFlagSplitNumber) { FLAG_LONG_uint(foo, 31, false); const char* argv[] = {"binary", "--foo= 2 2", nullptr}; EXPECT_EQ(foo.value(), 31); EXPECT_FALSE(flags::Parse(argv)); } TEST_F(FlagTest, UintLongFlagHex) { FLAG_LONG_uint(foo, 32, false); const char* argv[] = {"binary", "--foo=0xA", nullptr}; EXPECT_EQ(foo.value(), 32); EXPECT_FALSE(flags::Parse(argv)); } TEST_F(FlagTest, UintLongFlagZeros) { FLAG_LONG_uint(foo, 33, false); const char* argv[] = {"binary", "--foo=0000", nullptr}; EXPECT_EQ(foo.value(), 33); EXPECT_TRUE(flags::Parse(argv)); EXPECT_EQ(foo.value(), 0); EXPECT_EQ(flags::positional_arguments.size(), 0); }