diff --git a/include/fakeit/StubbingProgress.hpp b/include/fakeit/StubbingProgress.hpp index a93252e1..24e5996d 100644 --- a/include/fakeit/StubbingProgress.hpp +++ b/include/fakeit/StubbingProgress.hpp @@ -23,6 +23,9 @@ namespace fakeit { + template + struct MethodStubbingProgress; + namespace helper { template @@ -38,35 +41,63 @@ namespace fakeit { template struct ParamWalker; - } // namespace helper + template + struct BasicDoImpl { + virtual ~BasicDoImpl() FAKEIT_THROWS { + } + virtual MethodStubbingProgress& Do(std::function::type...)> method) { + return DoImpl(new Repeat(method)); + } - template - struct MethodStubbingProgress { + protected: + virtual MethodStubbingProgress& DoImpl(Action *action) = 0; + }; - virtual ~MethodStubbingProgress() FAKEIT_THROWS { - } + template + struct BasicReturnImpl; - template - typename std::enable_if::value, MethodStubbingProgress &>::type - Return(const R &r) { - return Do([r](const typename fakeit::test_arg::type...) -> R { return r; }); - } + // If R is a reference. + template + struct BasicReturnImpl : public BasicDoImpl { + using BasicDoImpl::Do; - template - typename std::enable_if::value, MethodStubbingProgress &>::type - Return(const R &r) { - return Do([&r](const typename fakeit::test_arg::type...) -> R { return r; }); - } + MethodStubbingProgress& Return(const R& r) { + return Do([&r](const typename fakeit::test_arg::type...) -> R { return r; }); + } + }; - template - typename std::enable_if::value, MethodStubbingProgress&>::type - Return(R&& r) { - auto store = std::make_shared(std::move(r)); // work around for lack of move_only_funciton( C++23) - move into a shared_ptr which we can copy. - return Do([store](const typename fakeit::test_arg::type...) mutable -> R { - return std::move(*store); - }); - } + // If R is not a reference. + template + struct BasicReturnImpl : public BasicDoImpl { + using BasicDoImpl::Do; + + MethodStubbingProgress& Return(const R& r) { + return Do([r](const typename fakeit::test_arg::type...) -> R { return r; }); + } + + MethodStubbingProgress& Return(R&& r) { + auto store = std::make_shared(std::move(r)); // work around for lack of move_only_funciton( C++23) - move into a shared_ptr which we can copy. + return Do([store](const typename fakeit::test_arg::type...) mutable -> R { + return std::move(*store); + }); + } + }; + + template + using BasicReturnImplHelper = BasicReturnImpl::value, arglist...>; + } // namespace helper + + + template + struct MethodStubbingProgress : public helper::BasicReturnImplHelper { + + protected: + using helper::BasicReturnImplHelper::DoImpl; + + public: + using helper::BasicReturnImplHelper::Do; + using helper::BasicReturnImplHelper::Return; MethodStubbingProgress & Return(const Quantifier &q) { @@ -75,11 +106,11 @@ namespace fakeit { return DoImpl(new Repeat(method, q.quantity)); } - template + template MethodStubbingProgress & - Return(const first &f, const second &s, const tail &... t) { - Return(f); - return Return(s, t...); + Return(First&& f, Second&& s, Tail&&... t) { + Return(std::forward(f)); + return Return(std::forward(s), std::forward(t)...); } @@ -142,11 +173,6 @@ namespace fakeit { std::forward(arg_vals)...)); } - virtual MethodStubbingProgress & - Do(std::function::type...)> method) { - return DoImpl(new Repeat(method)); - } - template MethodStubbingProgress & Do(const Quantifier &q) { @@ -164,10 +190,6 @@ namespace fakeit { DoImpl(new RepeatForever(method)); } - protected: - - virtual MethodStubbingProgress &DoImpl(Action *action) = 0; - private: MethodStubbingProgress &operator=(const MethodStubbingProgress &other) = delete; diff --git a/tests/move_only_return_tests.cpp b/tests/move_only_return_tests.cpp index dbc4461b..802f2cb9 100644 --- a/tests/move_only_return_tests.cpp +++ b/tests/move_only_return_tests.cpp @@ -16,72 +16,113 @@ using namespace fakeit; struct MoveOnlyReturnTests: tpunit::TestFixture { - class AbstractType { - public: - virtual ~AbstractType() = default; - virtual void foo() = 0; - }; - - class ConcreteType : public AbstractType { + class MoveOnlyType { public: int state; - ConcreteType(int value) : + MoveOnlyType(int value) : state(value) { } - ConcreteType(const ConcreteType&) = delete; - ConcreteType(ConcreteType&&) = default; - - void foo() override { - } + MoveOnlyType(const MoveOnlyType&) = delete; + MoveOnlyType(MoveOnlyType&&) = default; - bool operator==(const ConcreteType& other) const { + bool operator==(const MoveOnlyType& other) const { return (other.state == this->state); } }; - struct ReferenceInterface { + struct MoveOnlyInterface { + virtual std::string returnCopyable() = 0; virtual std::unique_ptr returnMoveOnlyUniqueString() = 0; - virtual ConcreteType returnMoveOnlyConcreteTypeByRef() = 0; + virtual MoveOnlyType returnMoveOnlyType() = 0; + virtual std::vector returnVectorOfMoveOnly() = 0; }; + static std::vector constructVectorOfMoveOnly(int i) { + std::vector vectorOfMoveOnly; + vectorOfMoveOnly.emplace_back(i); + return vectorOfMoveOnly; + } + MoveOnlyReturnTests() : tpunit::TestFixture( // TEST(MoveOnlyReturnTests::explicitStubbingReturnValuesFromTemporary), - TEST(MoveOnlyReturnTests::explicitStubbingReturnValuesFromMove) + TEST(MoveOnlyReturnTests::explicitStubbingReturnValuesFromMove), + TEST(MoveOnlyReturnTests::explicitStubbingReturnMultipleValuesMoveAndCopy) // ) { } void explicitStubbingReturnValuesFromTemporary() { - Mock mock; + Mock mock; When(Method(mock, returnMoveOnlyUniqueString)).Return(std::unique_ptr(new std::string("value"))); - When(Method(mock, returnMoveOnlyConcreteTypeByRef)).Return(ConcreteType(10)); + When(Method(mock, returnMoveOnlyType)).Return(MoveOnlyType(10)); + When(Method(mock, returnVectorOfMoveOnly)).Return(constructVectorOfMoveOnly(5)); - ReferenceInterface & i = mock.get(); + MoveOnlyInterface & i = mock.get(); ASSERT_EQUAL("value", *i.returnMoveOnlyUniqueString()); - - ASSERT_EQUAL(ConcreteType(10), i.returnMoveOnlyConcreteTypeByRef()); + ASSERT_EQUAL(MoveOnlyType(10), i.returnMoveOnlyType()); + ASSERT_EQUAL(constructVectorOfMoveOnly(5), i.returnVectorOfMoveOnly()); } void explicitStubbingReturnValuesFromMove() { - Mock mock; + Mock mock; - ConcreteType c(10); - std::unique_ptr string(new std::string("value")); + std::string str{"copyable"}; + std::unique_ptr strPtr(new std::string("value")); + MoveOnlyType moveOnly(10); + std::vector vectorOfMoveOnly = constructVectorOfMoveOnly(5); - When(Method(mock, returnMoveOnlyUniqueString)).Return(std::move(string)); - When(Method(mock, returnMoveOnlyConcreteTypeByRef)).Return(std::move(c)); + When(Method(mock, returnCopyable)).Return(std::move(str)); + When(Method(mock, returnMoveOnlyUniqueString)).Return(std::move(strPtr)); + When(Method(mock, returnMoveOnlyType)).Return(std::move(moveOnly)); + When(Method(mock, returnVectorOfMoveOnly)).Return(std::move(vectorOfMoveOnly)); - ASSERT_FALSE(string); // check move did happen + // check move did happen + ASSERT_TRUE(str.empty()); + ASSERT_EQUAL(strPtr, nullptr); + ASSERT_TRUE(vectorOfMoveOnly.empty()); - ReferenceInterface& i = mock.get(); + MoveOnlyInterface& i = mock.get(); + ASSERT_EQUAL(std::string("copyable"), i.returnCopyable()); ASSERT_EQUAL(std::string("value"), *i.returnMoveOnlyUniqueString()); + ASSERT_EQUAL(MoveOnlyType(10), i.returnMoveOnlyType()); + ASSERT_EQUAL(constructVectorOfMoveOnly(5), i.returnVectorOfMoveOnly()); + } - ASSERT_EQUAL(ConcreteType(10), i.returnMoveOnlyConcreteTypeByRef()); + void explicitStubbingReturnMultipleValuesMoveAndCopy() { + Mock mock; + + std::string copiedStr{"copied"}; + std::string movedStr{"moved"}; + std::unique_ptr strPtr(new std::string("strPtrMove")); + MoveOnlyType moveOnly(100); + std::vector vectorOfMoveOnly = constructVectorOfMoveOnly(50); + + When(Method(mock, returnCopyable)).Return(copiedStr, std::move(movedStr)); + When(Method(mock, returnMoveOnlyUniqueString)).Return(std::unique_ptr(new std::string("strPtrRval")), std::move(strPtr)); + When(Method(mock, returnMoveOnlyType)).Return(MoveOnlyType(10), std::move(moveOnly)); + When(Method(mock, returnVectorOfMoveOnly)).Return(constructVectorOfMoveOnly(5), std::move(vectorOfMoveOnly)); + + ASSERT_EQUAL(copiedStr, "copied"); // check move did NOT happen + // check move did happen + ASSERT_TRUE(movedStr.empty()); + ASSERT_EQUAL(strPtr, nullptr); + ASSERT_TRUE(vectorOfMoveOnly.empty()); + + MoveOnlyInterface& i = mock.get(); + + ASSERT_EQUAL(std::string("copied"), i.returnCopyable()); + ASSERT_EQUAL(std::string("moved"), i.returnCopyable()); + ASSERT_EQUAL(std::string("strPtrRval"), *i.returnMoveOnlyUniqueString()); + ASSERT_EQUAL(std::string("strPtrMove"), *i.returnMoveOnlyUniqueString()); + ASSERT_EQUAL(MoveOnlyType(10), i.returnMoveOnlyType()); + ASSERT_EQUAL(MoveOnlyType(100), i.returnMoveOnlyType()); + ASSERT_EQUAL(constructVectorOfMoveOnly(5), i.returnVectorOfMoveOnly()); + ASSERT_EQUAL(constructVectorOfMoveOnly(50), i.returnVectorOfMoveOnly()); } } __MoveOnlyReturnTests; diff --git a/tests/stubbing_tests.cpp b/tests/stubbing_tests.cpp index bff76c72..ada64205 100644 --- a/tests/stubbing_tests.cpp +++ b/tests/stubbing_tests.cpp @@ -412,7 +412,8 @@ struct BasicStubbing : tpunit::TestFixture { void stub_a_method_with_mutable_lambda_delegate_always() { Mock mock; - When(Method(mock, funcNoArgs)).AlwaysDo([mutableVar = 0]() mutable { + int mutableVar = 0; + When(Method(mock, funcNoArgs)).AlwaysDo([mutableVar]() mutable { return ++mutableVar; });