abseil-cpp/absl/hash/hash_testing.h
Go to the documentation of this file.
1 // Copyright 2018 The Abseil Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef ABSL_HASH_HASH_TESTING_H_
16 #define ABSL_HASH_HASH_TESTING_H_
17 
18 #include <initializer_list>
19 #include <tuple>
20 #include <type_traits>
21 #include <vector>
22 
23 #include "gmock/gmock.h"
24 #include "gtest/gtest.h"
25 #include "absl/hash/internal/spy_hash_state.h"
26 #include "absl/meta/type_traits.h"
27 #include "absl/strings/str_cat.h"
28 #include "absl/types/variant.h"
29 
30 namespace absl {
32 
33 // Run the absl::Hash algorithm over all the elements passed in and verify that
34 // their hash expansion is congruent with their `==` operator.
35 //
36 // It is used in conjunction with EXPECT_TRUE. Failures will output information
37 // on what requirement failed and on which objects.
38 //
39 // Users should pass a collection of types as either an initializer list or a
40 // container of cases.
41 //
42 // EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(
43 // {v1, v2, ..., vN}));
44 //
45 // std::vector<MyType> cases;
46 // // Fill cases...
47 // EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(cases));
48 //
49 // Users can pass a variety of types for testing heterogeneous lookup with
50 // `std::make_tuple`:
51 //
52 // EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(
53 // std::make_tuple(v1, v2, ..., vN)));
54 //
55 //
56 // Ideally, the values passed should provide enough coverage of the `==`
57 // operator and the AbslHashValue implementations.
58 // For dynamically sized types, the empty state should usually be included in
59 // the values.
60 //
61 // The function accepts an optional comparator function, in case that `==` is
62 // not enough for the values provided.
63 //
64 // Usage:
65 //
66 // EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(
67 // std::make_tuple(v1, v2, ..., vN), MyCustomEq{}));
68 //
69 // It checks the following requirements:
70 // 1. The expansion for a value is deterministic.
71 // 2. For any two objects `a` and `b` in the sequence, if `a == b` evaluates
72 // to true, then their hash expansion must be equal.
73 // 3. If `a == b` evaluates to false their hash expansion must be unequal.
74 // 4. If `a == b` evaluates to false neither hash expansion can be a
75 // suffix of the other.
76 // 5. AbslHashValue overloads should not be called by the user. They are only
77 // meant to be called by the framework. Users should call H::combine() and
78 // H::combine_contiguous().
79 // 6. No moved-from instance of the hash state is used in the implementation
80 // of AbslHashValue.
81 //
82 // The values do not have to have the same type. This can be useful for
83 // equivalent types that support heterogeneous lookup.
84 //
85 // A possible reason for breaking (2) is combining state in the hash expansion
86 // that was not used in `==`.
87 // For example:
88 //
89 // struct Bad2 {
90 // int a, b;
91 // template <typename H>
92 // friend H AbslHashValue(H state, Bad2 x) {
93 // // Uses a and b.
94 // return H::combine(std::move(state), x.a, x.b);
95 // }
96 // friend bool operator==(Bad2 x, Bad2 y) {
97 // // Only uses a.
98 // return x.a == y.a;
99 // }
100 // };
101 //
102 // As for (3), breaking this usually means that there is state being passed to
103 // the `==` operator that is not used in the hash expansion.
104 // For example:
105 //
106 // struct Bad3 {
107 // int a, b;
108 // template <typename H>
109 // friend H AbslHashValue(H state, Bad3 x) {
110 // // Only uses a.
111 // return H::combine(std::move(state), x.a);
112 // }
113 // friend bool operator==(Bad3 x, Bad3 y) {
114 // // Uses a and b.
115 // return x.a == y.a && x.b == y.b;
116 // }
117 // };
118 //
119 // Finally, a common way to break 4 is by combining dynamic ranges without
120 // combining the size of the range.
121 // For example:
122 //
123 // struct Bad4 {
124 // int *p, size;
125 // template <typename H>
126 // friend H AbslHashValue(H state, Bad4 x) {
127 // return H::combine_contiguous(std::move(state), x.p, x.p + x.size);
128 // }
129 // friend bool operator==(Bad4 x, Bad4 y) {
130 // // Compare two ranges for equality. C++14 code can instead use std::equal.
131 // return absl::equal(x.p, x.p + x.size, y.p, y.p + y.size);
132 // }
133 // };
134 //
135 // An easy solution to this is to combine the size after combining the range,
136 // like so:
137 // template <typename H>
138 // friend H AbslHashValue(H state, Bad4 x) {
139 // return H::combine(
140 // H::combine_contiguous(std::move(state), x.p, x.p + x.size), x.size);
141 // }
142 //
143 template <int&... ExplicitBarrier, typename Container>
146 
147 template <int&... ExplicitBarrier, typename Container, typename Eq>
149 VerifyTypeImplementsAbslHashCorrectly(const Container& values, Eq equals);
150 
151 template <int&..., typename T>
153 VerifyTypeImplementsAbslHashCorrectly(std::initializer_list<T> values);
154 
155 template <int&..., typename T, typename Eq>
157 VerifyTypeImplementsAbslHashCorrectly(std::initializer_list<T> values,
158  Eq equals);
159 
160 namespace hash_internal {
161 
162 struct PrintVisitor {
163  size_t index;
164  template <typename T>
165  std::string operator()(const T* value) const {
166  return absl::StrCat("#", index, "(", testing::PrintToString(*value), ")");
167  }
168 };
169 
170 template <typename Eq>
171 struct EqVisitor {
173  template <typename T, typename U>
174  bool operator()(const T* t, const U* u) const {
175  return eq(*t, *u);
176  }
177 };
178 
180  template <typename T>
181  SpyHashState operator()(const T* value) const {
183  }
184 };
185 
186 template <typename Container, typename Eq>
189  using V = typename Container::value_type;
190 
191  struct Info {
192  const V& value;
193  size_t index;
194  std::string ToString() const {
196  }
197  SpyHashState expand() const { return absl::visit(ExpandVisitor{}, value); }
198  };
199 
200  using EqClass = std::vector<Info>;
201  std::vector<EqClass> classes;
202 
203  // Gather the values in equivalence classes.
204  size_t i = 0;
205  for (const auto& value : values) {
206  EqClass* c = nullptr;
207  for (auto& eqclass : classes) {
208  if (absl::visit(EqVisitor<Eq>{equals}, value, eqclass[0].value)) {
209  c = &eqclass;
210  break;
211  }
212  }
213  if (c == nullptr) {
214  classes.emplace_back();
215  c = &classes.back();
216  }
217  c->push_back({value, i});
218  ++i;
219 
220  // Verify potential errors captured by SpyHashState.
221  if (auto error = c->back().expand().error()) {
222  return testing::AssertionFailure() << *error;
223  }
224  }
225 
226  if (classes.size() < 2) {
228  << "At least two equivalence classes are expected.";
229  }
230 
231  // We assume that equality is correctly implemented.
232  // Now we verify that AbslHashValue is also correctly implemented.
233 
234  for (const auto& c : classes) {
235  // All elements of the equivalence class must have the same hash
236  // expansion.
237  const SpyHashState expected = c[0].expand();
238  for (const Info& v : c) {
239  if (v.expand() != v.expand()) {
241  << "Hash expansion for " << v.ToString()
242  << " is non-deterministic.";
243  }
244  if (v.expand() != expected) {
246  << "Values " << c[0].ToString() << " and " << v.ToString()
247  << " evaluate as equal but have an unequal hash expansion.";
248  }
249  }
250 
251  // Elements from other classes must have different hash expansion.
252  for (const auto& c2 : classes) {
253  if (&c == &c2) continue;
254  const SpyHashState c2_hash = c2[0].expand();
255  switch (SpyHashState::Compare(expected, c2_hash)) {
258  << "Values " << c[0].ToString() << " and " << c2[0].ToString()
259  << " evaluate as unequal but have an equal hash expansion.";
262  << "Hash expansion of " << c2[0].ToString()
263  << " is a suffix of the hash expansion of " << c[0].ToString()
264  << ".";
267  << "Hash expansion of " << c[0].ToString()
268  << " is a suffix of the hash expansion of " << c2[0].ToString()
269  << ".";
271  break;
272  }
273  }
274  }
275  return testing::AssertionSuccess();
276 }
277 
278 template <typename... T>
279 struct TypeSet {
280  template <typename U, bool = disjunction<std::is_same<T, U>...>::value>
281  struct Insert {
282  using type = TypeSet<U, T...>;
283  };
284  template <typename U>
285  struct Insert<U, true> {
286  using type = TypeSet;
287  };
288 
289  template <template <typename...> class C>
290  using apply = C<T...>;
291 };
292 
293 template <typename... T>
294 struct MakeTypeSet : TypeSet<> {};
295 template <typename T, typename... Ts>
296 struct MakeTypeSet<T, Ts...> : MakeTypeSet<Ts...>::template Insert<T>::type {};
297 
298 template <typename... T>
299 using VariantForTypes = typename MakeTypeSet<
300  const typename std::decay<T>::type*...>::template apply<absl::variant>;
301 
302 template <typename Container>
305  using Out = std::vector<V>;
306 
307  static Out Do(const Container& values) {
308  Out out;
309  for (const auto& v : values) out.push_back(&v);
310  return out;
311  }
312 };
313 
314 template <typename... T>
315 struct ContainerAsVector<std::tuple<T...>> {
316  using V = VariantForTypes<T...>;
317  using Out = std::vector<V>;
318 
319  template <size_t... I>
320  static Out DoImpl(const std::tuple<T...>& tuple, absl::index_sequence<I...>) {
321  return Out{&std::get<I>(tuple)...};
322  }
323 
324  static Out Do(const std::tuple<T...>& values) {
325  return DoImpl(values, absl::index_sequence_for<T...>());
326  }
327 };
328 
329 template <>
330 struct ContainerAsVector<std::tuple<>> {
331  static std::vector<VariantForTypes<int>> Do(std::tuple<>) { return {}; }
332 };
333 
335  template <typename T, typename U>
336  bool operator()(const T& t, const U& u) const {
337  return t == u;
338  }
339 };
340 
341 } // namespace hash_internal
342 
343 template <int&..., typename Container>
349 }
350 
351 template <int&..., typename Container, typename Eq>
356 }
357 
358 template <int&..., typename T>
362  hash_internal::ContainerAsVector<std::initializer_list<T>>::Do(values),
364 }
365 
366 template <int&..., typename T, typename Eq>
369  Eq equals) {
371  hash_internal::ContainerAsVector<std::initializer_list<T>>::Do(values),
372  equals);
373 }
374 
376 } // namespace absl
377 
378 #endif // ABSL_HASH_HASH_TESTING_H_
absl::hash_internal::SpyHashStateImpl
Definition: abseil-cpp/absl/hash/internal/spy_hash_state.h:42
absl::hash_internal::DefaultEquals
Definition: abseil-cpp/absl/hash/hash_testing.h:334
testing::AssertionFailure
AssertionResult AssertionFailure()
Definition: bloaty/third_party/googletest/googletest/src/gtest.cc:1028
absl::hash_internal::DefaultEquals::operator()
bool operator()(const T &t, const U &u) const
Definition: abseil-cpp/absl/hash/hash_testing.h:336
absl::VerifyTypeImplementsAbslHashCorrectly
ABSL_NAMESPACE_BEGIN ABSL_MUST_USE_RESULT testing::AssertionResult VerifyTypeImplementsAbslHashCorrectly(const Container &values)
Definition: abseil-cpp/absl/hash/hash_testing.h:345
absl::hash_internal::ContainerAsVector
Definition: abseil-cpp/absl/hash/hash_testing.h:303
absl::StrCat
std::string StrCat(const AlphaNum &a, const AlphaNum &b)
Definition: abseil-cpp/absl/strings/str_cat.cc:98
absl::hash_internal::SpyHashStateImpl::combine
static SpyHashStateImpl combine(SpyHashStateImpl s, const A &a, const Args &... args)
Definition: abseil-cpp/absl/hash/internal/spy_hash_state.h:73
absl::hash_internal::ContainerAsVector< std::tuple< T... > >::Out
std::vector< V > Out
Definition: abseil-cpp/absl/hash/hash_testing.h:317
absl::hash_internal::ContainerAsVector::Do
static Out Do(const Container &values)
Definition: abseil-cpp/absl/hash/hash_testing.h:307
C
#define C(x)
Definition: abseil-cpp/absl/hash/internal/city_test.cc:49
testing::internal::string
::std::string string
Definition: bloaty/third_party/protobuf/third_party/googletest/googletest/include/gtest/internal/gtest-port.h:881
error
grpc_error_handle error
Definition: retry_filter.cc:499
absl::hash_internal::ContainerAsVector< std::tuple< T... > >::V
VariantForTypes< T... > V
Definition: abseil-cpp/absl/hash/hash_testing.h:316
u
OPENSSL_EXPORT pem_password_cb void * u
Definition: pem.h:351
ABSL_NAMESPACE_END
#define ABSL_NAMESPACE_END
Definition: third_party/abseil-cpp/absl/base/config.h:171
T
#define T(upbtypeconst, upbtype, ctype, default_value)
absl::hash_internal::EqVisitor::operator()
bool operator()(const T *t, const U *u) const
Definition: abseil-cpp/absl/hash/hash_testing.h:174
true
#define true
Definition: setup_once.h:324
absl::hash_internal::TypeSet::Insert
Definition: abseil-cpp/absl/hash/hash_testing.h:281
absl::hash_internal::TypeSet
Definition: abseil-cpp/absl/hash/hash_testing.h:279
ABSL_MUST_USE_RESULT
#define ABSL_MUST_USE_RESULT
Definition: abseil-cpp/absl/base/attributes.h:441
absl::ToString
absl::string_view ToString(TestCordSize size)
Definition: abseil-cpp/absl/strings/cord_test_helpers.h:59
ABSL_NAMESPACE_BEGIN
#define ABSL_NAMESPACE_BEGIN
Definition: third_party/abseil-cpp/absl/base/config.h:170
absl::hash_internal::EqVisitor
Definition: abseil-cpp/absl/hash/hash_testing.h:171
absl::hash_internal::ContainerAsVector< std::tuple< T... > >::DoImpl
static Out DoImpl(const std::tuple< T... > &tuple, absl::index_sequence< I... >)
Definition: abseil-cpp/absl/hash/hash_testing.h:320
absl::hash_internal::PrintVisitor::operator()
std::string operator()(const T *value) const
Definition: abseil-cpp/absl/hash/hash_testing.h:165
absl::integer_sequence
Definition: abseil-cpp/absl/utility/utility.h:76
setup.v
v
Definition: third_party/bloaty/third_party/capstone/bindings/python/setup.py:42
absl::hash_internal::PrintVisitor::index
size_t index
Definition: abseil-cpp/absl/hash/hash_testing.h:163
absl::hash_internal::ExpandVisitor
Definition: abseil-cpp/absl/hash/hash_testing.h:179
absl::hash_internal::EqVisitor::eq
Eq eq
Definition: abseil-cpp/absl/hash/hash_testing.h:172
testing::Eq
internal::EqMatcher< T > Eq(T x)
Definition: cares/cares/test/gmock-1.8.0/gmock/gmock.h:8561
testing::AssertionResult
Definition: cares/cares/test/gmock-1.8.0/gtest/gtest.h:18855
absl::compare_internal::value_type
int8_t value_type
Definition: abseil-cpp/absl/types/compare.h:45
testing::AssertionSuccess
AssertionResult AssertionSuccess()
Definition: bloaty/third_party/googletest/googletest/src/gtest.cc:1023
absl::hash_internal::PrintVisitor
Definition: abseil-cpp/absl/hash/hash_testing.h:162
absl::hash_internal::ContainerAsVector< std::tuple<> >::Do
static std::vector< VariantForTypes< int > > Do(std::tuple<>)
Definition: abseil-cpp/absl/hash/hash_testing.h:331
absl::index_sequence_for
make_index_sequence< sizeof...(Ts)> index_sequence_for
Definition: abseil-cpp/absl/utility/utility.h:158
value
const char * value
Definition: hpack_parser_table.cc:165
absl::hash_internal::SpyHashStateImpl::CompareResult::kBSuffixA
@ kBSuffixA
I
#define I(b, c, d)
Definition: md5.c:120
classes
static const struct nv classes[]
Definition: adig.c:75
absl::hash_internal::SpyHashState
SpyHashStateImpl< void > SpyHashState
Definition: abseil-cpp/absl/hash/internal/spy_hash_state.h:260
absl::hash_internal::ContainerAsVector< std::tuple< T... > >::Do
static Out Do(const std::tuple< T... > &values)
Definition: abseil-cpp/absl/hash/hash_testing.h:324
index
int index
Definition: bloaty/third_party/protobuf/php/ext/google/protobuf/protobuf.h:1184
absl::hash_internal::ContainerAsVector::Out
std::vector< V > Out
Definition: abseil-cpp/absl/hash/hash_testing.h:305
std
Definition: grpcpp/impl/codegen/async_unary_call.h:407
absl::hash_internal::SpyHashStateImpl::CompareResult::kUnequal
@ kUnequal
values
std::array< int64_t, Size > values
Definition: abseil-cpp/absl/container/btree_benchmark.cc:608
absl::hash_internal::SpyHashStateImpl::Compare
static CompareResult Compare(const SpyHashStateImpl &a, const SpyHashStateImpl &b)
Definition: abseil-cpp/absl/hash/internal/spy_hash_state.h:114
absl::hash_internal::VerifyTypeImplementsAbslHashCorrectly
ABSL_MUST_USE_RESULT testing::AssertionResult VerifyTypeImplementsAbslHashCorrectly(const Container &values, Eq equals)
Definition: abseil-cpp/absl/hash/hash_testing.h:188
absl::hash_internal::SpyHashStateImpl::CompareResult::kASuffixB
@ kASuffixB
absl::hash_internal::MakeTypeSet
Definition: abseil-cpp/absl/hash/hash_testing.h:294
absl
Definition: abseil-cpp/absl/algorithm/algorithm.h:31
google::protobuf::python::repeated_composite_container::Insert
static PyObject * Insert(PyObject *pself, PyObject *args)
Definition: bloaty/third_party/protobuf/python/google/protobuf/pyext/repeated_composite_container.cc:139
asyncio_get_stats.type
type
Definition: asyncio_get_stats.py:37
absl::out
char * out
Definition: abseil-cpp/absl/synchronization/mutex.h:1048
absl::hash_internal::SpyHashStateImpl::CompareResult::kEqual
@ kEqual
absl::visit
variant_internal::VisitResult< Visitor, Variants... > visit(Visitor &&vis, Variants &&... vars)
Definition: abseil-cpp/absl/types/variant.h:430
setup.template
template
Definition: setup.py:47
absl::hash_internal::ExpandVisitor::operator()
SpyHashState operator()(const T *value) const
Definition: abseil-cpp/absl/hash/hash_testing.h:181
absl::variant
Definition: abseil-cpp/absl/types/internal/variant.h:46
absl::hash_internal::VariantForTypes
typename MakeTypeSet< const typename std::decay< T >::type *... >::template apply< absl::variant > VariantForTypes
Definition: abseil-cpp/absl/hash/hash_testing.h:300
i
uint64_t i
Definition: abseil-cpp/absl/container/btree_benchmark.cc:230
absl::hash_internal::TypeSet<>::apply
C< T... > apply
Definition: abseil-cpp/absl/hash/hash_testing.h:290
absl::hash_internal::c2
static const uint32_t c2
Definition: abseil-cpp/absl/hash/internal/city.cc:59
testing::PrintToString
::std::string PrintToString(const T &value)
Definition: bloaty/third_party/googletest/googletest/include/gtest/gtest-printers.h:915


grpc
Author(s):
autogenerated on Fri May 16 2025 02:59:01