hash_testing.h
Go to the documentation of this file.
00001 // Copyright 2018 The Abseil Authors.
00002 //
00003 // Licensed under the Apache License, Version 2.0 (the "License");
00004 // you may not use this file except in compliance with the License.
00005 // You may obtain a copy of the License at
00006 //
00007 //      https://www.apache.org/licenses/LICENSE-2.0
00008 //
00009 // Unless required by applicable law or agreed to in writing, software
00010 // distributed under the License is distributed on an "AS IS" BASIS,
00011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00012 // See the License for the specific language governing permissions and
00013 // limitations under the License.
00014 
00015 #ifndef ABSL_HASH_HASH_TESTING_H_
00016 #define ABSL_HASH_HASH_TESTING_H_
00017 
00018 #include <initializer_list>
00019 #include <tuple>
00020 #include <type_traits>
00021 #include <vector>
00022 
00023 #include "gmock/gmock.h"
00024 #include "gtest/gtest.h"
00025 #include "absl/hash/internal/spy_hash_state.h"
00026 #include "absl/meta/type_traits.h"
00027 #include "absl/strings/str_cat.h"
00028 #include "absl/types/variant.h"
00029 
00030 namespace absl {
00031 
00032 // Run the absl::Hash algorithm over all the elements passed in and verify that
00033 // their hash expansion is congruent with their `==` operator.
00034 //
00035 // It is used in conjunction with EXPECT_TRUE. Failures will output information
00036 // on what requirement failed and on which objects.
00037 //
00038 // Users should pass a collection of types as either an initializer list or a
00039 // container of cases.
00040 //
00041 //   EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(
00042 //       {v1, v2, ..., vN}));
00043 //
00044 //   std::vector<MyType> cases;
00045 //   // Fill cases...
00046 //   EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(cases));
00047 //
00048 // Users can pass a variety of types for testing heterogeneous lookup with
00049 // `std::make_tuple`:
00050 //
00051 //   EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(
00052 //       std::make_tuple(v1, v2, ..., vN)));
00053 //
00054 //
00055 // Ideally, the values passed should provide enough coverage of the `==`
00056 // operator and the AbslHashValue implementations.
00057 // For dynamically sized types, the empty state should usually be included in
00058 // the values.
00059 //
00060 // The function accepts an optional comparator function, in case that `==` is
00061 // not enough for the values provided.
00062 //
00063 // Usage:
00064 //
00065 //   EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly(
00066 //       std::make_tuple(v1, v2, ..., vN), MyCustomEq{}));
00067 //
00068 // It checks the following requirements:
00069 //   1. The expansion for a value is deterministic.
00070 //   2. For any two objects `a` and `b` in the sequence, if `a == b` evaluates
00071 //      to true, then their hash expansion must be equal.
00072 //   3. If `a == b` evaluates to false their hash expansion must be unequal.
00073 //   4. If `a == b` evaluates to false neither hash expansion can be a
00074 //      suffix of the other.
00075 //   5. AbslHashValue overloads should not be called by the user. They are only
00076 //      meant to be called by the framework. Users should call H::combine() and
00077 //      H::combine_contiguous().
00078 //   6. No moved-from instance of the hash state is used in the implementation
00079 //      of AbslHashValue.
00080 //
00081 // The values do not have to have the same type. This can be useful for
00082 // equivalent types that support heterogeneous lookup.
00083 //
00084 // A possible reason for breaking (2) is combining state in the hash expansion
00085 // that was not used in `==`.
00086 // For example:
00087 //
00088 // struct Bad2 {
00089 //   int a, b;
00090 //   template <typename H>
00091 //   friend H AbslHashValue(H state, Bad2 x) {
00092 //     // Uses a and b.
00093 //     return H::combine(std::move(state), x.a, x.b);
00094 //   }
00095 //   friend bool operator==(Bad2 x, Bad2 y) {
00096 //     // Only uses a.
00097 //     return x.a == y.a;
00098 //   }
00099 // };
00100 //
00101 // As for (3), breaking this usually means that there is state being passed to
00102 // the `==` operator that is not used in the hash expansion.
00103 // For example:
00104 //
00105 // struct Bad3 {
00106 //   int a, b;
00107 //   template <typename H>
00108 //   friend H AbslHashValue(H state, Bad3 x) {
00109 //     // Only uses a.
00110 //     return H::combine(std::move(state), x.a);
00111 //   }
00112 //   friend bool operator==(Bad3 x, Bad3 y) {
00113 //     // Uses a and b.
00114 //     return x.a == y.a && x.b == y.b;
00115 //   }
00116 // };
00117 //
00118 // Finally, a common way to break 4 is by combining dynamic ranges without
00119 // combining the size of the range.
00120 // For example:
00121 //
00122 // struct Bad4 {
00123 //   int *p, size;
00124 //   template <typename H>
00125 //   friend H AbslHashValue(H state, Bad4 x) {
00126 //     return H::combine_contiguous(std::move(state), x.p, x.p + x.size);
00127 //   }
00128 //   friend bool operator==(Bad4 x, Bad4 y) {
00129 //    // Compare two ranges for equality. C++14 code can instead use std::equal.
00130 //     return absl::equal(x.p, x.p + x.size, y.p, y.p + y.size);
00131 //   }
00132 // };
00133 //
00134 // An easy solution to this is to combine the size after combining the range,
00135 // like so:
00136 // template <typename H>
00137 // friend H AbslHashValue(H state, Bad4 x) {
00138 //   return H::combine(
00139 //       H::combine_contiguous(std::move(state), x.p, x.p + x.size), x.size);
00140 // }
00141 //
00142 template <int&... ExplicitBarrier, typename Container>
00143 ABSL_MUST_USE_RESULT testing::AssertionResult
00144 VerifyTypeImplementsAbslHashCorrectly(const Container& values);
00145 
00146 template <int&... ExplicitBarrier, typename Container, typename Eq>
00147 ABSL_MUST_USE_RESULT testing::AssertionResult
00148 VerifyTypeImplementsAbslHashCorrectly(const Container& values, Eq equals);
00149 
00150 template <int&..., typename T>
00151 ABSL_MUST_USE_RESULT testing::AssertionResult
00152 VerifyTypeImplementsAbslHashCorrectly(std::initializer_list<T> values);
00153 
00154 template <int&..., typename T, typename Eq>
00155 ABSL_MUST_USE_RESULT testing::AssertionResult
00156 VerifyTypeImplementsAbslHashCorrectly(std::initializer_list<T> values,
00157                                       Eq equals);
00158 
00159 namespace hash_internal {
00160 
00161 struct PrintVisitor {
00162   size_t index;
00163   template <typename T>
00164   std::string operator()(const T* value) const {
00165     return absl::StrCat("#", index, "(", testing::PrintToString(*value), ")");
00166   }
00167 };
00168 
00169 template <typename Eq>
00170 struct EqVisitor {
00171   Eq eq;
00172   template <typename T, typename U>
00173   bool operator()(const T* t, const U* u) const {
00174     return eq(*t, *u);
00175   }
00176 };
00177 
00178 struct ExpandVisitor {
00179   template <typename T>
00180   SpyHashState operator()(const T* value) const {
00181     return SpyHashState::combine(SpyHashState(), *value);
00182   }
00183 };
00184 
00185 template <typename Container, typename Eq>
00186 ABSL_MUST_USE_RESULT testing::AssertionResult
00187 VerifyTypeImplementsAbslHashCorrectly(const Container& values, Eq equals) {
00188   using V = typename Container::value_type;
00189 
00190   struct Info {
00191     const V& value;
00192     size_t index;
00193     std::string ToString() const {
00194       return absl::visit(PrintVisitor{index}, value);
00195     }
00196     SpyHashState expand() const { return absl::visit(ExpandVisitor{}, value); }
00197   };
00198 
00199   using EqClass = std::vector<Info>;
00200   std::vector<EqClass> classes;
00201 
00202   // Gather the values in equivalence classes.
00203   size_t i = 0;
00204   for (const auto& value : values) {
00205     EqClass* c = nullptr;
00206     for (auto& eqclass : classes) {
00207       if (absl::visit(EqVisitor<Eq>{equals}, value, eqclass[0].value)) {
00208         c = &eqclass;
00209         break;
00210       }
00211     }
00212     if (c == nullptr) {
00213       classes.emplace_back();
00214       c = &classes.back();
00215     }
00216     c->push_back({value, i});
00217     ++i;
00218 
00219     // Verify potential errors captured by SpyHashState.
00220     if (auto error = c->back().expand().error()) {
00221       return testing::AssertionFailure() << *error;
00222     }
00223   }
00224 
00225   if (classes.size() < 2) {
00226     return testing::AssertionFailure()
00227            << "At least two equivalence classes are expected.";
00228   }
00229 
00230   // We assume that equality is correctly implemented.
00231   // Now we verify that AbslHashValue is also correctly implemented.
00232 
00233   for (const auto& c : classes) {
00234     // All elements of the equivalence class must have the same hash
00235     // expansion.
00236     const SpyHashState expected = c[0].expand();
00237     for (const Info& v : c) {
00238       if (v.expand() != v.expand()) {
00239         return testing::AssertionFailure()
00240                << "Hash expansion for " << v.ToString()
00241                << " is non-deterministic.";
00242       }
00243       if (v.expand() != expected) {
00244         return testing::AssertionFailure()
00245                << "Values " << c[0].ToString() << " and " << v.ToString()
00246                << " evaluate as equal but have an unequal hash expansion.";
00247       }
00248     }
00249 
00250     // Elements from other classes must have different hash expansion.
00251     for (const auto& c2 : classes) {
00252       if (&c == &c2) continue;
00253       const SpyHashState c2_hash = c2[0].expand();
00254       switch (SpyHashState::Compare(expected, c2_hash)) {
00255         case SpyHashState::CompareResult::kEqual:
00256           return testing::AssertionFailure()
00257                  << "Values " << c[0].ToString() << " and " << c2[0].ToString()
00258                  << " evaluate as unequal but have an equal hash expansion.";
00259         case SpyHashState::CompareResult::kBSuffixA:
00260           return testing::AssertionFailure()
00261                  << "Hash expansion of " << c2[0].ToString()
00262                  << " is a suffix of the hash expansion of " << c[0].ToString()
00263                  << ".";
00264         case SpyHashState::CompareResult::kASuffixB:
00265           return testing::AssertionFailure()
00266                  << "Hash expansion of " << c[0].ToString()
00267                  << " is a suffix of the hash expansion of " << c2[0].ToString()
00268                  << ".";
00269         case SpyHashState::CompareResult::kUnequal:
00270           break;
00271       }
00272     }
00273   }
00274   return testing::AssertionSuccess();
00275 }
00276 
00277 template <typename... T>
00278 struct TypeSet {
00279   template <typename U, bool = disjunction<std::is_same<T, U>...>::value>
00280   struct Insert {
00281     using type = TypeSet<U, T...>;
00282   };
00283   template <typename U>
00284   struct Insert<U, true> {
00285     using type = TypeSet;
00286   };
00287 
00288   template <template <typename...> class C>
00289   using apply = C<T...>;
00290 };
00291 
00292 template <typename... T>
00293 struct MakeTypeSet : TypeSet<> {};
00294 template <typename T, typename... Ts>
00295 struct MakeTypeSet<T, Ts...> : MakeTypeSet<Ts...>::template Insert<T>::type {};
00296 
00297 template <typename... T>
00298 using VariantForTypes = typename MakeTypeSet<
00299     const typename std::decay<T>::type*...>::template apply<absl::variant>;
00300 
00301 template <typename Container>
00302 struct ContainerAsVector {
00303   using V = absl::variant<const typename Container::value_type*>;
00304   using Out = std::vector<V>;
00305 
00306   static Out Do(const Container& values) {
00307     Out out;
00308     for (const auto& v : values) out.push_back(&v);
00309     return out;
00310   }
00311 };
00312 
00313 template <typename... T>
00314 struct ContainerAsVector<std::tuple<T...>> {
00315   using V = VariantForTypes<T...>;
00316   using Out = std::vector<V>;
00317 
00318   template <size_t... I>
00319   static Out DoImpl(const std::tuple<T...>& tuple, absl::index_sequence<I...>) {
00320     return Out{&std::get<I>(tuple)...};
00321   }
00322 
00323   static Out Do(const std::tuple<T...>& values) {
00324     return DoImpl(values, absl::index_sequence_for<T...>());
00325   }
00326 };
00327 
00328 template <>
00329 struct ContainerAsVector<std::tuple<>> {
00330   static std::vector<VariantForTypes<int>> Do(std::tuple<>) { return {}; }
00331 };
00332 
00333 struct DefaultEquals {
00334   template <typename T, typename U>
00335   bool operator()(const T& t, const U& u) const {
00336     return t == u;
00337   }
00338 };
00339 
00340 }  // namespace hash_internal
00341 
00342 template <int&..., typename Container>
00343 ABSL_MUST_USE_RESULT testing::AssertionResult
00344 VerifyTypeImplementsAbslHashCorrectly(const Container& values) {
00345   return hash_internal::VerifyTypeImplementsAbslHashCorrectly(
00346       hash_internal::ContainerAsVector<Container>::Do(values),
00347       hash_internal::DefaultEquals{});
00348 }
00349 
00350 template <int&..., typename Container, typename Eq>
00351 ABSL_MUST_USE_RESULT testing::AssertionResult
00352 VerifyTypeImplementsAbslHashCorrectly(const Container& values, Eq equals) {
00353   return hash_internal::VerifyTypeImplementsAbslHashCorrectly(
00354       hash_internal::ContainerAsVector<Container>::Do(values), equals);
00355 }
00356 
00357 template <int&..., typename T>
00358 ABSL_MUST_USE_RESULT testing::AssertionResult
00359 VerifyTypeImplementsAbslHashCorrectly(std::initializer_list<T> values) {
00360   return hash_internal::VerifyTypeImplementsAbslHashCorrectly(
00361       hash_internal::ContainerAsVector<std::initializer_list<T>>::Do(values),
00362       hash_internal::DefaultEquals{});
00363 }
00364 
00365 template <int&..., typename T, typename Eq>
00366 ABSL_MUST_USE_RESULT testing::AssertionResult
00367 VerifyTypeImplementsAbslHashCorrectly(std::initializer_list<T> values,
00368                                       Eq equals) {
00369   return hash_internal::VerifyTypeImplementsAbslHashCorrectly(
00370       hash_internal::ContainerAsVector<std::initializer_list<T>>::Do(values),
00371       equals);
00372 }
00373 
00374 }  // namespace absl
00375 
00376 #endif  // ABSL_HASH_HASH_TESTING_H_


abseil_cpp
Author(s):
autogenerated on Wed Jun 19 2019 19:42:14