Index: third_party/googletest/googlemock/include/gmock/gmock-matchers.h =================================================================== --- third_party/googletest/googlemock/include/gmock/gmock-matchers.h +++ third_party/googletest/googlemock/include/gmock/gmock-matchers.h @@ -3942,20 +3942,155 @@ } ::std::vector> matchers_; }; +// Implements ContainsSubequence(). +template +class [[nodiscard]] ContainsSubsequenceMatcherImpl + : public MatcherInterface { + public: + typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Container) RawContainer; + typedef internal::StlContainerView View; + typedef typename View::type StlContainer; + typedef typename View::const_reference StlContainerReference; + typedef typename internal::RangeTraits::value_type Element; + + // Constructs the matcher from a sequence of element values or + // element matchers. + template + ContainsSubsequenceMatcherImpl(InputIter first, InputIter last) { + std::copy(first, last, std::back_inserter(matchers_)); + } + + // Describes what this matcher does. + void DescribeTo(::std::ostream* os) const override { + if (matchers_.size() == 0) { + *os << "contains an empty sequence"; + return; + } + *os << "contains in order a subsequence of elements that matches: "; + for (size_t i = 0; i != matchers_.size(); ++i) { + if (i > 0) { + *os << ", then "; + } + matchers_[i].DescribeTo(os); + } + } + + // Describes what the negation of this matcher does. + void DescribeNegationTo(::std::ostream* os) const override { + if (matchers_.size() == 0) { + *os << "does not contain an empty sequence"; + return; + } + *os << "does not contain in order a subsequence of elements that matches "; + for (size_t i = 0; i != matchers_.size(); ++i) { + if (i > 0) { + *os << ", then "; + } + matchers_[i].DescribeTo(os); + } + } + + bool MatchAndExplain(Container container, + MatchResultListener* listener) const override { + StlContainerReference stl_container = View::ConstReference(container); + + size_t num_matches = 0; + // Track which elements match which matcher. + size_t num_elements_examined = 0; + std::vector match_indices; + for (const auto& element : stl_container) { + if (num_matches == matchers_.size()) { + break; + } + StringMatchResultListener inner_listener; + if (matchers_[num_matches].MatchAndExplain(element, &inner_listener)) { + ++num_matches; + match_indices.push_back(num_elements_examined); + } + + ++num_elements_examined; + } + + if (num_matches < matchers_.size()) { + // We provide an explanation of the first matcher that failed to match + // when trying to match the subsequence greedily. A better approach would + // be to compute the longest common subsequence (LCS) between the + // elements and the matchers and then provide explanations for any + // remaining matchers and elements that couldn't match each other, but + // this introduces a fair bit of complexity. + if (listener->IsInterested()) { + for (size_t i = 0; i < match_indices.size(); ++i) { + *listener << "found match for matcher #" << i + << " with element at position #" << match_indices[i] + << ", "; + } + + if (num_matches > 0) { + *listener << "but "; + } + + *listener << "could not find a match for matcher #" << num_matches + << " ("; + matchers_[num_matches].DescribeTo(listener->stream()); + *listener << ")"; + + if (num_matches > 0) { + *listener << " after the last match at position #" + << match_indices[num_matches - 1]; + } + } + + return false; + } + + return true; + } + + private: + ::std::vector> matchers_; +}; + // Functor for use in TransformTuple. // Performs MatcherCast on an input argument of any type. template struct CastAndAppendTransform { template Matcher operator()(const Arg& a) const { return MatcherCast(a); } }; +// Implements ContainsSubsequence. +template +class [[nodiscard]] ContainsSubsequenceMatcher { + public: + explicit ContainsSubsequenceMatcher(const MatcherTuple& args) + : matchers_(args) {} + + template + // NOLINTNEXTLINE(google-explicit-constructor) + operator Matcher() const { + typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Container) RawContainer; + typedef typename internal::StlContainerView::type View; + typedef typename internal::RangeTraits::value_type Element; + typedef ::std::vector> MatcherVec; + MatcherVec matchers; + matchers.reserve(::std::tuple_size::value); + TransformTupleValues(CastAndAppendTransform(), matchers_, + ::std::back_inserter(matchers)); + return Matcher( + new ContainsSubsequenceMatcherImpl(matchers.begin(), + matchers.end())); + } + + private: + const MatcherTuple matchers_; +}; + // Implements UnorderedElementsAre. template class [[nodiscard]] UnorderedElementsAreMatcher { public: explicit UnorderedElementsAreMatcher(const MatcherTuple& args) @@ -5420,10 +5555,22 @@ return internal::UnorderedElementsAreMatcher< std::tuple::type...>>( std::make_tuple(matchers...)); } +// ContainsSubsequence(m1, m2, ..., mk) matches a container that contains +// elements that match m1, m2, ..., mk in that order with possible gaps +// between them. +template +internal::ContainsSubsequenceMatcher< + ::std::tuple::type...>> +ContainsSubsequence(const Args&... matchers) { + return internal::ContainsSubsequenceMatcher< + ::std::tuple::type...>>( + ::std::make_tuple(matchers...)); +} + // Define variadic matcher versions. template internal::AllOfMatcher::type...> AllOf( const Args&... matchers) { return internal::AllOfMatcher::type...>( Index: third_party/googletest/googlemock/include/gmock/gmock-spec-builders.h =================================================================== --- third_party/googletest/googlemock/include/gmock/gmock-spec-builders.h +++ third_party/googletest/googlemock/include/gmock/gmock-spec-builders.h @@ -1955,10 +1955,15 @@ template struct SignatureOf { using type = R(Args...); }; +template +struct SignatureOf { + using type = R(Args...); +}; + template