You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

hash_testing.h 15 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  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. #ifndef ABSL_HASH_HASH_TESTING_H_
  15. #define ABSL_HASH_HASH_TESTING_H_
  16. #include <initializer_list>
  17. #include <tuple>
  18. #include <type_traits>
  19. #include <vector>
  20. #include "gmock/gmock.h"
  21. #include "gtest/gtest.h"
  22. #include "absl/hash/internal/spy_hash_state.h"
  23. #include "absl/meta/type_traits.h"
  24. #include "absl/strings/str_cat.h"
  25. #include "absl/types/variant.h"
  26. namespace absl
  27. {
  28. ABSL_NAMESPACE_BEGIN
  29. // Run the absl::Hash algorithm over all the elements passed in and verify that
  30. // their hash expansion is congruent with their `==` operator.
  31. //
  32. // It is used in conjunction with EXPECT_TRUE. Failures will output information
  33. // on what requirement failed and on which objects.
  34. //
  35. // Users should pass a collection of types as either an initializer list or a
  36. // container of cases.
  37. //
  38. // EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(
  39. // {v1, v2, ..., vN}));
  40. //
  41. // std::vector<MyType> cases;
  42. // // Fill cases...
  43. // EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(cases));
  44. //
  45. // Users can pass a variety of types for testing heterogeneous lookup with
  46. // `std::make_tuple`:
  47. //
  48. // EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(
  49. // std::make_tuple(v1, v2, ..., vN)));
  50. //
  51. //
  52. // Ideally, the values passed should provide enough coverage of the `==`
  53. // operator and the AbslHashValue implementations.
  54. // For dynamically sized types, the empty state should usually be included in
  55. // the values.
  56. //
  57. // The function accepts an optional comparator function, in case that `==` is
  58. // not enough for the values provided.
  59. //
  60. // Usage:
  61. //
  62. // EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(
  63. // std::make_tuple(v1, v2, ..., vN), MyCustomEq{}));
  64. //
  65. // It checks the following requirements:
  66. // 1. The expansion for a value is deterministic.
  67. // 2. For any two objects `a` and `b` in the sequence, if `a == b` evaluates
  68. // to true, then their hash expansion must be equal.
  69. // 3. If `a == b` evaluates to false their hash expansion must be unequal.
  70. // 4. If `a == b` evaluates to false neither hash expansion can be a
  71. // suffix of the other.
  72. // 5. AbslHashValue overloads should not be called by the user. They are only
  73. // meant to be called by the framework. Users should call H::combine() and
  74. // H::combine_contiguous().
  75. // 6. No moved-from instance of the hash state is used in the implementation
  76. // of AbslHashValue.
  77. //
  78. // The values do not have to have the same type. This can be useful for
  79. // equivalent types that support heterogeneous lookup.
  80. //
  81. // A possible reason for breaking (2) is combining state in the hash expansion
  82. // that was not used in `==`.
  83. // For example:
  84. //
  85. // struct Bad2 {
  86. // int a, b;
  87. // template <typename H>
  88. // friend H AbslHashValue(H state, Bad2 x) {
  89. // // Uses a and b.
  90. // return H::combine(std::move(state), x.a, x.b);
  91. // }
  92. // friend bool operator==(Bad2 x, Bad2 y) {
  93. // // Only uses a.
  94. // return x.a == y.a;
  95. // }
  96. // };
  97. //
  98. // As for (3), breaking this usually means that there is state being passed to
  99. // the `==` operator that is not used in the hash expansion.
  100. // For example:
  101. //
  102. // struct Bad3 {
  103. // int a, b;
  104. // template <typename H>
  105. // friend H AbslHashValue(H state, Bad3 x) {
  106. // // Only uses a.
  107. // return H::combine(std::move(state), x.a);
  108. // }
  109. // friend bool operator==(Bad3 x, Bad3 y) {
  110. // // Uses a and b.
  111. // return x.a == y.a && x.b == y.b;
  112. // }
  113. // };
  114. //
  115. // Finally, a common way to break 4 is by combining dynamic ranges without
  116. // combining the size of the range.
  117. // For example:
  118. //
  119. // struct Bad4 {
  120. // int *p, size;
  121. // template <typename H>
  122. // friend H AbslHashValue(H state, Bad4 x) {
  123. // return H::combine_contiguous(std::move(state), x.p, x.p + x.size);
  124. // }
  125. // friend bool operator==(Bad4 x, Bad4 y) {
  126. // // Compare two ranges for equality. C++14 code can instead use std::equal.
  127. // return absl::equal(x.p, x.p + x.size, y.p, y.p + y.size);
  128. // }
  129. // };
  130. //
  131. // An easy solution to this is to combine the size after combining the range,
  132. // like so:
  133. // template <typename H>
  134. // friend H AbslHashValue(H state, Bad4 x) {
  135. // return H::combine(
  136. // H::combine_contiguous(std::move(state), x.p, x.p + x.size), x.size);
  137. // }
  138. //
  139. template<int&... ExplicitBarrier, typename Container>
  140. ABSL_MUST_USE_RESULT testing::AssertionResult
  141. VerifyTypeImplementsAbslHashCorrectly(const Container& values);
  142. template<int&... ExplicitBarrier, typename Container, typename Eq>
  143. ABSL_MUST_USE_RESULT testing::AssertionResult
  144. VerifyTypeImplementsAbslHashCorrectly(const Container& values, Eq equals);
  145. template<int&..., typename T>
  146. ABSL_MUST_USE_RESULT testing::AssertionResult
  147. VerifyTypeImplementsAbslHashCorrectly(std::initializer_list<T> values);
  148. template<int&..., typename T, typename Eq>
  149. ABSL_MUST_USE_RESULT testing::AssertionResult
  150. VerifyTypeImplementsAbslHashCorrectly(std::initializer_list<T> values, Eq equals);
  151. namespace hash_internal
  152. {
  153. struct PrintVisitor
  154. {
  155. size_t index;
  156. template<typename T>
  157. std::string operator()(const T* value) const
  158. {
  159. return absl::StrCat("#", index, "(", testing::PrintToString(*value), ")");
  160. }
  161. };
  162. template<typename Eq>
  163. struct EqVisitor
  164. {
  165. Eq eq;
  166. template<typename T, typename U>
  167. bool operator()(const T* t, const U* u) const
  168. {
  169. return eq(*t, *u);
  170. }
  171. };
  172. struct ExpandVisitor
  173. {
  174. template<typename T>
  175. SpyHashState operator()(const T* value) const
  176. {
  177. return SpyHashState::combine(SpyHashState(), *value);
  178. }
  179. };
  180. template<typename Container, typename Eq>
  181. ABSL_MUST_USE_RESULT testing::AssertionResult
  182. VerifyTypeImplementsAbslHashCorrectly(const Container& values, Eq equals)
  183. {
  184. using V = typename Container::value_type;
  185. struct Info
  186. {
  187. const V& value;
  188. size_t index;
  189. std::string ToString() const
  190. {
  191. return absl::visit(PrintVisitor{index}, value);
  192. }
  193. SpyHashState expand() const
  194. {
  195. return absl::visit(ExpandVisitor{}, value);
  196. }
  197. };
  198. using EqClass = std::vector<Info>;
  199. std::vector<EqClass> classes;
  200. // Gather the values in equivalence classes.
  201. size_t i = 0;
  202. for (const auto& value : values)
  203. {
  204. EqClass* c = nullptr;
  205. for (auto& eqclass : classes)
  206. {
  207. if (absl::visit(EqVisitor<Eq>{equals}, value, eqclass[0].value))
  208. {
  209. c = &eqclass;
  210. break;
  211. }
  212. }
  213. if (c == nullptr)
  214. {
  215. classes.emplace_back();
  216. c = &classes.back();
  217. }
  218. c->push_back({value, i});
  219. ++i;
  220. // Verify potential errors captured by SpyHashState.
  221. if (auto error = c->back().expand().error())
  222. {
  223. return testing::AssertionFailure() << *error;
  224. }
  225. }
  226. if (classes.size() < 2)
  227. {
  228. return testing::AssertionFailure()
  229. << "At least two equivalence classes are expected.";
  230. }
  231. // We assume that equality is correctly implemented.
  232. // Now we verify that AbslHashValue is also correctly implemented.
  233. for (const auto& c : classes)
  234. {
  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. {
  240. if (v.expand() != v.expand())
  241. {
  242. return testing::AssertionFailure()
  243. << "Hash expansion for " << v.ToString()
  244. << " is non-deterministic.";
  245. }
  246. if (v.expand() != expected)
  247. {
  248. return testing::AssertionFailure()
  249. << "Values " << c[0].ToString() << " and " << v.ToString()
  250. << " evaluate as equal but have an unequal hash expansion.";
  251. }
  252. }
  253. // Elements from other classes must have different hash expansion.
  254. for (const auto& c2 : classes)
  255. {
  256. if (&c == &c2)
  257. continue;
  258. const SpyHashState c2_hash = c2[0].expand();
  259. switch (SpyHashState::Compare(expected, c2_hash))
  260. {
  261. case SpyHashState::CompareResult::kEqual:
  262. return testing::AssertionFailure()
  263. << "Values " << c[0].ToString() << " and " << c2[0].ToString()
  264. << " evaluate as unequal but have an equal hash expansion.";
  265. case SpyHashState::CompareResult::kBSuffixA:
  266. return testing::AssertionFailure()
  267. << "Hash expansion of " << c2[0].ToString()
  268. << " is a suffix of the hash expansion of " << c[0].ToString()
  269. << ".";
  270. case SpyHashState::CompareResult::kASuffixB:
  271. return testing::AssertionFailure()
  272. << "Hash expansion of " << c[0].ToString()
  273. << " is a suffix of the hash expansion of " << c2[0].ToString()
  274. << ".";
  275. case SpyHashState::CompareResult::kUnequal:
  276. break;
  277. }
  278. }
  279. }
  280. return testing::AssertionSuccess();
  281. }
  282. template<typename... T>
  283. struct TypeSet
  284. {
  285. template<typename U, bool = disjunction<std::is_same<T, U>...>::value>
  286. struct Insert
  287. {
  288. using type = TypeSet<U, T...>;
  289. };
  290. template<typename U>
  291. struct Insert<U, true>
  292. {
  293. using type = TypeSet;
  294. };
  295. template<template<typename...> class C>
  296. using apply = C<T...>;
  297. };
  298. template<typename... T>
  299. struct MakeTypeSet : TypeSet<>
  300. {
  301. };
  302. template<typename T, typename... Ts>
  303. struct MakeTypeSet<T, Ts...> : MakeTypeSet<Ts...>::template Insert<T>::type
  304. {
  305. };
  306. template<typename... T>
  307. using VariantForTypes = typename MakeTypeSet<
  308. const typename std::decay<T>::type*...>::template apply<absl::variant>;
  309. template<typename Container>
  310. struct ContainerAsVector
  311. {
  312. using V = absl::variant<const typename Container::value_type*>;
  313. using Out = std::vector<V>;
  314. static Out Do(const Container& values)
  315. {
  316. Out out;
  317. for (const auto& v : values)
  318. out.push_back(&v);
  319. return out;
  320. }
  321. };
  322. template<typename... T>
  323. struct ContainerAsVector<std::tuple<T...>>
  324. {
  325. using V = VariantForTypes<T...>;
  326. using Out = std::vector<V>;
  327. template<size_t... I>
  328. static Out DoImpl(const std::tuple<T...>& tuple, absl::index_sequence<I...>)
  329. {
  330. return Out{&std::get<I>(tuple)...};
  331. }
  332. static Out Do(const std::tuple<T...>& values)
  333. {
  334. return DoImpl(values, absl::index_sequence_for<T...>());
  335. }
  336. };
  337. template<>
  338. struct ContainerAsVector<std::tuple<>>
  339. {
  340. static std::vector<VariantForTypes<int>> Do(std::tuple<>)
  341. {
  342. return {};
  343. }
  344. };
  345. struct DefaultEquals
  346. {
  347. template<typename T, typename U>
  348. bool operator()(const T& t, const U& u) const
  349. {
  350. return t == u;
  351. }
  352. };
  353. } // namespace hash_internal
  354. template<int&..., typename Container>
  355. ABSL_MUST_USE_RESULT testing::AssertionResult
  356. VerifyTypeImplementsAbslHashCorrectly(const Container& values)
  357. {
  358. return hash_internal::VerifyTypeImplementsAbslHashCorrectly(
  359. hash_internal::ContainerAsVector<Container>::Do(values),
  360. hash_internal::DefaultEquals{}
  361. );
  362. }
  363. template<int&..., typename Container, typename Eq>
  364. ABSL_MUST_USE_RESULT testing::AssertionResult
  365. VerifyTypeImplementsAbslHashCorrectly(const Container& values, Eq equals)
  366. {
  367. return hash_internal::VerifyTypeImplementsAbslHashCorrectly(
  368. hash_internal::ContainerAsVector<Container>::Do(values), equals
  369. );
  370. }
  371. template<int&..., typename T>
  372. ABSL_MUST_USE_RESULT testing::AssertionResult
  373. VerifyTypeImplementsAbslHashCorrectly(std::initializer_list<T> values)
  374. {
  375. return hash_internal::VerifyTypeImplementsAbslHashCorrectly(
  376. hash_internal::ContainerAsVector<std::initializer_list<T>>::Do(values),
  377. hash_internal::DefaultEquals{}
  378. );
  379. }
  380. template<int&..., typename T, typename Eq>
  381. ABSL_MUST_USE_RESULT testing::AssertionResult
  382. VerifyTypeImplementsAbslHashCorrectly(std::initializer_list<T> values, Eq equals)
  383. {
  384. return hash_internal::VerifyTypeImplementsAbslHashCorrectly(
  385. hash_internal::ContainerAsVector<std::initializer_list<T>>::Do(values),
  386. equals
  387. );
  388. }
  389. ABSL_NAMESPACE_END
  390. } // namespace absl
  391. #endif // ABSL_HASH_HASH_TESTING_H_