diff --git a/include/fakeit/StubbingProgress.hpp b/include/fakeit/StubbingProgress.hpp index 519ba622..d95be1a1 100644 --- a/include/fakeit/StubbingProgress.hpp +++ b/include/fakeit/StubbingProgress.hpp @@ -88,7 +88,11 @@ namespace fakeit { template MethodStubbingProgress& ReturnCapture(T&& r) { - auto store = std::make_shared::type>(std::forward(r)); + static_assert(std::is_constructible< + typename std::remove_cv::type>::type&, + typename std::remove_cv::type>::type&>::value, + "The type captured by ReturnCapture() (named T) must be compatible with the return type of the function (named R), i.e. T t{...}; R& r = t; must compile without creating temporaries."); + auto store = std::make_shared::type>::type>(std::forward(r)); return Do([store](const typename fakeit::test_arg::type...) mutable -> R { return std::forward(*store); }); @@ -96,7 +100,11 @@ namespace fakeit { template void AlwaysReturnCapture(T&& r) { - auto store = std::make_shared::type>(std::forward(r)); + static_assert(std::is_constructible< + typename std::remove_cv::type>::type&, + typename std::remove_cv::type>::type&>::value, + "The type captured by AlwaysReturnCapture() (named T) must be compatible with the return type of the function (named R), i.e. T t{...}; R& r = t; must compile without creating temporaries."); + auto store = std::make_shared::type>::type>(std::forward(r)); return AlwaysDo([store](const typename fakeit::test_arg::type...) mutable -> R { return std::forward(*store); }); diff --git a/tests/referece_types_tests.cpp b/tests/referece_types_tests.cpp index a7f2d0c6..4f1ab509 100644 --- a/tests/referece_types_tests.cpp +++ b/tests/referece_types_tests.cpp @@ -18,20 +18,29 @@ struct ReferenceTypesTests: tpunit::TestFixture { class AbstractType { public: - virtual void foo() = 0; + virtual const std::string& getContent() = 0; + + // Return true if object was sliced, false otherwise. + virtual bool wasObjectSliced() { + return true; + } }; class ConcreteType: public AbstractType { public: - int state; - ConcreteType(int value = 10) : - state(value) { + std::string content; + ConcreteType(const std::string& newContent = "defaultContent") : + content{newContent} { + } + const std::string& getContent() override { + return content; } - void foo() override { + bool wasObjectSliced() override { + return false; } bool operator==(const ConcreteType& other) const { - return (other.state == this->state); + return (other.content == this->content); } }; @@ -42,6 +51,8 @@ struct ReferenceTypesTests: tpunit::TestFixture { virtual ConcreteType& returnConcreteTypeByRef() = 0; virtual const std::string& returnStringByConstRef() = 0; virtual std::string& returnStringByRef() = 0; + virtual const std::unique_ptr& returnMoveOnlyByConstRef() = 0; + virtual std::unique_ptr& returnMoveOnlyByRef() = 0; }; ReferenceTypesTests() : @@ -49,14 +60,16 @@ struct ReferenceTypesTests: tpunit::TestFixture { // TEST(ReferenceTypesTests::implicitStubbingDefaultReturnValues), TEST(ReferenceTypesTests::explicitStubbingDefaultReturnValues), - TEST(ReferenceTypesTests::explicitStubbingReturnCopyValuesForRef), - TEST(ReferenceTypesTests::explicitStubbingReturnValuesCopyForRRef), + TEST(ReferenceTypesTests::explicitStubbingReturnValues), TEST(ReferenceTypesTests::explicitStubbingDefaultReturnValues_with_AlwaysReturn), TEST(ReferenceTypesTests::explicitStubbingReturnValues_with_AlwaysReturn), - TEST(ReferenceTypesTests::explicitStubbingReturnCopyValuesForRef_with_AlwaysReturn), - TEST(ReferenceTypesTests::explicitStubbingReturnCopyValues_with_AlwaysReturn), TEST(ReferenceTypesTests::explicitStubbingReturnValues_by_assignment), - TEST(ReferenceTypesTests::explicitStubbingReturnValues) + TEST(ReferenceTypesTests::explicitStubbingReturnCaptureByCopy), + TEST(ReferenceTypesTests::explicitStubbingReturnCaptureByMove), + TEST(ReferenceTypesTests::explicitStubbingReturnCaptureByTemporary), + TEST(ReferenceTypesTests::explicitStubbingReturnCaptureByCopy_with_AlwaysReturn), + TEST(ReferenceTypesTests::explicitStubbingReturnCaptureByMove_with_AlwaysReturn), + TEST(ReferenceTypesTests::explicitStubbingReturnCaptureByTemporary_with_AlwaysReturn) // ) { } @@ -140,66 +153,6 @@ struct ReferenceTypesTests: tpunit::TestFixture { ASSERT_EQUAL(&c, &i.returnAbstractTypeByRef()); } - void explicitStubbingReturnCopyValuesForRef() { - Mock mock; - - // add scope so we know we are copying - { - std::string a_string{"myString"}; - int num{ 1 }; - - // explicit copy here - When(Method(mock, returnStringByConstRef)).ReturnCapture(a_string); - When(Method(mock, returnIntByRef)).ReturnCapture(num); - - // modify now so know whether or not is was copied - a_string = "modified"; - num = 2; - } - - ReferenceInterface& i = mock.get(); - - // Fundamental types are initiated to 0. - EXPECT_EQUAL("myString", i.returnStringByConstRef()); - EXPECT_EQUAL(1, i.returnIntByRef()); - } - - void explicitStubbingReturnValuesCopyForRRef() { - Mock mock; - - { - When(Method(mock, returnStringByConstRef)).ReturnCapture(std::string{ "myConstRefString" }); - When(Method(mock, returnStringByRef)).ReturnCapture(std::string{ "myRefString" }); - When(Method(mock, returnConcreteTypeByRef)).ReturnCapture(ConcreteType(20)); - When(Method(mock, returnIntByRef)).ReturnCapture(1); - } - - ReferenceInterface& i = mock.get(); - - EXPECT_EQUAL("myConstRefString", i.returnStringByConstRef()); - EXPECT_EQUAL("myRefString", i.returnStringByRef()); - EXPECT_EQUAL(ConcreteType(20), i.returnConcreteTypeByRef()); - EXPECT_EQUAL(1, i.returnIntByRef()); - } - - void explicitStubbingReturnCopyValuesForRef_with_AlwaysReturn() { - Mock mock; - - { - When(Method(mock, returnStringByConstRef)).AlwaysReturnCapture(std::string{ "myConstRefString" }); - When(Method(mock, returnStringByRef)).AlwaysReturnCapture(std::string{ "myRefString" }); - When(Method(mock, returnConcreteTypeByRef)).AlwaysReturnCapture(ConcreteType(20)); - When(Method(mock, returnIntByRef)).AlwaysReturnCapture(1); - } - - ReferenceInterface& i = mock.get(); - - EXPECT_EQUAL("myConstRefString", i.returnStringByConstRef()); - EXPECT_EQUAL("myRefString", i.returnStringByRef()); - EXPECT_EQUAL(ConcreteType(20), i.returnConcreteTypeByRef()); - EXPECT_EQUAL(1, i.returnIntByRef()); - } - void explicitStubbingReturnValues_with_AlwaysReturn() { Mock mock; // @@ -224,33 +177,6 @@ struct ReferenceTypesTests: tpunit::TestFixture { ASSERT_EQUAL(&c, &i.returnAbstractTypeByRef()); } - void explicitStubbingReturnCopyValues_with_AlwaysReturn() { - Mock mock; - - // add scope so we know we are copying - { - std::string a_string{ "myString" }; - int num{ 1 }; - - // explicit copy here - When(Method(mock, returnStringByConstRef)).AlwaysReturnCapture(a_string); - When(Method(mock, returnIntByRef)).AlwaysReturnCapture(num); - - // modify now so know whether or not is was copied - a_string = "modified"; - num = 2; - } - - ReferenceInterface& i = mock.get(); - - // Fundamental types are initiated to 0. - EXPECT_EQUAL("myString", i.returnStringByConstRef()); - EXPECT_EQUAL("myString", i.returnStringByConstRef()); - - EXPECT_EQUAL(1, i.returnIntByRef()); - EXPECT_EQUAL(1, i.returnIntByRef()); - } - void explicitStubbingReturnValues_by_assignment() { Mock mock; // @@ -299,4 +225,211 @@ struct ReferenceTypesTests: tpunit::TestFixture { #endif } + void explicitStubbingReturnCaptureByCopy() { + Mock mock; + + // add scope so we know we are copying + { + std::string aString{"myString"}; + int num = 1; + ConcreteType concrete{"myConcreteType"}; + + // explicit copy here + When(Method(mock, returnStringByConstRef)).ReturnCapture(aString); + When(Method(mock, returnIntByRef)).ReturnCapture(num); + When(Method(mock, returnAbstractTypeByRef)).ReturnCapture(concrete); + + // modify now so know whether or not is was copied + aString = "modified"; + num = 2; + concrete.content = "modified"; + } + + ReferenceInterface& i = mock.get(); + + EXPECT_EQUAL("myString", i.returnStringByConstRef()); + EXPECT_EQUAL(1, i.returnIntByRef()); + AbstractType& abstract = i.returnAbstractTypeByRef(); + EXPECT_FALSE(abstract.wasObjectSliced()); + EXPECT_EQUAL("myConcreteType", abstract.getContent()); + } + + void explicitStubbingReturnCaptureByMove() { + Mock mock; + + // add scope so we know we are moving + { + std::string aString{"aString"}; + std::string bString{"bString"}; + std::unique_ptr aPtrString{new std::string{"aPtrString"}}; + std::unique_ptr bPtrString{new std::string{"bPtrString"}}; + ConcreteType concrete{"myConcreteType"}; + + // explicit move here + When(Method(mock, returnStringByConstRef)).ReturnCapture(std::move(aString)); + When(Method(mock, returnStringByRef)).ReturnCapture(std::move(bString)); + When(Method(mock, returnMoveOnlyByConstRef)).ReturnCapture(std::move(aPtrString)); + When(Method(mock, returnMoveOnlyByRef)).ReturnCapture(std::move(bPtrString)); + When(Method(mock, returnAbstractTypeByRef)).ReturnCapture(std::move(concrete)); + + // Verify objects were moved. + EXPECT_TRUE(aString.empty()); + EXPECT_TRUE(bString.empty()); + EXPECT_EQUAL(aPtrString, nullptr); + EXPECT_EQUAL(bPtrString, nullptr); + EXPECT_TRUE(concrete.content.empty()); + } + + ReferenceInterface& i = mock.get(); + + EXPECT_EQUAL("aString", i.returnStringByConstRef()); + EXPECT_EQUAL("bString", i.returnStringByRef()); + EXPECT_EQUAL("aPtrString", *i.returnMoveOnlyByConstRef()); + EXPECT_EQUAL("bPtrString", *i.returnMoveOnlyByRef()); + AbstractType& abstract = i.returnAbstractTypeByRef(); + EXPECT_FALSE(abstract.wasObjectSliced()); + EXPECT_EQUAL("myConcreteType", abstract.getContent()); + } + + void explicitStubbingReturnCaptureByTemporary() { + Mock mock; + + { + When(Method(mock, returnStringByConstRef)).ReturnCapture(std::string{"aString"}); + When(Method(mock, returnStringByRef)).ReturnCapture(std::string{"bString"}); + When(Method(mock, returnMoveOnlyByConstRef)).ReturnCapture(std::unique_ptr(new std::string{"aPtrString"})); + When(Method(mock, returnMoveOnlyByRef)).ReturnCapture(std::unique_ptr(new std::string{"bPtrString"})); + When(Method(mock, returnAbstractTypeByRef)).ReturnCapture(ConcreteType{"myConcreteType"}); + } + + ReferenceInterface& i = mock.get(); + + EXPECT_EQUAL("aString", i.returnStringByConstRef()); + EXPECT_EQUAL("bString", i.returnStringByRef()); + EXPECT_EQUAL("aPtrString", *i.returnMoveOnlyByConstRef()); + EXPECT_EQUAL("bPtrString", *i.returnMoveOnlyByRef()); + AbstractType& abstract = i.returnAbstractTypeByRef(); + EXPECT_FALSE(abstract.wasObjectSliced()); + EXPECT_EQUAL("myConcreteType", abstract.getContent()); + } + + void explicitStubbingReturnCaptureByCopy_with_AlwaysReturn() { + Mock mock; + + // add scope so we know we are copying + { + std::string aString{"myString"}; + int num = 1; + ConcreteType concrete{"myConcreteType"}; + + // explicit copy here + When(Method(mock, returnStringByConstRef)).AlwaysReturnCapture(aString); + When(Method(mock, returnIntByRef)).AlwaysReturnCapture(num); + When(Method(mock, returnAbstractTypeByRef)).AlwaysReturnCapture(concrete); + + // modify now so know whether or not is was copied + aString = "modified"; + num = 2; + concrete.content = "modified"; + } + + ReferenceInterface& i = mock.get(); + + EXPECT_EQUAL("myString", i.returnStringByConstRef()); + EXPECT_EQUAL("myString", i.returnStringByConstRef()); + EXPECT_EQUAL(1, i.returnIntByRef()); + EXPECT_EQUAL(1, i.returnIntByRef()); + { + AbstractType& abstract = i.returnAbstractTypeByRef(); + EXPECT_FALSE(abstract.wasObjectSliced()); + EXPECT_EQUAL("myConcreteType", abstract.getContent()); + } + { + AbstractType& abstract = i.returnAbstractTypeByRef(); + EXPECT_FALSE(abstract.wasObjectSliced()); + EXPECT_EQUAL("myConcreteType", abstract.getContent()); + } + } + + void explicitStubbingReturnCaptureByMove_with_AlwaysReturn() { + Mock mock; + + // add scope so we know we are moving + { + std::string aString{"aString"}; + std::string bString{"bString"}; + std::unique_ptr aPtrString{new std::string{"aPtrString"}}; + std::unique_ptr bPtrString{new std::string{"bPtrString"}}; + ConcreteType concrete{"myConcreteType"}; + + // explicit move here + When(Method(mock, returnStringByConstRef)).AlwaysReturnCapture(std::move(aString)); + When(Method(mock, returnStringByRef)).AlwaysReturnCapture(std::move(bString)); + When(Method(mock, returnMoveOnlyByConstRef)).AlwaysReturnCapture(std::move(aPtrString)); + When(Method(mock, returnMoveOnlyByRef)).AlwaysReturnCapture(std::move(bPtrString)); + When(Method(mock, returnAbstractTypeByRef)).AlwaysReturnCapture(std::move(concrete)); + + // Verify objects were moved. + EXPECT_TRUE(aString.empty()); + EXPECT_TRUE(bString.empty()); + EXPECT_EQUAL(aPtrString, nullptr); + EXPECT_EQUAL(bPtrString, nullptr); + EXPECT_TRUE(concrete.content.empty()); + } + + ReferenceInterface& i = mock.get(); + + EXPECT_EQUAL("aString", i.returnStringByConstRef()); + EXPECT_EQUAL("aString", i.returnStringByConstRef()); + EXPECT_EQUAL("bString", i.returnStringByRef()); + EXPECT_EQUAL("bString", i.returnStringByRef()); + EXPECT_EQUAL("aPtrString", *i.returnMoveOnlyByConstRef()); + EXPECT_EQUAL("aPtrString", *i.returnMoveOnlyByConstRef()); + EXPECT_EQUAL("bPtrString", *i.returnMoveOnlyByRef()); + EXPECT_EQUAL("bPtrString", *i.returnMoveOnlyByRef()); + { + AbstractType& abstract = i.returnAbstractTypeByRef(); + EXPECT_FALSE(abstract.wasObjectSliced()); + EXPECT_EQUAL("myConcreteType", abstract.getContent()); + } + { + AbstractType& abstract = i.returnAbstractTypeByRef(); + EXPECT_FALSE(abstract.wasObjectSliced()); + EXPECT_EQUAL("myConcreteType", abstract.getContent()); + } + } + + void explicitStubbingReturnCaptureByTemporary_with_AlwaysReturn() { + Mock mock; + + { + When(Method(mock, returnStringByConstRef)).AlwaysReturnCapture(std::string{"aString"}); + When(Method(mock, returnStringByRef)).AlwaysReturnCapture(std::string{"bString"}); + When(Method(mock, returnMoveOnlyByConstRef)).AlwaysReturnCapture(std::unique_ptr(new std::string{"aPtrString"})); + When(Method(mock, returnMoveOnlyByRef)).AlwaysReturnCapture(std::unique_ptr(new std::string{"bPtrString"})); + When(Method(mock, returnAbstractTypeByRef)).AlwaysReturnCapture(ConcreteType{"myConcreteType"}); + } + + ReferenceInterface& i = mock.get(); + + EXPECT_EQUAL("aString", i.returnStringByConstRef()); + EXPECT_EQUAL("aString", i.returnStringByConstRef()); + EXPECT_EQUAL("bString", i.returnStringByRef()); + EXPECT_EQUAL("bString", i.returnStringByRef()); + EXPECT_EQUAL("aPtrString", *i.returnMoveOnlyByConstRef()); + EXPECT_EQUAL("aPtrString", *i.returnMoveOnlyByConstRef()); + EXPECT_EQUAL("bPtrString", *i.returnMoveOnlyByRef()); + EXPECT_EQUAL("bPtrString", *i.returnMoveOnlyByRef()); + { + AbstractType& abstract = i.returnAbstractTypeByRef(); + EXPECT_FALSE(abstract.wasObjectSliced()); + EXPECT_EQUAL("myConcreteType", abstract.getContent()); + } + { + AbstractType& abstract = i.returnAbstractTypeByRef(); + EXPECT_FALSE(abstract.wasObjectSliced()); + EXPECT_EQUAL("myConcreteType", abstract.getContent()); + } + } + } __ReferenceTypesTests;