diff options
author | Abseil Team <absl-team@google.com> | 2020-10-14 20:55:07 (GMT) |
---|---|---|
committer | Derek Mauro <dmauro@google.com> | 2020-10-14 22:27:07 (GMT) |
commit | a4621888650aaae118de93f9faf8ef5a18cab303 (patch) | |
tree | cdef9c7c10bd81a75ad8e0b2dd9cf878ba85f20a /googlemock/include/gmock/gmock-matchers.h | |
parent | f3dbe3ec44e0163c4659ec7cd541ca7af66632bc (diff) | |
download | googletest-a4621888650aaae118de93f9faf8ef5a18cab303.zip googletest-a4621888650aaae118de93f9faf8ef5a18cab303.tar.gz googletest-a4621888650aaae118de93f9faf8ef5a18cab303.tar.bz2 |
Googletest export
Add ::testing::FieldsAre matcher for objects that support get<> and structured bindings.
PiperOrigin-RevId: 337165285
Diffstat (limited to 'googlemock/include/gmock/gmock-matchers.h')
-rw-r--r-- | googlemock/include/gmock/gmock-matchers.h | 213 |
1 files changed, 213 insertions, 0 deletions
diff --git a/googlemock/include/gmock/gmock-matchers.h b/googlemock/include/gmock/gmock-matchers.h index a897611..696f9e8 100644 --- a/googlemock/include/gmock/gmock-matchers.h +++ b/googlemock/include/gmock/gmock-matchers.h @@ -2879,6 +2879,203 @@ class PairMatcher { const SecondMatcher second_matcher_; }; +template <typename T, size_t... I> +auto UnpackStructImpl(const T& t, IndexSequence<I...>, int) + -> decltype(std::tie(get<I>(t)...)) { + static_assert(std::tuple_size<T>::value == sizeof...(I), + "Number of arguments doesn't match the number of fields."); + return std::tie(get<I>(t)...); +} + +#if defined(__cpp_structured_bindings) && __cpp_structured_bindings >= 201606 +template <typename T> +auto UnpackStructImpl(const T& t, MakeIndexSequence<1>, char) { + const auto& [a] = t; + return std::tie(a); +} +template <typename T> +auto UnpackStructImpl(const T& t, MakeIndexSequence<2>, char) { + const auto& [a, b] = t; + return std::tie(a, b); +} +template <typename T> +auto UnpackStructImpl(const T& t, MakeIndexSequence<3>, char) { + const auto& [a, b, c] = t; + return std::tie(a, b, c); +} +template <typename T> +auto UnpackStructImpl(const T& t, MakeIndexSequence<4>, char) { + const auto& [a, b, c, d] = t; + return std::tie(a, b, c, d); +} +template <typename T> +auto UnpackStructImpl(const T& t, MakeIndexSequence<5>, char) { + const auto& [a, b, c, d, e] = t; + return std::tie(a, b, c, d, e); +} +template <typename T> +auto UnpackStructImpl(const T& t, MakeIndexSequence<6>, char) { + const auto& [a, b, c, d, e, f] = t; + return std::tie(a, b, c, d, e, f); +} +template <typename T> +auto UnpackStructImpl(const T& t, MakeIndexSequence<7>, char) { + const auto& [a, b, c, d, e, f, g] = t; + return std::tie(a, b, c, d, e, f, g); +} +template <typename T> +auto UnpackStructImpl(const T& t, MakeIndexSequence<8>, char) { + const auto& [a, b, c, d, e, f, g, h] = t; + return std::tie(a, b, c, d, e, f, g, h); +} +template <typename T> +auto UnpackStructImpl(const T& t, MakeIndexSequence<9>, char) { + const auto& [a, b, c, d, e, f, g, h, i] = t; + return std::tie(a, b, c, d, e, f, g, h, i); +} +template <typename T> +auto UnpackStructImpl(const T& t, MakeIndexSequence<10>, char) { + const auto& [a, b, c, d, e, f, g, h, i, j] = t; + return std::tie(a, b, c, d, e, f, g, h, i, j); +} +template <typename T> +auto UnpackStructImpl(const T& t, MakeIndexSequence<11>, char) { + const auto& [a, b, c, d, e, f, g, h, i, j, k] = t; + return std::tie(a, b, c, d, e, f, g, h, i, j, k); +} +template <typename T> +auto UnpackStructImpl(const T& t, MakeIndexSequence<12>, char) { + const auto& [a, b, c, d, e, f, g, h, i, j, k, l] = t; + return std::tie(a, b, c, d, e, f, g, h, i, j, k, l); +} +template <typename T> +auto UnpackStructImpl(const T& t, MakeIndexSequence<13>, char) { + const auto& [a, b, c, d, e, f, g, h, i, j, k, l, m] = t; + return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m); +} +template <typename T> +auto UnpackStructImpl(const T& t, MakeIndexSequence<14>, char) { + const auto& [a, b, c, d, e, f, g, h, i, j, k, l, m, n] = t; + return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m, n); +} +template <typename T> +auto UnpackStructImpl(const T& t, MakeIndexSequence<15>, char) { + const auto& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o] = t; + return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o); +} +template <typename T> +auto UnpackStructImpl(const T& t, MakeIndexSequence<16>, char) { + const auto& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p] = t; + return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p); +} +#endif // defined(__cpp_structured_bindings) + +template <size_t I, typename T> +auto UnpackStruct(const T& t) + -> decltype((UnpackStructImpl)(t, MakeIndexSequence<I>{}, 0)) { + return (UnpackStructImpl)(t, MakeIndexSequence<I>{}, 0); +} + +// Helper function to do comma folding in C++11. +// The array ensures left-to-right order of evaluation. +// Usage: VariadicExpand({expr...}); +template <typename T, size_t N> +void VariadicExpand(const T (&a)[N]) {} + +template <typename Struct, typename StructSize> +class FieldsAreMatcherImpl; + +template <typename Struct, size_t... I> +class FieldsAreMatcherImpl<Struct, IndexSequence<I...>> + : public MatcherInterface<Struct> { + using UnpackedType = + decltype(UnpackStruct<sizeof...(I)>(std::declval<const Struct&>())); + using MatchersType = std::tuple< + Matcher<const typename std::tuple_element<I, UnpackedType>::type&>...>; + + public: + template <typename Inner> + explicit FieldsAreMatcherImpl(const Inner& matchers) + : matchers_(testing::SafeMatcherCast< + const typename std::tuple_element<I, UnpackedType>::type&>( + std::get<I>(matchers))...) {} + + void DescribeTo(::std::ostream* os) const override { + const char* separator = ""; + VariadicExpand( + {(*os << separator << "has field #" << I << " that ", + std::get<I>(matchers_).DescribeTo(os), separator = ", and ")...}); + } + + void DescribeNegationTo(::std::ostream* os) const override { + const char* separator = ""; + VariadicExpand({(*os << separator << "has field #" << I << " that ", + std::get<I>(matchers_).DescribeNegationTo(os), + separator = ", or ")...}); + } + + bool MatchAndExplain(Struct t, MatchResultListener* listener) const override { + return MatchInternal((UnpackStruct<sizeof...(I)>)(t), listener); + } + + private: + bool MatchInternal(UnpackedType tuple, MatchResultListener* listener) const { + if (!listener->IsInterested()) { + // If the listener is not interested, we don't need to construct the + // explanation. + bool good = true; + VariadicExpand({good = good && std::get<I>(matchers_).Matches( + std::get<I>(tuple))...}); + return good; + } + + int failed_pos = -1; + + std::vector<StringMatchResultListener> inner_listener(sizeof...(I)); + + VariadicExpand( + {failed_pos == -1 && !std::get<I>(matchers_).MatchAndExplain( + std::get<I>(tuple), &inner_listener[I]) + ? failed_pos = I + : 0 ...}); + if (failed_pos != ~size_t{}) { + *listener << "whose field #" << failed_pos << " does not match"; + PrintIfNotEmpty(inner_listener[failed_pos].str(), listener->stream()); + return false; + } + + *listener << "whose all elements match"; + const char* separator = ", where"; + for (size_t index = 0; index < sizeof...(I); ++index) { + const std::string str = inner_listener[index].str(); + if (!str.empty()) { + *listener << separator << " field #" << index << " is a value " << str; + separator = ", and"; + } + } + + return true; + } + + MatchersType matchers_; +}; + +template <typename... Inner> +class FieldsAreMatcher { + public: + explicit FieldsAreMatcher(Inner... inner) : matchers_(std::move(inner)...) {} + + template <typename Struct> + operator Matcher<Struct>() const { // NOLINT + return Matcher<Struct>( + new FieldsAreMatcherImpl<const Struct&, IndexSequenceFor<Inner...>>( + matchers_)); + } + + private: + std::tuple<Inner...> matchers_; +}; + // Implements ElementsAre() and ElementsAreArray(). template <typename Container> class ElementsAreMatcherImpl : public MatcherInterface<Container> { @@ -4514,6 +4711,19 @@ Pair(FirstMatcher first_matcher, SecondMatcher second_matcher) { first_matcher, second_matcher); } +namespace no_adl { +// FieldsAre(matchers...) matches piecewise the fields of compatible structs. +// These include those that support `get<I>(obj)`, and when structured bindings +// are enabled any class that supports them. +// In particular, `std::tuple`, `std::pair`, `std::array` and aggregate types. +template <typename... M> +internal::FieldsAreMatcher<typename std::decay<M>::type...> FieldsAre( + M&&... matchers) { + return internal::FieldsAreMatcher<typename std::decay<M>::type...>( + std::forward<M>(matchers)...); +} +} // namespace no_adl + // Returns a predicate that is satisfied by anything that matches the // given matcher. template <typename M> @@ -5053,6 +5263,9 @@ PolymorphicMatcher<internal::ExceptionMatcherImpl<Err>> ThrowsMessage( #define GMOCK_INTERNAL_MATCHER_ARG_USAGE(i, data_unused, arg_unused) \ , gmock_p##i +// To prevent ADL on certain functions we put them on a separate namespace. +using namespace no_adl; // NOLINT + } // namespace testing GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 5046 |