Skip to content

Commit

Permalink
Merge pull request #330 from eranpeer/better-return-move-only
Browse files Browse the repository at this point in the history
Improve Return for move-only return types.
  • Loading branch information
FranckRJ authored Apr 20, 2024
2 parents fa53ef2 + 7825ac7 commit 1ed5f80
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 67 deletions.
94 changes: 58 additions & 36 deletions include/fakeit/StubbingProgress.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@

namespace fakeit {

template<typename R, typename ... arglist>
struct MethodStubbingProgress;

namespace helper
{
template <typename T, int N>
Expand All @@ -38,35 +41,63 @@ namespace fakeit {
template<int N>
struct ParamWalker;

} // namespace helper
template<typename R, typename ... arglist>
struct BasicDoImpl {
virtual ~BasicDoImpl() FAKEIT_THROWS {
}

virtual MethodStubbingProgress<R, arglist...>& Do(std::function<R(const typename fakeit::test_arg<arglist>::type...)> method) {
return DoImpl(new Repeat<R, arglist...>(method));
}

template<typename R, typename ... arglist>
struct MethodStubbingProgress {
protected:
virtual MethodStubbingProgress<R, arglist...>& DoImpl(Action<R, arglist...> *action) = 0;
};

virtual ~MethodStubbingProgress() FAKEIT_THROWS {
}
template<typename R, bool RIsARef, typename ... arglist>
struct BasicReturnImpl;

template<typename U = R>
typename std::enable_if<!std::is_reference<U>::value, MethodStubbingProgress<R, arglist...> &>::type
Return(const R &r) {
return Do([r](const typename fakeit::test_arg<arglist>::type...) -> R { return r; });
}
// If R is a reference.
template<typename R, typename ... arglist>
struct BasicReturnImpl<R, true, arglist...> : public BasicDoImpl<R, arglist...> {
using BasicDoImpl<R, arglist...>::Do;

template<typename U = R>
typename std::enable_if<std::is_reference<U>::value, MethodStubbingProgress<R, arglist...> &>::type
Return(const R &r) {
return Do([&r](const typename fakeit::test_arg<arglist>::type...) -> R { return r; });
}
MethodStubbingProgress<R, arglist...>& Return(const R& r) {
return Do([&r](const typename fakeit::test_arg<arglist>::type...) -> R { return r; });
}
};

template<typename U = R>
typename std::enable_if<!std::is_copy_constructible<U>::value, MethodStubbingProgress<R, arglist...>&>::type
Return(R&& r) {
auto store = std::make_shared<R>(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<arglist>::type...) mutable -> R {
return std::move(*store);
});
}
// If R is not a reference.
template<typename R, typename ... arglist>
struct BasicReturnImpl<R, false, arglist...> : public BasicDoImpl<R, arglist...> {
using BasicDoImpl<R, arglist...>::Do;

MethodStubbingProgress<R, arglist...>& Return(const R& r) {
return Do([r](const typename fakeit::test_arg<arglist>::type...) -> R { return r; });
}

MethodStubbingProgress<R, arglist...>& Return(R&& r) {
auto store = std::make_shared<R>(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<arglist>::type...) mutable -> R {
return std::move(*store);
});
}
};

template<typename R, typename ... arglist>
using BasicReturnImplHelper = BasicReturnImpl<R, std::is_reference<R>::value, arglist...>;
} // namespace helper


template<typename R, typename ... arglist>
struct MethodStubbingProgress : public helper::BasicReturnImplHelper<R, arglist...> {

protected:
using helper::BasicReturnImplHelper<R, arglist...>::DoImpl;

public:
using helper::BasicReturnImplHelper<R, arglist...>::Do;
using helper::BasicReturnImplHelper<R, arglist...>::Return;

MethodStubbingProgress<R, arglist...> &
Return(const Quantifier<R> &q) {
Expand All @@ -75,11 +106,11 @@ namespace fakeit {
return DoImpl(new Repeat<R, arglist...>(method, q.quantity));
}

template<typename first, typename second, typename ... tail>
template<typename First, typename Second, typename... Tail>
MethodStubbingProgress<R, arglist...> &
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<First>(f));
return Return(std::forward<Second>(s), std::forward<Tail>(t)...);
}


Expand Down Expand Up @@ -142,11 +173,6 @@ namespace fakeit {
std::forward<valuelist>(arg_vals)...));
}

virtual MethodStubbingProgress<R, arglist...> &
Do(std::function<R(const typename fakeit::test_arg<arglist>::type...)> method) {
return DoImpl(new Repeat<R, arglist...>(method));
}

template<typename F>
MethodStubbingProgress<R, arglist...> &
Do(const Quantifier<F> &q) {
Expand All @@ -164,10 +190,6 @@ namespace fakeit {
DoImpl(new RepeatForever<R, arglist...>(method));
}

protected:

virtual MethodStubbingProgress<R, arglist...> &DoImpl(Action<R, arglist...> *action) = 0;

private:
MethodStubbingProgress &operator=(const MethodStubbingProgress &other) = delete;

Expand Down
101 changes: 71 additions & 30 deletions tests/move_only_return_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::string> returnMoveOnlyUniqueString() = 0;
virtual ConcreteType returnMoveOnlyConcreteTypeByRef() = 0;
virtual MoveOnlyType returnMoveOnlyType() = 0;
virtual std::vector<MoveOnlyType> returnVectorOfMoveOnly() = 0;
};

static std::vector<MoveOnlyType> constructVectorOfMoveOnly(int i) {
std::vector<MoveOnlyType> 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<ReferenceInterface> mock;
Mock<MoveOnlyInterface> mock;

When(Method(mock, returnMoveOnlyUniqueString)).Return(std::unique_ptr<std::string>(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<ReferenceInterface> mock;
Mock<MoveOnlyInterface> mock;

ConcreteType c(10);
std::unique_ptr<std::string> string(new std::string("value"));
std::string str{"copyable"};
std::unique_ptr<std::string> strPtr(new std::string("value"));
MoveOnlyType moveOnly(10);
std::vector<MoveOnlyType> 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<MoveOnlyInterface> mock;

std::string copiedStr{"copied"};
std::string movedStr{"moved"};
std::unique_ptr<std::string> strPtr(new std::string("strPtrMove"));
MoveOnlyType moveOnly(100);
std::vector<MoveOnlyType> vectorOfMoveOnly = constructVectorOfMoveOnly(50);

When(Method(mock, returnCopyable)).Return(copiedStr, std::move(movedStr));
When(Method(mock, returnMoveOnlyUniqueString)).Return(std::unique_ptr<std::string>(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;
3 changes: 2 additions & 1 deletion tests/stubbing_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,8 @@ struct BasicStubbing : tpunit::TestFixture {
void stub_a_method_with_mutable_lambda_delegate_always() {
Mock<SomeInterface> mock;

When(Method(mock, funcNoArgs)).AlwaysDo([mutableVar = 0]() mutable {
int mutableVar = 0;
When(Method(mock, funcNoArgs)).AlwaysDo([mutableVar]() mutable {
return ++mutableVar;
});

Expand Down

0 comments on commit 1ed5f80

Please sign in to comment.