diff options
Diffstat (limited to 'absl/flags/flag_test.cc')
-rw-r--r-- | absl/flags/flag_test.cc | 200 |
1 files changed, 173 insertions, 27 deletions
diff --git a/absl/flags/flag_test.cc b/absl/flags/flag_test.cc index 8bc7cdfd..8af5bf7f 100644 --- a/absl/flags/flag_test.cc +++ b/absl/flags/flag_test.cc @@ -31,6 +31,7 @@ #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" @@ -126,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); @@ -267,7 +268,7 @@ TEST_F(FlagTest, TestFlagDeclaration) { #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 of no Tail calls like OnUpdate) is constexpr and thus can and +// (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 { @@ -515,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; @@ -975,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 |