aboutsummaryrefslogtreecommitdiff
path: root/absl/flags/flag_test.cc
diff options
context:
space:
mode:
Diffstat (limited to 'absl/flags/flag_test.cc')
-rw-r--r--absl/flags/flag_test.cc200
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