hash_testing.h 12 KB

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