diff --git a/HippoMocks/hippomocks.h b/HippoMocks/hippomocks.h index 48e81cf..928558c 100644 --- a/HippoMocks/hippomocks.h +++ b/HippoMocks/hippomocks.h @@ -1481,6 +1481,13 @@ class mock : public base_mock } template void mockedDestructor(int); + void defaultDestructor(int); +}; + +template +struct unique_mock : mock +{ + unique_mock(MockRepository *repository); }; // Do() function wrapping @@ -3361,6 +3368,10 @@ class MockRepository { mock *realMock = (mock *)mck; realMock->members.push_back(new MemberWrap(realRealMember)); } + template + void SetupDefaultDestructor(Z2 *mck, int x); + template + int SetupMockedDestructor(Z2 *mck); template TCall &RegisterExpectDestructor(Z2 *mck, RegistrationType expect, const char *fileName, unsigned long lineNo); @@ -4424,6 +4435,10 @@ noexcept(false) } template base *Mock(); + template + std::unique_ptr UniqueMock(); + template + std::unique_ptr UniqueMock(D deleter); }; // mock function providers @@ -5311,6 +5326,22 @@ void mock::mockedDestructor(int) isZombie = true; } +template +void mock::defaultDestructor(int) +{ + repo->VerifyPartial(this); + isZombie = true; +} + +template +unique_mock::unique_mock(MockRepository *repository) : mock(repository) +{ + // restore function table from mock + *(void **)this = this->funcTables[0]; + // setup destructor - since MockRepository is not the owner of the object + this->repo->SetupDefaultDestructor(reinterpret_cast(this), -1); +} + template void MockRepository::BasicRegisterExpect(mock *zMock, int baseOffset, int funcIndex, void (base_mock::*func)(), int X) { @@ -5332,8 +5363,27 @@ void MockRepository::BasicRegisterExpect(mock *zMock, int baseOffset, int fun } } +template +void MockRepository::SetupDefaultDestructor(Z2 *mck, int X) +{ + func_index idx; + ((Z2 *)&idx)->~Z2(); + int funcIndex = idx.lci * FUNCTION_STRIDE + FUNCTION_BASE; + void (mock::*member)(int); + member = &mock::defaultDestructor; + BasicRegisterExpect(reinterpret_cast *>(mck), + 0, funcIndex, + reinterpret_cast(member), X); +#ifdef EXTRA_DESTRUCTOR + BasicRegisterExpect(reinterpret_cast *>(mck), + 0, funcIndex+1, + reinterpret_cast(member), X); +#endif +} + + template -TCall &MockRepository::RegisterExpectDestructor(Z2 *mck, RegistrationType expect, const char *fileName, unsigned long lineNo) +int MockRepository::SetupMockedDestructor(Z2 *mck) { func_index idx; ((Z2 *)&idx)->~Z2(); @@ -5348,6 +5398,13 @@ TCall &MockRepository::RegisterExpectDestructor(Z2 *mck, RegistrationType 0, funcIndex+1, reinterpret_cast(member), X); #endif + return funcIndex; +} + +template +TCall &MockRepository::RegisterExpectDestructor(Z2 *mck, RegistrationType expect, const char *fileName, unsigned long lineNo) +{ + int funcIndex = this->template SetupMockedDestructor(mck); TCall *call = new TCall(Once, reinterpret_cast(mck), std::pair(0, funcIndex), lineNo, "destructor", fileName); addCall( call, expect ); return *call; @@ -6361,6 +6418,23 @@ base *MockRepository::Mock() { mocks.push_back(m); return reinterpret_cast(m); } + +template +std::unique_ptr MockRepository::UniqueMock() { + return std::move(std::unique_ptr{reinterpret_cast(new unique_mock(this))}); +} + +template +std::unique_ptr MockRepository::UniqueMock(Deleter deleter) { + return std::move(std::unique_ptr{reinterpret_cast(new unique_mock(this)), deleter}); +} + +template +std::unique_ptr tee(base*& capture, std::unique_ptr && 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 diff --git a/HippoMocksTest/CMakeLists.txt b/HippoMocksTest/CMakeLists.txt index fe41176..9e321fb 100644 --- a/HippoMocksTest/CMakeLists.txt +++ b/HippoMocksTest/CMakeLists.txt @@ -34,6 +34,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 ) diff --git a/HippoMocksTest/test_unique_ptr.cpp b/HippoMocksTest/test_unique_ptr.cpp new file mode 100644 index 0000000..6bb4386 --- /dev/null +++ b/HippoMocksTest/test_unique_ptr.cpp @@ -0,0 +1,87 @@ +#include + +#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(); +} + +TEST(TestUniquePtr, checkCallsWorksOnUniquePtr) { + MockRepository mocks; + std::unique_ptr iu = mocks.UniqueMock(); + mocks.ExpectCall(iu.get(), IU::f); + iu->f(); +} + +TEST(TestUniquePtr, checkMissingExpectationsWorksOnUniquePtr) { + MockRepository mocks; + bool exceptionCaught = false; + std::unique_ptr iu = mocks.UniqueMock(); + try { + iu->f(); + } catch (HippoMocks::NotImplementedException const &) { + exceptionCaught = true; + } + EXPECT_TRUE(exceptionCaught); +} + +TEST(TestUniquePtr, checkNeverCallWorksOnUniquePtr) { + bool exceptionCaught = false; + MockRepository mocks; + auto iu = mocks.UniqueMock(); + 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(); + 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(); + 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(); + mocks.OnCall(iamock.get(), IU::i); + iamock->i("bye"); +}