Path: blob/main/contrib/googletest/googlemock/src/gmock-matchers.cc
48255 views
// Copyright 2007, Google Inc.1// All rights reserved.2//3// Redistribution and use in source and binary forms, with or without4// modification, are permitted provided that the following conditions are5// met:6//7// * Redistributions of source code must retain the above copyright8// notice, this list of conditions and the following disclaimer.9// * Redistributions in binary form must reproduce the above10// copyright notice, this list of conditions and the following disclaimer11// in the documentation and/or other materials provided with the12// distribution.13// * Neither the name of Google Inc. nor the names of its14// contributors may be used to endorse or promote products derived from15// this software without specific prior written permission.16//17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS18// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT19// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR20// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT21// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,22// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT23// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,24// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY25// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT26// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE27// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.2829// Google Mock - a framework for writing C++ mock classes.30//31// This file implements Matcher<const string&>, Matcher<string>, and32// utilities for defining matchers.3334#include "gmock/gmock-matchers.h"3536#include <string.h>3738#include <iostream>39#include <sstream>40#include <string>41#include <vector>4243namespace testing {44namespace internal {4546// Returns the description for a matcher defined using the MATCHER*()47// macro where the user-supplied description string is "", if48// 'negation' is false; otherwise returns the description of the49// negation of the matcher. 'param_values' contains a list of strings50// that are the print-out of the matcher's parameters.51GTEST_API_ std::string FormatMatcherDescription(52bool negation, const char* matcher_name,53const std::vector<const char*>& param_names, const Strings& param_values) {54std::string result = ConvertIdentifierNameToWords(matcher_name);55if (!param_values.empty()) {56result += " " + JoinAsKeyValueTuple(param_names, param_values);57}58return negation ? "not (" + result + ")" : result;59}6061// FindMaxBipartiteMatching and its helper class.62//63// Uses the well-known Ford-Fulkerson max flow method to find a maximum64// bipartite matching. Flow is considered to be from left to right.65// There is an implicit source node that is connected to all of the left66// nodes, and an implicit sink node that is connected to all of the67// right nodes. All edges have unit capacity.68//69// Neither the flow graph nor the residual flow graph are represented70// explicitly. Instead, they are implied by the information in 'graph' and71// a vector<int> called 'left_' whose elements are initialized to the72// value kUnused. This represents the initial state of the algorithm,73// where the flow graph is empty, and the residual flow graph has the74// following edges:75// - An edge from source to each left_ node76// - An edge from each right_ node to sink77// - An edge from each left_ node to each right_ node, if the78// corresponding edge exists in 'graph'.79//80// When the TryAugment() method adds a flow, it sets left_[l] = r for some81// nodes l and r. This induces the following changes:82// - The edges (source, l), (l, r), and (r, sink) are added to the83// flow graph.84// - The same three edges are removed from the residual flow graph.85// - The reverse edges (l, source), (r, l), and (sink, r) are added86// to the residual flow graph, which is a directional graph87// representing unused flow capacity.88//89// When the method augments a flow (moving left_[l] from some r1 to some90// other r2), this can be thought of as "undoing" the above steps with91// respect to r1 and "redoing" them with respect to r2.92//93// It bears repeating that the flow graph and residual flow graph are94// never represented explicitly, but can be derived by looking at the95// information in 'graph' and in left_.96//97// As an optimization, there is a second vector<int> called right_ which98// does not provide any new information. Instead, it enables more99// efficient queries about edges entering or leaving the right-side nodes100// of the flow or residual flow graphs. The following invariants are101// maintained:102//103// left[l] == kUnused or right[left[l]] == l104// right[r] == kUnused or left[right[r]] == r105//106// . [ source ] .107// . ||| .108// . ||| .109// . ||\--> left[0]=1 ---\ right[0]=-1 ----\ .110// . || | | .111// . |\---> left[1]=-1 \--> right[1]=0 ---\| .112// . | || .113// . \----> left[2]=2 ------> right[2]=2 --\|| .114// . ||| .115// . elements matchers vvv .116// . [ sink ] .117//118// See Also:119// [1] Cormen, et al (2001). "Section 26.2: The Ford-Fulkerson method".120// "Introduction to Algorithms (Second ed.)", pp. 651-664.121// [2] "Ford-Fulkerson algorithm", Wikipedia,122// 'https://en.wikipedia.org/wiki/Ford%E2%80%93Fulkerson_algorithm'123class MaxBipartiteMatchState {124public:125explicit MaxBipartiteMatchState(const MatchMatrix& graph)126: graph_(&graph),127left_(graph_->LhsSize(), kUnused),128right_(graph_->RhsSize(), kUnused) {}129130// Returns the edges of a maximal match, each in the form {left, right}.131ElementMatcherPairs Compute() {132// 'seen' is used for path finding { 0: unseen, 1: seen }.133::std::vector<char> seen;134// Searches the residual flow graph for a path from each left node to135// the sink in the residual flow graph, and if one is found, add flow136// to the graph. It's okay to search through the left nodes once. The137// edge from the implicit source node to each previously-visited left138// node will have flow if that left node has any path to the sink139// whatsoever. Subsequent augmentations can only add flow to the140// network, and cannot take away that previous flow unit from the source.141// Since the source-to-left edge can only carry one flow unit (or,142// each element can be matched to only one matcher), there is no need143// to visit the left nodes more than once looking for augmented paths.144// The flow is known to be possible or impossible by looking at the145// node once.146for (size_t ilhs = 0; ilhs < graph_->LhsSize(); ++ilhs) {147// Reset the path-marking vector and try to find a path from148// source to sink starting at the left_[ilhs] node.149GTEST_CHECK_(left_[ilhs] == kUnused)150<< "ilhs: " << ilhs << ", left_[ilhs]: " << left_[ilhs];151// 'seen' initialized to 'graph_->RhsSize()' copies of 0.152seen.assign(graph_->RhsSize(), 0);153TryAugment(ilhs, &seen);154}155ElementMatcherPairs result;156for (size_t ilhs = 0; ilhs < left_.size(); ++ilhs) {157size_t irhs = left_[ilhs];158if (irhs == kUnused) continue;159result.push_back(ElementMatcherPair(ilhs, irhs));160}161return result;162}163164private:165static const size_t kUnused = static_cast<size_t>(-1);166167// Perform a depth-first search from left node ilhs to the sink. If a168// path is found, flow is added to the network by linking the left and169// right vector elements corresponding each segment of the path.170// Returns true if a path to sink was found, which means that a unit of171// flow was added to the network. The 'seen' vector elements correspond172// to right nodes and are marked to eliminate cycles from the search.173//174// Left nodes will only be explored at most once because they175// are accessible from at most one right node in the residual flow176// graph.177//178// Note that left_[ilhs] is the only element of left_ that TryAugment will179// potentially transition from kUnused to another value. Any other180// left_ element holding kUnused before TryAugment will be holding it181// when TryAugment returns.182//183bool TryAugment(size_t ilhs, ::std::vector<char>* seen) {184for (size_t irhs = 0; irhs < graph_->RhsSize(); ++irhs) {185if ((*seen)[irhs]) continue;186if (!graph_->HasEdge(ilhs, irhs)) continue;187// There's an available edge from ilhs to irhs.188(*seen)[irhs] = 1;189// Next a search is performed to determine whether190// this edge is a dead end or leads to the sink.191//192// right_[irhs] == kUnused means that there is residual flow from193// right node irhs to the sink, so we can use that to finish this194// flow path and return success.195//196// Otherwise there is residual flow to some ilhs. We push flow197// along that path and call ourselves recursively to see if this198// ultimately leads to sink.199if (right_[irhs] == kUnused || TryAugment(right_[irhs], seen)) {200// Add flow from left_[ilhs] to right_[irhs].201left_[ilhs] = irhs;202right_[irhs] = ilhs;203return true;204}205}206return false;207}208209const MatchMatrix* graph_; // not owned210// Each element of the left_ vector represents a left hand side node211// (i.e. an element) and each element of right_ is a right hand side212// node (i.e. a matcher). The values in the left_ vector indicate213// outflow from that node to a node on the right_ side. The values214// in the right_ indicate inflow, and specify which left_ node is215// feeding that right_ node, if any. For example, left_[3] == 1 means216// there's a flow from element #3 to matcher #1. Such a flow would also217// be redundantly represented in the right_ vector as right_[1] == 3.218// Elements of left_ and right_ are either kUnused or mutually219// referent. Mutually referent means that left_[right_[i]] = i and220// right_[left_[i]] = i.221::std::vector<size_t> left_;222::std::vector<size_t> right_;223};224225const size_t MaxBipartiteMatchState::kUnused;226227GTEST_API_ ElementMatcherPairs FindMaxBipartiteMatching(const MatchMatrix& g) {228return MaxBipartiteMatchState(g).Compute();229}230231static void LogElementMatcherPairVec(const ElementMatcherPairs& pairs,232::std::ostream* stream) {233typedef ElementMatcherPairs::const_iterator Iter;234::std::ostream& os = *stream;235os << "{";236const char* sep = "";237for (Iter it = pairs.begin(); it != pairs.end(); ++it) {238os << sep << "\n (" << "element #" << it->first << ", " << "matcher #"239<< it->second << ")";240sep = ",";241}242os << "\n}";243}244245bool MatchMatrix::NextGraph() {246for (size_t ilhs = 0; ilhs < LhsSize(); ++ilhs) {247for (size_t irhs = 0; irhs < RhsSize(); ++irhs) {248char& b = matched_[SpaceIndex(ilhs, irhs)];249if (!b) {250b = 1;251return true;252}253b = 0;254}255}256return false;257}258259void MatchMatrix::Randomize() {260for (size_t ilhs = 0; ilhs < LhsSize(); ++ilhs) {261for (size_t irhs = 0; irhs < RhsSize(); ++irhs) {262char& b = matched_[SpaceIndex(ilhs, irhs)];263b = static_cast<char>(rand() & 1); // NOLINT264}265}266}267268std::string MatchMatrix::DebugString() const {269::std::stringstream ss;270const char* sep = "";271for (size_t i = 0; i < LhsSize(); ++i) {272ss << sep;273for (size_t j = 0; j < RhsSize(); ++j) {274ss << HasEdge(i, j);275}276sep = ";";277}278return ss.str();279}280281void UnorderedElementsAreMatcherImplBase::DescribeToImpl(282::std::ostream* os) const {283switch (match_flags()) {284case UnorderedMatcherRequire::ExactMatch:285if (matcher_describers_.empty()) {286*os << "is empty";287return;288}289if (matcher_describers_.size() == 1) {290*os << "has " << Elements(1) << " and that element ";291matcher_describers_[0]->DescribeTo(os);292return;293}294*os << "has " << Elements(matcher_describers_.size())295<< " and there exists some permutation of elements such that:\n";296break;297case UnorderedMatcherRequire::Superset:298*os << "a surjection from elements to requirements exists such that:\n";299break;300case UnorderedMatcherRequire::Subset:301*os << "an injection from elements to requirements exists such that:\n";302break;303}304305const char* sep = "";306for (size_t i = 0; i != matcher_describers_.size(); ++i) {307*os << sep;308if (match_flags() == UnorderedMatcherRequire::ExactMatch) {309*os << " - element #" << i << " ";310} else {311*os << " - an element ";312}313matcher_describers_[i]->DescribeTo(os);314if (match_flags() == UnorderedMatcherRequire::ExactMatch) {315sep = ", and\n";316} else {317sep = "\n";318}319}320}321322void UnorderedElementsAreMatcherImplBase::DescribeNegationToImpl(323::std::ostream* os) const {324switch (match_flags()) {325case UnorderedMatcherRequire::ExactMatch:326if (matcher_describers_.empty()) {327*os << "isn't empty";328return;329}330if (matcher_describers_.size() == 1) {331*os << "doesn't have " << Elements(1) << ", or has " << Elements(1)332<< " that ";333matcher_describers_[0]->DescribeNegationTo(os);334return;335}336*os << "doesn't have " << Elements(matcher_describers_.size())337<< ", or there exists no permutation of elements such that:\n";338break;339case UnorderedMatcherRequire::Superset:340*os << "no surjection from elements to requirements exists such that:\n";341break;342case UnorderedMatcherRequire::Subset:343*os << "no injection from elements to requirements exists such that:\n";344break;345}346const char* sep = "";347for (size_t i = 0; i != matcher_describers_.size(); ++i) {348*os << sep;349if (match_flags() == UnorderedMatcherRequire::ExactMatch) {350*os << " - element #" << i << " ";351} else {352*os << " - an element ";353}354matcher_describers_[i]->DescribeTo(os);355if (match_flags() == UnorderedMatcherRequire::ExactMatch) {356sep = ", and\n";357} else {358sep = "\n";359}360}361}362363// Checks that all matchers match at least one element, and that all364// elements match at least one matcher. This enables faster matching365// and better error reporting.366// Returns false, writing an explanation to 'listener', if and only367// if the success criteria are not met.368bool UnorderedElementsAreMatcherImplBase::VerifyMatchMatrix(369const ::std::vector<std::string>& element_printouts,370const MatchMatrix& matrix, MatchResultListener* listener) const {371if (matrix.LhsSize() == 0 && matrix.RhsSize() == 0) {372return true;373}374375const bool is_exact_match_with_size_discrepency =376match_flags() == UnorderedMatcherRequire::ExactMatch &&377matrix.LhsSize() != matrix.RhsSize();378if (is_exact_match_with_size_discrepency) {379// The element count doesn't match. If the container is empty,380// there's no need to explain anything as Google Mock already381// prints the empty container. Otherwise we just need to show382// how many elements there actually are.383if (matrix.LhsSize() != 0 && listener->IsInterested()) {384*listener << "which has " << Elements(matrix.LhsSize()) << "\n";385}386}387388bool result = !is_exact_match_with_size_discrepency;389::std::vector<char> element_matched(matrix.LhsSize(), 0);390::std::vector<char> matcher_matched(matrix.RhsSize(), 0);391392for (size_t ilhs = 0; ilhs < matrix.LhsSize(); ilhs++) {393for (size_t irhs = 0; irhs < matrix.RhsSize(); irhs++) {394char matched = matrix.HasEdge(ilhs, irhs);395element_matched[ilhs] |= matched;396matcher_matched[irhs] |= matched;397}398}399400if (match_flags() & UnorderedMatcherRequire::Superset) {401const char* sep =402"where the following matchers don't match any elements:\n";403for (size_t mi = 0; mi < matcher_matched.size(); ++mi) {404if (matcher_matched[mi]) continue;405result = false;406if (listener->IsInterested()) {407*listener << sep << "matcher #" << mi << ": ";408matcher_describers_[mi]->DescribeTo(listener->stream());409sep = ",\n";410}411}412}413414if (match_flags() & UnorderedMatcherRequire::Subset) {415const char* sep =416"where the following elements don't match any matchers:\n";417const char* outer_sep = "";418if (!result) {419outer_sep = "\nand ";420}421for (size_t ei = 0; ei < element_matched.size(); ++ei) {422if (element_matched[ei]) continue;423result = false;424if (listener->IsInterested()) {425*listener << outer_sep << sep << "element #" << ei << ": "426<< element_printouts[ei];427sep = ",\n";428outer_sep = "";429}430}431}432return result;433}434435bool UnorderedElementsAreMatcherImplBase::FindPairing(436const MatchMatrix& matrix, MatchResultListener* listener) const {437ElementMatcherPairs matches = FindMaxBipartiteMatching(matrix);438439size_t max_flow = matches.size();440if ((match_flags() & UnorderedMatcherRequire::Superset) &&441max_flow < matrix.RhsSize()) {442if (listener->IsInterested()) {443*listener << "where no permutation of the elements can satisfy all "444"matchers, and the closest match is "445<< max_flow << " of " << matrix.RhsSize()446<< " matchers with the pairings:\n";447LogElementMatcherPairVec(matches, listener->stream());448}449return false;450}451if ((match_flags() & UnorderedMatcherRequire::Subset) &&452max_flow < matrix.LhsSize()) {453if (listener->IsInterested()) {454*listener455<< "where not all elements can be matched, and the closest match is "456<< max_flow << " of " << matrix.RhsSize()457<< " matchers with the pairings:\n";458LogElementMatcherPairVec(matches, listener->stream());459}460return false;461}462463if (matches.size() > 1) {464if (listener->IsInterested()) {465const char* sep = "where:\n";466for (size_t mi = 0; mi < matches.size(); ++mi) {467*listener << sep << " - element #" << matches[mi].first468<< " is matched by matcher #" << matches[mi].second;469sep = ",\n";470}471}472}473return true;474}475476} // namespace internal477} // namespace testing478479480