// // Copyright 2019 The Abseil Authors. // // 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 // // https://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 "absl/flags/flag.h" #include #include #include #include #include // NOLINT #include #include "gtest/gtest.h" #include "absl/base/attributes.h" #include "absl/base/internal/raw_logging.h" #include "absl/base/macros.h" #include "absl/flags/config.h" #include "absl/flags/declare.h" #include "absl/flags/internal/flag.h" #include "absl/flags/marshalling.h" #include "absl/flags/parse.h" #include "absl/flags/reflection.h" #include "absl/flags/usage_config.h" #include "absl/numeric/int128.h" #include "absl/strings/match.h" #include "absl/strings/numbers.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_split.h" #include "absl/strings/string_view.h" #include "absl/time/clock.h" #include "absl/time/time.h" #include "absl/types/optional.h" ABSL_DECLARE_FLAG(int64_t, mistyped_int_flag); ABSL_DECLARE_FLAG(std::vector, mistyped_string_flag); namespace { namespace flags = absl::flags_internal; std::string TestHelpMsg() { return "dynamic help"; } #if defined(_MSC_VER) && !defined(__clang__) std::string TestLiteralHelpMsg() { return "literal help"; } #endif template void TestMakeDflt(void* dst) { new (dst) T{}; } void TestCallback() {} struct UDT { UDT() = default; UDT(const UDT&) = default; UDT& operator=(const UDT&) = default; }; bool AbslParseFlag(absl::string_view, UDT*, std::string*) { return true; } std::string AbslUnparseFlag(const UDT&) { return ""; } class FlagTest : public testing::Test { protected: static void SetUpTestSuite() { // Install a function to normalize filenames before this test is run. absl::FlagsUsageConfig default_config; default_config.normalize_filename = &FlagTest::NormalizeFileName; absl::SetFlagsUsageConfig(default_config); } private: static std::string NormalizeFileName(absl::string_view fname) { #ifdef _WIN32 std::string normalized(fname); std::replace(normalized.begin(), normalized.end(), '\\', '/'); fname = normalized; #endif return std::string(fname); } absl::FlagSaver flag_saver_; }; struct S1 { S1() = default; S1(const S1&) = default; int32_t f1; int64_t f2; }; struct S2 { S2() = default; S2(const S2&) = default; int64_t f1; double f2; }; TEST_F(FlagTest, Traits) { EXPECT_EQ(flags::StorageKind(), flags::FlagValueStorageKind::kValueAndInitBit); EXPECT_EQ(flags::StorageKind(), flags::FlagValueStorageKind::kValueAndInitBit); EXPECT_EQ(flags::StorageKind(), flags::FlagValueStorageKind::kOneWordAtomic); EXPECT_EQ(flags::StorageKind(), flags::FlagValueStorageKind::kOneWordAtomic); EXPECT_EQ(flags::StorageKind(), flags::FlagValueStorageKind::kSequenceLocked); EXPECT_EQ(flags::StorageKind(), flags::FlagValueStorageKind::kSequenceLocked); // Make sure absl::Duration uses the sequence-locked code path. MSVC 2015 // doesn't consider absl::Duration to be trivially-copyable so we just // restrict this to clang as it seems to be a well-behaved compiler. #ifdef __clang__ EXPECT_EQ(flags::StorageKind(), flags::FlagValueStorageKind::kSequenceLocked); #endif EXPECT_EQ(flags::StorageKind(), flags::FlagValueStorageKind::kHeapAllocated); EXPECT_EQ(flags::StorageKind>(), flags::FlagValueStorageKind::kHeapAllocated); EXPECT_EQ(flags::StorageKind(), flags::FlagValueStorageKind::kSequenceLocked); EXPECT_EQ(flags::StorageKind(), flags::FlagValueStorageKind::kSequenceLocked); } // -------------------------------------------------------------------- constexpr flags::FlagHelpArg help_arg{flags::FlagHelpMsg("literal help"), flags::FlagHelpKind::kLiteral}; using String = std::string; using int128 = absl::int128; using uint128 = absl::uint128; #define DEFINE_CONSTRUCTED_FLAG(T, dflt, dflt_kind) \ constexpr flags::FlagDefaultArg f1default##T{ \ flags::FlagDefaultSrc{dflt}, flags::FlagDefaultKind::dflt_kind}; \ constexpr absl::Flag f1##T{"f1", "file", help_arg, f1default##T}; \ ABSL_CONST_INIT absl::Flag f2##T { \ "f2", "file", \ {flags::FlagHelpMsg(&TestHelpMsg), flags::FlagHelpKind::kGenFunc}, \ flags::FlagDefaultArg { \ flags::FlagDefaultSrc(&TestMakeDflt), \ flags::FlagDefaultKind::kGenFunc \ } \ } DEFINE_CONSTRUCTED_FLAG(bool, true, kOneWord); DEFINE_CONSTRUCTED_FLAG(int16_t, 1, kOneWord); DEFINE_CONSTRUCTED_FLAG(uint16_t, 2, kOneWord); DEFINE_CONSTRUCTED_FLAG(int32_t, 3, kOneWord); DEFINE_CONSTRUCTED_FLAG(uint32_t, 4, kOneWord); DEFINE_CONSTRUCTED_FLAG(int64_t, 5, kOneWord); DEFINE_CONSTRUCTED_FLAG(uint64_t, 6, kOneWord); DEFINE_CONSTRUCTED_FLAG(float, 7.8, kOneWord); DEFINE_CONSTRUCTED_FLAG(double, 9.10, kOneWord); DEFINE_CONSTRUCTED_FLAG(String, &TestMakeDflt, kGenFunc); DEFINE_CONSTRUCTED_FLAG(UDT, &TestMakeDflt, kGenFunc); DEFINE_CONSTRUCTED_FLAG(int128, 13, kGenFunc); DEFINE_CONSTRUCTED_FLAG(uint128, 14, kGenFunc); template bool TestConstructionFor(const absl::Flag& f1, absl::Flag& f2) { EXPECT_EQ(absl::GetFlagReflectionHandle(f1).Name(), "f1"); EXPECT_EQ(absl::GetFlagReflectionHandle(f1).Help(), "literal help"); EXPECT_EQ(absl::GetFlagReflectionHandle(f1).Filename(), "file"); flags::FlagRegistrar(ABSL_FLAG_IMPL_FLAG_PTR(f2), nullptr) .OnUpdate(TestCallback); EXPECT_EQ(absl::GetFlagReflectionHandle(f2).Name(), "f2"); EXPECT_EQ(absl::GetFlagReflectionHandle(f2).Help(), "dynamic help"); EXPECT_EQ(absl::GetFlagReflectionHandle(f2).Filename(), "file"); return true; } #define TEST_CONSTRUCTED_FLAG(T) TestConstructionFor(f1##T, f2##T); TEST_F(FlagTest, TestConstruction) { TEST_CONSTRUCTED_FLAG(bool); TEST_CONSTRUCTED_FLAG(int16_t); TEST_CONSTRUCTED_FLAG(uint16_t); TEST_CONSTRUCTED_FLAG(int32_t); TEST_CONSTRUCTED_FLAG(uint32_t); TEST_CONSTRUCTED_FLAG(int64_t); TEST_CONSTRUCTED_FLAG(uint64_t); TEST_CONSTRUCTED_FLAG(float); TEST_CONSTRUCTED_FLAG(double); TEST_CONSTRUCTED_FLAG(String); TEST_CONSTRUCTED_FLAG(UDT); TEST_CONSTRUCTED_FLAG(int128); TEST_CONSTRUCTED_FLAG(uint128); } // -------------------------------------------------------------------- } // namespace ABSL_DECLARE_FLAG(bool, test_flag_01); ABSL_DECLARE_FLAG(int, test_flag_02); ABSL_DECLARE_FLAG(int16_t, test_flag_03); ABSL_DECLARE_FLAG(uint16_t, test_flag_04); ABSL_DECLARE_FLAG(int32_t, test_flag_05); ABSL_DECLARE_FLAG(uint32_t, test_flag_06); ABSL_DECLARE_FLAG(int64_t, test_flag_07); ABSL_DECLARE_FLAG(uint64_t, test_flag_08); ABSL_DECLARE_FLAG(double, test_flag_09); ABSL_DECLARE_FLAG(float, test_flag_10); ABSL_DECLARE_FLAG(std::string, test_flag_11); ABSL_DECLARE_FLAG(absl::Duration, test_flag_12); ABSL_DECLARE_FLAG(absl::int128, test_flag_13); ABSL_DECLARE_FLAG(absl::uint128, test_flag_14); namespace { TEST_F(FlagTest, TestFlagDeclaration) { #if ABSL_FLAGS_STRIP_NAMES GTEST_SKIP() << "This test requires flag names to be present"; #endif // test that we can access flag objects. EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_01).Name(), "test_flag_01"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_02).Name(), "test_flag_02"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_03).Name(), "test_flag_03"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_04).Name(), "test_flag_04"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_05).Name(), "test_flag_05"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_06).Name(), "test_flag_06"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_07).Name(), "test_flag_07"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_08).Name(), "test_flag_08"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_09).Name(), "test_flag_09"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_10).Name(), "test_flag_10"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_11).Name(), "test_flag_11"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_12).Name(), "test_flag_12"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_13).Name(), "test_flag_13"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_14).Name(), "test_flag_14"); } } // namespace #if ABSL_FLAGS_STRIP_NAMES // The intent of this helper struct and an expression below is to make sure that // in the configuration where ABSL_FLAGS_STRIP_NAMES=1 registrar construction // (in cases of no Tail calls like OnUpdate) is constexpr and thus can and // should be completely optimized away, thus avoiding the cost/overhead of // static initializers. struct VerifyConsteval { friend consteval flags::FlagRegistrarEmpty operator+( flags::FlagRegistrarEmpty, VerifyConsteval) { return {}; } }; ABSL_FLAG(int, test_registrar_const_init, 0, "") + VerifyConsteval(); #endif // -------------------------------------------------------------------- ABSL_FLAG(bool, test_flag_01, true, "test flag 01"); ABSL_FLAG(int, test_flag_02, 1234, "test flag 02"); ABSL_FLAG(int16_t, test_flag_03, -34, "test flag 03"); ABSL_FLAG(uint16_t, test_flag_04, 189, "test flag 04"); ABSL_FLAG(int32_t, test_flag_05, 10765, "test flag 05"); ABSL_FLAG(uint32_t, test_flag_06, 40000, "test flag 06"); ABSL_FLAG(int64_t, test_flag_07, -1234567, "test flag 07"); ABSL_FLAG(uint64_t, test_flag_08, 9876543, "test flag 08"); ABSL_FLAG(double, test_flag_09, -9.876e-50, "test flag 09"); ABSL_FLAG(float, test_flag_10, 1.234e12f, "test flag 10"); ABSL_FLAG(std::string, test_flag_11, "", "test flag 11"); ABSL_FLAG(absl::Duration, test_flag_12, absl::Minutes(10), "test flag 12"); ABSL_FLAG(absl::int128, test_flag_13, absl::MakeInt128(-1, 0), "test flag 13"); ABSL_FLAG(absl::uint128, test_flag_14, absl::MakeUint128(0, 0xFFFAAABBBCCCDDD), "test flag 14"); namespace { TEST_F(FlagTest, TestFlagDefinition) { #if ABSL_FLAGS_STRIP_NAMES GTEST_SKIP() << "This test requires flag names to be present"; #endif absl::string_view expected_file_name = "absl/flags/flag_test.cc"; EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_01).Name(), "test_flag_01"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_01).Help(), "test flag 01"); EXPECT_TRUE(absl::EndsWith( absl::GetFlagReflectionHandle(FLAGS_test_flag_01).Filename(), expected_file_name)) << absl::GetFlagReflectionHandle(FLAGS_test_flag_01).Filename(); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_02).Name(), "test_flag_02"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_02).Help(), "test flag 02"); EXPECT_TRUE(absl::EndsWith( absl::GetFlagReflectionHandle(FLAGS_test_flag_02).Filename(), expected_file_name)) << absl::GetFlagReflectionHandle(FLAGS_test_flag_02).Filename(); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_03).Name(), "test_flag_03"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_03).Help(), "test flag 03"); EXPECT_TRUE(absl::EndsWith( absl::GetFlagReflectionHandle(FLAGS_test_flag_03).Filename(), expected_file_name)) << absl::GetFlagReflectionHandle(FLAGS_test_flag_03).Filename(); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_04).Name(), "test_flag_04"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_04).Help(), "test flag 04"); EXPECT_TRUE(absl::EndsWith( absl::GetFlagReflectionHandle(FLAGS_test_flag_04).Filename(), expected_file_name)) << absl::GetFlagReflectionHandle(FLAGS_test_flag_04).Filename(); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_05).Name(), "test_flag_05"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_05).Help(), "test flag 05"); EXPECT_TRUE(absl::EndsWith( absl::GetFlagReflectionHandle(FLAGS_test_flag_05).Filename(), expected_file_name)) << absl::GetFlagReflectionHandle(FLAGS_test_flag_05).Filename(); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_06).Name(), "test_flag_06"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_06).Help(), "test flag 06"); EXPECT_TRUE(absl::EndsWith( absl::GetFlagReflectionHandle(FLAGS_test_flag_06).Filename(), expected_file_name)) << absl::GetFlagReflectionHandle(FLAGS_test_flag_06).Filename(); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_07).Name(), "test_flag_07"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_07).Help(), "test flag 07"); EXPECT_TRUE(absl::EndsWith( absl::GetFlagReflectionHandle(FLAGS_test_flag_07).Filename(), expected_file_name)) << absl::GetFlagReflectionHandle(FLAGS_test_flag_07).Filename(); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_08).Name(), "test_flag_08"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_08).Help(), "test flag 08"); EXPECT_TRUE(absl::EndsWith( absl::GetFlagReflectionHandle(FLAGS_test_flag_08).Filename(), expected_file_name)) << absl::GetFlagReflectionHandle(FLAGS_test_flag_08).Filename(); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_09).Name(), "test_flag_09"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_09).Help(), "test flag 09"); EXPECT_TRUE(absl::EndsWith( absl::GetFlagReflectionHandle(FLAGS_test_flag_09).Filename(), expected_file_name)) << absl::GetFlagReflectionHandle(FLAGS_test_flag_09).Filename(); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_10).Name(), "test_flag_10"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_10).Help(), "test flag 10"); EXPECT_TRUE(absl::EndsWith( absl::GetFlagReflectionHandle(FLAGS_test_flag_10).Filename(), expected_file_name)) << absl::GetFlagReflectionHandle(FLAGS_test_flag_10).Filename(); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_11).Name(), "test_flag_11"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_11).Help(), "test flag 11"); EXPECT_TRUE(absl::EndsWith( absl::GetFlagReflectionHandle(FLAGS_test_flag_11).Filename(), expected_file_name)) << absl::GetFlagReflectionHandle(FLAGS_test_flag_11).Filename(); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_12).Name(), "test_flag_12"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_12).Help(), "test flag 12"); EXPECT_TRUE(absl::EndsWith( absl::GetFlagReflectionHandle(FLAGS_test_flag_12).Filename(), expected_file_name)) << absl::GetFlagReflectionHandle(FLAGS_test_flag_12).Filename(); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_13).Name(), "test_flag_13"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_13).Help(), "test flag 13"); EXPECT_TRUE(absl::EndsWith( absl::GetFlagReflectionHandle(FLAGS_test_flag_13).Filename(), expected_file_name)) << absl::GetFlagReflectionHandle(FLAGS_test_flag_13).Filename(); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_14).Name(), "test_flag_14"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_14).Help(), "test flag 14"); EXPECT_TRUE(absl::EndsWith( absl::GetFlagReflectionHandle(FLAGS_test_flag_14).Filename(), expected_file_name)) << absl::GetFlagReflectionHandle(FLAGS_test_flag_14).Filename(); } // -------------------------------------------------------------------- TEST_F(FlagTest, TestDefault) { EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_01).DefaultValue(), "true"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_02).DefaultValue(), "1234"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_03).DefaultValue(), "-34"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_04).DefaultValue(), "189"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_05).DefaultValue(), "10765"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_06).DefaultValue(), "40000"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_07).DefaultValue(), "-1234567"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_08).DefaultValue(), "9876543"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_09).DefaultValue(), "-9.876e-50"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_10).DefaultValue(), "1.234e+12"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_11).DefaultValue(), ""); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_12).DefaultValue(), "10m"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_13).DefaultValue(), "-18446744073709551616"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_14).DefaultValue(), "1152827684197027293"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_01).CurrentValue(), "true"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_02).CurrentValue(), "1234"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_03).CurrentValue(), "-34"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_04).CurrentValue(), "189"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_05).CurrentValue(), "10765"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_06).CurrentValue(), "40000"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_07).CurrentValue(), "-1234567"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_08).CurrentValue(), "9876543"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_09).CurrentValue(), "-9.876e-50"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_10).CurrentValue(), "1.234e+12"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_11).CurrentValue(), ""); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_12).CurrentValue(), "10m"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_13).CurrentValue(), "-18446744073709551616"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_14).CurrentValue(), "1152827684197027293"); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_01), true); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_02), 1234); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_03), -34); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_04), 189); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_05), 10765); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_06), 40000); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_07), -1234567); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_08), 9876543); EXPECT_NEAR(absl::GetFlag(FLAGS_test_flag_09), -9.876e-50, 1e-55); EXPECT_NEAR(absl::GetFlag(FLAGS_test_flag_10), 1.234e12f, 1e5f); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_11), ""); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_12), absl::Minutes(10)); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_13), absl::MakeInt128(-1, 0)); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_14), absl::MakeUint128(0, 0xFFFAAABBBCCCDDD)); } // -------------------------------------------------------------------- struct NonTriviallyCopyableAggregate { NonTriviallyCopyableAggregate() = default; // NOLINTNEXTLINE NonTriviallyCopyableAggregate(const NonTriviallyCopyableAggregate& rhs) : value(rhs.value) {} // NOLINTNEXTLINE NonTriviallyCopyableAggregate& operator=( const NonTriviallyCopyableAggregate& rhs) { value = rhs.value; return *this; } int value; }; bool AbslParseFlag(absl::string_view src, NonTriviallyCopyableAggregate* f, std::string* e) { return absl::ParseFlag(src, &f->value, e); } std::string AbslUnparseFlag(const NonTriviallyCopyableAggregate& ntc) { return absl::StrCat(ntc.value); } bool operator==(const NonTriviallyCopyableAggregate& ntc1, const NonTriviallyCopyableAggregate& ntc2) { return ntc1.value == ntc2.value; } } // namespace ABSL_FLAG(bool, test_flag_eb_01, {}, ""); ABSL_FLAG(int32_t, test_flag_eb_02, {}, ""); ABSL_FLAG(int64_t, test_flag_eb_03, {}, ""); ABSL_FLAG(double, test_flag_eb_04, {}, ""); ABSL_FLAG(std::string, test_flag_eb_05, {}, ""); ABSL_FLAG(NonTriviallyCopyableAggregate, test_flag_eb_06, {}, ""); namespace { TEST_F(FlagTest, TestEmptyBracesDefault) { EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_eb_01).DefaultValue(), "false"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_eb_02).DefaultValue(), "0"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_eb_03).DefaultValue(), "0"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_eb_04).DefaultValue(), "0"); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_eb_05).DefaultValue(), ""); EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_eb_06).DefaultValue(), "0"); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_eb_01), false); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_eb_02), 0); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_eb_03), 0); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_eb_04), 0.0); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_eb_05), ""); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_eb_06), NonTriviallyCopyableAggregate{}); } // -------------------------------------------------------------------- TEST_F(FlagTest, TestGetSet) { absl::SetFlag(&FLAGS_test_flag_01, false); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_01), false); absl::SetFlag(&FLAGS_test_flag_02, 321); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_02), 321); absl::SetFlag(&FLAGS_test_flag_03, 67); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_03), 67); absl::SetFlag(&FLAGS_test_flag_04, 1); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_04), 1); absl::SetFlag(&FLAGS_test_flag_05, -908); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_05), -908); absl::SetFlag(&FLAGS_test_flag_06, 4001); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_06), 4001); absl::SetFlag(&FLAGS_test_flag_07, -23456); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_07), -23456); absl::SetFlag(&FLAGS_test_flag_08, 975310); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_08), 975310); absl::SetFlag(&FLAGS_test_flag_09, 1.00001); EXPECT_NEAR(absl::GetFlag(FLAGS_test_flag_09), 1.00001, 1e-10); absl::SetFlag(&FLAGS_test_flag_10, -3.54f); EXPECT_NEAR(absl::GetFlag(FLAGS_test_flag_10), -3.54f, 1e-6f); absl::SetFlag(&FLAGS_test_flag_11, "asdf"); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_11), "asdf"); absl::SetFlag(&FLAGS_test_flag_12, absl::Seconds(110)); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_12), absl::Seconds(110)); absl::SetFlag(&FLAGS_test_flag_13, absl::MakeInt128(-1, 0)); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_13), absl::MakeInt128(-1, 0)); absl::SetFlag(&FLAGS_test_flag_14, absl::MakeUint128(0, 0xFFFAAABBBCCCDDD)); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_14), absl::MakeUint128(0, 0xFFFAAABBBCCCDDD)); } // -------------------------------------------------------------------- TEST_F(FlagTest, TestGetViaReflection) { #if ABSL_FLAGS_STRIP_NAMES GTEST_SKIP() << "This test requires flag names to be present"; #endif auto* handle = absl::FindCommandLineFlag("test_flag_01"); EXPECT_EQ(*handle->TryGet(), true); handle = absl::FindCommandLineFlag("test_flag_02"); EXPECT_EQ(*handle->TryGet(), 1234); handle = absl::FindCommandLineFlag("test_flag_03"); EXPECT_EQ(*handle->TryGet(), -34); handle = absl::FindCommandLineFlag("test_flag_04"); EXPECT_EQ(*handle->TryGet(), 189); handle = absl::FindCommandLineFlag("test_flag_05"); EXPECT_EQ(*handle->TryGet(), 10765); handle = absl::FindCommandLineFlag("test_flag_06"); EXPECT_EQ(*handle->TryGet(), 40000); handle = absl::FindCommandLineFlag("test_flag_07"); EXPECT_EQ(*handle->TryGet(), -1234567); handle = absl::FindCommandLineFlag("test_flag_08"); EXPECT_EQ(*handle->TryGet(), 9876543); handle = absl::FindCommandLineFlag("test_flag_09"); EXPECT_NEAR(*handle->TryGet(), -9.876e-50, 1e-55); handle = absl::FindCommandLineFlag("test_flag_10"); EXPECT_NEAR(*handle->TryGet(), 1.234e12f, 1e5f); handle = absl::FindCommandLineFlag("test_flag_11"); EXPECT_EQ(*handle->TryGet(), ""); handle = absl::FindCommandLineFlag("test_flag_12"); EXPECT_EQ(*handle->TryGet(), absl::Minutes(10)); handle = absl::FindCommandLineFlag("test_flag_13"); EXPECT_EQ(*handle->TryGet(), absl::MakeInt128(-1, 0)); handle = absl::FindCommandLineFlag("test_flag_14"); EXPECT_EQ(*handle->TryGet(), absl::MakeUint128(0, 0xFFFAAABBBCCCDDD)); } // -------------------------------------------------------------------- TEST_F(FlagTest, ConcurrentSetAndGet) { #if ABSL_FLAGS_STRIP_NAMES GTEST_SKIP() << "This test requires flag names to be present"; #endif static constexpr int kNumThreads = 8; // Two arbitrary durations. One thread will concurrently flip the flag // between these two values, while the other threads read it and verify // that no other value is seen. static const absl::Duration kValidDurations[] = { absl::Seconds(int64_t{0x6cebf47a9b68c802}) + absl::Nanoseconds(229702057), absl::Seconds(int64_t{0x23fec0307e4e9d3}) + absl::Nanoseconds(44555374)}; absl::SetFlag(&FLAGS_test_flag_12, kValidDurations[0]); std::atomic stop{false}; std::vector threads; auto* handle = absl::FindCommandLineFlag("test_flag_12"); for (int i = 0; i < kNumThreads; i++) { threads.emplace_back([&]() { while (!stop.load(std::memory_order_relaxed)) { // Try loading the flag both directly and via a reflection // handle. absl::Duration v = absl::GetFlag(FLAGS_test_flag_12); EXPECT_TRUE(v == kValidDurations[0] || v == kValidDurations[1]); v = *handle->TryGet(); EXPECT_TRUE(v == kValidDurations[0] || v == kValidDurations[1]); } }); } absl::Time end_time = absl::Now() + absl::Seconds(1); int i = 0; while (absl::Now() < end_time) { absl::SetFlag(&FLAGS_test_flag_12, kValidDurations[i++ % ABSL_ARRAYSIZE(kValidDurations)]); } stop.store(true, std::memory_order_relaxed); for (auto& t : threads) t.join(); } // -------------------------------------------------------------------- int GetDflt1() { return 1; } } // namespace ABSL_FLAG(int, test_int_flag_with_non_const_default, GetDflt1(), "test int flag non const default"); ABSL_FLAG(std::string, test_string_flag_with_non_const_default, absl::StrCat("AAA", "BBB"), "test string flag non const default"); namespace { TEST_F(FlagTest, TestNonConstexprDefault) { EXPECT_EQ(absl::GetFlag(FLAGS_test_int_flag_with_non_const_default), 1); EXPECT_EQ(absl::GetFlag(FLAGS_test_string_flag_with_non_const_default), "AAABBB"); } // -------------------------------------------------------------------- } // namespace ABSL_FLAG(bool, test_flag_with_non_const_help, true, absl::StrCat("test ", "flag ", "non const help")); namespace { #if !ABSL_FLAGS_STRIP_HELP TEST_F(FlagTest, TestNonConstexprHelp) { EXPECT_EQ( absl::GetFlagReflectionHandle(FLAGS_test_flag_with_non_const_help).Help(), "test flag non const help"); } #endif //! ABSL_FLAGS_STRIP_HELP // -------------------------------------------------------------------- int cb_test_value = -1; void TestFlagCB(); } // namespace ABSL_FLAG(int, test_flag_with_cb, 100, "").OnUpdate(TestFlagCB); ABSL_FLAG(int, test_flag_with_lambda_cb, 200, "").OnUpdate([]() { cb_test_value = absl::GetFlag(FLAGS_test_flag_with_lambda_cb) + absl::GetFlag(FLAGS_test_flag_with_cb); }); namespace { void TestFlagCB() { cb_test_value = absl::GetFlag(FLAGS_test_flag_with_cb); } // Tests side-effects of callback invocation. TEST_F(FlagTest, CallbackInvocation) { EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_with_cb), 100); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_with_lambda_cb), 200); EXPECT_EQ(cb_test_value, 300); absl::SetFlag(&FLAGS_test_flag_with_cb, 1); EXPECT_EQ(cb_test_value, 1); absl::SetFlag(&FLAGS_test_flag_with_lambda_cb, 3); EXPECT_EQ(cb_test_value, 4); } // -------------------------------------------------------------------- struct CustomUDT { CustomUDT() : a(1), b(1) {} CustomUDT(int a_, int b_) : a(a_), b(b_) {} friend bool operator==(const CustomUDT& f1, const CustomUDT& f2) { return f1.a == f2.a && f1.b == f2.b; } int a; int b; }; bool AbslParseFlag(absl::string_view in, CustomUDT* f, std::string*) { std::vector parts = absl::StrSplit(in, ':', absl::SkipWhitespace()); if (parts.size() != 2) return false; if (!absl::SimpleAtoi(parts[0], &f->a)) return false; if (!absl::SimpleAtoi(parts[1], &f->b)) return false; return true; } std::string AbslUnparseFlag(const CustomUDT& f) { return absl::StrCat(f.a, ":", f.b); } } // namespace ABSL_FLAG(CustomUDT, test_flag_custom_udt, CustomUDT(), "test flag custom UDT"); namespace { TEST_F(FlagTest, TestCustomUDT) { EXPECT_EQ(flags::StorageKind(), flags::FlagValueStorageKind::kOneWordAtomic); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_custom_udt), CustomUDT(1, 1)); absl::SetFlag(&FLAGS_test_flag_custom_udt, CustomUDT(2, 3)); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_custom_udt), CustomUDT(2, 3)); } // MSVC produces link error on the type mismatch. // Linux does not have build errors and validations work as expected. #if !defined(_WIN32) && GTEST_HAS_DEATH_TEST using FlagDeathTest = FlagTest; TEST_F(FlagDeathTest, TestTypeMismatchValidations) { #if ABSL_FLAGS_STRIP_NAMES GTEST_SKIP() << "This test requires flag names to be present"; #endif #if !defined(NDEBUG) EXPECT_DEATH_IF_SUPPORTED( static_cast(absl::GetFlag(FLAGS_mistyped_int_flag)), "Flag 'mistyped_int_flag' is defined as one type and declared " "as another"); EXPECT_DEATH_IF_SUPPORTED( static_cast(absl::GetFlag(FLAGS_mistyped_string_flag)), "Flag 'mistyped_string_flag' is defined as one type and " "declared as another"); #endif EXPECT_DEATH_IF_SUPPORTED( absl::SetFlag(&FLAGS_mistyped_int_flag, 1), "Flag 'mistyped_int_flag' is defined as one type and declared " "as another"); EXPECT_DEATH_IF_SUPPORTED( absl::SetFlag(&FLAGS_mistyped_string_flag, std::vector{}), "Flag 'mistyped_string_flag' is defined as one type and declared as " "another"); } #endif // -------------------------------------------------------------------- // A contrived type that offers implicit and explicit conversion from specific // source types. struct ConversionTestVal { ConversionTestVal() = default; explicit ConversionTestVal(int a_in) : a(a_in) {} enum class ViaImplicitConv { kTen = 10, kEleven }; // NOLINTNEXTLINE ConversionTestVal(ViaImplicitConv from) : a(static_cast(from)) {} int a; }; bool AbslParseFlag(absl::string_view in, ConversionTestVal* val_out, std::string*) { if (!absl::SimpleAtoi(in, &val_out->a)) { return false; } return true; } std::string AbslUnparseFlag(const ConversionTestVal& val) { return absl::StrCat(val.a); } } // namespace // Flag default values can be specified with a value that converts to the flag // value type implicitly. ABSL_FLAG(ConversionTestVal, test_flag_implicit_conv, ConversionTestVal::ViaImplicitConv::kTen, "test flag init via implicit conversion"); namespace { TEST_F(FlagTest, CanSetViaImplicitConversion) { EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_implicit_conv).a, 10); absl::SetFlag(&FLAGS_test_flag_implicit_conv, ConversionTestVal::ViaImplicitConv::kEleven); EXPECT_EQ(absl::GetFlag(FLAGS_test_flag_implicit_conv).a, 11); } // -------------------------------------------------------------------- struct NonDfltConstructible { public: // This constructor tests that we can initialize the flag with int value NonDfltConstructible(int i) : value(i) {} // NOLINT // This constructor tests that we can't initialize the flag with char value // but can with explicitly constructed NonDfltConstructible. explicit NonDfltConstructible(char c) : value(100 + static_cast(c)) {} int value; }; bool AbslParseFlag(absl::string_view in, NonDfltConstructible* ndc_out, std::string*) { return absl::SimpleAtoi(in, &ndc_out->value); } std::string AbslUnparseFlag(const NonDfltConstructible& ndc) { return absl::StrCat(ndc.value); } } // namespace ABSL_FLAG(NonDfltConstructible, ndc_flag1, NonDfltConstructible('1'), "Flag with non default constructible type"); ABSL_FLAG(NonDfltConstructible, ndc_flag2, 0, "Flag with non default constructible type"); namespace { TEST_F(FlagTest, TestNonDefaultConstructibleType) { EXPECT_EQ(absl::GetFlag(FLAGS_ndc_flag1).value, '1' + 100); EXPECT_EQ(absl::GetFlag(FLAGS_ndc_flag2).value, 0); absl::SetFlag(&FLAGS_ndc_flag1, NonDfltConstructible('A')); absl::SetFlag(&FLAGS_ndc_flag2, 25); EXPECT_EQ(absl::GetFlag(FLAGS_ndc_flag1).value, 'A' + 100); EXPECT_EQ(absl::GetFlag(FLAGS_ndc_flag2).value, 25); } } // namespace // -------------------------------------------------------------------- ABSL_RETIRED_FLAG(bool, old_bool_flag, true, "old descr"); ABSL_RETIRED_FLAG(int, old_int_flag, (int)std::sqrt(10), "old descr"); ABSL_RETIRED_FLAG(std::string, old_str_flag, "", absl::StrCat("old ", "descr")); namespace { bool initialization_order_fiasco_test ABSL_ATTRIBUTE_UNUSED = [] { // Iterate over all the flags during static initialization. // This should not trigger ASan's initialization-order-fiasco. auto* handle1 = absl::FindCommandLineFlag("flag_on_separate_file"); auto* handle2 = absl::FindCommandLineFlag("retired_flag_on_separate_file"); if (handle1 != nullptr && handle2 != nullptr) { return handle1->Name() == handle2->Name(); } return true; }(); TEST_F(FlagTest, TestRetiredFlagRegistration) { auto* handle = absl::FindCommandLineFlag("old_bool_flag"); EXPECT_TRUE(handle->IsOfType()); EXPECT_TRUE(handle->IsRetired()); handle = absl::FindCommandLineFlag("old_int_flag"); EXPECT_TRUE(handle->IsOfType()); EXPECT_TRUE(handle->IsRetired()); handle = absl::FindCommandLineFlag("old_str_flag"); EXPECT_TRUE(handle->IsOfType()); EXPECT_TRUE(handle->IsRetired()); } } // namespace // -------------------------------------------------------------------- namespace { // User-defined type with small alignment, but size exceeding 16. struct SmallAlignUDT { SmallAlignUDT() : c('A'), s(12) {} char c; int16_t s; char bytes[14]; }; bool AbslParseFlag(absl::string_view, SmallAlignUDT*, std::string*) { return true; } std::string AbslUnparseFlag(const SmallAlignUDT&) { return ""; } } // namespace ABSL_FLAG(SmallAlignUDT, test_flag_sa_udt, {}, "help"); namespace { TEST_F(FlagTest, TestSmallAlignUDT) { EXPECT_EQ(flags::StorageKind(), flags::FlagValueStorageKind::kSequenceLocked); SmallAlignUDT value = absl::GetFlag(FLAGS_test_flag_sa_udt); EXPECT_EQ(value.c, 'A'); EXPECT_EQ(value.s, 12); value.c = 'B'; value.s = 45; absl::SetFlag(&FLAGS_test_flag_sa_udt, value); value = absl::GetFlag(FLAGS_test_flag_sa_udt); EXPECT_EQ(value.c, 'B'); EXPECT_EQ(value.s, 45); } } // namespace // -------------------------------------------------------------------- namespace { // User-defined not trivially copyable type. template struct NonTriviallyCopyableUDT { NonTriviallyCopyableUDT() : c('A') { s_num_instance++; } NonTriviallyCopyableUDT(const NonTriviallyCopyableUDT& rhs) : c(rhs.c) { s_num_instance++; } NonTriviallyCopyableUDT& operator=(const NonTriviallyCopyableUDT& rhs) { c = rhs.c; return *this; } ~NonTriviallyCopyableUDT() { s_num_instance--; } static uint64_t s_num_instance; char c; }; template uint64_t NonTriviallyCopyableUDT::s_num_instance = 0; template bool AbslParseFlag(absl::string_view txt, NonTriviallyCopyableUDT* f, std::string*) { f->c = txt.empty() ? '\0' : txt[0]; return true; } template std::string AbslUnparseFlag(const NonTriviallyCopyableUDT&) { return ""; } template void TestExpectedLeaks( F&& f, uint64_t num_leaks, absl::optional num_new_instances = absl::nullopt) { if (!num_new_instances.has_value()) num_new_instances = num_leaks; auto num_leaked_before = flags::NumLeakedFlagValues(); auto num_instances_before = NonTriviallyCopyableUDT::s_num_instance; f(); EXPECT_EQ(num_leaked_before + num_leaks, flags::NumLeakedFlagValues()); EXPECT_EQ(num_instances_before + num_new_instances.value(), NonTriviallyCopyableUDT::s_num_instance); } } // namespace ABSL_FLAG(NonTriviallyCopyableUDT<1>, test_flag_ntc_udt1, {}, "help"); ABSL_FLAG(NonTriviallyCopyableUDT<2>, test_flag_ntc_udt2, {}, "help"); ABSL_FLAG(NonTriviallyCopyableUDT<3>, test_flag_ntc_udt3, {}, "help"); ABSL_FLAG(NonTriviallyCopyableUDT<4>, test_flag_ntc_udt4, {}, "help"); ABSL_FLAG(NonTriviallyCopyableUDT<5>, test_flag_ntc_udt5, {}, "help"); namespace { TEST_F(FlagTest, TestNonTriviallyCopyableGetSetSet) { EXPECT_EQ(flags::StorageKind>(), flags::FlagValueStorageKind::kHeapAllocated); TestExpectedLeaks<1>( [&] { NonTriviallyCopyableUDT<1> value = absl::GetFlag(FLAGS_test_flag_ntc_udt1); EXPECT_EQ(value.c, 'A'); }, 0); TestExpectedLeaks<1>( [&] { NonTriviallyCopyableUDT<1> value; value.c = 'B'; absl::SetFlag(&FLAGS_test_flag_ntc_udt1, value); EXPECT_EQ(value.c, 'B'); }, 1); TestExpectedLeaks<1>( [&] { NonTriviallyCopyableUDT<1> value; value.c = 'C'; absl::SetFlag(&FLAGS_test_flag_ntc_udt1, value); }, 0); } TEST_F(FlagTest, TestNonTriviallyCopyableParseSet) { TestExpectedLeaks<2>( [&] { const char* in_argv[] = {"testbin", "--test_flag_ntc_udt2=A"}; absl::ParseCommandLine(2, const_cast(in_argv)); }, 0); TestExpectedLeaks<2>( [&] { NonTriviallyCopyableUDT<2> value; value.c = 'B'; absl::SetFlag(&FLAGS_test_flag_ntc_udt2, value); EXPECT_EQ(value.c, 'B'); }, 0); } TEST_F(FlagTest, TestNonTriviallyCopyableSet) { TestExpectedLeaks<3>( [&] { NonTriviallyCopyableUDT<3> value; value.c = 'B'; absl::SetFlag(&FLAGS_test_flag_ntc_udt3, value); EXPECT_EQ(value.c, 'B'); }, 0); } // One new instance created during initialization and stored in the flag. auto premain_utd4_get = (TestExpectedLeaks<4>([] { (void)absl::GetFlag(FLAGS_test_flag_ntc_udt4); }, 0, 1), false); TEST_F(FlagTest, TestNonTriviallyCopyableGetBeforeMainParseGet) { TestExpectedLeaks<4>( [&] { const char* in_argv[] = {"testbin", "--test_flag_ntc_udt4=C"}; absl::ParseCommandLine(2, const_cast(in_argv)); }, 1); TestExpectedLeaks<4>( [&] { NonTriviallyCopyableUDT<4> value = absl::GetFlag(FLAGS_test_flag_ntc_udt4); EXPECT_EQ(value.c, 'C'); }, 0); } // One new instance created during initialization, which is reused since it was // never read. auto premain_utd5_set = (TestExpectedLeaks<5>( [] { NonTriviallyCopyableUDT<5> value; value.c = 'B'; absl::SetFlag(&FLAGS_test_flag_ntc_udt5, value); }, 0, 1), false); TEST_F(FlagTest, TestNonTriviallyCopyableSetParseGet) { TestExpectedLeaks<5>( [&] { const char* in_argv[] = {"testbin", "--test_flag_ntc_udt5=C"}; absl::ParseCommandLine(2, const_cast(in_argv)); }, 0); TestExpectedLeaks<5>( [&] { NonTriviallyCopyableUDT<5> value = absl::GetFlag(FLAGS_test_flag_ntc_udt5); EXPECT_EQ(value.c, 'C'); }, 0); } } // namespace // -------------------------------------------------------------------- namespace { enum TestE { A = 1, B = 2, C = 3 }; struct EnumWrapper { EnumWrapper() : e(A) {} TestE e; }; bool AbslParseFlag(absl::string_view, EnumWrapper*, std::string*) { return true; } std::string AbslUnparseFlag(const EnumWrapper&) { return ""; } } // namespace ABSL_FLAG(EnumWrapper, test_enum_wrapper_flag, {}, "help"); TEST_F(FlagTest, TesTypeWrappingEnum) { EnumWrapper value = absl::GetFlag(FLAGS_test_enum_wrapper_flag); EXPECT_EQ(value.e, A); value.e = B; absl::SetFlag(&FLAGS_test_enum_wrapper_flag, value); value = absl::GetFlag(FLAGS_test_enum_wrapper_flag); EXPECT_EQ(value.e, B); } // This is a compile test to ensure macros are expanded within ABSL_FLAG and // ABSL_DECLARE_FLAG. #define FLAG_NAME_MACRO(name) prefix_##name ABSL_DECLARE_FLAG(int, FLAG_NAME_MACRO(test_macro_named_flag)); ABSL_FLAG(int, FLAG_NAME_MACRO(test_macro_named_flag), 0, "Testing macro expansion within ABSL_FLAG"); TEST_F(FlagTest, MacroWithinAbslFlag) { EXPECT_EQ(absl::GetFlag(FLAGS_prefix_test_macro_named_flag), 0); absl::SetFlag(&FLAGS_prefix_test_macro_named_flag, 1); EXPECT_EQ(absl::GetFlag(FLAGS_prefix_test_macro_named_flag), 1); } // -------------------------------------------------------------------- ABSL_FLAG(absl::optional, optional_bool, absl::nullopt, "help"); ABSL_FLAG(absl::optional, optional_int, {}, "help"); ABSL_FLAG(absl::optional, optional_double, 9.3, "help"); ABSL_FLAG(absl::optional, optional_string, absl::nullopt, "help"); ABSL_FLAG(absl::optional, optional_duration, absl::nullopt, "help"); ABSL_FLAG(absl::optional>, optional_optional_int, absl::nullopt, "help"); #if defined(ABSL_HAVE_STD_OPTIONAL) && !defined(ABSL_USES_STD_OPTIONAL) ABSL_FLAG(std::optional, std_optional_int64, std::nullopt, "help"); #endif namespace { TEST_F(FlagTest, TestOptionalBool) { EXPECT_FALSE(absl::GetFlag(FLAGS_optional_bool).has_value()); EXPECT_EQ(absl::GetFlag(FLAGS_optional_bool), absl::nullopt); absl::SetFlag(&FLAGS_optional_bool, false); EXPECT_TRUE(absl::GetFlag(FLAGS_optional_bool).has_value()); EXPECT_EQ(absl::GetFlag(FLAGS_optional_bool), false); absl::SetFlag(&FLAGS_optional_bool, true); EXPECT_TRUE(absl::GetFlag(FLAGS_optional_bool).has_value()); EXPECT_EQ(absl::GetFlag(FLAGS_optional_bool), true); absl::SetFlag(&FLAGS_optional_bool, absl::nullopt); EXPECT_FALSE(absl::GetFlag(FLAGS_optional_bool).has_value()); EXPECT_EQ(absl::GetFlag(FLAGS_optional_bool), absl::nullopt); } // -------------------------------------------------------------------- TEST_F(FlagTest, TestOptionalInt) { EXPECT_FALSE(absl::GetFlag(FLAGS_optional_int).has_value()); EXPECT_EQ(absl::GetFlag(FLAGS_optional_int), absl::nullopt); absl::SetFlag(&FLAGS_optional_int, 0); EXPECT_TRUE(absl::GetFlag(FLAGS_optional_int).has_value()); EXPECT_EQ(absl::GetFlag(FLAGS_optional_int), 0); absl::SetFlag(&FLAGS_optional_int, 10); EXPECT_TRUE(absl::GetFlag(FLAGS_optional_int).has_value()); EXPECT_EQ(absl::GetFlag(FLAGS_optional_int), 10); absl::SetFlag(&FLAGS_optional_int, absl::nullopt); EXPECT_FALSE(absl::GetFlag(FLAGS_optional_int).has_value()); EXPECT_EQ(absl::GetFlag(FLAGS_optional_int), absl::nullopt); } // -------------------------------------------------------------------- TEST_F(FlagTest, TestOptionalDouble) { EXPECT_TRUE(absl::GetFlag(FLAGS_optional_double).has_value()); EXPECT_DOUBLE_EQ(*absl::GetFlag(FLAGS_optional_double), 9.3); absl::SetFlag(&FLAGS_optional_double, 0.0); EXPECT_TRUE(absl::GetFlag(FLAGS_optional_double).has_value()); EXPECT_EQ(absl::GetFlag(FLAGS_optional_double), 0.0); absl::SetFlag(&FLAGS_optional_double, 1.234); EXPECT_TRUE(absl::GetFlag(FLAGS_optional_double).has_value()); EXPECT_DOUBLE_EQ(*absl::GetFlag(FLAGS_optional_double), 1.234); absl::SetFlag(&FLAGS_optional_double, absl::nullopt); EXPECT_FALSE(absl::GetFlag(FLAGS_optional_double).has_value()); EXPECT_EQ(absl::GetFlag(FLAGS_optional_double), absl::nullopt); } // -------------------------------------------------------------------- TEST_F(FlagTest, TestOptionalString) { EXPECT_FALSE(absl::GetFlag(FLAGS_optional_string).has_value()); EXPECT_EQ(absl::GetFlag(FLAGS_optional_string), absl::nullopt); // Setting optional string to "" leads to undefined behavior. absl::SetFlag(&FLAGS_optional_string, " "); EXPECT_TRUE(absl::GetFlag(FLAGS_optional_string).has_value()); EXPECT_EQ(absl::GetFlag(FLAGS_optional_string), " "); absl::SetFlag(&FLAGS_optional_string, "QWERTY"); EXPECT_TRUE(absl::GetFlag(FLAGS_optional_string).has_value()); EXPECT_EQ(absl::GetFlag(FLAGS_optional_string), "QWERTY"); absl::SetFlag(&FLAGS_optional_string, absl::nullopt); EXPECT_FALSE(absl::GetFlag(FLAGS_optional_string).has_value()); EXPECT_EQ(absl::GetFlag(FLAGS_optional_string), absl::nullopt); } // -------------------------------------------------------------------- TEST_F(FlagTest, TestOptionalDuration) { EXPECT_FALSE(absl::GetFlag(FLAGS_optional_duration).has_value()); EXPECT_EQ(absl::GetFlag(FLAGS_optional_duration), absl::nullopt); absl::SetFlag(&FLAGS_optional_duration, absl::ZeroDuration()); EXPECT_TRUE(absl::GetFlag(FLAGS_optional_duration).has_value()); EXPECT_EQ(absl::GetFlag(FLAGS_optional_duration), absl::Seconds(0)); absl::SetFlag(&FLAGS_optional_duration, absl::Hours(3)); EXPECT_TRUE(absl::GetFlag(FLAGS_optional_duration).has_value()); EXPECT_EQ(absl::GetFlag(FLAGS_optional_duration), absl::Hours(3)); absl::SetFlag(&FLAGS_optional_duration, absl::nullopt); EXPECT_FALSE(absl::GetFlag(FLAGS_optional_duration).has_value()); EXPECT_EQ(absl::GetFlag(FLAGS_optional_duration), absl::nullopt); } // -------------------------------------------------------------------- TEST_F(FlagTest, TestOptionalOptional) { EXPECT_FALSE(absl::GetFlag(FLAGS_optional_optional_int).has_value()); EXPECT_EQ(absl::GetFlag(FLAGS_optional_optional_int), absl::nullopt); absl::optional nullint{absl::nullopt}; absl::SetFlag(&FLAGS_optional_optional_int, nullint); EXPECT_TRUE(absl::GetFlag(FLAGS_optional_optional_int).has_value()); EXPECT_NE(absl::GetFlag(FLAGS_optional_optional_int), nullint); EXPECT_EQ(absl::GetFlag(FLAGS_optional_optional_int), absl::optional>{nullint}); absl::SetFlag(&FLAGS_optional_optional_int, 0); EXPECT_TRUE(absl::GetFlag(FLAGS_optional_optional_int).has_value()); EXPECT_EQ(absl::GetFlag(FLAGS_optional_optional_int), 0); absl::SetFlag(&FLAGS_optional_optional_int, absl::optional{0}); EXPECT_TRUE(absl::GetFlag(FLAGS_optional_optional_int).has_value()); EXPECT_EQ(absl::GetFlag(FLAGS_optional_optional_int), 0); EXPECT_EQ(absl::GetFlag(FLAGS_optional_optional_int), absl::optional{0}); absl::SetFlag(&FLAGS_optional_optional_int, absl::nullopt); EXPECT_FALSE(absl::GetFlag(FLAGS_optional_optional_int).has_value()); EXPECT_EQ(absl::GetFlag(FLAGS_optional_optional_int), absl::nullopt); } // -------------------------------------------------------------------- #if defined(ABSL_HAVE_STD_OPTIONAL) && !defined(ABSL_USES_STD_OPTIONAL) TEST_F(FlagTest, TestStdOptional) { EXPECT_FALSE(absl::GetFlag(FLAGS_std_optional_int64).has_value()); EXPECT_EQ(absl::GetFlag(FLAGS_std_optional_int64), std::nullopt); absl::SetFlag(&FLAGS_std_optional_int64, 0); EXPECT_TRUE(absl::GetFlag(FLAGS_std_optional_int64).has_value()); EXPECT_EQ(absl::GetFlag(FLAGS_std_optional_int64), 0); absl::SetFlag(&FLAGS_std_optional_int64, 0xFFFFFFFFFF16); EXPECT_TRUE(absl::GetFlag(FLAGS_std_optional_int64).has_value()); EXPECT_EQ(absl::GetFlag(FLAGS_std_optional_int64), 0xFFFFFFFFFF16); absl::SetFlag(&FLAGS_std_optional_int64, std::nullopt); EXPECT_FALSE(absl::GetFlag(FLAGS_std_optional_int64).has_value()); EXPECT_EQ(absl::GetFlag(FLAGS_std_optional_int64), std::nullopt); } // -------------------------------------------------------------------- #endif } // namespace