Skip to content

Commit

Permalink
Added tests for *ReturnCapture with functions returning rval references.
Browse files Browse the repository at this point in the history
  • Loading branch information
FranckRJ committed Apr 27, 2024
1 parent 6ecefba commit f928961
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 16 deletions.
16 changes: 6 additions & 10 deletions include/fakeit/StubbingProgress.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ namespace fakeit {
}

template <typename U = R>
MethodStubbingProgress<R, arglist...>& Return(typename std::remove_cv<typename std::remove_reference<R>::type>::type&& r) {
MethodStubbingProgress<R, arglist...>& Return(fk_remove_cvref_t<R>&& r) {
static_assert(sizeof(U) != sizeof(U), "Return() cannot take an rvalue references for functions returning a reference because it would make it dangling, use ReturnCapture() instead.");
return Return(r); // Only written to silence warning about not returning from a non-void function, but will never be executed.
}
Expand All @@ -82,29 +82,25 @@ namespace fakeit {
}

template <typename U = R>
void AlwaysReturn(typename std::remove_cv<typename std::remove_reference<R>::type>::type&&) {
void AlwaysReturn(fk_remove_cvref_t<R>&&) {
static_assert(sizeof(U) != sizeof(U), "AlwaysReturn() cannot take an rvalue references for functions returning a reference because it would make it dangling, use AlwaysReturnCapture() instead.");
}

template<typename T>
MethodStubbingProgress<R, arglist...>& ReturnCapture(T&& r) {
static_assert(std::is_constructible<
typename std::remove_cv<typename std::remove_reference<R>::type>::type&,
typename std::remove_cv<typename std::remove_reference<T>::type>::type&>::value,
static_assert(std::is_constructible<fk_remove_cvref_t<R>&, fk_remove_cvref_t<T>&>::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<typename std::remove_cv<typename std::remove_reference<T>::type>::type>(std::forward<T>(r));
auto store = std::make_shared<fk_remove_cvref_t<T>>(std::forward<T>(r));
return Do([store](const typename fakeit::test_arg<arglist>::type...) mutable -> R {
return std::forward<R>(*store);
});
}

template<typename T>
void AlwaysReturnCapture(T&& r) {
static_assert(std::is_constructible<
typename std::remove_cv<typename std::remove_reference<R>::type>::type&,
typename std::remove_cv<typename std::remove_reference<T>::type>::type&>::value,
static_assert(std::is_constructible<fk_remove_cvref_t<R>&, fk_remove_cvref_t<T>&>::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<typename std::remove_cv<typename std::remove_reference<T>::type>::type>(std::forward<T>(r));
auto store = std::make_shared<fk_remove_cvref_t<T>>(std::forward<T>(r));
return AlwaysDo([store](const typename fakeit::test_arg<arglist>::type...) mutable -> R {
return std::forward<R>(*store);
});
Expand Down
5 changes: 4 additions & 1 deletion include/mockutils/type_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,16 @@
#pragma once

#include <tuple>

#include <type_traits>

namespace fakeit {

template<class...>
using fk_void_t = void;

template<typename T>
using fk_remove_cvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;

template <bool...> struct bool_pack;

template <bool... v>
Expand Down
74 changes: 69 additions & 5 deletions tests/referece_types_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ struct ReferenceTypesTests: tpunit::TestFixture {
virtual std::string& returnStringByRef() = 0;
virtual const std::unique_ptr<std::string>& returnMoveOnlyByConstRef() = 0;
virtual std::unique_ptr<std::string>& returnMoveOnlyByRef() = 0;
virtual std::string&& returnStringByRValRef() = 0;
virtual std::unique_ptr<std::string>&& returnMoveOnlyByRValRef() = 0;
};

ReferenceTypesTests() :
Expand Down Expand Up @@ -230,24 +232,28 @@ struct ReferenceTypesTests: tpunit::TestFixture {

// add scope so we know we are copying
{
std::string aString{"myString"};
std::string aString{"aString"};
std::string bString{"bString"};
int num = 1;
ConcreteType concrete{"myConcreteType"};

// explicit copy here
When(Method(mock, returnStringByConstRef)).ReturnCapture(aString);
When(Method(mock, returnStringByRValRef)).ReturnCapture(bString);
When(Method(mock, returnIntByRef)).ReturnCapture(num);
When(Method(mock, returnAbstractTypeByRef)).ReturnCapture(concrete);

// modify now so know whether or not is was copied
aString = "modified";
bString = "modified";
num = 2;
concrete.content = "modified";
}

ReferenceInterface& i = mock.get();

EXPECT_EQUAL("myString", i.returnStringByConstRef());
EXPECT_EQUAL("aString", i.returnStringByConstRef());
EXPECT_EQUAL("bString", i.returnStringByRValRef());
EXPECT_EQUAL(1, i.returnIntByRef());
AbstractType& abstract = i.returnAbstractTypeByRef();
EXPECT_FALSE(abstract.wasObjectSliced());
Expand All @@ -261,31 +267,39 @@ struct ReferenceTypesTests: tpunit::TestFixture {
{
std::string aString{"aString"};
std::string bString{"bString"};
std::string cString{"cString"};
std::unique_ptr<std::string> aPtrString{new std::string{"aPtrString"}};
std::unique_ptr<std::string> bPtrString{new std::string{"bPtrString"}};
std::unique_ptr<std::string> cPtrString{new std::string{"cPtrString"}};
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, returnStringByRValRef)).ReturnCapture(std::move(cString));
When(Method(mock, returnMoveOnlyByConstRef)).ReturnCapture(std::move(aPtrString));
When(Method(mock, returnMoveOnlyByRef)).ReturnCapture(std::move(bPtrString));
When(Method(mock, returnMoveOnlyByRValRef)).ReturnCapture(std::move(cPtrString));
When(Method(mock, returnAbstractTypeByRef)).ReturnCapture(std::move(concrete));

// Verify objects were moved.
EXPECT_TRUE(aString.empty());
EXPECT_TRUE(bString.empty());
EXPECT_TRUE(cString.empty());
EXPECT_EQUAL(aPtrString, nullptr);
EXPECT_EQUAL(bPtrString, nullptr);
EXPECT_EQUAL(cPtrString, nullptr);
EXPECT_TRUE(concrete.content.empty());
}

ReferenceInterface& i = mock.get();

EXPECT_EQUAL("aString", i.returnStringByConstRef());
EXPECT_EQUAL("bString", i.returnStringByRef());
EXPECT_EQUAL("cString", i.returnStringByRValRef());
EXPECT_EQUAL("aPtrString", *i.returnMoveOnlyByConstRef());
EXPECT_EQUAL("bPtrString", *i.returnMoveOnlyByRef());
EXPECT_EQUAL("cPtrString", *i.returnMoveOnlyByRValRef());
AbstractType& abstract = i.returnAbstractTypeByRef();
EXPECT_FALSE(abstract.wasObjectSliced());
EXPECT_EQUAL("myConcreteType", abstract.getContent());
Expand All @@ -297,17 +311,21 @@ struct ReferenceTypesTests: tpunit::TestFixture {
{
When(Method(mock, returnStringByConstRef)).ReturnCapture(std::string{"aString"});
When(Method(mock, returnStringByRef)).ReturnCapture(std::string{"bString"});
When(Method(mock, returnStringByRValRef)).ReturnCapture(std::string{"cString"});
When(Method(mock, returnMoveOnlyByConstRef)).ReturnCapture(std::unique_ptr<std::string>(new std::string{"aPtrString"}));
When(Method(mock, returnMoveOnlyByRef)).ReturnCapture(std::unique_ptr<std::string>(new std::string{"bPtrString"}));
When(Method(mock, returnMoveOnlyByRValRef)).ReturnCapture(std::unique_ptr<std::string>(new std::string{"cPtrString"}));
When(Method(mock, returnAbstractTypeByRef)).ReturnCapture(ConcreteType{"myConcreteType"});
}

ReferenceInterface& i = mock.get();

EXPECT_EQUAL("aString", i.returnStringByConstRef());
EXPECT_EQUAL("bString", i.returnStringByRef());
EXPECT_EQUAL("cString", i.returnStringByRValRef());
EXPECT_EQUAL("aPtrString", *i.returnMoveOnlyByConstRef());
EXPECT_EQUAL("bPtrString", *i.returnMoveOnlyByRef());
EXPECT_EQUAL("cPtrString", *i.returnMoveOnlyByRValRef());
AbstractType& abstract = i.returnAbstractTypeByRef();
EXPECT_FALSE(abstract.wasObjectSliced());
EXPECT_EQUAL("myConcreteType", abstract.getContent());
Expand All @@ -318,25 +336,35 @@ struct ReferenceTypesTests: tpunit::TestFixture {

// add scope so we know we are copying
{
std::string aString{"myString"};
std::string aString{"aString"};
std::string bString{"bString"};
int num = 1;
ConcreteType concrete{"myConcreteType"};

// explicit copy here
When(Method(mock, returnStringByConstRef)).AlwaysReturnCapture(aString);
When(Method(mock, returnStringByRValRef)).AlwaysReturnCapture(bString);
When(Method(mock, returnIntByRef)).AlwaysReturnCapture(num);
When(Method(mock, returnAbstractTypeByRef)).AlwaysReturnCapture(concrete);

// modify now so know whether or not is was copied
aString = "modified";
bString = "modified";
num = 2;
concrete.content = "modified";
}

ReferenceInterface& i = mock.get();

EXPECT_EQUAL("myString", i.returnStringByConstRef());
EXPECT_EQUAL("myString", i.returnStringByConstRef());
EXPECT_EQUAL("aString", i.returnStringByConstRef());
EXPECT_EQUAL("aString", i.returnStringByConstRef());
EXPECT_EQUAL("bString", i.returnStringByRValRef());
EXPECT_EQUAL("bString", i.returnStringByRValRef());
{
std::string bStr = i.returnStringByRValRef();
EXPECT_EQUAL("bString", bStr);
EXPECT_TRUE(i.returnStringByRValRef().empty());
}
EXPECT_EQUAL(1, i.returnIntByRef());
EXPECT_EQUAL(1, i.returnIntByRef());
{
Expand All @@ -358,22 +386,28 @@ struct ReferenceTypesTests: tpunit::TestFixture {
{
std::string aString{"aString"};
std::string bString{"bString"};
std::string cString{"cString"};
std::unique_ptr<std::string> aPtrString{new std::string{"aPtrString"}};
std::unique_ptr<std::string> bPtrString{new std::string{"bPtrString"}};
std::unique_ptr<std::string> cPtrString{new std::string{"cPtrString"}};
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, returnStringByRValRef)).AlwaysReturnCapture(std::move(cString));
When(Method(mock, returnMoveOnlyByConstRef)).AlwaysReturnCapture(std::move(aPtrString));
When(Method(mock, returnMoveOnlyByRef)).AlwaysReturnCapture(std::move(bPtrString));
When(Method(mock, returnMoveOnlyByRValRef)).AlwaysReturnCapture(std::move(cPtrString));
When(Method(mock, returnAbstractTypeByRef)).AlwaysReturnCapture(std::move(concrete));

// Verify objects were moved.
EXPECT_TRUE(aString.empty());
EXPECT_TRUE(bString.empty());
EXPECT_TRUE(cString.empty());
EXPECT_EQUAL(aPtrString, nullptr);
EXPECT_EQUAL(bPtrString, nullptr);
EXPECT_EQUAL(cPtrString, nullptr);
EXPECT_TRUE(concrete.content.empty());
}

Expand All @@ -383,10 +417,24 @@ struct ReferenceTypesTests: tpunit::TestFixture {
EXPECT_EQUAL("aString", i.returnStringByConstRef());
EXPECT_EQUAL("bString", i.returnStringByRef());
EXPECT_EQUAL("bString", i.returnStringByRef());
EXPECT_EQUAL("cString", i.returnStringByRValRef());
EXPECT_EQUAL("cString", i.returnStringByRValRef());
{
std::string cStr = i.returnStringByRValRef();
EXPECT_EQUAL("cString", cStr);
EXPECT_TRUE(i.returnStringByRValRef().empty());
}
EXPECT_EQUAL("aPtrString", *i.returnMoveOnlyByConstRef());
EXPECT_EQUAL("aPtrString", *i.returnMoveOnlyByConstRef());
EXPECT_EQUAL("bPtrString", *i.returnMoveOnlyByRef());
EXPECT_EQUAL("bPtrString", *i.returnMoveOnlyByRef());
EXPECT_EQUAL("cPtrString", *i.returnMoveOnlyByRValRef());
EXPECT_EQUAL("cPtrString", *i.returnMoveOnlyByRValRef());
{
std::unique_ptr<std::string> cPtrStr = i.returnMoveOnlyByRValRef();
EXPECT_EQUAL("cPtrString", *cPtrStr);
EXPECT_EQUAL(i.returnMoveOnlyByRValRef(), nullptr);
}
{
AbstractType& abstract = i.returnAbstractTypeByRef();
EXPECT_FALSE(abstract.wasObjectSliced());
Expand All @@ -405,8 +453,10 @@ struct ReferenceTypesTests: tpunit::TestFixture {
{
When(Method(mock, returnStringByConstRef)).AlwaysReturnCapture(std::string{"aString"});
When(Method(mock, returnStringByRef)).AlwaysReturnCapture(std::string{"bString"});
When(Method(mock, returnStringByRValRef)).AlwaysReturnCapture(std::string{"cString"});
When(Method(mock, returnMoveOnlyByConstRef)).AlwaysReturnCapture(std::unique_ptr<std::string>(new std::string{"aPtrString"}));
When(Method(mock, returnMoveOnlyByRef)).AlwaysReturnCapture(std::unique_ptr<std::string>(new std::string{"bPtrString"}));
When(Method(mock, returnMoveOnlyByRValRef)).AlwaysReturnCapture(std::unique_ptr<std::string>(new std::string{"cPtrString"}));
When(Method(mock, returnAbstractTypeByRef)).AlwaysReturnCapture(ConcreteType{"myConcreteType"});
}

Expand All @@ -416,10 +466,24 @@ struct ReferenceTypesTests: tpunit::TestFixture {
EXPECT_EQUAL("aString", i.returnStringByConstRef());
EXPECT_EQUAL("bString", i.returnStringByRef());
EXPECT_EQUAL("bString", i.returnStringByRef());
EXPECT_EQUAL("cString", i.returnStringByRValRef());
EXPECT_EQUAL("cString", i.returnStringByRValRef());
{
std::string cStr = i.returnStringByRValRef();
EXPECT_EQUAL("cString", cStr);
EXPECT_TRUE(i.returnStringByRValRef().empty());
}
EXPECT_EQUAL("aPtrString", *i.returnMoveOnlyByConstRef());
EXPECT_EQUAL("aPtrString", *i.returnMoveOnlyByConstRef());
EXPECT_EQUAL("bPtrString", *i.returnMoveOnlyByRef());
EXPECT_EQUAL("bPtrString", *i.returnMoveOnlyByRef());
EXPECT_EQUAL("cPtrString", *i.returnMoveOnlyByRValRef());
EXPECT_EQUAL("cPtrString", *i.returnMoveOnlyByRValRef());
{
std::unique_ptr<std::string> cPtrStr = i.returnMoveOnlyByRValRef();
EXPECT_EQUAL("cPtrString", *cPtrStr);
EXPECT_EQUAL(i.returnMoveOnlyByRValRef(), nullptr);
}
{
AbstractType& abstract = i.returnAbstractTypeByRef();
EXPECT_FALSE(abstract.wasObjectSliced());
Expand Down

0 comments on commit f928961

Please sign in to comment.