diff --git a/include/gmock/gmock-actions.h b/include/gmock/gmock-actions.h index 7f21a7d4..214b2912 100644 --- a/include/gmock/gmock-actions.h +++ b/include/gmock/gmock-actions.h @@ -310,7 +310,7 @@ class Action { // This constructor allows us to turn an Action object into an // Action, as long as F's arguments can be implicitly converted - // to Func's and Func's return type cann be implicitly converted to + // to Func's and Func's return type can be implicitly converted to // F's. template explicit Action(const Action& action); @@ -425,6 +425,27 @@ class ActionAdaptor : public ActionInterface { // Implements the polymorphic Return(x) action, which can be used in // any function that returns the type of x, regardless of the argument // types. +// +// Note: The value passed into Return must be converted into +// Function::Result when this action is cast to Action rather than +// when that action is performed. This is important in scenarios like +// +// MOCK_METHOD1(Method, T(U)); +// ... +// { +// Foo foo; +// X x(&foo); +// EXPECT_CALL(mock, Method(_)).WillOnce(Return(x)); +// } +// +// In the example above the variable x holds reference to foo which leaves +// scope and gets destroyed. If copying X just copies a reference to foo, +// that copy will be left with a hanging reference. If conversion to T +// makes a copy of foo, the above code is safe. To support that scenario, we +// need to make sure that the type conversion happens inside the EXPECT_CALL +// statement, and conversion of the result of Return to Action is a +// good place for that. +// template class ReturnAction { public: @@ -459,12 +480,22 @@ class ReturnAction { typedef typename Function::Result Result; typedef typename Function::ArgumentTuple ArgumentTuple; - explicit Impl(R value) : value_(value) {} + // The implicit cast is necessary when Result has more than one + // single-argument constructor (e.g. Result is std::vector) and R + // has a type conversion operator template. In that case, value_(value) + // won't compile as the compiler doesn't known which constructor of + // Result to call. implicit_cast forces the compiler to convert R to + // Result without considering explicit constructors, thus resolving the + // ambiguity. value_ is then initialized using its copy constructor. + explicit Impl(R value) + : value_(::testing::internal::implicit_cast(value)) {} virtual Result Perform(const ArgumentTuple&) { return value_; } private: - R value_; + GMOCK_COMPILE_ASSERT_(!internal::is_reference::value, + Result_cannot_be_a_reference_type); + Result value_; }; R value_; diff --git a/test/gmock-actions_test.cc b/test/gmock-actions_test.cc index d3d96c6d..e2b1d052 100644 --- a/test/gmock-actions_test.cc +++ b/test/gmock-actions_test.cc @@ -515,6 +515,53 @@ TEST(ReturnTest, IsCovariant) { EXPECT_EQ(&derived, ret.Perform(make_tuple())); } +// Tests that the type of the value passed into Return is converted into T +// when the action is cast to Action rather than when the action is +// performed. See comments on testing::internal::ReturnAction in +// gmock-actions.h for more information. +class FromType { + public: + FromType(bool* converted) : converted_(converted) {} + bool* converted() const { return converted_; } + + private: + bool* const converted_; +}; + +class ToType { + public: + ToType(const FromType& x) { *x.converted() = true; } +}; + +TEST(ReturnTest, ConvertsArgumentWhenConverted) { + bool converted = false; + FromType x(&converted); + Action action(Return(x)); + EXPECT_TRUE(converted) << "Return must convert its argument in its own " + << "conversion operator."; + converted = false; + action.Perform(tuple<>()); + EXPECT_FALSE(converted) << "Action must NOT convert its argument " + << "when performed." ; +} + +// We do not support non-const type conversions on Symbian. See +// definition of implicit_cast in gmock-port.h for more information. +#if !GTEST_OS_SYMBIAN +class DestinationType {}; + +class SourceType { + public: + // Note: a non-const typecast operator. + operator DestinationType() { return DestinationType(); } +}; + +TEST(ReturnTest, CanConvertArgumentUsingNonConstTypeCastOperator) { + SourceType s; + Action action(Return(s)); +} +#endif // !GTEST_OS_SYMBIAN + // Tests that ReturnNull() returns NULL in a pointer-returning function. TEST(ReturnNullTest, WorksInPointerReturningFunction) { const Action a1 = ReturnNull();