Commit 821756c3 by Evan Brown Committed by Copybara-Service

Replace BtreeAllocatorTest with individual test cases for copy/move/swap…

Replace BtreeAllocatorTest with individual test cases for copy/move/swap propagation (defined in test_allocator.h) and minimal alignment.

Also remove some extraneous value_types from typed tests. The motivation is to reduce btree_test compile time.

PiperOrigin-RevId: 567376572
Change-Id: I6ac6130b99faeadaedab8c2c7b05d5e23e77cc1e
parent e313f0ed
...@@ -142,10 +142,13 @@ cc_library( ...@@ -142,10 +142,13 @@ cc_library(
name = "test_allocator", name = "test_allocator",
testonly = 1, testonly = 1,
hdrs = ["internal/test_allocator.h"], hdrs = ["internal/test_allocator.h"],
copts = ABSL_DEFAULT_COPTS, copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS, linkopts = ABSL_DEFAULT_LINKOPTS,
visibility = ["//visibility:private"], visibility = ["//visibility:private"],
deps = ["//absl/base:config"], deps = [
"//absl/base:config",
"@com_google_googletest//:gtest",
],
) )
cc_test( cc_test(
......
...@@ -211,6 +211,7 @@ absl_cc_library( ...@@ -211,6 +211,7 @@ absl_cc_library(
${ABSL_DEFAULT_COPTS} ${ABSL_DEFAULT_COPTS}
DEPS DEPS
absl::config absl::config
GTest::gmock
) )
absl_cc_test( absl_cc_test(
......
...@@ -76,16 +76,6 @@ void CheckPairEquals(const std::pair<T, U> &x, const std::pair<V, W> &y) { ...@@ -76,16 +76,6 @@ void CheckPairEquals(const std::pair<T, U> &x, const std::pair<V, W> &y) {
CheckPairEquals(x.first, y.first); CheckPairEquals(x.first, y.first);
CheckPairEquals(x.second, y.second); CheckPairEquals(x.second, y.second);
} }
bool IsAssertEnabled() {
// Use an assert with side-effects to figure out if they are actually enabled.
bool assert_enabled = false;
assert([&]() { // NOLINT
assert_enabled = true;
return true;
}());
return assert_enabled;
}
} // namespace } // namespace
// The base class for a sorted associative container checker. TreeType is the // The base class for a sorted associative container checker. TreeType is the
...@@ -667,96 +657,6 @@ void BtreeMultiTest() { ...@@ -667,96 +657,6 @@ void BtreeMultiTest() {
DoTest("identical: ", &container, identical_values); DoTest("identical: ", &container, identical_values);
} }
// TODO(ezb): get rid of BtreeAllocatorTest and replace with test cases using
// specific propagating allocs (e.g. CopyAssignPropagatingCountingAlloc) and
// also a test for MinimumAlignmentAlloc. Motivation is better test coverage and
// faster compilation time.
template <typename T>
void BtreeAllocatorTest() {
using value_type = typename T::value_type;
int64_t bytes1 = 0, bytes2 = 0;
PropagatingCountingAlloc<T> allocator1(&bytes1);
PropagatingCountingAlloc<T> allocator2(&bytes2);
Generator<value_type> generator(1000);
// Test that we allocate properly aligned memory. If we don't, then Layout
// will assert fail.
auto unused1 = allocator1.allocate(1);
auto unused2 = allocator2.allocate(1);
// Test copy assignment
{
T b1(typename T::key_compare(), allocator1);
T b2(typename T::key_compare(), allocator2);
int64_t original_bytes1 = bytes1;
b1.insert(generator(0));
EXPECT_GT(bytes1, original_bytes1);
// This should propagate the allocator.
b1 = b2;
EXPECT_EQ(b1.size(), 0);
EXPECT_EQ(b2.size(), 0);
EXPECT_EQ(bytes1, original_bytes1);
for (int i = 1; i < 1000; i++) {
b1.insert(generator(i));
}
// We should have allocated out of allocator2.
EXPECT_GT(bytes2, bytes1);
}
// Test move assignment
{
T b1(typename T::key_compare(), allocator1);
T b2(typename T::key_compare(), allocator2);
int64_t original_bytes1 = bytes1;
b1.insert(generator(0));
EXPECT_GT(bytes1, original_bytes1);
// This should propagate the allocator.
b1 = std::move(b2);
EXPECT_EQ(b1.size(), 0);
EXPECT_EQ(bytes1, original_bytes1);
for (int i = 1; i < 1000; i++) {
b1.insert(generator(i));
}
// We should have allocated out of allocator2.
EXPECT_GT(bytes2, bytes1);
}
// Test swap
{
T b1(typename T::key_compare(), allocator1);
T b2(typename T::key_compare(), allocator2);
int64_t original_bytes1 = bytes1;
b1.insert(generator(0));
EXPECT_GT(bytes1, original_bytes1);
// This should swap the allocators.
swap(b1, b2);
EXPECT_EQ(b1.size(), 0);
EXPECT_EQ(b2.size(), 1);
EXPECT_GT(bytes1, original_bytes1);
for (int i = 1; i < 1000; i++) {
b1.insert(generator(i));
}
// We should have allocated out of allocator2.
EXPECT_GT(bytes2, bytes1);
}
allocator1.deallocate(unused1, 1);
allocator2.deallocate(unused2, 1);
}
template <typename T> template <typename T>
void BtreeMapTest() { void BtreeMapTest() {
using value_type = typename T::value_type; using value_type = typename T::value_type;
...@@ -796,10 +696,7 @@ void SetTest() { ...@@ -796,10 +696,7 @@ void SetTest() {
sizeof(absl::btree_set<K>), sizeof(absl::btree_set<K>),
2 * sizeof(void *) + sizeof(typename absl::btree_set<K>::size_type)); 2 * sizeof(void *) + sizeof(typename absl::btree_set<K>::size_type));
using BtreeSet = absl::btree_set<K>; using BtreeSet = absl::btree_set<K>;
using CountingBtreeSet =
absl::btree_set<K, std::less<K>, PropagatingCountingAlloc<K>>;
BtreeTest<BtreeSet, std::set<K>>(); BtreeTest<BtreeSet, std::set<K>>();
BtreeAllocatorTest<CountingBtreeSet>();
} }
template <typename K, int N = 256> template <typename K, int N = 256>
...@@ -808,24 +705,16 @@ void MapTest() { ...@@ -808,24 +705,16 @@ void MapTest() {
sizeof(absl::btree_map<K, K>), sizeof(absl::btree_map<K, K>),
2 * sizeof(void *) + sizeof(typename absl::btree_map<K, K>::size_type)); 2 * sizeof(void *) + sizeof(typename absl::btree_map<K, K>::size_type));
using BtreeMap = absl::btree_map<K, K>; using BtreeMap = absl::btree_map<K, K>;
using CountingBtreeMap =
absl::btree_map<K, K, std::less<K>,
PropagatingCountingAlloc<std::pair<const K, K>>>;
BtreeTest<BtreeMap, std::map<K, K>>(); BtreeTest<BtreeMap, std::map<K, K>>();
BtreeAllocatorTest<CountingBtreeMap>();
BtreeMapTest<BtreeMap>(); BtreeMapTest<BtreeMap>();
} }
TEST(Btree, set_int32) { SetTest<int32_t>(); } TEST(Btree, set_int32) { SetTest<int32_t>(); }
TEST(Btree, set_int64) { SetTest<int64_t>(); }
TEST(Btree, set_string) { SetTest<std::string>(); } TEST(Btree, set_string) { SetTest<std::string>(); }
TEST(Btree, set_cord) { SetTest<absl::Cord>(); } TEST(Btree, set_cord) { SetTest<absl::Cord>(); }
TEST(Btree, set_pair) { SetTest<std::pair<int, int>>(); }
TEST(Btree, map_int32) { MapTest<int32_t>(); } TEST(Btree, map_int32) { MapTest<int32_t>(); }
TEST(Btree, map_int64) { MapTest<int64_t>(); }
TEST(Btree, map_string) { MapTest<std::string>(); } TEST(Btree, map_string) { MapTest<std::string>(); }
TEST(Btree, map_cord) { MapTest<absl::Cord>(); } TEST(Btree, map_cord) { MapTest<absl::Cord>(); }
TEST(Btree, map_pair) { MapTest<std::pair<int, int>>(); }
template <typename K, int N = 256> template <typename K, int N = 256>
void MultiSetTest() { void MultiSetTest() {
...@@ -833,10 +722,7 @@ void MultiSetTest() { ...@@ -833,10 +722,7 @@ void MultiSetTest() {
sizeof(absl::btree_multiset<K>), sizeof(absl::btree_multiset<K>),
2 * sizeof(void *) + sizeof(typename absl::btree_multiset<K>::size_type)); 2 * sizeof(void *) + sizeof(typename absl::btree_multiset<K>::size_type));
using BtreeMSet = absl::btree_multiset<K>; using BtreeMSet = absl::btree_multiset<K>;
using CountingBtreeMSet =
absl::btree_multiset<K, std::less<K>, PropagatingCountingAlloc<K>>;
BtreeMultiTest<BtreeMSet, std::multiset<K>>(); BtreeMultiTest<BtreeMSet, std::multiset<K>>();
BtreeAllocatorTest<CountingBtreeMSet>();
} }
template <typename K, int N = 256> template <typename K, int N = 256>
...@@ -845,24 +731,16 @@ void MultiMapTest() { ...@@ -845,24 +731,16 @@ void MultiMapTest() {
2 * sizeof(void *) + 2 * sizeof(void *) +
sizeof(typename absl::btree_multimap<K, K>::size_type)); sizeof(typename absl::btree_multimap<K, K>::size_type));
using BtreeMMap = absl::btree_multimap<K, K>; using BtreeMMap = absl::btree_multimap<K, K>;
using CountingBtreeMMap =
absl::btree_multimap<K, K, std::less<K>,
PropagatingCountingAlloc<std::pair<const K, K>>>;
BtreeMultiTest<BtreeMMap, std::multimap<K, K>>(); BtreeMultiTest<BtreeMMap, std::multimap<K, K>>();
BtreeMultiMapTest<BtreeMMap>(); BtreeMultiMapTest<BtreeMMap>();
BtreeAllocatorTest<CountingBtreeMMap>();
} }
TEST(Btree, multiset_int32) { MultiSetTest<int32_t>(); } TEST(Btree, multiset_int32) { MultiSetTest<int32_t>(); }
TEST(Btree, multiset_int64) { MultiSetTest<int64_t>(); }
TEST(Btree, multiset_string) { MultiSetTest<std::string>(); } TEST(Btree, multiset_string) { MultiSetTest<std::string>(); }
TEST(Btree, multiset_cord) { MultiSetTest<absl::Cord>(); } TEST(Btree, multiset_cord) { MultiSetTest<absl::Cord>(); }
TEST(Btree, multiset_pair) { MultiSetTest<std::pair<int, int>>(); }
TEST(Btree, multimap_int32) { MultiMapTest<int32_t>(); } TEST(Btree, multimap_int32) { MultiMapTest<int32_t>(); }
TEST(Btree, multimap_int64) { MultiMapTest<int64_t>(); }
TEST(Btree, multimap_string) { MultiMapTest<std::string>(); } TEST(Btree, multimap_string) { MultiMapTest<std::string>(); }
TEST(Btree, multimap_cord) { MultiMapTest<absl::Cord>(); } TEST(Btree, multimap_cord) { MultiMapTest<absl::Cord>(); }
TEST(Btree, multimap_pair) { MultiMapTest<std::pair<int, int>>(); }
struct CompareIntToString { struct CompareIntToString {
bool operator()(const std::string &a, const std::string &b) const { bool operator()(const std::string &a, const std::string &b) const {
...@@ -2511,50 +2389,23 @@ TEST(Btree, TryEmplaceWithHintAndMultipleValueArgsWorks) { ...@@ -2511,50 +2389,23 @@ TEST(Btree, TryEmplaceWithHintAndMultipleValueArgsWorks) {
EXPECT_EQ(std::string(10, 'a'), m[1]); EXPECT_EQ(std::string(10, 'a'), m[1]);
} }
TEST(Btree, MoveAssignmentAllocatorPropagation) { template <typename Alloc>
InstanceTracker tracker; using BtreeSetAlloc = absl::btree_set<int, std::less<int>, Alloc>;
int64_t bytes1 = 0, bytes2 = 0;
MoveAssignPropagatingCountingAlloc<MovableOnlyInstance> allocator1(&bytes1);
MoveAssignPropagatingCountingAlloc<MovableOnlyInstance> allocator2(&bytes2);
std::less<MovableOnlyInstance> cmp;
// Test propagating allocator_type.
{
absl::btree_set<MovableOnlyInstance, std::less<MovableOnlyInstance>,
MoveAssignPropagatingCountingAlloc<MovableOnlyInstance>>
set1(cmp, allocator1), set2(cmp, allocator2);
for (int i = 0; i < 100; ++i) set1.insert(MovableOnlyInstance(i));
tracker.ResetCopiesMovesSwaps();
set2 = std::move(set1);
EXPECT_EQ(tracker.moves(), 0);
}
// Test non-propagating allocator_type with equal allocators.
{
absl::btree_set<MovableOnlyInstance, std::less<MovableOnlyInstance>,
CountingAllocator<MovableOnlyInstance>>
set1(cmp, allocator1), set2(cmp, allocator1);
for (int i = 0; i < 100; ++i) set1.insert(MovableOnlyInstance(i)); TEST(Btree, AllocatorPropagation) {
TestAllocPropagation<BtreeSetAlloc>();
}
tracker.ResetCopiesMovesSwaps(); TEST(Btree, MinimumAlignmentAllocator) {
set2 = std::move(set1); absl::btree_set<int8_t, std::less<int8_t>, MinimumAlignmentAlloc<int8_t>> set;
EXPECT_EQ(tracker.moves(), 0);
}
// Test non-propagating allocator_type with different allocators.
{
absl::btree_set<MovableOnlyInstance, std::less<MovableOnlyInstance>,
CountingAllocator<MovableOnlyInstance>>
set1(cmp, allocator1), set2(cmp, allocator2);
for (int i = 0; i < 100; ++i) set1.insert(MovableOnlyInstance(i)); // Do some basic operations. Test that everything is fine when allocator uses
// minimal alignment.
for (int8_t i = 0; i < 100; ++i) set.insert(i);
set.erase(set.find(50), set.end());
for (int8_t i = 51; i < 101; ++i) set.insert(i);
tracker.ResetCopiesMovesSwaps(); EXPECT_EQ(set.size(), 100);
set2 = std::move(set1);
EXPECT_GE(tracker.moves(), 100);
}
} }
TEST(Btree, EmptyTree) { TEST(Btree, EmptyTree) {
......
...@@ -2084,16 +2084,6 @@ TEST(Table, UnstablePointers) { ...@@ -2084,16 +2084,6 @@ TEST(Table, UnstablePointers) {
EXPECT_NE(old_ptr, addr(0)); EXPECT_NE(old_ptr, addr(0));
} }
bool IsAssertEnabled() {
// Use an assert with side-effects to figure out if they are actually enabled.
bool assert_enabled = false;
assert([&]() { // NOLINT
assert_enabled = true;
return true;
}());
return assert_enabled;
}
TEST(TableDeathTest, InvalidIteratorAsserts) { TEST(TableDeathTest, InvalidIteratorAsserts) {
if (!IsAssertEnabled() && !SwisstableGenerationsEnabled()) if (!IsAssertEnabled() && !SwisstableGenerationsEnabled())
GTEST_SKIP() << "Assertions not enabled."; GTEST_SKIP() << "Assertions not enabled.";
......
...@@ -15,11 +15,13 @@ ...@@ -15,11 +15,13 @@
#ifndef ABSL_CONTAINER_INTERNAL_TEST_ALLOCATOR_H_ #ifndef ABSL_CONTAINER_INTERNAL_TEST_ALLOCATOR_H_
#define ABSL_CONTAINER_INTERNAL_TEST_ALLOCATOR_H_ #define ABSL_CONTAINER_INTERNAL_TEST_ALLOCATOR_H_
#include <cassert>
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <memory> #include <memory>
#include <type_traits> #include <type_traits>
#include "gtest/gtest.h"
#include "absl/base/config.h" #include "absl/base/config.h"
namespace absl { namespace absl {
...@@ -171,25 +173,6 @@ struct SwapPropagatingCountingAlloc : public CountingAllocator<T> { ...@@ -171,25 +173,6 @@ struct SwapPropagatingCountingAlloc : public CountingAllocator<T> {
}; };
}; };
template <typename T>
struct PropagatingCountingAlloc : public CountingAllocator<T> {
using propagate_on_container_copy_assignment = std::true_type;
using propagate_on_container_move_assignment = std::true_type;
using propagate_on_container_swap = std::true_type;
using Base = CountingAllocator<T>;
using Base::Base;
template <typename U>
explicit PropagatingCountingAlloc(const PropagatingCountingAlloc<U> &other)
: Base(other.bytes_used_, other.instance_count_) {}
template <typename U>
struct rebind {
using other = PropagatingCountingAlloc<U>;
};
};
// Tries to allocate memory at the minimum alignment even when the default // Tries to allocate memory at the minimum alignment even when the default
// allocator uses a higher alignment. // allocator uses a higher alignment.
template <typename T> template <typename T>
...@@ -218,6 +201,185 @@ struct MinimumAlignmentAlloc : std::allocator<T> { ...@@ -218,6 +201,185 @@ struct MinimumAlignmentAlloc : std::allocator<T> {
} }
}; };
inline bool IsAssertEnabled() {
// Use an assert with side-effects to figure out if they are actually enabled.
bool assert_enabled = false;
assert([&]() { // NOLINT
assert_enabled = true;
return true;
}());
return assert_enabled;
}
template <template <class Alloc> class Container>
void TestCopyAssignAllocPropagation() {
int64_t bytes1 = 0, instances1 = 0, bytes2 = 0, instances2 = 0;
CopyAssignPropagatingCountingAlloc<int> allocator1(&bytes1, &instances1);
CopyAssignPropagatingCountingAlloc<int> allocator2(&bytes2, &instances2);
// Test propagating allocator_type.
{
Container<CopyAssignPropagatingCountingAlloc<int>> c1(allocator1);
Container<CopyAssignPropagatingCountingAlloc<int>> c2(allocator2);
for (int i = 0; i < 100; ++i) c1.insert(i);
EXPECT_NE(c2.get_allocator(), allocator1);
EXPECT_EQ(instances1, 100);
EXPECT_EQ(instances2, 0);
c2 = c1;
EXPECT_EQ(c2.get_allocator(), allocator1);
EXPECT_EQ(instances1, 200);
EXPECT_EQ(instances2, 0);
}
// Test non-propagating allocator_type with different allocators.
{
Container<CountingAllocator<int>> c1(allocator1), c2(allocator2);
for (int i = 0; i < 100; ++i) c1.insert(i);
EXPECT_EQ(c2.get_allocator(), allocator2);
EXPECT_EQ(instances1, 100);
EXPECT_EQ(instances2, 0);
c2 = c1;
EXPECT_EQ(c2.get_allocator(), allocator2);
EXPECT_EQ(instances1, 100);
EXPECT_EQ(instances2, 100);
}
EXPECT_EQ(bytes1, 0);
EXPECT_EQ(instances1, 0);
EXPECT_EQ(bytes2, 0);
EXPECT_EQ(instances2, 0);
}
template <template <class Alloc> class Container>
void TestMoveAssignAllocPropagation() {
int64_t bytes1 = 0, instances1 = 0, bytes2 = 0, instances2 = 0;
MoveAssignPropagatingCountingAlloc<int> allocator1(&bytes1, &instances1);
MoveAssignPropagatingCountingAlloc<int> allocator2(&bytes2, &instances2);
// Test propagating allocator_type.
{
Container<MoveAssignPropagatingCountingAlloc<int>> c1(allocator1);
Container<MoveAssignPropagatingCountingAlloc<int>> c2(allocator2);
for (int i = 0; i < 100; ++i) c1.insert(i);
EXPECT_NE(c2.get_allocator(), allocator1);
EXPECT_EQ(instances1, 100);
EXPECT_EQ(instances2, 0);
c2 = std::move(c1);
EXPECT_EQ(c2.get_allocator(), allocator1);
EXPECT_EQ(instances1, 100);
EXPECT_EQ(instances2, 0);
}
// Test non-propagating allocator_type with equal allocators.
{
Container<CountingAllocator<int>> c1(allocator1), c2(allocator1);
for (int i = 0; i < 100; ++i) c1.insert(i);
EXPECT_EQ(c2.get_allocator(), allocator1);
EXPECT_EQ(instances1, 100);
EXPECT_EQ(instances2, 0);
c2 = std::move(c1);
EXPECT_EQ(c2.get_allocator(), allocator1);
EXPECT_EQ(instances1, 100);
EXPECT_EQ(instances2, 0);
}
// Test non-propagating allocator_type with different allocators.
{
Container<CountingAllocator<int>> c1(allocator1), c2(allocator2);
for (int i = 0; i < 100; ++i) c1.insert(i);
EXPECT_NE(c2.get_allocator(), allocator1);
EXPECT_EQ(instances1, 100);
EXPECT_EQ(instances2, 0);
c2 = std::move(c1);
EXPECT_EQ(c2.get_allocator(), allocator2);
EXPECT_LE(instances1, 100); // The values in c1 may or may not have been
// destroyed at this point.
EXPECT_EQ(instances2, 100);
}
EXPECT_EQ(bytes1, 0);
EXPECT_EQ(instances1, 0);
EXPECT_EQ(bytes2, 0);
EXPECT_EQ(instances2, 0);
}
template <template <class Alloc> class Container>
void TestSwapAllocPropagation() {
int64_t bytes1 = 0, instances1 = 0, bytes2 = 0, instances2 = 0;
SwapPropagatingCountingAlloc<int> allocator1(&bytes1, &instances1);
SwapPropagatingCountingAlloc<int> allocator2(&bytes2, &instances2);
// Test propagating allocator_type.
{
Container<SwapPropagatingCountingAlloc<int>> c1(allocator1), c2(allocator2);
for (int i = 0; i < 100; ++i) c1.insert(i);
EXPECT_NE(c2.get_allocator(), allocator1);
EXPECT_EQ(instances1, 100);
EXPECT_EQ(instances2, 0);
c2.swap(c1);
EXPECT_EQ(c2.get_allocator(), allocator1);
EXPECT_EQ(instances1, 100);
EXPECT_EQ(instances2, 0);
}
// Test non-propagating allocator_type with equal allocators.
{
Container<CountingAllocator<int>> c1(allocator1), c2(allocator1);
for (int i = 0; i < 100; ++i) c1.insert(i);
EXPECT_EQ(c2.get_allocator(), allocator1);
EXPECT_EQ(instances1, 100);
EXPECT_EQ(instances2, 0);
c2.swap(c1);
EXPECT_EQ(c2.get_allocator(), allocator1);
EXPECT_EQ(instances1, 100);
EXPECT_EQ(instances2, 0);
}
// Test non-propagating allocator_type with different allocators.
{
Container<CountingAllocator<int>> c1(allocator1), c2(allocator2);
for (int i = 0; i < 100; ++i) c1.insert(i);
EXPECT_NE(c1.get_allocator(), c2.get_allocator());
if (IsAssertEnabled()) {
EXPECT_DEATH(c2.swap(c1), "");
}
}
EXPECT_EQ(bytes1, 0);
EXPECT_EQ(instances1, 0);
EXPECT_EQ(bytes2, 0);
EXPECT_EQ(instances2, 0);
}
template <template <class Alloc> class Container>
void TestAllocPropagation() {
TestCopyAssignAllocPropagation<Container>();
TestMoveAssignAllocPropagation<Container>();
TestSwapAllocPropagation<Container>();
}
} // namespace container_internal } // namespace container_internal
ABSL_NAMESPACE_END ABSL_NAMESPACE_END
} // namespace absl } // namespace absl
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment