/* * 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 #include #ifdef _MSC_VER #include "mockutils/mscpp/VirtualTable.hpp" #else #include "mockutils/gcc/VirtualTable.hpp" #endif #include "mockutils/union_cast.hpp" #include "mockutils/MethodInvocationHandler.hpp" #include "mockutils/VTUtils.hpp" #include "mockutils/FakeObject.hpp" #include "mockutils/MethodProxy.hpp" #include "MethodProxyCreator.hpp" namespace fakeit { class InvocationHandlers : public InvocationHandlerCollection { std::vector> &_methodMocks; std::vector &_offsets; unsigned int getOffset(unsigned int id) { unsigned int offset = 0; for (; offset < _offsets.size(); offset++) { if (_offsets[offset] == id) { break; } } return offset; } public: InvocationHandlers( std::vector> &methodMocks, std::vector &offsets) : _methodMocks(methodMocks), _offsets(offsets) { } Destructible *getInvocatoinHandlerPtrById(unsigned int id) override { unsigned int offset = getOffset(id); std::shared_ptr ptr = _methodMocks[offset]; return ptr.get(); } }; template struct DynamicProxy { static_assert(std::is_polymorphic::value, "DynamicProxy requires a polymorphic type"); DynamicProxy(C &instance) : instance(instance), originalVtHandle(VirtualTable::getVTable(instance).createHandle()), _methodMocks(VTUtils::getVTSize()), _offsets(VTUtils::getVTSize()), _invocationHandlers(_methodMocks, _offsets) { _cloneVt.copyFrom(originalVtHandle.restore()); _cloneVt.setCookie(InvocationHandlerCollection::VT_COOKIE_INDEX, &_invocationHandlers); getFake().setVirtualTable(_cloneVt); } void detach() { getFake().setVirtualTable(originalVtHandle.restore()); } ~DynamicProxy() { _cloneVt.dispose(); } C &get() { return instance; } void Reset() { _methodMocks = {{}}; _members = {}; _cloneVt.copyFrom(originalVtHandle.restore()); } template void stubMethod(R(C::*vMethod)(arglist...), MethodInvocationHandler *methodInvocationHandler) { auto offset = VTUtils::getOffset(vMethod); MethodProxyCreator creator; bind(creator.template createMethodProxy(offset), methodInvocationHandler); } void stubDtor(MethodInvocationHandler *methodInvocationHandler) { auto offset = VTUtils::getDestructorOffset(); MethodProxyCreator creator; bindDtor(creator.createMethodProxy<0>(offset), methodInvocationHandler); } template bool isMethodStubbed(R(C::*vMethod)(arglist...)) { unsigned int offset = VTUtils::getOffset(vMethod); return isBinded(offset); } bool isDtorStubbed() { unsigned int offset = VTUtils::getDestructorOffset(); return isBinded(offset); } template Destructible *getMethodMock(R(C::*vMethod)(arglist...)) { auto offset = VTUtils::getOffset(vMethod); std::shared_ptr ptr = _methodMocks[offset]; return ptr.get(); } Destructible *getDtorMock() { auto offset = VTUtils::getDestructorOffset(); std::shared_ptr ptr = _methodMocks[offset]; return ptr.get(); } template void stubDataMember(DATA_TYPE C::*member, const arglist &... initargs) { DATA_TYPE C::*theMember = (DATA_TYPE C::*) member; C &mock = get(); DATA_TYPE *memberPtr = &(mock.*theMember); _members.push_back( std::shared_ptr > {new DataMemeberWrapper < DATA_TYPE, arglist...>(memberPtr, initargs...)}); } template void getMethodMocks(std::vector &into) const { for (std::shared_ptr ptr : _methodMocks) { DATA_TYPE p = dynamic_cast(ptr.get()); if (p) { into.push_back(p); } } } VirtualTable &getOriginalVT() { VirtualTable &vt = originalVtHandle.restore(); return vt; } private: template class DataMemeberWrapper : public Destructible { private: DATA_TYPE *dataMember; public: DataMemeberWrapper(DATA_TYPE *dataMember, const arglist &... initargs) : dataMember(dataMember) { new(dataMember) DATA_TYPE{initargs ...}; } ~DataMemeberWrapper() { dataMember->~DATA_TYPE(); } }; static_assert(sizeof(C) == sizeof(FakeObject), "This is a problem"); C &instance; typename VirtualTable::Handle originalVtHandle; // avoid delete!! this is the original! VirtualTable _cloneVt; // std::vector> _methodMocks; std::vector> _members; std::vector _offsets; InvocationHandlers _invocationHandlers; FakeObject &getFake() { return reinterpret_cast &>(instance); } void bind(const MethodProxy &methodProxy, Destructible *invocationHandler) { getFake().setMethod(methodProxy.getOffset(), methodProxy.getProxy()); _methodMocks[methodProxy.getOffset()].reset(invocationHandler); _offsets[methodProxy.getOffset()] = methodProxy.getId(); } void bindDtor(const MethodProxy &methodProxy, Destructible *invocationHandler) { getFake().setDtor(methodProxy.getProxy()); _methodMocks[methodProxy.getOffset()].reset(invocationHandler); _offsets[methodProxy.getOffset()] = methodProxy.getId(); } template DATA_TYPE getMethodMock(unsigned int offset) { std::shared_ptr ptr = _methodMocks[offset]; return dynamic_cast(ptr.get()); } template void checkMultipleInheritance() { C *ptr = (C *) (unsigned int) 1; BaseClass *basePtr = ptr; int delta = (unsigned long) basePtr - (unsigned long) ptr; if (delta > 0) { // base class does not start on same position as derived class. // this is multiple inheritance. throw std::invalid_argument(std::string("multiple inheritance is not supported")); } } bool isBinded(unsigned int offset) { std::shared_ptr ptr = _methodMocks[offset]; return ptr.get() != nullptr; } }; }