Commit c154d20a by Benjamin Barenblat Committed by Copybara-Service

Split absl/hash/hash_test.cc into two files

hash_test.cc leans heavily on INSTANTIATE_TYPED_TEST_SUITE_P, which is
quite memory- and CPU-hungry. Split a few heavyweight tests into a new
hash_instantiated_test.cc, reducing peak RAM consumption (or, on
multicore systems, compilation time).

PiperOrigin-RevId: 535305679
Change-Id: Ic204da0a47c749c3f7db5f902ade8d74ed3043bb
parent 4e4b48fa
......@@ -68,13 +68,17 @@ cc_library(
cc_test(
name = "hash_test",
srcs = ["hash_test.cc"],
srcs = [
"hash_test.cc",
"internal/hash_test.h",
],
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":hash",
":hash_testing",
":spy_hash_state",
"//absl/base:config",
"//absl/base:core_headers",
"//absl/container:btree",
"//absl/container:flat_hash_map",
......@@ -88,6 +92,27 @@ cc_test(
],
)
cc_test(
name = "hash_instantiated_test",
srcs = [
"hash_instantiated_test.cc",
"internal/hash_test.h",
],
copts = ABSL_TEST_COPTS,
linkopts = ABSL_DEFAULT_LINKOPTS,
deps = [
":hash",
":hash_testing",
"//absl/base:config",
"//absl/container:btree",
"//absl/container:flat_hash_map",
"//absl/container:flat_hash_set",
"//absl/container:node_hash_map",
"//absl/container:node_hash_set",
"@com_google_googletest//:gtest_main",
],
)
cc_binary(
name = "hash_benchmark",
testonly = 1,
......
......@@ -64,6 +64,7 @@ absl_cc_test(
hash_test
SRCS
"hash_test.cc"
"internal/hash_test.h"
COPTS
${ABSL_TEST_COPTS}
DEPS
......@@ -82,6 +83,26 @@ absl_cc_test(
GTest::gmock_main
)
absl_cc_test(
NAME
hash_instantiated_test
SRCS
"hash_test.cc"
"internal/hash_test.h"
COPTS
${ABSL_TEST_COPTS}
DEPS
absl::hash
absl::hash_testing
absl::config
absl::btree
absl::flat_hash_map
absl::flat_hash_set
absl::node_hash_map
absl::node_hash_set
GTest::gtest_main
)
# Internal-only target, do not depend on directly.
#
# Note: Even though external code should not depend on this target
......
// Copyright 2018 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.
// This file contains a few select absl::Hash tests that, due to their reliance
// on INSTANTIATE_TYPED_TEST_SUITE_P, require a large amount of memory to
// compile. Put new tests in hash_test.cc, not this file.
#include "absl/hash/hash.h"
#include <stddef.h>
#include <algorithm>
#include <deque>
#include <forward_list>
#include <initializer_list>
#include <list>
#include <map>
#include <set>
#include <string>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
#include "gtest/gtest.h"
#include "absl/container/btree_map.h"
#include "absl/container/btree_set.h"
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/container/node_hash_map.h"
#include "absl/container/node_hash_set.h"
#include "absl/hash/hash_testing.h"
#include "absl/hash/internal/hash_test.h"
namespace {
using ::absl::hash_test_internal::is_hashable;
using ::absl::hash_test_internal::TypeErasedContainer;
// Dummy type with unordered equality and hashing semantics. This preserves
// input order internally, and is used below to ensure we get test coverage
// for equal sequences with different iteraton orders.
template <typename T>
class UnorderedSequence {
public:
UnorderedSequence() = default;
template <typename TT>
UnorderedSequence(std::initializer_list<TT> l)
: values_(l.begin(), l.end()) {}
template <typename ForwardIterator,
typename std::enable_if<!std::is_integral<ForwardIterator>::value,
bool>::type = true>
UnorderedSequence(ForwardIterator begin, ForwardIterator end)
: values_(begin, end) {}
// one-argument constructor of value type T, to appease older toolchains that
// get confused by one-element initializer lists in some contexts
explicit UnorderedSequence(const T& v) : values_(&v, &v + 1) {}
using value_type = T;
size_t size() const { return values_.size(); }
typename std::vector<T>::const_iterator begin() const {
return values_.begin();
}
typename std::vector<T>::const_iterator end() const { return values_.end(); }
friend bool operator==(const UnorderedSequence& lhs,
const UnorderedSequence& rhs) {
return lhs.size() == rhs.size() &&
std::is_permutation(lhs.begin(), lhs.end(), rhs.begin());
}
friend bool operator!=(const UnorderedSequence& lhs,
const UnorderedSequence& rhs) {
return !(lhs == rhs);
}
template <typename H>
friend H AbslHashValue(H h, const UnorderedSequence& u) {
return H::combine(H::combine_unordered(std::move(h), u.begin(), u.end()),
u.size());
}
private:
std::vector<T> values_;
};
template <typename T>
class HashValueSequenceTest : public testing::Test {};
TYPED_TEST_SUITE_P(HashValueSequenceTest);
TYPED_TEST_P(HashValueSequenceTest, BasicUsage) {
EXPECT_TRUE((is_hashable<TypeParam>::value));
using IntType = typename TypeParam::value_type;
auto a = static_cast<IntType>(0);
auto b = static_cast<IntType>(23);
auto c = static_cast<IntType>(42);
std::vector<TypeParam> exemplars = {
TypeParam(), TypeParam(), TypeParam{a, b, c},
TypeParam{a, c, b}, TypeParam{c, a, b}, TypeParam{a},
TypeParam{a, a}, TypeParam{a, a, a}, TypeParam{a, a, b},
TypeParam{a, b, a}, TypeParam{b, a, a}, TypeParam{a, b},
TypeParam{b, c}};
EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(exemplars));
}
REGISTER_TYPED_TEST_SUITE_P(HashValueSequenceTest, BasicUsage);
using IntSequenceTypes = testing::Types<
std::deque<int>, std::forward_list<int>, std::list<int>, std::vector<int>,
std::vector<bool>, TypeErasedContainer<std::vector<int>>, std::set<int>,
std::multiset<int>, UnorderedSequence<int>,
TypeErasedContainer<UnorderedSequence<int>>, std::unordered_set<int>,
std::unordered_multiset<int>, absl::flat_hash_set<int>,
absl::node_hash_set<int>, absl::btree_set<int>>;
INSTANTIATE_TYPED_TEST_SUITE_P(My, HashValueSequenceTest, IntSequenceTypes);
template <typename T>
class HashValueNestedSequenceTest : public testing::Test {};
TYPED_TEST_SUITE_P(HashValueNestedSequenceTest);
TYPED_TEST_P(HashValueNestedSequenceTest, BasicUsage) {
using T = TypeParam;
using V = typename T::value_type;
std::vector<T> exemplars = {
// empty case
T{},
// sets of empty sets
T{V{}}, T{V{}, V{}}, T{V{}, V{}, V{}},
// multisets of different values
T{V{1}}, T{V{1, 1}, V{1, 1}}, T{V{1, 1, 1}, V{1, 1, 1}, V{1, 1, 1}},
// various orderings of same nested sets
T{V{}, V{1, 2}}, T{V{}, V{2, 1}}, T{V{1, 2}, V{}}, T{V{2, 1}, V{}},
// various orderings of various nested sets, case 2
T{V{1, 2}, V{3, 4}}, T{V{1, 2}, V{4, 3}}, T{V{1, 3}, V{2, 4}},
T{V{1, 3}, V{4, 2}}, T{V{1, 4}, V{2, 3}}, T{V{1, 4}, V{3, 2}},
T{V{2, 3}, V{1, 4}}, T{V{2, 3}, V{4, 1}}, T{V{2, 4}, V{1, 3}},
T{V{2, 4}, V{3, 1}}, T{V{3, 4}, V{1, 2}}, T{V{3, 4}, V{2, 1}}};
EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(exemplars));
}
REGISTER_TYPED_TEST_SUITE_P(HashValueNestedSequenceTest, BasicUsage);
template <typename T>
using TypeErasedSet = TypeErasedContainer<UnorderedSequence<T>>;
using NestedIntSequenceTypes = testing::Types<
std::vector<std::vector<int>>, std::vector<UnorderedSequence<int>>,
std::vector<TypeErasedSet<int>>, UnorderedSequence<std::vector<int>>,
UnorderedSequence<UnorderedSequence<int>>,
UnorderedSequence<TypeErasedSet<int>>, TypeErasedSet<std::vector<int>>,
TypeErasedSet<UnorderedSequence<int>>, TypeErasedSet<TypeErasedSet<int>>>;
INSTANTIATE_TYPED_TEST_SUITE_P(My, HashValueNestedSequenceTest,
NestedIntSequenceTypes);
template <typename T>
class HashValueAssociativeMapTest : public testing::Test {};
TYPED_TEST_SUITE_P(HashValueAssociativeMapTest);
TYPED_TEST_P(HashValueAssociativeMapTest, BasicUsage) {
using M = TypeParam;
using V = typename M::value_type;
std::vector<M> exemplars{M{},
M{V{0, "foo"}},
M{V{1, "foo"}},
M{V{0, "bar"}},
M{V{1, "bar"}},
M{V{0, "foo"}, V{42, "bar"}},
M{V{42, "bar"}, V{0, "foo"}},
M{V{1, "foo"}, V{42, "bar"}},
M{V{1, "foo"}, V{43, "bar"}},
M{V{1, "foo"}, V{43, "baz"}}};
EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(exemplars));
}
REGISTER_TYPED_TEST_SUITE_P(HashValueAssociativeMapTest, BasicUsage);
using AssociativeMapTypes = testing::Types<
std::map<int, std::string>, std::unordered_map<int, std::string>,
absl::flat_hash_map<int, std::string>,
absl::node_hash_map<int, std::string>, absl::btree_map<int, std::string>,
UnorderedSequence<std::pair<const int, std::string>>>;
INSTANTIATE_TYPED_TEST_SUITE_P(My, HashValueAssociativeMapTest,
AssociativeMapTypes);
template <typename T>
class HashValueAssociativeMultimapTest : public testing::Test {};
TYPED_TEST_SUITE_P(HashValueAssociativeMultimapTest);
TYPED_TEST_P(HashValueAssociativeMultimapTest, BasicUsage) {
using MM = TypeParam;
using V = typename MM::value_type;
std::vector<MM> exemplars{MM{},
MM{V{0, "foo"}},
MM{V{1, "foo"}},
MM{V{0, "bar"}},
MM{V{1, "bar"}},
MM{V{0, "foo"}, V{0, "bar"}},
MM{V{0, "bar"}, V{0, "foo"}},
MM{V{0, "foo"}, V{42, "bar"}},
MM{V{1, "foo"}, V{42, "bar"}},
MM{V{1, "foo"}, V{1, "foo"}, V{43, "bar"}},
MM{V{1, "foo"}, V{43, "bar"}, V{1, "foo"}},
MM{V{1, "foo"}, V{43, "baz"}}};
EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(exemplars));
}
REGISTER_TYPED_TEST_SUITE_P(HashValueAssociativeMultimapTest, BasicUsage);
using AssociativeMultimapTypes =
testing::Types<std::multimap<int, std::string>,
std::unordered_multimap<int, std::string>>;
INSTANTIATE_TYPED_TEST_SUITE_P(My, HashValueAssociativeMultimapTest,
AssociativeMultimapTypes);
} // namespace
// Copyright 2023 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.
// Common code shared between absl/hash/hash_test.cc and
// absl/hash/hash_instantiated_test.cc.
#ifndef ABSL_HASH_INTERNAL_HASH_TEST_H_
#define ABSL_HASH_INTERNAL_HASH_TEST_H_
#include <type_traits>
#include <utility>
#include "absl/base/config.h"
#include "absl/hash/hash.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace hash_test_internal {
// Utility wrapper of T for the purposes of testing the `AbslHash` type erasure
// mechanism. `TypeErasedValue<T>` can be constructed with a `T`, and can
// be compared and hashed. However, all hashing goes through the hashing
// type-erasure framework.
template <typename T>
class TypeErasedValue {
public:
TypeErasedValue() = default;
TypeErasedValue(const TypeErasedValue&) = default;
TypeErasedValue(TypeErasedValue&&) = default;
explicit TypeErasedValue(const T& n) : n_(n) {}
template <typename H>
friend H AbslHashValue(H hash_state, const TypeErasedValue& v) {
v.HashValue(absl::HashState::Create(&hash_state));
return hash_state;
}
void HashValue(absl::HashState state) const {
absl::HashState::combine(std::move(state), n_);
}
bool operator==(const TypeErasedValue& rhs) const { return n_ == rhs.n_; }
bool operator!=(const TypeErasedValue& rhs) const { return !(*this == rhs); }
private:
T n_;
};
// A TypeErasedValue refinement, for containers. It exposes the wrapped
// `value_type` and is constructible from an initializer list.
template <typename T>
class TypeErasedContainer : public TypeErasedValue<T> {
public:
using value_type = typename T::value_type;
TypeErasedContainer() = default;
TypeErasedContainer(const TypeErasedContainer&) = default;
TypeErasedContainer(TypeErasedContainer&&) = default;
explicit TypeErasedContainer(const T& n) : TypeErasedValue<T>(n) {}
TypeErasedContainer(std::initializer_list<value_type> init_list)
: TypeErasedContainer(T(init_list.begin(), init_list.end())) {}
// one-argument constructor of value type T, to appease older toolchains that
// get confused by one-element initializer lists in some contexts
explicit TypeErasedContainer(const value_type& v)
: TypeErasedContainer(T(&v, &v + 1)) {}
};
// Helper trait to verify if T is hashable. We use absl::Hash's poison status to
// detect it.
template <typename T>
using is_hashable = std::is_default_constructible<absl::Hash<T>>;
} // namespace hash_test_internal
ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_HASH_INTERNAL_HASH_TEST_H_
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