Add 'third_party/abseil_cpp/' from commit '768eb2ca28
'
git-subtree-dir: third_party/abseil_cpp git-subtree-mainline:ffb2ae54be
git-subtree-split:768eb2ca28
This commit is contained in:
commit
fc8dc48020
1276 changed files with 208196 additions and 0 deletions
378
third_party/abseil_cpp/absl/hash/hash_testing.h
vendored
Normal file
378
third_party/abseil_cpp/absl/hash/hash_testing.h
vendored
Normal file
|
@ -0,0 +1,378 @@
|
|||
// 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::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;
|
||||
|
||||
// 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_internal
|
||||
|
||||
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
|
||||
} // namespace absl
|
||||
|
||||
#endif // ABSL_HASH_HASH_TESTING_H_
|
Loading…
Add table
Add a link
Reference in a new issue