Skip to content

Commit

Permalink
Add support for unique_ptr
Browse files Browse the repository at this point in the history
[Issue/Feature Description]
Mocking of unique_ptr is currently not supported in HippoMocks.

[Resolution]
Cherry-pick changes from dascandy#82 which
add support for unique_ptr.
  • Loading branch information
jeremy-ir committed Jun 12, 2022
1 parent 7c1b0e8 commit cf60580
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 1 deletion.
76 changes: 75 additions & 1 deletion HippoMocks/hippomocks.h
Original file line number Diff line number Diff line change
Expand Up @@ -1487,6 +1487,13 @@ class mock : public base_mock
}
template <int X>
void mockedDestructor(int);
void defaultDestructor(int);
};

template <typename T>
struct unique_mock : mock<T>
{
unique_mock(MockRepository *repository);
};

// Do() function wrapping
Expand Down Expand Up @@ -3374,6 +3381,10 @@ class MockRepository {
mock<A> *realMock = (mock<A> *)mck;
realMock->members.push_back(new MemberWrap<C>(realRealMember));
}
template <typename Z2>
void SetupDefaultDestructor(Z2 *mck, int x);
template <int X, typename Z2>
int SetupMockedDestructor(Z2 *mck);
template <int X, typename Z2>
TCall<void> &RegisterExpectDestructor(Z2 *mck, RegistrationType expect, const char *fileName, unsigned long lineNo);

Expand Down Expand Up @@ -4444,6 +4455,10 @@ noexcept(false)
}
template <typename base>
base *Mock();
template <typename base>
std::unique_ptr<base> UniqueMock();
template <typename base, typename D>
std::unique_ptr<base, D> UniqueMock(D deleter);
};

// mock function providers
Expand Down Expand Up @@ -5331,6 +5346,22 @@ void mock<T>::mockedDestructor(int)
isZombie = true;
}

template <typename T>
void mock<T>::defaultDestructor(int)
{
repo->VerifyPartial(this);
isZombie = true;
}

template <typename T>
unique_mock<T>::unique_mock(MockRepository *repository) : mock<T>(repository)
{
// restore function table from mock<T>
*(void **)this = this->funcTables[0];
// setup destructor - since MockRepository is not the owner of the object
this->repo->SetupDefaultDestructor(reinterpret_cast<T*>(this), -1);
}

template <typename Z>
void MockRepository::BasicRegisterExpect(mock<Z> *zMock, int baseOffset, int funcIndex, void (base_mock::*func)(), int X)
{
Expand All @@ -5352,8 +5383,27 @@ void MockRepository::BasicRegisterExpect(mock<Z> *zMock, int baseOffset, int fun
}
}

template <typename Z2>
void MockRepository::SetupDefaultDestructor(Z2 *mck, int X)
{
func_index idx;
((Z2 *)&idx)->~Z2();
int funcIndex = idx.lci * FUNCTION_STRIDE + FUNCTION_BASE;
void (mock<Z2>::*member)(int);
member = &mock<Z2>::defaultDestructor;
BasicRegisterExpect(reinterpret_cast<mock<Z2> *>(mck),
0, funcIndex,
reinterpret_cast<void (base_mock::*)()>(member), X);
#ifdef EXTRA_DESTRUCTOR
BasicRegisterExpect(reinterpret_cast<mock<Z2> *>(mck),
0, funcIndex+1,
reinterpret_cast<void (base_mock::*)()>(member), X);
#endif
}


template <int X, typename Z2>
TCall<void> &MockRepository::RegisterExpectDestructor(Z2 *mck, RegistrationType expect, const char *fileName, unsigned long lineNo)
int MockRepository::SetupMockedDestructor(Z2 *mck)
{
func_index idx;
((Z2 *)&idx)->~Z2();
Expand All @@ -5368,6 +5418,13 @@ TCall<void> &MockRepository::RegisterExpectDestructor(Z2 *mck, RegistrationType
0, funcIndex+1,
reinterpret_cast<void (base_mock::*)()>(member), X);
#endif
return funcIndex;
}

