diff options
Diffstat (limited to 'absl/flags/flag_test.cc')
-rw-r--r-- | absl/flags/flag_test.cc | 252 |
1 files changed, 208 insertions, 44 deletions
diff --git a/absl/flags/flag_test.cc b/absl/flags/flag_test.cc index 8d14ba8d..8af5bf7f 100644 --- a/absl/flags/flag_test.cc +++ b/absl/flags/flag_test.cc @@ -19,19 +19,19 @@ #include <stdint.h> #include <atomic> -#include <cmath> -#include <new> #include <string> #include <thread> // NOLINT #include <vector> #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" @@ -40,7 +40,9 @@ #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<std::string>, mistyped_string_flag); @@ -125,9 +127,9 @@ TEST_F(FlagTest, Traits) { #endif EXPECT_EQ(flags::StorageKind<std::string>(), - flags::FlagValueStorageKind::kAlignedBuffer); + flags::FlagValueStorageKind::kHeapAllocated); EXPECT_EQ(flags::StorageKind<std::vector<std::string>>(), - flags::FlagValueStorageKind::kAlignedBuffer); + flags::FlagValueStorageKind::kHeapAllocated); EXPECT_EQ(flags::StorageKind<absl::int128>(), flags::FlagValueStorageKind::kSequenceLocked); @@ -226,9 +228,10 @@ ABSL_DECLARE_FLAG(absl::uint128, test_flag_14); namespace { -#if !ABSL_FLAGS_STRIP_NAMES - 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"); @@ -259,12 +262,27 @@ TEST_F(FlagTest, TestFlagDeclaration) { EXPECT_EQ(absl::GetFlagReflectionHandle(FLAGS_test_flag_14).Name(), "test_flag_14"); } -#endif // !ABSL_FLAGS_STRIP_NAMES - -// -------------------------------------------------------------------- } // 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"); @@ -283,8 +301,10 @@ ABSL_FLAG(absl::uint128, test_flag_14, absl::MakeUint128(0, 0xFFFAAABBBCCCDDD), namespace { -#if !ABSL_FLAGS_STRIP_NAMES 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(), @@ -413,7 +433,6 @@ TEST_F(FlagTest, TestFlagDefinition) { expected_file_name)) << absl::GetFlagReflectionHandle(FLAGS_test_flag_14).Filename(); } -#endif // !ABSL_FLAGS_STRIP_NAMES // -------------------------------------------------------------------- @@ -497,8 +516,10 @@ TEST_F(FlagTest, TestDefault) { struct NonTriviallyCopyableAggregate { NonTriviallyCopyableAggregate() = default; + // NOLINTNEXTLINE NonTriviallyCopyableAggregate(const NonTriviallyCopyableAggregate& rhs) : value(rhs.value) {} + // NOLINTNEXTLINE NonTriviallyCopyableAggregate& operator=( const NonTriviallyCopyableAggregate& rhs) { value = rhs.value; @@ -604,6 +625,9 @@ TEST_F(FlagTest, TestGetSet) { // -------------------------------------------------------------------- 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<bool>(), true); handle = absl::FindCommandLineFlag("test_flag_02"); @@ -638,6 +662,9 @@ TEST_F(FlagTest, TestGetViaReflection) { // -------------------------------------------------------------------- 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 @@ -785,10 +812,12 @@ TEST_F(FlagTest, TestCustomUDT) { // 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<void>(absl::GetFlag(FLAGS_mistyped_int_flag)), @@ -949,51 +978,194 @@ bool AbslParseFlag(absl::string_view, SmallAlignUDT*, std::string*) { } std::string AbslUnparseFlag(const SmallAlignUDT&) { return ""; } -// User-defined type with small size, but not trivially copyable. +} // namespace + +ABSL_FLAG(SmallAlignUDT, test_flag_sa_udt, {}, "help"); + +namespace { + +TEST_F(FlagTest, TestSmallAlignUDT) { + EXPECT_EQ(flags::StorageKind<SmallAlignUDT>(), + 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 <int id> struct NonTriviallyCopyableUDT { - NonTriviallyCopyableUDT() : c('A') {} - NonTriviallyCopyableUDT(const NonTriviallyCopyableUDT& rhs) : c(rhs.c) {} + 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; }; -bool AbslParseFlag(absl::string_view, NonTriviallyCopyableUDT*, std::string*) { +template <int id> +uint64_t NonTriviallyCopyableUDT<id>::s_num_instance = 0; + +template <int id> +bool AbslParseFlag(absl::string_view txt, NonTriviallyCopyableUDT<id>* f, + std::string*) { + f->c = txt.empty() ? '\0' : txt[0]; return true; } -std::string AbslUnparseFlag(const NonTriviallyCopyableUDT&) { return ""; } +template <int id> +std::string AbslUnparseFlag(const NonTriviallyCopyableUDT<id>&) { + return ""; +} +template <int id, typename F> +void TestExpectedLeaks( + F&& f, uint64_t num_leaks, + absl::optional<uint64_t> 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<id>::s_num_instance; + f(); + EXPECT_EQ(num_leaked_before + num_leaks, flags::NumLeakedFlagValues()); + EXPECT_EQ(num_instances_before + num_new_instances.value(), + NonTriviallyCopyableUDT<id>::s_num_instance); +} } // namespace -ABSL_FLAG(SmallAlignUDT, test_flag_sa_udt, {}, "help"); -ABSL_FLAG(NonTriviallyCopyableUDT, test_flag_ntc_udt, {}, "help"); +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, TestSmallAlignUDT) { - SmallAlignUDT value = absl::GetFlag(FLAGS_test_flag_sa_udt); - EXPECT_EQ(value.c, 'A'); - EXPECT_EQ(value.s, 12); +TEST_F(FlagTest, TestNonTriviallyCopyableGetSetSet) { + EXPECT_EQ(flags::StorageKind<NonTriviallyCopyableUDT<1>>(), + 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); +} - 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); +TEST_F(FlagTest, TestNonTriviallyCopyableParseSet) { + TestExpectedLeaks<2>( + [&] { + const char* in_argv[] = {"testbin", "--test_flag_ntc_udt2=A"}; + absl::ParseCommandLine(2, const_cast<char**>(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, TestNonTriviallyCopyableUDT) { - NonTriviallyCopyableUDT value = absl::GetFlag(FLAGS_test_flag_ntc_udt); - EXPECT_EQ(value.c, 'A'); +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); +} - value.c = 'B'; - absl::SetFlag(&FLAGS_test_flag_ntc_udt, value); - value = absl::GetFlag(FLAGS_test_flag_ntc_udt); - EXPECT_EQ(value.c, 'B'); +// 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<char**>(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<char**>(in_argv)); + }, + 0); + + TestExpectedLeaks<5>( + [&] { + NonTriviallyCopyableUDT<5> value = + absl::GetFlag(FLAGS_test_flag_ntc_udt5); + EXPECT_EQ(value.c, 'C'); + }, + 0); } } // namespace @@ -1044,13 +1216,7 @@ TEST_F(FlagTest, MacroWithinAbslFlag) { // -------------------------------------------------------------------- -#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 5 -#define ABSL_SKIP_OPTIONAL_BOOL_TEST_DUE_TO_GCC_BUG -#endif - -#ifndef ABSL_SKIP_OPTIONAL_BOOL_TEST_DUE_TO_GCC_BUG ABSL_FLAG(absl::optional<bool>, optional_bool, absl::nullopt, "help"); -#endif ABSL_FLAG(absl::optional<int>, optional_int, {}, "help"); ABSL_FLAG(absl::optional<double>, optional_double, 9.3, "help"); ABSL_FLAG(absl::optional<std::string>, optional_string, absl::nullopt, "help"); @@ -1064,7 +1230,6 @@ ABSL_FLAG(std::optional<int64_t>, std_optional_int64, std::nullopt, "help"); namespace { -#ifndef ABSL_SKIP_OPTIONAL_BOOL_TEST_DUE_TO_GCC_BUG TEST_F(FlagTest, TestOptionalBool) { EXPECT_FALSE(absl::GetFlag(FLAGS_optional_bool).has_value()); EXPECT_EQ(absl::GetFlag(FLAGS_optional_bool), absl::nullopt); @@ -1083,7 +1248,6 @@ TEST_F(FlagTest, TestOptionalBool) { } // -------------------------------------------------------------------- -#endif TEST_F(FlagTest, TestOptionalInt) { EXPECT_FALSE(absl::GetFlag(FLAGS_optional_int).has_value()); |