| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378 | // 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.#ifndef ABSL_HASH_HASH_TESTING_H_#define ABSL_HASH_HASH_TESTING_H_#include <initializer_list>#include <tuple>#include <type_traits>#include <vector>#include "gmock/gmock.h"#include "gtest/gtest.h"#include "absl/hash/internal/spy_hash_state.h"#include "absl/meta/type_traits.h"#include "absl/strings/str_cat.h"#include "absl/types/variant.h"namespace absl {ABSL_NAMESPACE_BEGIN// Run the absl::Hash algorithm over all the elements passed in and verify that// their hash expansion is congruent with their `==` operator.//// It is used in conjunction with EXPECT_TRUE. Failures will output information// on what requirement failed and on which objects.//// Users should pass a collection of types as either an initializer list or a// container of cases.////   EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(//       {v1, v2, ..., vN}));////   std::vector<MyType> cases;//   // Fill cases...//   EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(cases));//// Users can pass a variety of types for testing heterogeneous lookup with// `std::make_tuple`:////   EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(//       std::make_tuple(v1, v2, ..., vN)));////// Ideally, the values passed should provide enough coverage of the `==`// operator and the AbslHashValue implementations.// For dynamically sized types, the empty state should usually be included in// the values.//// The function accepts an optional comparator function, in case that `==` is// not enough for the values provided.//// Usage:////   EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(//       std::make_tuple(v1, v2, ..., vN), MyCustomEq{}));//// It checks the following requirements://   1. The expansion for a value is deterministic.//   2. For any two objects `a` and `b` in the sequence, if `a == b` evaluates//      to true, then their hash expansion must be equal.//   3. If `a == b` evaluates to false their hash expansion must be unequal.//   4. If `a == b` evaluates to false neither hash expansion can be a//      suffix of the other.//   5. AbslHashValue overloads should not be called by the user. They are only//      meant to be called by the framework. Users should call H::combine() and//      H::combine_contiguous().//   6. No moved-from instance of the hash state is used in the implementation//      of AbslHashValue.//// The values do not have to have the same type. This can be useful for// equivalent types that support heterogeneous lookup.//// A possible reason for breaking (2) is combining state in the hash expansion// that was not used in `==`.// For example://// struct Bad2 {//   int a, b;//   template <typename H>//   friend H AbslHashValue(H state, Bad2 x) {//     // Uses a and b.//     return H::combine(std::move(state), x.a, x.b);//   }//   friend bool operator==(Bad2 x, Bad2 y) {//     // Only uses a.//     return x.a == y.a;//   }// };//// As for (3), breaking this usually means that there is state being passed to// the `==` operator that is not used in the hash expansion.// For example://// struct Bad3 {//   int a, b;//   template <typename H>//   friend H AbslHashValue(H state, Bad3 x) {//     // Only uses a.//     return H::combine(std::move(state), x.a);//   }//   friend bool operator==(Bad3 x, Bad3 y) {//     // Uses a and b.//     return x.a == y.a && x.b == y.b;//   }// };//// Finally, a common way to break 4 is by combining dynamic ranges without// combining the size of the range.// For example://// struct Bad4 {//   int *p, size;//   template <typename H>//   friend H AbslHashValue(H state, Bad4 x) {//     return H::combine_contiguous(std::move(state), x.p, x.p + x.size);//   }//   friend bool operator==(Bad4 x, Bad4 y) {//    // Compare two ranges for equality. C++14 code can instead use std::equal.//     return absl::equal(x.p, x.p + x.size, y.p, y.p + y.size);//   }// };//// An easy solution to this is to combine the size after combining the range,// like so:// template <typename H>// friend H AbslHashValue(H state, Bad4 x) {//   return H::combine(//       H::combine_contiguous(std::move(state), x.p, x.p + x.size), x.size);// }//template <int&... ExplicitBarrier, typename Container>ABSL_MUST_USE_RESULT testing::AssertionResultVerifyTypeImplementsAbslHashCorrectly(const Container& values);template <int&... ExplicitBarrier, typename Container, typename Eq>ABSL_MUST_USE_RESULT testing::AssertionResultVerifyTypeImplementsAbslHashCorrectly(const Container& values, Eq equals);template <int&..., typename T>ABSL_MUST_USE_RESULT testing::AssertionResultVerifyTypeImplementsAbslHashCorrectly(std::initializer_list<T> values);template <int&..., typename T, typename Eq>ABSL_MUST_USE_RESULT testing::AssertionResultVerifyTypeImplementsAbslHashCorrectly(std::initializer_list<T> values,                                      Eq equals);namespace hash_internal {struct PrintVisitor {  size_t index;  template <typename T>  std::string operator()(const T* value) const {    return absl::StrCat("#", index, "(", testing::PrintToString(*value), ")");  }};template <typename Eq>struct EqVisitor {  Eq eq;  template <typename T, typename U>  bool operator()(const T* t, const U* u) const {    return eq(*t, *u);  }};struct ExpandVisitor {  template <typename T>  SpyHashState operator()(const T* value) const {    return SpyHashState::combine(SpyHashState(), *value);  }};template <typename Container, typename Eq>ABSL_MUST_USE_RESULT testing::AssertionResultVerifyTypeImplementsAbslHashCorrectly(const Container& values, Eq equals) {  using V = typename Container::value_type;  struct Info {    const V& value;    size_t index;    std::string ToString() const {      return absl::visit(PrintVisitor{index}, value);    }    SpyHashState expand() const { return absl::visit(ExpandVisitor{}, value); }  };  using EqClass = std::vector<Info>;  std::vector<EqClass> classes;  // Gather the values in equivalence classes.  size_t i = 0;  for (const auto& value : values) {    EqClass* c = nullptr;    for (auto& eqclass : classes) {      if (absl::visit(EqVisitor<Eq>{equals}, value, eqclass[0].value)) {        c = &eqclass;        break;      }    }    if (c == nullptr) {      classes.emplace_back();      c = &classes.back();    }    c->push_back({value, i});    ++i;    // Verify potential errors captured by SpyHashState.    if (auto error = c->back().expand().error()) {      return testing::AssertionFailure() << *error;    }  }  if (classes.size() < 2) {    return testing::AssertionFailure()           << "At least two equivalence classes are expected.";  }  // We assume that equality is correctly implemented.  // Now we verify that AbslHashValue is also correctly implemented.  for (const auto& c : classes) {    // All elements of the equivalence class must have the same hash    // expansion.    const SpyHashState expected = c[0].expand();    for (const Info& v : c) {      if (v.expand() != v.expand()) {        return testing::AssertionFailure()               << "Hash expansion for " << v.ToString()               << " is non-deterministic.";      }      if (v.expand() != expected) {        return testing::AssertionFailure()               << "Values " << c[0].ToString() << " and " << v.ToString()               << " evaluate as equal but have an unequal hash expansion.";      }    }    // Elements from other classes must have different hash expansion.    for (const auto& c2 : classes) {      if (&c == &c2) continue;      const SpyHashState c2_hash = c2[0].expand();      switch (SpyHashState::Compare(expected, c2_hash)) {        case SpyHashState::CompareResult::kEqual:          return testing::AssertionFailure()                 << "Values " << c[0].ToString() << " and " << c2[0].ToString()                 << " evaluate as unequal but have an equal hash expansion.";        case SpyHashState::CompareResult::kBSuffixA:          return testing::AssertionFailure()                 << "Hash expansion of " << c2[0].ToString()                 << " is a suffix of the hash expansion of " << c[0].ToString()                 << ".";        case SpyHashState::CompareResult::kASuffixB:          return testing::AssertionFailure()                 << "Hash expansion of " << c[0].ToString()                 << " is a suffix of the hash expansion of " << c2[0].ToString()                 << ".";        case SpyHashState::CompareResult::kUnequal:          break;      }    }  }  return testing::AssertionSuccess();}template <typename... T>struct TypeSet {  template <typename U, bool = disjunction<std::is_same<T, U>...>::value>  struct Insert {    using type = TypeSet<U, T...>;  };  template <typename U>  struct Insert<U, true> {    using type = TypeSet;  };  template <template <typename...> class C>  using apply = C<T...>;};template <typename... T>struct MakeTypeSet : TypeSet<> {};template <typename T, typename... Ts>struct MakeTypeSet<T, Ts...> : MakeTypeSet<Ts...>::template Insert<T>::type {};template <typename... T>using VariantForTypes = typename MakeTypeSet<    const typename std::decay<T>::type*...>::template apply<absl::variant>;template <typename Container>struct ContainerAsVector {  using V = absl::variant<const typename Container::value_type*>;  using Out = std::vector<V>;  static Out Do(const Container& values) {    Out out;    for (const auto& v : values) out.push_back(&v);    return out;  }};template <typename... T>struct ContainerAsVector<std::tuple<T...>> {  using V = VariantForTypes<T...>;  using Out = std::vector<V>;  template <size_t... I>  static Out DoImpl(const std::tuple<T...>& tuple, absl::index_sequence<I...>) {    return Out{&std::get<I>(tuple)...};  }  static Out Do(const std::tuple<T...>& values) {    return DoImpl(values, absl::index_sequence_for<T...>());  }};template <>struct ContainerAsVector<std::tuple<>> {  static std::vector<VariantForTypes<int>> Do(std::tuple<>) { return {}; }};struct DefaultEquals {  template <typename T, typename U>  bool operator()(const T& t, const U& u) const {    return t == u;  }};}  // namespace hash_internaltemplate <int&..., typename Container>ABSL_MUST_USE_RESULT testing::AssertionResultVerifyTypeImplementsAbslHashCorrectly(const Container& values) {  return hash_internal::VerifyTypeImplementsAbslHashCorrectly(      hash_internal::ContainerAsVector<Container>::Do(values),      hash_internal::DefaultEquals{});}template <int&..., typename Container, typename Eq>ABSL_MUST_USE_RESULT testing::AssertionResultVerifyTypeImplementsAbslHashCorrectly(const Container& values, Eq equals) {  return hash_internal::VerifyTypeImplementsAbslHashCorrectly(      hash_internal::ContainerAsVector<Container>::Do(values), equals);}template <int&..., typename T>ABSL_MUST_USE_RESULT testing::AssertionResultVerifyTypeImplementsAbslHashCorrectly(std::initializer_list<T> values) {  return hash_internal::VerifyTypeImplementsAbslHashCorrectly(      hash_internal::ContainerAsVector<std::initializer_list<T>>::Do(values),      hash_internal::DefaultEquals{});}template <int&..., typename T, typename Eq>ABSL_MUST_USE_RESULT testing::AssertionResultVerifyTypeImplementsAbslHashCorrectly(std::initializer_list<T> values,                                      Eq equals) {  return hash_internal::VerifyTypeImplementsAbslHashCorrectly(      hash_internal::ContainerAsVector<std::initializer_list<T>>::Do(values),      equals);}ABSL_NAMESPACE_END}  // namespace absl#endif  // ABSL_HASH_HASH_TESTING_H_
 |