diff --git a/include/gmock/gmock-generated-actions.h b/include/gmock/gmock-generated-actions.h index ee26f385..c2e155c2 100644 --- a/include/gmock/gmock-generated-actions.h +++ b/include/gmock/gmock-generated-actions.h @@ -998,51 +998,57 @@ class ActionHelper { public: static Result Perform(Impl* impl, const ::std::tr1::tuple<>& args) { using ::std::tr1::get; - return impl->gmock_PerformImpl(args, ExcessiveArg(), ExcessiveArg(), + return impl->template gmock_PerformImpl<>(args, ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), - ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg()); + ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg()); } template static Result Perform(Impl* impl, const ::std::tr1::tuple& args) { using ::std::tr1::get; - return impl->gmock_PerformImpl(args, get<0>(args), ExcessiveArg(), + return impl->template gmock_PerformImpl(args, get<0>(args), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), - ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg()); + ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg()); } template static Result Perform(Impl* impl, const ::std::tr1::tuple& args) { using ::std::tr1::get; - return impl->gmock_PerformImpl(args, get<0>(args), get<1>(args), + return impl->template gmock_PerformImpl(args, get<0>(args), + get<1>(args), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), - ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg()); + ExcessiveArg()); } template static Result Perform(Impl* impl, const ::std::tr1::tuple& args) { using ::std::tr1::get; - return impl->gmock_PerformImpl(args, get<0>(args), get<1>(args), - get<2>(args), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), - ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg()); + return impl->template gmock_PerformImpl(args, get<0>(args), + get<1>(args), get<2>(args), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg()); } template static Result Perform(Impl* impl, const ::std::tr1::tuple& args) { using ::std::tr1::get; - return impl->gmock_PerformImpl(args, get<0>(args), get<1>(args), - get<2>(args), get<3>(args), ExcessiveArg(), ExcessiveArg(), - ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg()); + return impl->template gmock_PerformImpl(args, get<0>(args), + get<1>(args), get<2>(args), get<3>(args), ExcessiveArg(), + ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg()); } template static Result Perform(Impl* impl, const ::std::tr1::tuple& args) { using ::std::tr1::get; - return impl->gmock_PerformImpl(args, get<0>(args), get<1>(args), - get<2>(args), get<3>(args), get<4>(args), ExcessiveArg(), - ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg()); + return impl->template gmock_PerformImpl(args, + get<0>(args), get<1>(args), get<2>(args), get<3>(args), get<4>(args), + ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg()); } template & args) { using ::std::tr1::get; - return impl->gmock_PerformImpl(args, get<0>(args), get<1>(args), - get<2>(args), get<3>(args), get<4>(args), get<5>(args), ExcessiveArg(), - ExcessiveArg(), ExcessiveArg(), ExcessiveArg()); + return impl->template gmock_PerformImpl(args, + get<0>(args), get<1>(args), get<2>(args), get<3>(args), get<4>(args), + get<5>(args), ExcessiveArg(), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg()); } template & args) { using ::std::tr1::get; - return impl->gmock_PerformImpl(args, get<0>(args), get<1>(args), - get<2>(args), get<3>(args), get<4>(args), get<5>(args), get<6>(args), - ExcessiveArg(), ExcessiveArg(), ExcessiveArg()); + return impl->template gmock_PerformImpl(args, + get<0>(args), get<1>(args), get<2>(args), get<3>(args), get<4>(args), + get<5>(args), get<6>(args), ExcessiveArg(), ExcessiveArg(), + ExcessiveArg()); } template & args) { using ::std::tr1::get; - return impl->gmock_PerformImpl(args, get<0>(args), get<1>(args), - get<2>(args), get<3>(args), get<4>(args), get<5>(args), get<6>(args), - get<7>(args), ExcessiveArg(), ExcessiveArg()); + return impl->template gmock_PerformImpl(args, get<0>(args), get<1>(args), get<2>(args), get<3>(args), + get<4>(args), get<5>(args), get<6>(args), get<7>(args), ExcessiveArg(), + ExcessiveArg()); } template & args) { using ::std::tr1::get; - return impl->gmock_PerformImpl(args, get<0>(args), get<1>(args), - get<2>(args), get<3>(args), get<4>(args), get<5>(args), get<6>(args), - get<7>(args), get<8>(args), ExcessiveArg()); + return impl->template gmock_PerformImpl(args, get<0>(args), get<1>(args), get<2>(args), get<3>(args), + get<4>(args), get<5>(args), get<6>(args), get<7>(args), get<8>(args), + ExcessiveArg()); } template & args) { using ::std::tr1::get; - return impl->gmock_PerformImpl(args, get<0>(args), get<1>(args), - get<2>(args), get<3>(args), get<4>(args), get<5>(args), get<6>(args), - get<7>(args), get<8>(args), get<9>(args)); + return impl->template gmock_PerformImpl(args, get<0>(args), get<1>(args), get<2>(args), get<3>(args), + get<4>(args), get<5>(args), get<6>(args), get<7>(args), get<8>(args), + get<9>(args)); } }; @@ -2303,8 +2314,44 @@ DoAll(Action1 a1, Action2 a2, Action3 a3, Action4 a4, Action5 a5, Action6 a6, arg4_type arg4, arg5_type arg5, arg6_type arg6, arg7_type arg7, \ arg8_type arg8, arg9_type arg9) const +// TODO(wan@google.com): move the following to a different .h file +// such that we don't have to run 'pump' every time the code is +// updated. namespace testing { +namespace internal { + +// Saves argument #0 to where the pointer points. +ACTION_P(SaveArg0, pointer) { *pointer = arg0; } + +// Assigns 'value' to the variable referenced by argument #0. +ACTION_P(SetArg0Referee, value) { + // Ensures that argument #0 is a reference. If you get a compiler + // error on the next line, you are using SetArgReferee(value) in + // a mock function whose k-th (0-based) argument is not a reference. + GMOCK_COMPILE_ASSERT_(internal::is_reference::value, + SetArgReferee_must_be_used_with_a_reference_argument); + arg0 = value; +} + +} // namespace internal + +// Action SaveArg(pointer) saves the k-th (0-based) argument of the +// mock function to *pointer. +template +inline internal::WithArgsAction, k> +SaveArg(const Pointer& pointer) { + return WithArg(internal::SaveArg0(pointer)); +} + +// Action SetArgReferee(value) assigns 'value' to the variable +// referenced by the k-th (0-based) argument of the mock function. +template +inline internal::WithArgsAction, k> +SetArgReferee(const Value& value) { + return WithArg(internal::SetArg0Referee(value)); +} + // Action Throw(exception) can be used in a mock function of any type // to throw the given exception. Any copyable value can be thrown. #if GTEST_HAS_EXCEPTIONS diff --git a/include/gmock/gmock-generated-actions.h.pump b/include/gmock/gmock-generated-actions.h.pump index f8bec55d..26f9319d 100644 --- a/include/gmock/gmock-generated-actions.h.pump +++ b/include/gmock/gmock-generated-actions.h.pump @@ -436,7 +436,7 @@ $var arg_list = [[$if (i==0) | (i==n) [[$as$eas]] $else [[$as, $eas]]]] $template static Result Perform(Impl* impl, const ::std::tr1::tuple<$As>& args) { using ::std::tr1::get; - return impl->gmock_PerformImpl(args, $arg_list); + return impl->template gmock_PerformImpl<$As>(args, $arg_list); } ]] @@ -774,10 +774,48 @@ $arg_types_and_names) const;\$param_field_decls gmock_Impl::gmock_PerformImpl(const args_type& args, [[]] $arg_types_and_names) const ]] +$$ } // This meta comment fixes auto-indentation in Emacs. It won't +$$ // show up in the generated code. +// TODO(wan@google.com): move the following to a different .h file +// such that we don't have to run 'pump' every time the code is +// updated. namespace testing { +namespace internal { + +// Saves argument #0 to where the pointer points. +ACTION_P(SaveArg0, pointer) { *pointer = arg0; } + +// Assigns 'value' to the variable referenced by argument #0. +ACTION_P(SetArg0Referee, value) { + // Ensures that argument #0 is a reference. If you get a compiler + // error on the next line, you are using SetArgReferee(value) in + // a mock function whose k-th (0-based) argument is not a reference. + GMOCK_COMPILE_ASSERT_(internal::is_reference::value, + SetArgReferee_must_be_used_with_a_reference_argument); + arg0 = value; +} + +} // namespace internal + +// Action SaveArg(pointer) saves the k-th (0-based) argument of the +// mock function to *pointer. +template +inline internal::WithArgsAction, k> +SaveArg(const Pointer& pointer) { + return WithArg(internal::SaveArg0(pointer)); +} + +// Action SetArgReferee(value) assigns 'value' to the variable +// referenced by the k-th (0-based) argument of the mock function. +template +inline internal::WithArgsAction, k> +SetArgReferee(const Value& value) { + return WithArg(internal::SetArg0Referee(value)); +} + // Action Throw(exception) can be used in a mock function of any type // to throw the given exception. Any copyable value can be thrown. #if GTEST_HAS_EXCEPTIONS diff --git a/test/gmock-generated-actions_test.cc b/test/gmock-generated-actions_test.cc index 0d5260a2..dd25a123 100644 --- a/test/gmock-generated-actions_test.cc +++ b/test/gmock-generated-actions_test.cc @@ -57,6 +57,8 @@ using testing::DoAll; using testing::Invoke; using testing::InvokeArgument; using testing::Return; +using testing::SaveArg; +using testing::SetArgReferee; using testing::SetArgumentPointee; using testing::StaticAssertTypeEq; using testing::Unused; @@ -1026,6 +1028,30 @@ TEST(ActionMacroTest, CanReferenceMockFunctionReturnType) { EXPECT_EQ(1, a1.Perform(make_tuple(false))); } +// Tests that ACTION() works for arguments passed by const reference. +ACTION(ReturnAddrOfConstBoolReferenceArg) { + StaticAssertTypeEq(); + return &arg1; +} + +TEST(ActionMacroTest, WorksForConstReferenceArg) { + Action a = ReturnAddrOfConstBoolReferenceArg(); + const bool b = false; + EXPECT_EQ(&b, a.Perform(tuple(0, b))); +} + +// Tests that ACTION() works for arguments passed by non-const reference. +ACTION(ReturnAddrOfIntReferenceArg) { + StaticAssertTypeEq(); + return &arg0; +} + +TEST(ActionMacroTest, WorksForNonConstReferenceArg) { + Action a = ReturnAddrOfIntReferenceArg(); + int n = 0; + EXPECT_EQ(&n, a.Perform(tuple(n, true, 1))); +} + // Tests that ACTION() can be used in a namespace. namespace action_test { ACTION(Sum) { return arg0 + arg1; } @@ -1310,6 +1336,41 @@ TEST(ActionPnMacroTest, CanExplicitlyInstantiateWithReferenceTypes) { EXPECT_EQ(55, a.Perform(empty)); } +TEST(SaveArgActionTest, WorksForSameType) { + int result = 0; + const Action a1 = SaveArg<0>(&result); + a1.Perform(make_tuple(5)); + EXPECT_EQ(5, result); +} + +TEST(SaveArgActionTest, WorksForCompatibleType) { + int result = 0; + const Action a1 = SaveArg<1>(&result); + a1.Perform(make_tuple(true, 'a')); + EXPECT_EQ('a', result); +} + +TEST(SetArgRefereeActionTest, WorksForSameType) { + int value = 0; + const Action a1 = SetArgReferee<0>(1); + a1.Perform(tuple(value)); + EXPECT_EQ(1, value); +} + +TEST(SetArgRefereeActionTest, WorksForCompatibleType) { + int value = 0; + const Action a1 = SetArgReferee<1>('a'); + a1.Perform(tuple(0, value)); + EXPECT_EQ('a', value); +} + +TEST(SetArgRefereeActionTest, WorksWithExtraArguments) { + int value = 0; + const Action a1 = SetArgReferee<2>('a'); + a1.Perform(tuple(true, 0, value, "hi")); + EXPECT_EQ('a', value); +} + #if GTEST_HAS_EXCEPTIONS TEST(ThrowActionTest, ThrowsGivenExceptionInVoidFunction) {