template <int X, typename Z2>
TCall<void> &MockRepository::RegisterExpectDestructor(Z2 *mck, RegistrationType expect, const char *fileName, unsigned long lineNo)
{
int funcIndex = this->template SetupMockedDestructor<X>(mck);
TCall<void> *call = new TCall<void>(Once, reinterpret_cast<base_mock *>(mck), std::pair<int, int>(0, funcIndex), lineNo, "destructor", fileName);
addCall( call, expect );
return *call;
Expand Down Expand Up @@ -6381,6 +6438,23 @@ base *MockRepository::Mock() {
mocks.push_back(m);
return reinterpret_cast<base *>(m);
}

template <typename base>
std::unique_ptr<base> MockRepository::UniqueMock() {
return std::move(std::unique_ptr<base>{reinterpret_cast<base *>(new unique_mock<base>(this))});
}

template <typename base, typename Deleter>
std::unique_ptr<base,Deleter> MockRepository::UniqueMock(Deleter deleter) {
return std::move(std::unique_ptr<base,Deleter>{reinterpret_cast<base *>(new unique_mock<base>(this)), deleter});
}

template<typename base, typename d>
std::unique_ptr<base,d> tee(base*& capture, std::unique_ptr<base,d> && obj) {
capture = obj.get();
return std::move(obj);
}

inline std::ostream &operator<<(std::ostream &os, const Call &call)
{
os << call.fileName << "(" << call.lineno << "): "; //format for Visual studio, enables doubleclick on output line
Expand Down
1 change: 1 addition & 0 deletions HippoMocksTest/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ add_executable(${PROJECT_NAME}
test_ref_args.cpp
test_regression_arg_count.cpp
test_retval.cpp
test_unique_ptr.cpp
test_transaction.cpp
test_zombie.cpp
)
Expand Down
87 changes: 87 additions & 0 deletions HippoMocksTest/test_unique_ptr.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#include <string>

#include "gtest/gtest.h"
#include "hippomocks.h"

class IU {
public:
virtual ~IU() {}
virtual void f() = 0;
virtual void g() {}
virtual int h() { return 0; }
virtual void i(std::string) {}
virtual void j(std::string) = 0;
};

TEST(TestUniquePtr, checkUniquePtrDestruction) {
MockRepository mocks;
auto iu = mocks.UniqueMock<IU>();
}

TEST(TestUniquePtr, checkCallsWorksOnUniquePtr) {
MockRepository mocks;
std::unique_ptr<IU> iu = mocks.UniqueMock<IU>();
mocks.ExpectCall(iu.get(), IU::f);
iu->f();
}

TEST(TestUniquePtr, checkMissingExpectationsWorksOnUniquePtr) {
MockRepository mocks;
bool exceptionCaught = false;
std::unique_ptr<IU> iu = mocks.UniqueMock<IU>();
try {
iu->f();
} catch (HippoMocks::NotImplementedException const &) {
exceptionCaught = true;
}
EXPECT_TRUE(exceptionCaught);
}

TEST(TestUniquePtr, checkNeverCallWorksOnUniquePtr) {
bool exceptionCaught = false;
MockRepository mocks;
auto iu = mocks.UniqueMock<IU>();
Call &callF = mocks.ExpectCall(iu.get(), IU::f);
mocks.OnCall(iu.get(), IU::g);
mocks.NeverCall(iu.get(), IU::g).After(callF);
iu->g();
iu->g();
iu->f();
try {
iu->g();
} catch (HippoMocks::ExpectationException &) {
exceptionCaught = true;
}
EXPECT_TRUE(exceptionCaught);
}

TEST(TestUniquePtr, checkClassArgumentsAcceptedWithUniquePtr) {
MockRepository mocks;
auto iamock = mocks.UniqueMock<IU>();
mocks.OnCall(iamock.get(), IU::i).With("hi");
mocks.OnCall(iamock.get(), IU::j).With("bye");
iamock->i("hi");
iamock->j("bye");
}

TEST(TestUniquePtr, checkClassArgumentsCheckedWithUniquePtr) {
MockRepository mocks;
auto iamock = mocks.UniqueMock<IU>();
mocks.OnCall(iamock.get(), IU::i).With("hi");
mocks.OnCall(iamock.get(), IU::j).With("bye");
bool exceptionCaught = false;
try {
iamock->i("bye");
} catch (HippoMocks::ExpectationException) {
exceptionCaught = true;
}
EXPECT_TRUE(exceptionCaught);
mocks.reset();
}

TEST(TestUniquePtr, checkClassArgumentsIgnoredWithUniquePtr) {
MockRepository mocks;
auto iamock = mocks.UniqueMock<IU>();
mocks.OnCall(iamock.get(), IU::i);
iamock->i("bye");
}

0 comments on commit cf60580

Please sign in to comment.