From 41b9b0b5614588d252d565646ae43e9607d46502 Mon Sep 17 00:00:00 2001 From: "zhanyong.wan" Date: Wed, 1 Jul 2009 19:04:51 +0000 Subject: [PATCH] Implements Expectation, ExpectationSet, and After for specifying expectation orders. --- include/gmock/gmock-spec-builders.h | 350 +++++++++++++++++++++------- src/gmock-spec-builders.cc | 34 ++- test/gmock-spec-builders_test.cc | 322 ++++++++++++++++++++++++- 3 files changed, 606 insertions(+), 100 deletions(-) diff --git a/include/gmock/gmock-spec-builders.h b/include/gmock/gmock-spec-builders.h index a22bcd11..0748d9d4 100644 --- a/include/gmock/gmock-spec-builders.h +++ b/include/gmock/gmock-spec-builders.h @@ -49,13 +49,13 @@ // .With(multi-argument-matchers) // .Times(cardinality) // .InSequence(sequences) +// .After(expectations) // .WillOnce(action) // .WillRepeatedly(action) // .RetiresOnSaturation(); // -// where all clauses are optional, .InSequence() and .WillOnce() can -// appear any number of times, and .Times() can be omitted only if -// .WillOnce() or .WillRepeatedly() is present. +// where all clauses are optional, and .InSequence()/.After()/ +// .WillOnce() can appear any number of times. #ifndef GMOCK_INCLUDE_GMOCK_GMOCK_SPEC_BUILDERS_H_ #define GMOCK_INCLUDE_GMOCK_GMOCK_SPEC_BUILDERS_H_ @@ -76,22 +76,30 @@ namespace testing { +// An abstract handle of an expectation. +class Expectation; + +// A set of expectation handles. +class ExpectationSet; + // Anything inside the 'internal' namespace IS INTERNAL IMPLEMENTATION // and MUST NOT BE USED IN USER CODE!!! namespace internal { -template -class FunctionMocker; +// Implements a mock function. +template class FunctionMocker; // Base class for expectations. class ExpectationBase; +// Implements an expectation. +template class TypedExpectation; + // Helper class for testing the Expectation class template. class ExpectationTester; // Base class for function mockers. -template -class FunctionMockerBase; +template class FunctionMockerBase; // Protects the mock object registry (in class Mock), all function // mockers, and all expectations. @@ -232,7 +240,8 @@ class DefaultActionSpec { Clause last_clause_; }; // class DefaultActionSpec -// Possible reactions on uninteresting calls. +// Possible reactions on uninteresting calls. TODO(wan@google.com): +// rename the enum values to the kFoo style. enum CallReaction { ALLOW, WARN, @@ -327,31 +336,166 @@ class Mock { static void UnregisterLocked(internal::UntypedFunctionMockerBase* mocker); }; // class Mock +// An abstract handle of an expectation. Useful in the .After() +// clause of EXPECT_CALL() for setting the (partial) order of +// expectations. The syntax: +// +// Expectation e1 = EXPECT_CALL(...)...; +// EXPECT_CALL(...).After(e1)...; +// +// sets two expectations where the latter can only be matched after +// the former has been satisfied. +// +// Notes: +// - This class is copyable and has value semantics. +// - Constness is shallow: a const Expectation object itself cannot +// be modified, but the mutable methods of the ExpectationBase +// object it references can be called via expectation_base(). +class Expectation { + public: + // Constructs a null object that doesn't reference any expectation. + Expectation() {} + + // This single-argument ctor must not be explicit, in order to support the + // Expectation e = EXPECT_CALL(...); + // syntax. + // + // A TypedExpectation object stores its pre-requisites as + // Expectation objects, and needs to call the non-const Retire() + // method on the ExpectationBase objects they reference. Therefore + // Expectation must receive a *non-const* reference to the + // ExpectationBase object. + Expectation(internal::ExpectationBase& exp); // NOLINT + + // The compiler-generated copy ctor and operator= work exactly as + // intended, so we don't need to define our own. + + // Returns true iff rhs references the same expectation as this object does. + bool operator==(const Expectation& rhs) const { + return expectation_base_ == rhs.expectation_base_; + } + + bool operator!=(const Expectation& rhs) const { return !(*this == rhs); } + + private: + friend class ExpectationSet; + friend class Sequence; + friend class ::testing::internal::ExpectationBase; + + template + friend class ::testing::internal::FunctionMockerBase; + + template + friend class ::testing::internal::TypedExpectation; + + // This comparator is needed for putting Expectation objects into a set. + class Less { + public: + bool operator()(const Expectation& lhs, const Expectation& rhs) const { + return lhs.expectation_base_.get() < rhs.expectation_base_.get(); + } + }; + + typedef ::std::set Set; + + Expectation( + const internal::linked_ptr& expectation_base) : + expectation_base_(expectation_base) {} + + // Returns the expectation this object references. + const internal::linked_ptr& + expectation_base() const { + return expectation_base_; + } + + // A linked_ptr that co-owns the expectation this handle references. + internal::linked_ptr expectation_base_; +}; + +// A set of expectation handles. Useful in the .After() clause of +// EXPECT_CALL() for setting the (partial) order of expectations. The +// syntax: +// +// ExpectationSet es; +// es += EXPECT_CALL(...)...; +// es += EXPECT_CALL(...)...; +// EXPECT_CALL(...).After(es)...; +// +// sets three expectations where the last one can only be matched +// after the first two have both been satisfied. +// +// This class is copyable and has value semantics. +class ExpectationSet { + public: + // A bidirectional iterator that can read a const element in the set. + typedef Expectation::Set::const_iterator const_iterator; + + // An object stored in the set. This is an alias of Expectation. + typedef Expectation::Set::value_type value_type; + + // Constructs an empty set. + ExpectationSet() {} + + // This single-argument ctor must not be explicit, in order to support the + // ExpectationSet es = EXPECT_CALL(...); + // syntax. + ExpectationSet(internal::ExpectationBase& exp) { // NOLINT + *this += Expectation(exp); + } + + // This single-argument ctor implements implicit conversion from + // Expectation and thus must not be explicit. This allows either an + // Expectation or an ExpectationSet to be used in .After(). + ExpectationSet(const Expectation& e) { // NOLINT + *this += e; + } + + // The compiler-generator ctor and operator= works exactly as + // intended, so we don't need to define our own. + + // Returns true iff rhs contains the same set of Expectation objects + // as this does. + bool operator==(const ExpectationSet& rhs) const { + return expectations_ == rhs.expectations_; + } + + bool operator!=(const ExpectationSet& rhs) const { return !(*this == rhs); } + + // Implements the syntax + // expectation_set += EXPECT_CALL(...); + ExpectationSet& operator+=(const Expectation& e) { + expectations_.insert(e); + return *this; + } + + int size() const { return static_cast(expectations_.size()); } + + const_iterator begin() const { return expectations_.begin(); } + const_iterator end() const { return expectations_.end(); } + + private: + Expectation::Set expectations_; +}; + + // Sequence objects are used by a user to specify the relative order // in which the expectations should match. They are copyable (we rely // on the compiler-defined copy constructor and assignment operator). class Sequence { public: // Constructs an empty sequence. - Sequence() - : last_expectation_( - new internal::linked_ptr(NULL)) {} + Sequence() : last_expectation_(new Expectation) {} // Adds an expectation to this sequence. The caller must ensure // that no other thread is accessing this Sequence object. - void AddExpectation( - const internal::linked_ptr& expectation) const; + void AddExpectation(const Expectation& expectation) const; + private: - // The last expectation in this sequence. We use a nested - // linked_ptr here because: - // - Sequence objects are copyable, and we want the copies to act - // as aliases. The outer linked_ptr allows the copies to co-own - // and share the same state. - // - An Expectation object is co-owned (via linked_ptr) by its - // FunctionMocker and its successors (other Expectation objects). - // Hence the inner linked_ptr. - internal::linked_ptr > - last_expectation_; + // The last expectation in this sequence. We use a linked_ptr here + // because Sequence objects are copyable and we want the copies to + // be aliases. The linked_ptr allows the copies to co-own and share + // the same Expectation object. + internal::linked_ptr last_expectation_; }; // class Sequence // An object of this type causes all EXPECT_CALL() statements @@ -431,9 +575,7 @@ class ExpectationBase { // L >= g_gmock_mutex virtual void DescribeCallCountTo(::std::ostream* os) const = 0; protected: - typedef std::set, - LinkedPtrLessThan > - ExpectationBaseSet; + friend class ::testing::Expectation; enum Clause { // Don't change the order of the enum members! @@ -441,11 +583,16 @@ class ExpectationBase { kWith, kTimes, kInSequence, + kAfter, kWillOnce, kWillRepeatedly, kRetiresOnSaturation, }; + // Returns an Expectation object that references and co-owns this + // expectation. + virtual Expectation GetHandle() = 0; + // Asserts that the EXPECT_CALL() statement has the given property. void AssertSpecProperty(bool property, const string& failure_message) const { Assert(property, file_, line_, failure_message); @@ -518,7 +665,7 @@ class ExpectationBase { // Adds unsatisfied pre-requisites of this expectation to 'result'. // L >= g_gmock_mutex - void FindUnsatisfiedPrerequisites(ExpectationBaseSet* result) const; + void FindUnsatisfiedPrerequisites(ExpectationSet* result) const; // Returns the number this expectation has been invoked. // L >= g_gmock_mutex @@ -539,7 +686,7 @@ class ExpectationBase { friend class ::testing::internal::ExpectationTester; template - friend class Expectation; + friend class TypedExpectation; // This group of fields are part of the spec and won't change after // an EXPECT_CALL() statement finishes. @@ -548,11 +695,13 @@ class ExpectationBase { // True iff the cardinality is specified explicitly. bool cardinality_specified_; Cardinality cardinality_; // The cardinality of the expectation. - // The immediate pre-requisites of this expectation. We use - // linked_ptr in the set because we want an Expectation object to be - // co-owned by its FunctionMocker and its successors. This allows - // multiple mock objects to be deleted at different times. - ExpectationBaseSet immediate_prerequisites_; + // The immediate pre-requisites (i.e. expectations that must be + // satisfied before this expectation can be matched) of this + // expectation. We use linked_ptr in the set because we want an + // Expectation object to be co-owned by its FunctionMocker and its + // successors. This allows multiple mock objects to be deleted at + // different times. + ExpectationSet immediate_prerequisites_; // This group of fields are the current state of the expectation, // and can change as the mock function is called. @@ -562,14 +711,14 @@ class ExpectationBase { // Impements an expectation for the given function type. template -class Expectation : public ExpectationBase { +class TypedExpectation : public ExpectationBase { public: typedef typename Function::ArgumentTuple ArgumentTuple; typedef typename Function::ArgumentMatcherTuple ArgumentMatcherTuple; typedef typename Function::Result Result; - Expectation(FunctionMockerBase* owner, const char* file, int line, - const ArgumentMatcherTuple& m) + TypedExpectation(FunctionMockerBase* owner, const char* file, int line, + const ArgumentMatcherTuple& m) : ExpectationBase(file, line), owner_(owner), matchers_(m), @@ -584,14 +733,14 @@ class Expectation : public ExpectationBase { last_clause_(kNone), action_count_checked_(false) {} - virtual ~Expectation() { + virtual ~TypedExpectation() { // Check the validity of the action count if it hasn't been done // yet (for example, if the expectation was never used). CheckActionCountIfNotDone(); } // Implements the .With() clause. - Expectation& With(const Matcher& m) { + TypedExpectation& With(const Matcher& m) { if (last_clause_ == kWith) { ExpectSpecProperty(false, ".With() cannot appear " @@ -608,7 +757,7 @@ class Expectation : public ExpectationBase { } // Implements the .Times() clause. - Expectation& Times(const Cardinality& cardinality) { + TypedExpectation& Times(const Cardinality& cardinality) { if (last_clause_ ==kTimes) { ExpectSpecProperty(false, ".Times() cannot appear " @@ -626,40 +775,70 @@ class Expectation : public ExpectationBase { } // Implements the .Times() clause. - Expectation& Times(int n) { + TypedExpectation& Times(int n) { return Times(Exactly(n)); } // Implements the .InSequence() clause. - Expectation& InSequence(const Sequence& s) { + TypedExpectation& InSequence(const Sequence& s) { ExpectSpecProperty(last_clause_ <= kInSequence, - ".InSequence() cannot appear after .WillOnce()," - " .WillRepeatedly(), or " + ".InSequence() cannot appear after .After()," + " .WillOnce(), .WillRepeatedly(), or " ".RetiresOnSaturation()."); last_clause_ = kInSequence; - s.AddExpectation(owner_->GetLinkedExpectationBase(this)); + s.AddExpectation(GetHandle()); return *this; } - Expectation& InSequence(const Sequence& s1, const Sequence& s2) { + TypedExpectation& InSequence(const Sequence& s1, const Sequence& s2) { return InSequence(s1).InSequence(s2); } - Expectation& InSequence(const Sequence& s1, const Sequence& s2, - const Sequence& s3) { + TypedExpectation& InSequence(const Sequence& s1, const Sequence& s2, + const Sequence& s3) { return InSequence(s1, s2).InSequence(s3); } - Expectation& InSequence(const Sequence& s1, const Sequence& s2, - const Sequence& s3, const Sequence& s4) { + TypedExpectation& InSequence(const Sequence& s1, const Sequence& s2, + const Sequence& s3, const Sequence& s4) { return InSequence(s1, s2, s3).InSequence(s4); } - Expectation& InSequence(const Sequence& s1, const Sequence& s2, - const Sequence& s3, const Sequence& s4, - const Sequence& s5) { + TypedExpectation& InSequence(const Sequence& s1, const Sequence& s2, + const Sequence& s3, const Sequence& s4, + const Sequence& s5) { return InSequence(s1, s2, s3, s4).InSequence(s5); } + // Implements that .After() clause. + TypedExpectation& After(const ExpectationSet& s) { + ExpectSpecProperty(last_clause_ <= kAfter, + ".After() cannot appear after .WillOnce()," + " .WillRepeatedly(), or " + ".RetiresOnSaturation()."); + last_clause_ = kAfter; + + for (ExpectationSet::const_iterator it = s.begin(); it != s.end(); ++it) { + immediate_prerequisites_ += *it; + } + return *this; + } + TypedExpectation& After(const ExpectationSet& s1, const ExpectationSet& s2) { + return After(s1).After(s2); + } + TypedExpectation& After(const ExpectationSet& s1, const ExpectationSet& s2, + const ExpectationSet& s3) { + return After(s1, s2).After(s3); + } + TypedExpectation& After(const ExpectationSet& s1, const ExpectationSet& s2, + const ExpectationSet& s3, const ExpectationSet& s4) { + return After(s1, s2, s3).After(s4); + } + TypedExpectation& After(const ExpectationSet& s1, const ExpectationSet& s2, + const ExpectationSet& s3, const ExpectationSet& s4, + const ExpectationSet& s5) { + return After(s1, s2, s3, s4).After(s5); + } + // Implements the .WillOnce() clause. - Expectation& WillOnce(const Action& action) { + TypedExpectation& WillOnce(const Action& action) { ExpectSpecProperty(last_clause_ <= kWillOnce, ".WillOnce() cannot appear after " ".WillRepeatedly() or .RetiresOnSaturation()."); @@ -673,7 +852,7 @@ class Expectation : public ExpectationBase { } // Implements the .WillRepeatedly() clause. - Expectation& WillRepeatedly(const Action& action) { + TypedExpectation& WillRepeatedly(const Action& action) { if (last_clause_ == kWillRepeatedly) { ExpectSpecProperty(false, ".WillRepeatedly() cannot appear " @@ -698,7 +877,7 @@ class Expectation : public ExpectationBase { } // Implements the .RetiresOnSaturation() clause. - Expectation& RetiresOnSaturation() { + TypedExpectation& RetiresOnSaturation() { ExpectSpecProperty(last_clause_ < kRetiresOnSaturation, ".RetiresOnSaturation() cannot appear " "more than once."); @@ -756,6 +935,12 @@ class Expectation : public ExpectationBase { template friend class FunctionMockerBase; + // Returns an Expectation object that references and co-owns this + // expectation. + virtual Expectation GetHandle() { + return owner_->GetHandleOf(this); + } + // The following methods will be called only after the EXPECT_CALL() // statement finishes and when the current thread holds // g_gmock_mutex. @@ -807,12 +992,12 @@ class Expectation : public ExpectationBase { *os << " Expected: all pre-requisites are satisfied\n" << " Actual: the following immediate pre-requisites " << "are not satisfied:\n"; - ExpectationBaseSet unsatisfied_prereqs; + ExpectationSet unsatisfied_prereqs; FindUnsatisfiedPrerequisites(&unsatisfied_prereqs); int i = 0; - for (ExpectationBaseSet::const_iterator it = unsatisfied_prereqs.begin(); + for (ExpectationSet::const_iterator it = unsatisfied_prereqs.begin(); it != unsatisfied_prereqs.end(); ++it) { - (*it)->DescribeLocationTo(os); + it->expectation_base()->DescribeLocationTo(os); *os << "pre-requisite #" << i++ << "\n"; } *os << " (end of pre-requisites)\n"; @@ -957,7 +1142,7 @@ class Expectation : public ExpectationBase { Clause last_clause_; mutable bool action_count_checked_; // Under mutex_. mutable Mutex mutex_; // Protects action_count_checked_. -}; // class Expectation +}; // class TypedExpectation // A MockSpec object is used by ON_CALL() or EXPECT_CALL() for // specifying the default behavior of, or expectation on, a mock @@ -992,7 +1177,7 @@ class MockSpec { // Adds a new expectation spec to the function mocker and returns // the newly created spec. - internal::Expectation& InternalExpectedAt( + internal::TypedExpectation& InternalExpectedAt( const char* file, int line, const char* obj, const char* call) { LogWithLocation(internal::INFO, file, line, string("EXPECT_CALL(") + obj + ", " + call + ") invoked"); @@ -1247,18 +1432,18 @@ class FunctionMockerBase : public UntypedFunctionMockerBase { // Adds and returns an expectation spec for this mock function. // L < g_gmock_mutex - Expectation& AddNewExpectation( + TypedExpectation& AddNewExpectation( const char* file, int line, const ArgumentMatcherTuple& m) { Mock::RegisterUseByOnCallOrExpectCall(MockObject(), file, line); - const linked_ptr > expectation( - new Expectation(this, file, line, m)); + const linked_ptr > expectation( + new TypedExpectation(this, file, line, m)); expectations_.push_back(expectation); // Adds this expectation into the implicit sequence if there is one. Sequence* const implicit_sequence = g_gmock_implicit_sequence.get(); if (implicit_sequence != NULL) { - implicit_sequence->AddExpectation(expectation); + implicit_sequence->AddExpectation(Expectation(expectation)); } return *expectation; @@ -1268,22 +1453,23 @@ class FunctionMockerBase : public UntypedFunctionMockerBase { // being described on this function mocker. MockSpec& current_spec() { return current_spec_; } private: - template friend class Expectation; + template friend class TypedExpectation; - typedef std::vector > > Expectations; + typedef std::vector > > + TypedExpectations; - // Gets the internal::linked_ptr object that co-owns 'exp'. - internal::linked_ptr GetLinkedExpectationBase( - Expectation* exp) { - for (typename Expectations::const_iterator it = expectations_.begin(); + // Returns an Expectation object that references and co-owns exp, + // which must be an expectation on this mock function. + Expectation GetHandleOf(TypedExpectation* exp) { + for (typename TypedExpectations::const_iterator it = expectations_.begin(); it != expectations_.end(); ++it) { if (it->get() == exp) { - return *it; + return Expectation(*it); } } Assert(false, __FILE__, __LINE__, "Cannot find expectation."); - return internal::linked_ptr(NULL); + return Expectation(); // The above statement is just to make the code compile, and will // never be executed. } @@ -1330,7 +1516,7 @@ class FunctionMockerBase : public UntypedFunctionMockerBase { // mock function) and excessive locking could cause a dead lock. // L < g_gmock_mutex bool FindMatchingExpectationAndAction( - const ArgumentTuple& args, Expectation** exp, Action* action, + const ArgumentTuple& args, TypedExpectation** exp, Action* action, bool* is_excessive, ::std::ostream* what, ::std::ostream* why) { MutexLock l(&g_gmock_mutex); *exp = this->FindMatchingExpectationLocked(args); @@ -1351,13 +1537,13 @@ class FunctionMockerBase : public UntypedFunctionMockerBase { // Returns the expectation that matches the arguments, or NULL if no // expectation matches them. // L >= g_gmock_mutex - Expectation* FindMatchingExpectationLocked( + TypedExpectation* FindMatchingExpectationLocked( const ArgumentTuple& args) const { g_gmock_mutex.AssertHeld(); - for (typename Expectations::const_reverse_iterator it = + for (typename TypedExpectations::const_reverse_iterator it = expectations_.rbegin(); it != expectations_.rend(); ++it) { - Expectation* const exp = it->get(); + TypedExpectation* const exp = it->get(); if (exp->ShouldHandleArguments(args)) { return exp; } @@ -1415,7 +1601,7 @@ class FunctionMockerBase : public UntypedFunctionMockerBase { // All default action specs for this function mocker. std::vector > default_actions_; // All expectations for this function mocker. - Expectations expectations_; + TypedExpectations expectations_; // There is no generally useful and implementable semantics of // copying a mock object, so copying a mock is usually a user error. @@ -1446,9 +1632,9 @@ template bool FunctionMockerBase::VerifyAndClearExpectationsLocked() { g_gmock_mutex.AssertHeld(); bool expectations_met = true; - for (typename Expectations::const_iterator it = expectations_.begin(); + for (typename TypedExpectations::const_iterator it = expectations_.begin(); it != expectations_.end(); ++it) { - Expectation* const exp = it->get(); + TypedExpectation* const exp = it->get(); if (exp->IsOverSaturated()) { // There was an upper-bound violation. Since the error was @@ -1532,7 +1718,7 @@ typename Function::Result FunctionMockerBase::InvokeWith( ::std::stringstream why; ::std::stringstream loc; Action action; - Expectation* exp; + TypedExpectation* exp; // The FindMatchingExpectationAndAction() function acquires and // releases g_gmock_mutex. @@ -1605,6 +1791,10 @@ using internal::MockSpec; template inline const T& Const(const T& x) { return x; } +// Constructs an Expectation object that references and co-owns exp. +inline Expectation::Expectation(internal::ExpectationBase& exp) // NOLINT + : expectation_base_(exp.GetHandle().expectation_base()) {} + } // namespace testing // A separate macro is required to avoid compile errors when the name diff --git a/src/gmock-spec-builders.cc b/src/gmock-spec-builders.cc index 465e4d63..00d16918 100644 --- a/src/gmock-spec-builders.cc +++ b/src/gmock-spec-builders.cc @@ -82,11 +82,9 @@ void ExpectationBase::RetireAllPreRequisites() { return; } - for (ExpectationBaseSet::const_iterator it = - immediate_prerequisites_.begin(); - it != immediate_prerequisites_.end(); - ++it) { - ExpectationBase* const prerequisite = (*it).get(); + for (ExpectationSet::const_iterator it = immediate_prerequisites_.begin(); + it != immediate_prerequisites_.end(); ++it) { + ExpectationBase* const prerequisite = it->expectation_base().get(); if (!prerequisite->is_retired()) { prerequisite->RetireAllPreRequisites(); prerequisite->Retire(); @@ -99,10 +97,10 @@ void ExpectationBase::RetireAllPreRequisites() { // L >= g_gmock_mutex bool ExpectationBase::AllPrerequisitesAreSatisfied() const { g_gmock_mutex.AssertHeld(); - for (ExpectationBaseSet::const_iterator it = immediate_prerequisites_.begin(); + for (ExpectationSet::const_iterator it = immediate_prerequisites_.begin(); it != immediate_prerequisites_.end(); ++it) { - if (!(*it)->IsSatisfied() || - !(*it)->AllPrerequisitesAreSatisfied()) + if (!(it->expectation_base()->IsSatisfied()) || + !(it->expectation_base()->AllPrerequisitesAreSatisfied())) return false; } return true; @@ -111,21 +109,21 @@ bool ExpectationBase::AllPrerequisitesAreSatisfied() const { // Adds unsatisfied pre-requisites of this expectation to 'result'. // L >= g_gmock_mutex void ExpectationBase::FindUnsatisfiedPrerequisites( - ExpectationBaseSet* result) const { + ExpectationSet* result) const { g_gmock_mutex.AssertHeld(); - for (ExpectationBaseSet::const_iterator it = immediate_prerequisites_.begin(); + for (ExpectationSet::const_iterator it = immediate_prerequisites_.begin(); it != immediate_prerequisites_.end(); ++it) { - if ((*it)->IsSatisfied()) { + if (it->expectation_base()->IsSatisfied()) { // If *it is satisfied and has a call count of 0, some of its // pre-requisites may not be satisfied yet. - if ((*it)->call_count_ == 0) { - (*it)->FindUnsatisfiedPrerequisites(result); + if (it->expectation_base()->call_count_ == 0) { + it->expectation_base()->FindUnsatisfiedPrerequisites(result); } } else { // Now that we know *it is unsatisfied, we are not so interested // in whether its pre-requisites are satisfied. Therefore we // don't recursively call FindUnsatisfiedPrerequisites() here. - result->insert(*it); + *result += *it; } } } @@ -421,11 +419,11 @@ void Mock::ClearDefaultActionsLocked(void* mock_obj) { } // Adds an expectation to a sequence. -void Sequence::AddExpectation( - const internal::linked_ptr& expectation) const { +void Sequence::AddExpectation(const Expectation& expectation) const { if (*last_expectation_ != expectation) { - if (*last_expectation_ != NULL) { - expectation->immediate_prerequisites_.insert(*last_expectation_); + if (last_expectation_->expectation_base() != NULL) { + expectation.expectation_base()->immediate_prerequisites_ + += *last_expectation_; } *last_expectation_ = expectation; } diff --git a/test/gmock-spec-builders_test.cc b/test/gmock-spec-builders_test.cc index 614e4ab5..f6c3141b 100644 --- a/test/gmock-spec-builders_test.cc +++ b/test/gmock-spec-builders_test.cc @@ -71,6 +71,9 @@ using testing::CardinalityInterface; using testing::Const; using testing::DoAll; using testing::DoDefault; +using testing::Eq; +using testing::Expectation; +using testing::ExpectationSet; using testing::GMOCK_FLAG(verbose); using testing::Gt; using testing::InSequence; @@ -80,13 +83,13 @@ using testing::IsSubstring; using testing::Lt; using testing::Message; using testing::Mock; +using testing::Ne; using testing::Return; using testing::Sequence; using testing::internal::g_gmock_mutex; using testing::internal::kErrorVerbosity; using testing::internal::kInfoVerbosity; using testing::internal::kWarningVerbosity; -using testing::internal::Expectation; using testing::internal::ExpectationTester; using testing::internal::string; @@ -345,7 +348,22 @@ TEST(ExpectCallSyntaxTest, InSequenceCanAppearMultipleTimes) { a.DoA(1); } -TEST(ExpectCallSyntaxTest, InSequenceMustBeBeforeWill) { +TEST(ExpectCallSyntaxTest, InSequenceMustBeBeforeAfter) { + MockA a; + Sequence s; + + Expectation e = EXPECT_CALL(a, DoA(1)) + .Times(AnyNumber()); + EXPECT_NONFATAL_FAILURE({ // NOLINT + EXPECT_CALL(a, DoA(2)) + .After(e) + .InSequence(s); + }, ".InSequence() cannot appear after "); + + a.DoA(2); +} + +TEST(ExpectCallSyntaxTest, InSequenceMustBeBeforeWillOnce) { MockA a; Sequence s; @@ -358,6 +376,20 @@ TEST(ExpectCallSyntaxTest, InSequenceMustBeBeforeWill) { a.DoA(1); } +TEST(ExpectCallSyntaxTest, AfterMustBeBeforeWillOnce) { + MockA a; + + Expectation e = EXPECT_CALL(a, DoA(1)); + EXPECT_NONFATAL_FAILURE({ + EXPECT_CALL(a, DoA(2)) + .WillOnce(Return()) + .After(e); + }, ".After() cannot appear after "); + + a.DoA(1); + a.DoA(2); +} + TEST(ExpectCallSyntaxTest, WillIsOptional) { MockA a; @@ -1248,6 +1280,292 @@ TEST(SequenceTest, Retirement) { a.DoA(1); } +// Tests Expectation. + +TEST(ExpectationTest, ConstrutorsWork) { + MockA a; + Expectation e1; // Default ctor. + Expectation e2 = EXPECT_CALL(a, DoA(1)); // Ctor from EXPECT_CALL. + Expectation e3 = e2; // Copy ctor. + + EXPECT_THAT(e1, Ne(e2)); + EXPECT_THAT(e2, Eq(e3)); + a.DoA(1); +} + +TEST(ExpectationTest, AssignmentWorks) { + MockA a; + Expectation e1; + Expectation e2 = EXPECT_CALL(a, DoA(1)); + + EXPECT_THAT(e1, Ne(e2)); + + e1 = e2; + EXPECT_THAT(e1, Eq(e2)); + + a.DoA(1); +} + +// Tests ExpectationSet. + +TEST(ExpectationSetTest, MemberTypesAreCorrect) { + ::testing::StaticAssertTypeEq(); +} + +TEST(ExpectationSetTest, ConstructorsWork) { + MockA a; + + Expectation e1; + const Expectation e2; + ExpectationSet es1; // Default ctor. + ExpectationSet es2 = EXPECT_CALL(a, DoA(1)); // Ctor from EXPECT_CALL. + ExpectationSet es3 = e1; // Ctor from Expectation. + ExpectationSet es4(e1); // Ctor from Expectation; alternative syntax. + ExpectationSet es5 = e2; // Ctor from const Expectation. + ExpectationSet es6(e2); // Ctor from const Expectation; alternative syntax. + ExpectationSet es7 = es2; // Copy ctor. + + EXPECT_EQ(0, es1.size()); + EXPECT_EQ(1, es2.size()); + EXPECT_EQ(1, es3.size()); + EXPECT_EQ(1, es4.size()); + EXPECT_EQ(1, es5.size()); + EXPECT_EQ(1, es6.size()); + EXPECT_EQ(1, es7.size()); + + EXPECT_THAT(es3, Ne(es2)); + EXPECT_THAT(es4, Eq(es3)); + EXPECT_THAT(es5, Eq(es4)); + EXPECT_THAT(es6, Eq(es5)); + EXPECT_THAT(es7, Eq(es2)); + a.DoA(1); +} + +TEST(ExpectationSetTest, AssignmentWorks) { + ExpectationSet es1; + ExpectationSet es2 = Expectation(); + + es1 = es2; + EXPECT_EQ(1, es1.size()); + EXPECT_THAT(*(es1.begin()), Eq(Expectation())); + EXPECT_THAT(es1, Eq(es2)); +} + +TEST(ExpectationSetTest, InsertionWorks) { + ExpectationSet es1; + Expectation e1; + es1 += e1; + EXPECT_EQ(1, es1.size()); + EXPECT_THAT(*(es1.begin()), Eq(e1)); + + MockA a; + Expectation e2 = EXPECT_CALL(a, DoA(1)); + es1 += e2; + EXPECT_EQ(2, es1.size()); + + ExpectationSet::const_iterator it1 = es1.begin(); + ExpectationSet::const_iterator it2 = it1; + ++it2; + EXPECT_TRUE(*it1 == e1 || *it2 == e1); // e1 must be in the set. + EXPECT_TRUE(*it1 == e2 || *it2 == e2); // e2 must be in the set too. + a.DoA(1); +} + +TEST(ExpectationSetTest, SizeWorks) { + ExpectationSet es; + EXPECT_EQ(0, es.size()); + + es += Expectation(); + EXPECT_EQ(1, es.size()); + + MockA a; + es += EXPECT_CALL(a, DoA(1)); + EXPECT_EQ(2, es.size()); + + a.DoA(1); +} + +TEST(ExpectationSetTest, IsEnumerable) { + ExpectationSet es; + EXPECT_THAT(es.begin(), Eq(es.end())); + + es += Expectation(); + ExpectationSet::const_iterator it = es.begin(); + EXPECT_THAT(it, Ne(es.end())); + EXPECT_THAT(*it, Eq(Expectation())); + ++it; + EXPECT_THAT(it, Eq(es.end())); +} + +// Tests the .After() clause. + +TEST(AfterTest, SucceedsWhenPartialOrderIsSatisfied) { + MockA a; + ExpectationSet es; + es += EXPECT_CALL(a, DoA(1)); + es += EXPECT_CALL(a, DoA(2)); + EXPECT_CALL(a, DoA(3)) + .After(es); + + a.DoA(1); + a.DoA(2); + a.DoA(3); +} + +TEST(AfterTest, SucceedsWhenTotalOrderIsSatisfied) { + MockA a; + MockB b; + // The following also verifies that const Expectation objects work + // too. Do not remove the const modifiers. + const Expectation e1 = EXPECT_CALL(a, DoA(1)); + const Expectation e2 = EXPECT_CALL(b, DoB()) + .Times(2) + .After(e1); + EXPECT_CALL(a, DoA(2)).After(e2); + + a.DoA(1); + b.DoB(); + b.DoB(); + a.DoA(2); +} + +// Calls must be in strict order when specified so. +TEST(AfterTest, CallsMustBeInStrictOrderWhenSpecifiedSo) { + MockA a; + MockB b; + Expectation e1 = EXPECT_CALL(a, DoA(1)); + Expectation e2 = EXPECT_CALL(b, DoB()) + .Times(2) + .After(e1); + EXPECT_CALL(a, ReturnResult(2)) + .After(e2) + .WillOnce(Return(Result())); + + a.DoA(1); + // If a call to ReturnResult() violates the specified order, no + // matching expectation will be found, and thus the default action + // will be done. Since the return type of ReturnResult() is not a + // built-in type, gmock won't know what to return and will thus + // abort the program. Therefore a death test can tell us whether + // gmock catches the order violation correctly. + // + // gtest and gmock print messages to stdout, which isn't captured by + // death tests. Therefore we have to match with an empty regular + // expression in all the EXPECT_DEATH()s. + EXPECT_DEATH(a.ReturnResult(2), ""); + + b.DoB(); + EXPECT_DEATH(a.ReturnResult(2), ""); + + b.DoB(); + a.ReturnResult(2); +} + +// Calls must satisfy the partial order when specified so. +TEST(AfterTest, CallsMustSatisfyPartialOrderWhenSpecifiedSo) { + MockA a; + Expectation e = EXPECT_CALL(a, DoA(1)); + const ExpectationSet es = EXPECT_CALL(a, DoA(2)); + EXPECT_CALL(a, ReturnResult(3)) + .After(e, es) + .WillOnce(Return(Result())); + + EXPECT_DEATH(a.ReturnResult(3), ""); + + a.DoA(2); + EXPECT_DEATH(a.ReturnResult(3), ""); + + a.DoA(1); + a.ReturnResult(3); +} + +// .After() can be combined with .InSequence(). +TEST(AfterTest, CanBeUsedWithInSequence) { + MockA a; + Sequence s; + Expectation e = EXPECT_CALL(a, DoA(1)); + EXPECT_CALL(a, DoA(2)).InSequence(s); + EXPECT_CALL(a, ReturnResult(3)) + .InSequence(s).After(e) + .WillOnce(Return(Result())); + + a.DoA(1); + EXPECT_DEATH(a.ReturnResult(3), ""); + + a.DoA(2); + a.ReturnResult(3); +} + +// .After() can be called multiple times. +TEST(AfterTest, CanBeCalledManyTimes) { + MockA a; + Expectation e1 = EXPECT_CALL(a, DoA(1)); + Expectation e2 = EXPECT_CALL(a, DoA(2)); + Expectation e3 = EXPECT_CALL(a, DoA(3)); + EXPECT_CALL(a, DoA(4)) + .After(e1) + .After(e2) + .After(e3); + + a.DoA(3); + a.DoA(1); + a.DoA(2); + a.DoA(4); +} + +// .After() accepts up to 5 arguments. +TEST(AfterTest, AcceptsUpToFiveArguments) { + MockA a; + Expectation e1 = EXPECT_CALL(a, DoA(1)); + Expectation e2 = EXPECT_CALL(a, DoA(2)); + Expectation e3 = EXPECT_CALL(a, DoA(3)); + ExpectationSet es1 = EXPECT_CALL(a, DoA(4)); + ExpectationSet es2 = EXPECT_CALL(a, DoA(5)); + EXPECT_CALL(a, DoA(6)) + .After(e1, e2, e3, es1, es2); + + a.DoA(5); + a.DoA(2); + a.DoA(4); + a.DoA(1); + a.DoA(3); + a.DoA(6); +} + +// .After() allows input to contain duplicated Expectations. +TEST(AfterTest, AcceptsDuplicatedInput) { + MockA a; + Expectation e1 = EXPECT_CALL(a, DoA(1)); + Expectation e2 = EXPECT_CALL(a, DoA(2)); + ExpectationSet es; + es += e1; + es += e2; + EXPECT_CALL(a, ReturnResult(3)) + .After(e1, e2, es, e1) + .WillOnce(Return(Result())); + + a.DoA(1); + EXPECT_DEATH(a.ReturnResult(3), ""); + + a.DoA(2); + a.ReturnResult(3); +} + +// An Expectation added to an ExpectationSet after it has been used in +// an .After() has no effect. +TEST(AfterTest, ChangesToExpectationSetHaveNoEffectAfterwards) { + MockA a; + ExpectationSet es1 = EXPECT_CALL(a, DoA(1)); + Expectation e2 = EXPECT_CALL(a, DoA(2)); + EXPECT_CALL(a, DoA(3)) + .After(es1); + es1 += e2; + + a.DoA(1); + a.DoA(3); + a.DoA(2); +} + // Tests that Google Mock correctly handles calls to mock functions // after a mock object owning one of their pre-requisites has died.