| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #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 |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| template <int&... ExplicitBarrier, typename Container> |
| ABSL_MUST_USE_RESULT testing::AssertionResult |
| VerifyTypeImplementsAbslHashCorrectly(const Container& values); |
|
|
| template <int&... ExplicitBarrier, typename Container, typename Eq> |
| ABSL_MUST_USE_RESULT testing::AssertionResult |
| VerifyTypeImplementsAbslHashCorrectly(const Container& values, Eq equals); |
|
|
| template <int&..., typename T> |
| ABSL_MUST_USE_RESULT testing::AssertionResult |
| VerifyTypeImplementsAbslHashCorrectly(std::initializer_list<T> values); |
|
|
| template <int&..., typename T, typename Eq> |
| ABSL_MUST_USE_RESULT testing::AssertionResult |
| VerifyTypeImplementsAbslHashCorrectly(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::AssertionResult |
| VerifyTypeImplementsAbslHashCorrectly(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; |
|
|
| |
| 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; |
|
|
| |
| 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."; |
| } |
|
|
| |
| |
|
|
| for (const auto& c : classes) { |
| |
| |
| 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."; |
| } |
| } |
|
|
| |
| 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; |
| } |
| }; |
|
|
| } |
|
|
| template <int&..., typename Container> |
| ABSL_MUST_USE_RESULT testing::AssertionResult |
| VerifyTypeImplementsAbslHashCorrectly(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::AssertionResult |
| VerifyTypeImplementsAbslHashCorrectly(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::AssertionResult |
| VerifyTypeImplementsAbslHashCorrectly(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::AssertionResult |
| VerifyTypeImplementsAbslHashCorrectly(std::initializer_list<T> values, |
| Eq equals) { |
| return hash_internal::VerifyTypeImplementsAbslHashCorrectly( |
| hash_internal::ContainerAsVector<std::initializer_list<T>>::Do(values), |
| equals); |
| } |
|
|
| ABSL_NAMESPACE_END |
| } |
|
|
| #endif |
|
|