diff options
| author | zhanyong.wan <zhanyong.wan@8415998a-534a-0410-bf83-d39667b30386> | 2011-02-23 19:39:27 +0000 | 
|---|---|---|
| committer | zhanyong.wan <zhanyong.wan@8415998a-534a-0410-bf83-d39667b30386> | 2011-02-23 19:39:27 +0000 | 
| commit | ed6c9277bb12f2808bb812ae8f91492dac9517b4 (patch) | |
| tree | 463781d99fd74011cb43829a73bad5023350a22f /src | |
| parent | b3e904227f81fc6d2ea577dadbc3e510a989bbd2 (diff) | |
| download | googletest-ed6c9277bb12f2808bb812ae8f91492dac9517b4.tar.gz googletest-ed6c9277bb12f2808bb812ae8f91492dac9517b4.tar.bz2 googletest-ed6c9277bb12f2808bb812ae8f91492dac9517b4.zip | |
Makes Google Mock compile much faster and use much less memory; reviewed by Nico Weber.  This fixes issue 68.
Diffstat (limited to 'src')
| -rw-r--r-- | src/gmock-spec-builders.cc | 338 | 
1 files changed, 335 insertions, 3 deletions
| diff --git a/src/gmock-spec-builders.cc b/src/gmock-spec-builders.cc index 0d40d9cd..a99caef2 100644 --- a/src/gmock-spec-builders.cc +++ b/src/gmock-spec-builders.cc @@ -55,6 +55,15 @@ namespace internal {  // mockers, and all expectations.  GTEST_DEFINE_STATIC_MUTEX_(g_gmock_mutex); +// Logs a message including file and line number information. +void LogWithLocation(testing::internal::LogSeverity severity, +                     const char* file, int line, +                     const string& message) { +  ::std::ostringstream s; +  s << file << ":" << line << ": " << message << ::std::endl; +  Log(severity, s.str(), 0); +} +  // Constructs an ExpectationBase object.  ExpectationBase::ExpectationBase(const char* a_file,                                   int a_line, @@ -65,8 +74,12 @@ ExpectationBase::ExpectationBase(const char* a_file,        cardinality_specified_(false),        cardinality_(Exactly(1)),        call_count_(0), -      retired_(false) { -} +      retired_(false), +      extra_matcher_specified_(false), +      repeated_action_specified_(false), +      retires_on_saturation_(false), +      last_clause_(kNone), +      action_count_checked_(false) {}  // Destructs an ExpectationBase object.  ExpectationBase::~ExpectationBase() {} @@ -132,6 +145,99 @@ void ExpectationBase::FindUnsatisfiedPrerequisites(    }  } +// Describes how many times a function call matching this +// expectation has occurred. +// L >= g_gmock_mutex +void ExpectationBase::DescribeCallCountTo(::std::ostream* os) const { +  g_gmock_mutex.AssertHeld(); + +  // Describes how many times the function is expected to be called. +  *os << "         Expected: to be "; +  cardinality().DescribeTo(os); +  *os << "\n           Actual: "; +  Cardinality::DescribeActualCallCountTo(call_count(), os); + +  // Describes the state of the expectation (e.g. is it satisfied? +  // is it active?). +  *os << " - " << (IsOverSaturated() ? "over-saturated" : +                   IsSaturated() ? "saturated" : +                   IsSatisfied() ? "satisfied" : "unsatisfied") +      << " and " +      << (is_retired() ? "retired" : "active"); +} + +// Checks the action count (i.e. the number of WillOnce() and +// WillRepeatedly() clauses) against the cardinality if this hasn't +// been done before.  Prints a warning if there are too many or too +// few actions. +// L < mutex_ +void ExpectationBase::CheckActionCountIfNotDone() const { +  bool should_check = false; +  { +    MutexLock l(&mutex_); +    if (!action_count_checked_) { +      action_count_checked_ = true; +      should_check = true; +    } +  } + +  if (should_check) { +    if (!cardinality_specified_) { +      // The cardinality was inferred - no need to check the action +      // count against it. +      return; +    } + +    // The cardinality was explicitly specified. +    const int action_count = static_cast<int>(untyped_actions_.size()); +    const int upper_bound = cardinality().ConservativeUpperBound(); +    const int lower_bound = cardinality().ConservativeLowerBound(); +    bool too_many;  // True if there are too many actions, or false +    // if there are too few. +    if (action_count > upper_bound || +        (action_count == upper_bound && repeated_action_specified_)) { +      too_many = true; +    } else if (0 < action_count && action_count < lower_bound && +               !repeated_action_specified_) { +      too_many = false; +    } else { +      return; +    } + +    ::std::stringstream ss; +    DescribeLocationTo(&ss); +    ss << "Too " << (too_many ? "many" : "few") +       << " actions specified in " << source_text() << "...\n" +       << "Expected to be "; +    cardinality().DescribeTo(&ss); +    ss << ", but has " << (too_many ? "" : "only ") +       << action_count << " WillOnce()" +       << (action_count == 1 ? "" : "s"); +    if (repeated_action_specified_) { +      ss << " and a WillRepeatedly()"; +    } +    ss << "."; +    Log(WARNING, ss.str(), -1);  // -1 means "don't print stack trace". +  } +} + +// Implements the .Times() clause. +void ExpectationBase::UntypedTimes(const Cardinality& a_cardinality) { +  if (last_clause_ == kTimes) { +    ExpectSpecProperty(false, +                       ".Times() cannot appear " +                       "more than once in an EXPECT_CALL()."); +  } else { +    ExpectSpecProperty(last_clause_ < kTimes, +                       ".Times() cannot appear after " +                       ".InSequence(), .WillOnce(), .WillRepeatedly(), " +                       "or .RetiresOnSaturation()."); +  } +  last_clause_ = kTimes; + +  SpecifyCardinality(a_cardinality); +} +  // Points to the implicit sequence introduced by a living InSequence  // object (if any) in the current thread or NULL.  ThreadLocal<Sequence*> g_gmock_implicit_sequence; @@ -151,6 +257,233 @@ void ReportUninterestingCall(CallReaction reaction, const string& msg) {    }  } +UntypedFunctionMockerBase::UntypedFunctionMockerBase() +    : mock_obj_(NULL), name_("") {} + +UntypedFunctionMockerBase::~UntypedFunctionMockerBase() {} + +// Sets the mock object this mock method belongs to, and registers +// this information in the global mock registry.  Will be called +// whenever an EXPECT_CALL() or ON_CALL() is executed on this mock +// method. +// L < g_gmock_mutex +void UntypedFunctionMockerBase::RegisterOwner(const void* mock_obj) { +  { +    MutexLock l(&g_gmock_mutex); +    mock_obj_ = mock_obj; +  } +  Mock::Register(mock_obj, this); +} + +// Sets the mock object this mock method belongs to, and sets the name +// of the mock function.  Will be called upon each invocation of this +// mock function. +// L < g_gmock_mutex +void UntypedFunctionMockerBase::SetOwnerAndName( +    const void* mock_obj, const char* name) { +  // We protect name_ under g_gmock_mutex in case this mock function +  // is called from two threads concurrently. +  MutexLock l(&g_gmock_mutex); +  mock_obj_ = mock_obj; +  name_ = name; +} + +// Returns the name of the function being mocked.  Must be called +// after RegisterOwner() or SetOwnerAndName() has been called. +// L < g_gmock_mutex +const void* UntypedFunctionMockerBase::MockObject() const { +  const void* mock_obj; +  { +    // We protect mock_obj_ under g_gmock_mutex in case this mock +    // function is called from two threads concurrently. +    MutexLock l(&g_gmock_mutex); +    Assert(mock_obj_ != NULL, __FILE__, __LINE__, +           "MockObject() must not be called before RegisterOwner() or " +           "SetOwnerAndName() has been called."); +    mock_obj = mock_obj_; +  } +  return mock_obj; +} + +// Returns the name of this mock method.  Must be called after +// SetOwnerAndName() has been called. +// L < g_gmock_mutex +const char* UntypedFunctionMockerBase::Name() const { +  const char* name; +  { +    // We protect name_ under g_gmock_mutex in case this mock +    // function is called from two threads concurrently. +    MutexLock l(&g_gmock_mutex); +    Assert(name_ != NULL, __FILE__, __LINE__, +           "Name() must not be called before SetOwnerAndName() has " +           "been called."); +    name = name_; +  } +  return name; +} + +// Calculates the result of invoking this mock function with the given +// arguments, prints it, and returns it.  The caller is responsible +// for deleting the result. +// L < g_gmock_mutex +const UntypedActionResultHolderBase* +UntypedFunctionMockerBase::UntypedInvokeWith(const void* const untyped_args) { +  if (untyped_expectations_.size() == 0) { +    // No expectation is set on this mock method - we have an +    // uninteresting call. + +    // We must get Google Mock's reaction on uninteresting calls +    // made on this mock object BEFORE performing the action, +    // because the action may DELETE the mock object and make the +    // following expression meaningless. +    const CallReaction reaction = +        Mock::GetReactionOnUninterestingCalls(MockObject()); + +    // True iff we need to print this call's arguments and return +    // value.  This definition must be kept in sync with +    // the behavior of ReportUninterestingCall(). +    const bool need_to_report_uninteresting_call = +        // If the user allows this uninteresting call, we print it +        // only when he wants informational messages. +        reaction == ALLOW ? LogIsVisible(INFO) : +        // If the user wants this to be a warning, we print it only +        // when he wants to see warnings. +        reaction == WARN ? LogIsVisible(WARNING) : +        // Otherwise, the user wants this to be an error, and we +        // should always print detailed information in the error. +        true; + +    if (!need_to_report_uninteresting_call) { +      // Perform the action without printing the call information. +      return this->UntypedPerformDefaultAction(untyped_args, ""); +    } + +    // Warns about the uninteresting call. +    ::std::stringstream ss; +    this->UntypedDescribeUninterestingCall(untyped_args, &ss); + +    // Calculates the function result. +    const UntypedActionResultHolderBase* const result = +        this->UntypedPerformDefaultAction(untyped_args, ss.str()); + +    // Prints the function result. +    if (result != NULL) +      result->PrintAsActionResult(&ss); + +    ReportUninterestingCall(reaction, ss.str()); +    return result; +  } + +  bool is_excessive = false; +  ::std::stringstream ss; +  ::std::stringstream why; +  ::std::stringstream loc; +  const void* untyped_action = NULL; + +  // The UntypedFindMatchingExpectation() function acquires and +  // releases g_gmock_mutex. +  const ExpectationBase* const untyped_expectation = +      this->UntypedFindMatchingExpectation( +          untyped_args, &untyped_action, &is_excessive, +          &ss, &why); +  const bool found = untyped_expectation != NULL; + +  // True iff we need to print the call's arguments and return value. +  // This definition must be kept in sync with the uses of Expect() +  // and Log() in this function. +  const bool need_to_report_call = !found || is_excessive || LogIsVisible(INFO); +  if (!need_to_report_call) { +    // Perform the action without printing the call information. +    return +        untyped_action == NULL ? +        this->UntypedPerformDefaultAction(untyped_args, "") : +        this->UntypedPerformAction(untyped_action, untyped_args); +  } + +  ss << "    Function call: " << Name(); +  this->UntypedPrintArgs(untyped_args, &ss); + +  // In case the action deletes a piece of the expectation, we +  // generate the message beforehand. +  if (found && !is_excessive) { +    untyped_expectation->DescribeLocationTo(&loc); +  } + +  const UntypedActionResultHolderBase* const result = +      untyped_action == NULL ? +      this->UntypedPerformDefaultAction(untyped_args, ss.str()) : +      this->UntypedPerformAction(untyped_action, untyped_args); +  if (result != NULL) +    result->PrintAsActionResult(&ss); +  ss << "\n" << why.str(); + +  if (!found) { +    // No expectation matches this call - reports a failure. +    Expect(false, NULL, -1, ss.str()); +  } else if (is_excessive) { +    // We had an upper-bound violation and the failure message is in ss. +    Expect(false, untyped_expectation->file(), +           untyped_expectation->line(), ss.str()); +  } else { +    // We had an expected call and the matching expectation is +    // described in ss. +    Log(INFO, loc.str() + ss.str(), 2); +  } + +  return result; +} + +// Returns an Expectation object that references and co-owns exp, +// which must be an expectation on this mock function. +Expectation UntypedFunctionMockerBase::GetHandleOf(ExpectationBase* exp) { +  for (UntypedExpectations::const_iterator it = +           untyped_expectations_.begin(); +       it != untyped_expectations_.end(); ++it) { +    if (it->get() == exp) { +      return Expectation(*it); +    } +  } + +  Assert(false, __FILE__, __LINE__, "Cannot find expectation."); +  return Expectation(); +  // The above statement is just to make the code compile, and will +  // never be executed. +} + +// Verifies that all expectations on this mock function have been +// satisfied.  Reports one or more Google Test non-fatal failures +// and returns false if not. +// L >= g_gmock_mutex +bool UntypedFunctionMockerBase::VerifyAndClearExpectationsLocked() { +  g_gmock_mutex.AssertHeld(); +  bool expectations_met = true; +  for (UntypedExpectations::const_iterator it = +           untyped_expectations_.begin(); +       it != untyped_expectations_.end(); ++it) { +    ExpectationBase* const untyped_expectation = it->get(); +    if (untyped_expectation->IsOverSaturated()) { +      // There was an upper-bound violation.  Since the error was +      // already reported when it occurred, there is no need to do +      // anything here. +      expectations_met = false; +    } else if (!untyped_expectation->IsSatisfied()) { +      expectations_met = false; +      ::std::stringstream ss; +      ss  << "Actual function call count doesn't match " +          << untyped_expectation->source_text() << "...\n"; +      // No need to show the source file location of the expectation +      // in the description, as the Expect() call that follows already +      // takes care of it. +      untyped_expectation->MaybeDescribeExtraMatcherTo(&ss); +      untyped_expectation->DescribeCallCountTo(&ss); +      Expect(false, untyped_expectation->file(), +             untyped_expectation->line(), ss.str()); +    } +  } +  untyped_expectations_.clear(); +  return expectations_met; +} +  }  // namespace internal  // Class Mock. @@ -190,7 +523,6 @@ class MockObjectRegistry {    // object alive.  Therefore we report any living object as test    // failure, unless the user explicitly asked us to ignore it.    ~MockObjectRegistry() { -      // "using ::std::cout;" doesn't work with Symbian's STLport, where cout is      // a macro. | 
