Skip to content

Commit

Permalink
Fix a bug: spy did not detach from instance on ~Mock.
Browse files Browse the repository at this point in the history
  • Loading branch information
eranpeer committed Jan 22, 2015
1 parent 941ede0 commit ef04d85
Show file tree
Hide file tree
Showing 14 changed files with 489 additions and 411 deletions.
16 changes: 9 additions & 7 deletions include/fakeit/ActualInvocation.hpp
Expand Up @@ -23,13 +23,15 @@


namespace fakeit {

static std::atomic_int invocationOrdinal;

static int nextInvocationOrdinal(){
return ++invocationOrdinal;
}


namespace internal {

static std::atomic_int invocationOrdinal;

static int nextInvocationOrdinal(){
return ++invocationOrdinal;
}
}

template<typename ... arglist>
struct ActualInvocation: public Invocation {
Expand Down
45 changes: 16 additions & 29 deletions include/fakeit/DomainObjects.hpp
Expand Up @@ -13,36 +13,23 @@

namespace fakeit {

struct FakeitContext;

template<typename C>
struct MockObject {
virtual ~MockObject() = default;
virtual C & get() = 0;
virtual FakeitContext & getFakeIt() = 0;
};

struct Method {
Method() = default;
virtual ~Method() = default;
virtual std::string name() const = 0;
};

struct UnknownMethod : public Method {

virtual ~UnknownMethod() = default;

virtual std::string name() const {
return {"unknown"};
struct Method {
Method() = default;
virtual ~Method() = default;
virtual std::string name() const = 0;
};

struct FakeitContext;

namespace internal {

template<typename C>
struct MockObject {
virtual ~MockObject() = default;
virtual C & get() = 0;
virtual FakeitContext & getFakeIt() = 0;
};
}

static Method& instance() {
static UnknownMethod instance;
return instance;
}

};

}

#endif // DomainObjects_h__
278 changes: 148 additions & 130 deletions include/fakeit/MockImpl.hpp
Expand Up @@ -22,171 +22,189 @@

namespace fakeit {

template<typename C, typename ... baseclasses>
class MockImpl: private MockObject<C>, public virtual ActualInvocationsSource {
public:
namespace internal {
struct UnknownMethod : public Method {

MockImpl(FakeitContext& fakeit, C &obj) :
MockImpl<C, baseclasses...>(fakeit, obj, true) {
}
virtual ~UnknownMethod() = default;

MockImpl(FakeitContext& fakeit) :
MockImpl<C, baseclasses...>(fakeit, *(createFakeInstance()), false) {
FakeObject<C, baseclasses...>* fake = reinterpret_cast<FakeObject<C, baseclasses...>*>(_instance);
fake->getVirtualTable().setCookie(1, this);
}
virtual std::string name() const {
return{ "unknown" };
}

virtual ~MockImpl() {
if (_isSpy)
return;
_proxy.detach();
FakeObject<C, baseclasses...>* fake = reinterpret_cast<FakeObject<C, baseclasses...>*>(_instance);
delete fake;
static Method& instance() {
static UnknownMethod instance;
return instance;
}
};
}

/**
* Return all actual invocations of this mock.
*/
void getActualInvocations(std::unordered_set<Invocation*>& into) const override {
std::vector<ActualInvocationsSource*> vec;
_proxy.getMethodMocks(vec);
for (ActualInvocationsSource * s : vec) {
s->getActualInvocations(into);
template<typename C, typename ... baseclasses>
class MockImpl : private fakeit::internal::MockObject<C>, public virtual ActualInvocationsSource {
public:

MockImpl(FakeitContext& fakeit, C &obj) :
MockImpl<C, baseclasses...>(fakeit, obj, true) {
}
}

void reset() {
_proxy.Reset();
if (!_isSpy) {
MockImpl(FakeitContext& fakeit) :
MockImpl<C, baseclasses...>(fakeit, *(createFakeInstance()), false) {
FakeObject<C, baseclasses...>* fake = reinterpret_cast<FakeObject<C, baseclasses...>*>(_instance);
fake->initializeDataMembersArea();
fake->getVirtualTable().setCookie(1, this);
}
}

virtual C& get() override
{
return _proxy.get();
}
virtual ~MockImpl() {
_proxy.detach();
if (_isOwner){
FakeObject<C, baseclasses...>* fake = reinterpret_cast<FakeObject<C, baseclasses...>*>(_instance);
delete fake;
}
}

virtual FakeitContext & getFakeIt() override {
return _fakeit;
}
/**
* Return all actual invocations of this mock.
*/
void getActualInvocations(std::unordered_set<Invocation*>& into) const override {
std::vector<ActualInvocationsSource*> vec;
_proxy.getMethodMocks(vec);
for (ActualInvocationsSource * s : vec) {
s->getActualInvocations(into);
}
}

template<class DATA_TYPE, typename ... arglist>
DataMemberStubbingRoot<C, DATA_TYPE> stubDataMember(DATA_TYPE C::*member, const arglist&... ctorargs) {
_proxy.stubDataMember(member, ctorargs...);
return DataMemberStubbingRoot<C, DATA_TYPE>();
}
void reset() {
_proxy.Reset();
if (_isOwner) {
FakeObject<C, baseclasses...>* fake = reinterpret_cast<FakeObject<C, baseclasses...>*>(_instance);
fake->initializeDataMembersArea();
}
}

template<typename R, typename ... arglist>
MockingContext<R, arglist...> stub(R (C::*vMethod)(arglist...)) {
return MockingContext<R, arglist...>(new MethodStubbingContextImpl<R, arglist...>(*this, vMethod));
}
virtual C& get() override
{
return _proxy.get();
}

private:
DynamicProxy<C, baseclasses...> _proxy;
C* _instance; //
bool _isSpy;
FakeitContext& _fakeit;
virtual FakeitContext & getFakeIt() override {
return _fakeit;
}

template<typename R, typename ... arglist>
class MethodStubbingContextImpl: public MethodMockingContext<R, arglist...>::Context {
MockImpl<C, baseclasses...>& _mock;
R (C::*_vMethod)(arglist...);
template<class DATA_TYPE, typename ... arglist>
DataMemberStubbingRoot<C, DATA_TYPE> stubDataMember(DATA_TYPE C::*member, const arglist&... ctorargs) {
_proxy.stubDataMember(member, ctorargs...);
return DataMemberStubbingRoot<C, DATA_TYPE>();
}

RecordedMethodBody<C, R, arglist...>& getRecordedMethodBody() {
return _mock.stubMethodIfNotStubbed(_mock._proxy, _vMethod);
template<typename R, typename ... arglist>
MockingContext<R, arglist...> stub(R(C::*vMethod)(arglist...)) {
return MockingContext<R, arglist...>(new MethodStubbingContextImpl<R, arglist...>(*this, vMethod));
}

public:
virtual ~MethodStubbingContextImpl() = default;
private:
DynamicProxy<C, baseclasses...> _proxy;
C* _instance; //
bool _isOwner;
FakeitContext& _fakeit;

template<typename R, typename ... arglist>
class MethodStubbingContextImpl : public MethodMockingContext<R, arglist...>::Context {
MockImpl<C, baseclasses...>& _mock;
R(C::*_vMethod)(arglist...);

RecordedMethodBody<C, R, arglist...>& getRecordedMethodBody() {
return _mock.stubMethodIfNotStubbed(_mock._proxy, _vMethod);
}

MethodStubbingContextImpl(MockImpl<C, baseclasses...>& mock, R (C::*vMethod)(arglist...)) :
public:
virtual ~MethodStubbingContextImpl() = default;

MethodStubbingContextImpl(MockImpl<C, baseclasses...>& mock, R(C::*vMethod)(arglist...)) :
_mock(mock), _vMethod(vMethod) {
}

ActualInvocationsSource& getInvolvedMock(){
return _mock;
}

virtual std::function<R(arglist&...)> getOriginalMethod() override {
void * mPtr = _mock.getOriginalMethod(_vMethod);
C& instance = _mock.get();
return [=, &instance](arglist&... args)->R{
auto m = union_cast<decltype(_vMethod)>(mPtr);
return ((&instance)->*m)(args...);
};
}

std::string getMethodName(){
return getRecordedMethodBody().getMethod().name();
}

void addMethodInvocationHandler(typename ActualInvocation<arglist...>::Matcher* matcher,
MethodInvocationHandler<R, arglist...>* invocationHandler){
getRecordedMethodBody().addMethodInvocationHandler(matcher, invocationHandler);
}

void scanActualInvocations(const std::function<void(ActualInvocation<arglist...>&)>& scanner) {
getRecordedMethodBody().scanActualInvocations(scanner);
}

void setMethodDetails(std::string mockName, std::string methodName){
getRecordedMethodBody().setMethodDetails(mockName, methodName);
}

bool isOfMethod(Method& method){
return getRecordedMethodBody().isOfMethod(method);
}
};

static MockImpl<C, baseclasses...>* getMockImpl(void * instance) {
FakeObject<C, baseclasses...>* fake = reinterpret_cast<FakeObject<C, baseclasses...>*>(instance);
MockImpl<C, baseclasses...> * mock = reinterpret_cast<MockImpl<C, baseclasses...>*>(fake->getVirtualTable().getCookie(1));
return mock;
}

ActualInvocationsSource& getInvolvedMock(){
return _mock;
}
void unmocked() {

virtual std::function<R(arglist&...)> getOriginalMethod() override {
void * mPtr = _mock.getOriginalMethod(_vMethod);
C& instance = _mock.get();
return [=, &instance](arglist&... args)->R{
auto m = union_cast<decltype(_vMethod)>(mPtr);
return ((&instance)->*m)(args...);
};
}
ActualInvocation<> invocation(internal::nextInvocationOrdinal(), internal::UnknownMethod::instance());
UnexpectedMethodCallEvent event(UnexpectedType::Unmocked, invocation);
auto& fakeit = getMockImpl(this)->_fakeit;
fakeit.handle(event);

std::string getMethodName(){
return getRecordedMethodBody().getMethod().name();
std::string format = fakeit.format(event);
UnexpectedMethodCallException e(format);
throw e;
}

void addMethodInvocationHandler(typename ActualInvocation<arglist...>::Matcher* matcher,
MethodInvocationHandler<R, arglist...>* invocationHandler){
getRecordedMethodBody().addMethodInvocationHandler(matcher, invocationHandler);
static C* createFakeInstance() {
FakeObject<C, baseclasses...>* fake = new FakeObject<C, baseclasses...>();
//fake->initializeDataMembersArea();
void* unmockedMethodStubPtr = union_cast<void*>(&MockImpl<C, baseclasses...>::unmocked);
fake->getVirtualTable().initAll(unmockedMethodStubPtr);
return reinterpret_cast<C*>(fake);
}

void scanActualInvocations(const std::function<void(ActualInvocation<arglist...>&)>& scanner) {
getRecordedMethodBody().scanActualInvocations(scanner);
template<typename R, typename ... arglist>
void * getOriginalMethod(R(C::*vMethod)(arglist...)) {
auto vt = _proxy.getOriginalVT();
auto offset = VTUtils::getOffset(vMethod);
void * origMethodPtr = vt.getMethod(offset);
return origMethodPtr;
}

void setMethodDetails(std::string mockName,std::string methodName){
getRecordedMethodBody().setMethodDetails(mockName, methodName);
template<typename R, typename ... arglist>
RecordedMethodBody<C, R, arglist...>& stubMethodIfNotStubbed(DynamicProxy<C, baseclasses...> &proxy, R(C::*vMethod)(arglist...)) {
if (!proxy.isStubbed(vMethod)) {
proxy.stubMethod(vMethod, new RecordedMethodBody<C, R, arglist...>(*this, vMethod));
}
Destructable * d = proxy.getMethodMock(vMethod);
RecordedMethodBody<C, R, arglist...> * methodMock = dynamic_cast<RecordedMethodBody<C, R, arglist...> *>(d);
return *methodMock;
}

bool isOfMethod(Method& method){
return getRecordedMethodBody().isOfMethod(method);
MockImpl(FakeitContext& fakeit, C &obj, bool isSpy) :
_proxy{ obj }, _instance(&obj), _isOwner(!isSpy), _fakeit(fakeit) {
}
};

static MockImpl<C, baseclasses...>* getMockImpl(void * instance) {
FakeObject<C, baseclasses...>* fake = reinterpret_cast<FakeObject<C, baseclasses...>*>(instance);
MockImpl<C, baseclasses...> * mock = reinterpret_cast<MockImpl<C, baseclasses...>*>(fake->getVirtualTable().getCookie(1));
return mock;
}

void unmocked() {
ActualInvocation<> 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<C, baseclasses...>* fake = new FakeObject<C, baseclasses...>();
//fake->initializeDataMembersArea();
void* unmockedMethodStubPtr = union_cast<void*>(&MockImpl<C, baseclasses...>::unmocked);
fake->getVirtualTable().initAll(unmockedMethodStubPtr);
return reinterpret_cast<C*>(fake);
}

template<typename R, typename ... arglist>
void * getOriginalMethod(R (C::*vMethod)(arglist...)) {
auto vt = _proxy.getOriginalVT();
auto offset = VTUtils::getOffset(vMethod);
void * origMethodPtr = vt.getMethod(offset);
return origMethodPtr;
}

template<typename R, typename ... arglist>
RecordedMethodBody<C, R, arglist...>& stubMethodIfNotStubbed(DynamicProxy<C, baseclasses...> &proxy, R (C::*vMethod)(arglist...)) {
if (!proxy.isStubbed(vMethod)) {
proxy.stubMethod(vMethod, new RecordedMethodBody<C, R, arglist...>(*this, vMethod));
}
Destructable * d = proxy.getMethodMock(vMethod);
RecordedMethodBody<C, R, arglist...> * methodMock = dynamic_cast<RecordedMethodBody<C, R, arglist...> *>(d);
return *methodMock;
}

MockImpl(FakeitContext& fakeit, C &obj, bool isSpy) :
_proxy { obj }, _instance(&obj), _isSpy(isSpy), _fakeit(fakeit) {
}
};
}

#endif

0 comments on commit ef04d85

Please sign in to comment.