Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:27:18

0001 // Copyright 2018 The Abseil Authors.
0002 //
0003 // Licensed under the Apache License, Version 2.0 (the "License");
0004 // you may not use this file except in compliance with the License.
0005 // You may obtain a copy of the License at
0006 //
0007 //      https://www.apache.org/licenses/LICENSE-2.0
0008 //
0009 // Unless required by applicable law or agreed to in writing, software
0010 // distributed under the License is distributed on an "AS IS" BASIS,
0011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0012 // See the License for the specific language governing permissions and
0013 // limitations under the License.
0014 
0015 #ifndef ABSL_HASH_HASH_TESTING_H_
0016 #define ABSL_HASH_HASH_TESTING_H_
0017 
0018 #include <initializer_list>
0019 #include <tuple>
0020 #include <type_traits>
0021 #include <vector>
0022 
0023 #include "gmock/gmock.h"
0024 #include "gtest/gtest.h"
0025 #include "absl/hash/internal/spy_hash_state.h"
0026 #include "absl/meta/type_traits.h"
0027 #include "absl/strings/str_cat.h"
0028 #include "absl/types/variant.h"
0029 
0030 namespace absl {
0031 ABSL_NAMESPACE_BEGIN
0032 
0033 // Run the absl::Hash algorithm over all the elements passed in and verify that
0034 // their hash expansion is congruent with their `==` operator.
0035 //
0036 // It is used in conjunction with EXPECT_TRUE. Failures will output information
0037 // on what requirement failed and on which objects.
0038 //
0039 // Users should pass a collection of types as either an initializer list or a
0040 // container of cases.
0041 //
0042 //   EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(
0043 //       {v1, v2, ..., vN}));
0044 //
0045 //   std::vector<MyType> cases;
0046 //   // Fill cases...
0047 //   EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(cases));
0048 //
0049 // Users can pass a variety of types for testing heterogeneous lookup with
0050 // `std::make_tuple`:
0051 //
0052 //   EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(
0053 //       std::make_tuple(v1, v2, ..., vN)));
0054 //
0055 //
0056 // Ideally, the values passed should provide enough coverage of the `==`
0057 // operator and the AbslHashValue implementations.
0058 // For dynamically sized types, the empty state should usually be included in
0059 // the values.
0060 //
0061 // The function accepts an optional comparator function, in case that `==` is
0062 // not enough for the values provided.
0063 //
0064 // Usage:
0065 //
0066 //   EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(
0067 //       std::make_tuple(v1, v2, ..., vN), MyCustomEq{}));
0068 //
0069 // It checks the following requirements:
0070 //   1. The expansion for a value is deterministic.
0071 //   2. For any two objects `a` and `b` in the sequence, if `a == b` evaluates
0072 //      to true, then their hash expansion must be equal.
0073 //   3. If `a == b` evaluates to false their hash expansion must be unequal.
0074 //   4. If `a == b` evaluates to false neither hash expansion can be a
0075 //      suffix of the other.
0076 //   5. AbslHashValue overloads should not be called by the user. They are only
0077 //      meant to be called by the framework. Users should call H::combine() and
0078 //      H::combine_contiguous().
0079 //   6. No moved-from instance of the hash state is used in the implementation
0080 //      of AbslHashValue.
0081 //
0082 // The values do not have to have the same type. This can be useful for
0083 // equivalent types that support heterogeneous lookup.
0084 //
0085 // A possible reason for breaking (2) is combining state in the hash expansion
0086 // that was not used in `==`.
0087 // For example:
0088 //
0089 // struct Bad2 {
0090 //   int a, b;
0091 //   template <typename H>
0092 //   friend H AbslHashValue(H state, Bad2 x) {
0093 //     // Uses a and b.
0094 //     return H::combine(std::move(state), x.a, x.b);
0095 //   }
0096 //   friend bool operator==(Bad2 x, Bad2 y) {
0097 //     // Only uses a.
0098 //     return x.a == y.a;
0099 //   }
0100 // };
0101 //
0102 // As for (3), breaking this usually means that there is state being passed to
0103 // the `==` operator that is not used in the hash expansion.
0104 // For example:
0105 //
0106 // struct Bad3 {
0107 //   int a, b;
0108 //   template <typename H>
0109 //   friend H AbslHashValue(H state, Bad3 x) {
0110 //     // Only uses a.
0111 //     return H::combine(std::move(state), x.a);
0112 //   }
0113 //   friend bool operator==(Bad3 x, Bad3 y) {
0114 //     // Uses a and b.
0115 //     return x.a == y.a && x.b == y.b;
0116 //   }
0117 // };
0118 //
0119 // Finally, a common way to break 4 is by combining dynamic ranges without
0120 // combining the size of the range.
0121 // For example:
0122 //
0123 // struct Bad4 {
0124 //   int *p, size;
0125 //   template <typename H>
0126 //   friend H AbslHashValue(H state, Bad4 x) {
0127 //     return H::combine_contiguous(std::move(state), x.p, x.p + x.size);
0128 //   }
0129 //   friend bool operator==(Bad4 x, Bad4 y) {
0130 //    // Compare two ranges for equality. C++14 code can instead use std::equal.
0131 //     return absl::equal(x.p, x.p + x.size, y.p, y.p + y.size);
0132 //   }
0133 // };
0134 //
0135 // An easy solution to this is to combine the size after combining the range,
0136 // like so:
0137 // template <typename H>
0138 // friend H AbslHashValue(H state, Bad4 x) {
0139 //   return H::combine(
0140 //       H::combine_contiguous(std::move(state), x.p, x.p + x.size), x.size);
0141 // }
0142 //
0143 template <int&... ExplicitBarrier, typename Container>
0144 ABSL_MUST_USE_RESULT testing::AssertionResult
0145 VerifyTypeImplementsAbslHashCorrectly(const Container& values);
0146 
0147 template <int&... ExplicitBarrier, typename Container, typename Eq>
0148 ABSL_MUST_USE_RESULT testing::AssertionResult
0149 VerifyTypeImplementsAbslHashCorrectly(const Container& values, Eq equals);
0150 
0151 template <int&..., typename T>
0152 ABSL_MUST_USE_RESULT testing::AssertionResult
0153 VerifyTypeImplementsAbslHashCorrectly(std::initializer_list<T> values);
0154 
0155 template <int&..., typename T, typename Eq>
0156 ABSL_MUST_USE_RESULT testing::AssertionResult
0157 VerifyTypeImplementsAbslHashCorrectly(std::initializer_list<T> values,
0158                                       Eq equals);
0159 
0160 namespace hash_internal {
0161 
0162 struct PrintVisitor {
0163   size_t index;
0164   template <typename T>
0165   std::string operator()(const T* value) const {
0166     return absl::StrCat("#", index, "(", testing::PrintToString(*value), ")");
0167   }
0168 };
0169 
0170 template <typename Eq>
0171 struct EqVisitor {
0172   Eq eq;
0173   template <typename T, typename U>
0174   bool operator()(const T* t, const U* u) const {
0175     return eq(*t, *u);
0176   }
0177 };
0178 
0179 struct ExpandVisitor {
0180   template <typename T>
0181   SpyHashState operator()(const T* value) const {
0182     return SpyHashState::combine(SpyHashState(), *value);
0183   }
0184 };
0185 
0186 template <typename Container, typename Eq>
0187 ABSL_MUST_USE_RESULT testing::AssertionResult
0188 VerifyTypeImplementsAbslHashCorrectly(const Container& values, Eq equals) {
0189   using V = typename Container::value_type;
0190 
0191   struct Info {
0192     const V& value;
0193     size_t index;
0194     std::string ToString() const {
0195       return absl::visit(PrintVisitor{index}, value);
0196     }
0197     SpyHashState expand() const { return absl::visit(ExpandVisitor{}, value); }
0198   };
0199 
0200   using EqClass = std::vector<Info>;
0201   std::vector<EqClass> classes;
0202 
0203   // Gather the values in equivalence classes.
0204   size_t i = 0;
0205   for (const auto& value : values) {
0206     EqClass* c = nullptr;
0207     for (auto& eqclass : classes) {
0208       if (absl::visit(EqVisitor<Eq>{equals}, value, eqclass[0].value)) {
0209         c = &eqclass;
0210         break;
0211       }
0212     }
0213     if (c == nullptr) {
0214       classes.emplace_back();
0215       c = &classes.back();
0216     }
0217     c->push_back({value, i});
0218     ++i;
0219 
0220     // Verify potential errors captured by SpyHashState.
0221     if (auto error = c->back().expand().error()) {
0222       return testing::AssertionFailure() << *error;
0223     }
0224   }
0225 
0226   if (classes.size() < 2) {
0227     return testing::AssertionFailure()
0228            << "At least two equivalence classes are expected.";
0229   }
0230 
0231   // We assume that equality is correctly implemented.
0232   // Now we verify that AbslHashValue is also correctly implemented.
0233 
0234   for (const auto& c : classes) {
0235     // All elements of the equivalence class must have the same hash
0236     // expansion.
0237     const SpyHashState expected = c[0].expand();
0238     for (const Info& v : c) {
0239       if (v.expand() != v.expand()) {
0240         return testing::AssertionFailure()
0241                << "Hash expansion for " << v.ToString()
0242                << " is non-deterministic.";
0243       }
0244       if (v.expand() != expected) {
0245         return testing::AssertionFailure()
0246                << "Values " << c[0].ToString() << " and " << v.ToString()
0247                << " evaluate as equal but have an unequal hash expansion.";
0248       }
0249     }
0250 
0251     // Elements from other classes must have different hash expansion.
0252     for (const auto& c2 : classes) {
0253       if (&c == &c2) continue;
0254       const SpyHashState c2_hash = c2[0].expand();
0255       switch (SpyHashState::Compare(expected, c2_hash)) {
0256         case SpyHashState::CompareResult::kEqual:
0257           return testing::AssertionFailure()
0258                  << "Values " << c[0].ToString() << " and " << c2[0].ToString()
0259                  << " evaluate as unequal but have an equal hash expansion.";
0260         case SpyHashState::CompareResult::kBSuffixA:
0261           return testing::AssertionFailure()
0262                  << "Hash expansion of " << c2[0].ToString()
0263                  << " is a suffix of the hash expansion of " << c[0].ToString()
0264                  << ".";
0265         case SpyHashState::CompareResult::kASuffixB:
0266           return testing::AssertionFailure()
0267                  << "Hash expansion of " << c[0].ToString()
0268                  << " is a suffix of the hash expansion of " << c2[0].ToString()
0269                  << ".";
0270         case SpyHashState::CompareResult::kUnequal:
0271           break;
0272       }
0273     }
0274   }
0275   return testing::AssertionSuccess();
0276 }
0277 
0278 template <typename... T>
0279 struct TypeSet {
0280   template <typename U, bool = disjunction<std::is_same<T, U>...>::value>
0281   struct Insert {
0282     using type = TypeSet<U, T...>;
0283   };
0284   template <typename U>
0285   struct Insert<U, true> {
0286     using type = TypeSet;
0287   };
0288 
0289   template <template <typename...> class C>
0290   using apply = C<T...>;
0291 };
0292 
0293 template <typename... T>
0294 struct MakeTypeSet : TypeSet<> {};
0295 template <typename T, typename... Ts>
0296 struct MakeTypeSet<T, Ts...> : MakeTypeSet<Ts...>::template Insert<T>::type {};
0297 
0298 template <typename... T>
0299 using VariantForTypes = typename MakeTypeSet<
0300     const typename std::decay<T>::type*...>::template apply<absl::variant>;
0301 
0302 template <typename Container>
0303 struct ContainerAsVector {
0304   using V = absl::variant<const typename Container::value_type*>;
0305   using Out = std::vector<V>;
0306 
0307   static Out Do(const Container& values) {
0308     Out out;
0309     for (const auto& v : values) out.push_back(&v);
0310     return out;
0311   }
0312 };
0313 
0314 template <typename... T>
0315 struct ContainerAsVector<std::tuple<T...>> {
0316   using V = VariantForTypes<T...>;
0317   using Out = std::vector<V>;
0318 
0319   template <size_t... I>
0320   static Out DoImpl(const std::tuple<T...>& tuple, absl::index_sequence<I...>) {
0321     return Out{&std::get<I>(tuple)...};
0322   }
0323 
0324   static Out Do(const std::tuple<T...>& values) {
0325     return DoImpl(values, absl::index_sequence_for<T...>());
0326   }
0327 };
0328 
0329 template <>
0330 struct ContainerAsVector<std::tuple<>> {
0331   static std::vector<VariantForTypes<int>> Do(std::tuple<>) { return {}; }
0332 };
0333 
0334 struct DefaultEquals {
0335   template <typename T, typename U>
0336   bool operator()(const T& t, const U& u) const {
0337     return t == u;
0338   }
0339 };
0340 
0341 }  // namespace hash_internal
0342 
0343 template <int&..., typename Container>
0344 ABSL_MUST_USE_RESULT testing::AssertionResult
0345 VerifyTypeImplementsAbslHashCorrectly(const Container& values) {
0346   return hash_internal::VerifyTypeImplementsAbslHashCorrectly(
0347       hash_internal::ContainerAsVector<Container>::Do(values),
0348       hash_internal::DefaultEquals{});
0349 }
0350 
0351 template <int&..., typename Container, typename Eq>
0352 ABSL_MUST_USE_RESULT testing::AssertionResult
0353 VerifyTypeImplementsAbslHashCorrectly(const Container& values, Eq equals) {
0354   return hash_internal::VerifyTypeImplementsAbslHashCorrectly(
0355       hash_internal::ContainerAsVector<Container>::Do(values), equals);
0356 }
0357 
0358 template <int&..., typename T>
0359 ABSL_MUST_USE_RESULT testing::AssertionResult
0360 VerifyTypeImplementsAbslHashCorrectly(std::initializer_list<T> values) {
0361   return hash_internal::VerifyTypeImplementsAbslHashCorrectly(
0362       hash_internal::ContainerAsVector<std::initializer_list<T>>::Do(values),
0363       hash_internal::DefaultEquals{});
0364 }
0365 
0366 template <int&..., typename T, typename Eq>
0367 ABSL_MUST_USE_RESULT testing::AssertionResult
0368 VerifyTypeImplementsAbslHashCorrectly(std::initializer_list<T> values,
0369                                       Eq equals) {
0370   return hash_internal::VerifyTypeImplementsAbslHashCorrectly(
0371       hash_internal::ContainerAsVector<std::initializer_list<T>>::Do(values),
0372       equals);
0373 }
0374 
0375 ABSL_NAMESPACE_END
0376 }  // namespace absl
0377 
0378 #endif  // ABSL_HASH_HASH_TESTING_H_