/* * Copyright (c) 2014 Eran Pe'er. * * This program is made available under the terms of the MIT License. * * Created on Mar 10, 2014 */ #pragma once #include #include #include #include "mockutils/DynamicProxy.hpp" #include "fakeit/StubbingImpl.hpp" #include "fakeit/MethodMockingContext.hpp" #include "fakeit/DomainObjects.hpp" #include "fakeit/FakeitContext.hpp" namespace fakeit { template class MockImpl : private MockObject, public virtual ActualInvocationsSource { public: MockImpl(FakeitContext &fakeit, C &obj) : MockImpl(fakeit, obj, true) { } MockImpl(FakeitContext &fakeit) : MockImpl(fakeit, *(createFakeInstance()), false) { FakeObject *fake = reinterpret_cast *>(_instance); fake->getVirtualTable().setCookie(1, this); } virtual ~MockImpl() THROWS { _proxy.detach(); if (_isOwner) { FakeObject *fake = reinterpret_cast *>(_instance); delete fake; } } void detach() { _isOwner = false; _proxy.detach(); } /** * Return all actual invocations of this mock. */ void getActualInvocations(std::unordered_set &into) const override { std::vector vec; _proxy.getMethodMocks(vec); for (ActualInvocationsSource *s : vec) { s->getActualInvocations(into); } } void reset() { _proxy.Reset(); if (_isOwner) { FakeObject *fake = reinterpret_cast *>(_instance); fake->initializeDataMembersArea(); } } virtual C &get() override { return _proxy.get(); } virtual FakeitContext &getFakeIt() override { return _fakeit; } template::value>::type> DataMemberStubbingRoot stubDataMember(DATA_TYPE T::*member, const arglist &... ctorargs) { _proxy.stubDataMember(member, ctorargs...); return DataMemberStubbingRoot(); } template::value>::type> MockingContext stubMethod(R(T::*vMethod)(arglist...)) { return MockingContext(new UniqueMethodMockingContextImpl < id, R, arglist... > (*this, vMethod)); } DtorMockingContext stubDtor() { return DtorMockingContext(new DtorMockingContextImpl(*this)); } private: DynamicProxy _proxy; C *_instance; // bool _isOwner; FakeitContext &_fakeit; template class MethodMockingContextBase : public MethodMockingContext::Context { protected: MockImpl &_mock; virtual RecordedMethodBody &getRecordedMethodBody() = 0; public: MethodMockingContextBase(MockImpl &mock) : _mock(mock) { } virtual ~MethodMockingContextBase() = default; void addMethodInvocationHandler(typename ActualInvocation::Matcher *matcher, MethodInvocationHandler *invocationHandler) { getRecordedMethodBody().addMethodInvocationHandler(matcher, invocationHandler); } void scanActualInvocations(const std::function &)> &scanner) { getRecordedMethodBody().scanActualInvocations(scanner); } void setMethodDetails(std::string mockName, std::string methodName) { getRecordedMethodBody().setMethodDetails(mockName, methodName); } bool isOfMethod(MethodInfo &method) { return getRecordedMethodBody().isOfMethod(method); } ActualInvocationsSource &getInvolvedMock() { return _mock; } std::string getMethodName() { return getRecordedMethodBody().getMethod().name(); } }; template class MethodMockingContextImpl : public MethodMockingContextBase { protected: R (C::*_vMethod)(arglist...); #if defined (__GNUG__) typedef R(*VTableMethodType)(void *, arglist...); #elif defined (_MSC_VER) typedef R(__thiscall *VTableMethodType)(void *, arglist...); #endif public: virtual ~MethodMockingContextImpl() = default; MethodMockingContextImpl(MockImpl &mock, R (C::*vMethod)(arglist...)) : MethodMockingContextBase(mock), _vMethod(vMethod) { } virtual std::function getOriginalMethod() override { void *mPtr = MethodMockingContextBase::_mock.getOriginalMethod(_vMethod); C * instance = &(MethodMockingContextBase::_mock.get()); return [=](arglist &... args) -> R { auto m = union_cast(mPtr); return m(instance, args...); }; } }; template class UniqueMethodMockingContextImpl : public MethodMockingContextImpl { protected: virtual RecordedMethodBody &getRecordedMethodBody() override { return MethodMockingContextBase::_mock.template stubMethodIfNotStubbed( MethodMockingContextBase::_mock._proxy, MethodMockingContextImpl::_vMethod); } public: UniqueMethodMockingContextImpl(MockImpl &mock, R (C::*vMethod)(arglist...)) : MethodMockingContextImpl(mock, vMethod) { } }; class DtorMockingContextImpl : public MethodMockingContextBase { protected: virtual RecordedMethodBody &getRecordedMethodBody() override { return MethodMockingContextBase::_mock.stubDtorIfNotStubbed( MethodMockingContextBase::_mock._proxy); } public: virtual ~DtorMockingContextImpl() = default; DtorMockingContextImpl(MockImpl &mock) : MethodMockingContextBase(mock) { } virtual std::function getOriginalMethod() override { C &instance = MethodMockingContextBase::_mock.get(); return [=, &instance]() -> void { }; } }; static MockImpl *getMockImpl(void *instance) { FakeObject *fake = reinterpret_cast *>(instance); MockImpl *mock = reinterpret_cast *>(fake->getVirtualTable().getCookie( 1)); return mock; } void unmocked() { ActualInvocation<> invocation(Invocation::nextInvocationOrdinal(), UnknownMethod::instance()); UnexpectedMethodCallEvent event(UnexpectedType::Unmocked, invocation); auto &fakeit = getMockImpl(this)->_fakeit; fakeit.handle(event); std::string format = fakeit.format(event); UnexpectedMethodCallException e(format); throw e; } static C *createFakeInstance() { FakeObject *fake = new FakeObject(); void *unmockedMethodStubPtr = union_cast(&MockImpl::unmocked); fake->getVirtualTable().initAll(unmockedMethodStubPtr); return reinterpret_cast(fake); } template void *getOriginalMethod(R (C::*vMethod)(arglist...)) { auto vt = _proxy.getOriginalVT(); auto offset = VTUtils::getOffset(vMethod); void *origMethodPtr = vt.getMethod(offset); return origMethodPtr; } void *getOriginalDtor() { auto vt = _proxy.getOriginalVT(); auto offset = VTUtils::getDestructorOffset(); void *origMethodPtr = vt.getMethod(offset); return origMethodPtr; } template RecordedMethodBody &stubMethodIfNotStubbed(DynamicProxy &proxy, R (C::*vMethod)(arglist...)) { if (!proxy.isMethodStubbed(vMethod)) { proxy.template stubMethod(vMethod, createRecordedMethodBody < R, arglist... > (*this, vMethod)); } Destructible *d = proxy.getMethodMock(vMethod); RecordedMethodBody *methodMock = dynamic_cast *>(d); return *methodMock; } RecordedMethodBody &stubDtorIfNotStubbed(DynamicProxy &proxy) { if (!proxy.isDtorStubbed()) { proxy.stubDtor(createRecordedDtorBody(*this)); } Destructible *d = proxy.getDtorMock(); RecordedMethodBody *dtorMock = dynamic_cast *>(d); return *dtorMock; } MockImpl(FakeitContext &fakeit, C &obj, bool isSpy) : _proxy{obj}, _instance(&obj), _isOwner(!isSpy), _fakeit(fakeit) { } template static RecordedMethodBody *createRecordedMethodBody(MockObject &mock, R(C::*vMethod)(arglist...)) { return new RecordedMethodBody(mock.getFakeIt(), typeid(vMethod).name()); } static RecordedMethodBody *createRecordedDtorBody(MockObject &mock) { return new RecordedMethodBody(mock.getFakeIt(), "dtor"); } }; }