mockcpp is a generic C/C++ mock framework.
Copyright (C) <2009> <Darwin Yuan: darwin.yuan@gmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#include <iostream>
#include <string>
#include <testcpp/testcpp.hpp>
#include <mockcpp/VirtualTable.h>
#include <mockcpp/IndexInvokableGetter.h>
#include <mockcpp/Invokable.h>
#include <mockcpp/Exception.h>
#include <mockcpp/MethodInfoReader.h>
#include <mockcpp/DestructorChecker.h>
#include <mockcpp/ObjNameGetter.h>
USING_MOCKCPP_NS
struct IndexInvokableGetterStub : public IndexInvokableGetter
{
Invokable* getInvokable(unsigned int indexOfVtbl, unsigned int indexOfVptr = 0) const
{
return 0;
}
};
struct TestMethodHolder
{
long base11(bool p) const
{
return p?100:-100;
}
};
class TestVirtualTable: public TESTCPP_NS::TestFixture
{
struct Base0
{
virtual int base00() = 0;
virtual bool base01(int) const = 0;
virtual ~Base0() noexcept(false) {}
};
struct Base1
{
virtual void base10() = 0;
virtual long base11(bool) const = 0;
virtual ~Base1() noexcept(false) {}
};
struct Interface: public Base0, public Base1
{
virtual void a() {}
virtual void b(bool) {}
};
struct ThisObject : public ObjectNameGetter
{
ThisObject():name("object") {}
const std::string& getName() const
{ return name; }
const std::string name;
};
private:
TESTCPP_RCP checkpoint;
IndexInvokableGetterStub indexInvokableGetter;
VirtualTable* vtbl;
Interface* pI;
ThisObject object;
public:
void setUp()
{
checkpoint = TESTCPP_SET_RESOURCE_CHECK_POINT();
vtbl = new VirtualTable(&indexInvokableGetter
, &object
, sizeof(Interface)/sizeof(void*)
, typeid(Interface));
TS_ASSERT(vtbl != 0);
Interface* pI = (Interface*) vtbl->toPointerToInterface();
}
void tearDown()
{
pI = 0;
delete vtbl;
TESTCPP_VERIFY_RESOURCE_CHECK_POINT(checkpoint);
}
template <typename Method>
int getIndexOfMethod(Method m)
{
return MOCKCPP_NS::getIndexOfMethod<Interface, Method>(m);
}
template <typename Method>
int getDeltaOfMethod(Method m)
{
return MOCKCPP_NS::getDeltaOfMethod<Interface, Method>(m);
}
void shouldBeAbleToConvertToPointerToInterfaceType()
{
Interface* p = (Interface*) vtbl->toPointerToInterface();
TS_ASSERT_THROWS(p->a(), MOCKCPP_NS::Exception);
}
void shouldThrowExceptionIfTheNumberOfVptrExceedsTheMaxSettingOfConfiguration()
{
TS_ASSERT_THROWS( new VirtualTable(&indexInvokableGetter
, &object
, 10
, typeid(Interface)), MOCKCPP_NS::Exception);
}
void shouldBeAbleToSetMethod()
{
unsigned int indexOfVtbl = getIndexOfMethod(&Interface::base11);
unsigned int indexOfVptr = getDeltaOfMethod(&Interface::base11);
void* methodAddr = MOCKCPP_NS::getAddrOfMethod(&TestMethodHolder::base11);
vtbl->addMethod(methodAddr, indexOfVtbl, indexOfVptr);
Interface* p = (Interface*) vtbl->toPointerToInterface();
TestMethodHolder holder;
TS_ASSERT_EQUALS(p->base11(true), holder.base11(true));
TS_ASSERT_EQUALS(p->base11(false), holder.base11(false));
TS_ASSERT(p->base11(true) != p->base11(false));
}
void shouldThrowExceptionIfAMethodIsNotSet()
{
unsigned int indexOfVtbl = getIndexOfMethod(&Interface::base11);
unsigned int indexOfVptr = getDeltaOfMethod(&Interface::base11);
void* methodAddr = MOCKCPP_NS::getAddrOfMethod(&TestMethodHolder::base11);
vtbl->addMethod(methodAddr, indexOfVtbl, indexOfVptr);
Interface* p = (Interface*) vtbl->toPointerToInterface();
p->base11(true);
TS_ASSERT_THROWS(p->base00(), MOCKCPP_NS::Exception);
TS_ASSERT_THROWS(p->base01(0), MOCKCPP_NS::Exception);
TS_ASSERT_THROWS(p->base10(), MOCKCPP_NS::Exception);
TS_ASSERT_THROWS(p->a(), MOCKCPP_NS::Exception);
TS_ASSERT_THROWS(p->b(true), MOCKCPP_NS::Exception);
}
void shouldThrowExceptionIfIndexOfVtblExceedsTheLimitationOfConfiguration()
{
unsigned int indexOfVtbl = 20;
unsigned int indexOfVptr = getDeltaOfMethod(&Interface::base11);
void* methodAddr = MOCKCPP_NS::getAddrOfMethod(&TestMethodHolder::base11);
TS_ASSERT_THROWS(vtbl->addMethod(methodAddr, indexOfVtbl, indexOfVptr), MOCKCPP_NS::Exception);
}
void shouldThrowExceptionIfIndexOfVtblExceedsTheNumberOfVptr()
{
unsigned int indexOfVtbl = getIndexOfMethod(&Interface::base11);
unsigned int indexOfVptr = 5;
void* methodAddr = MOCKCPP_NS::getAddrOfMethod(&TestMethodHolder::base11);
TS_ASSERT_THROWS(vtbl->addMethod(methodAddr, indexOfVtbl, indexOfVptr), MOCKCPP_NS::Exception);
}
void shouldBeAbleToGetPreviouslySetIndexInvokableGetterByReturnedPointerToInterface()
{
unsigned int indexOfVtbl = getIndexOfMethod(&Interface::base11);
unsigned int indexOfVptr = getDeltaOfMethod(&Interface::base11);
void* methodAddr = MOCKCPP_NS::getAddrOfMethod(&TestMethodHolder::base11);
vtbl->addMethod(methodAddr, indexOfVtbl, indexOfVptr);
IndexInvokableGetter* getter = VirtualTable::getInvokableGetter(vtbl->toPointerToInterface(), 0);
TS_ASSERT_EQUALS(getter, &indexInvokableGetter);
}
void shouldThrowExceptionIfItExpectedToBeDeletedButActuallyNotWhenItIsVerified()
{
std::pair<unsigned int, unsigned int> indices = \
getIndexOfDestructor<Interface, Interface>();
vtbl->setDestructor(indices.first, indices.second);
vtbl->expectsBeingDeleted();
TS_ASSERT_THROWS(vtbl->verify(), Exception);
}
void shouldPassVerifyIfThePointerIsDeletedAsExpected()
{
std::pair<unsigned int, unsigned int> indices = \
getIndexOfDestructor<Interface, Interface>();
vtbl->setDestructor(indices.first, indices.second);
vtbl->expectsBeingDeleted();
Interface* p = (Interface*) vtbl->toPointerToInterface();
delete p;
TS_ASSERT_THROWS_NOTHING(vtbl->verify());
}
void shouldPassVerifyIfThePointerIsNotDeletedAsExpected()
{
std::pair<unsigned int, unsigned int> indices = \
getIndexOfDestructor<Interface, Interface>();
vtbl->setDestructor(indices.first, indices.second);
TS_ASSERT_THROWS_NOTHING(vtbl->verify());
}
void shouldFailThrowExceptionWhileTryingToDeleteThePointerWhichWasExpectedNotTo()
{
std::pair<unsigned int, unsigned int> indices = \
getIndexOfDestructor<Interface, Interface>();
vtbl->setDestructor(indices.first, indices.second);
vtbl->expectsKeepAlive();
Interface* p = (Interface*) vtbl->toPointerToInterface();
TS_ASSERT_THROWS(delete p, Exception);
}
void shouldThrowExceptionIfAnObjectIsExpectedBothKeepAliveAndBeingDelete()
{
std::pair<unsigned int, unsigned int> indices = \
getIndexOfDestructor<Interface, Interface>();
vtbl->setDestructor(indices.first, indices.second);
vtbl->expectsKeepAlive();
TS_ASSERT_THROWS(vtbl->expectsBeingDeleted(), Exception);
}
void shouldThrowExceptionIfAnObjectIsExpectedBothKeepAliveAndBeingDelete2()
{
std::pair<unsigned int, unsigned int> indices = \
getIndexOfDestructor<Interface, Interface>();
vtbl->setDestructor(indices.first, indices.second);
vtbl->expectsBeingDeleted();
TS_ASSERT_THROWS(vtbl->expectsKeepAlive(), Exception);
}
void shouldThrowExceptionWhenTryingToExpectsADeletedObjectKeepAlive()
{
std::pair<unsigned int, unsigned int> indices = \
getIndexOfDestructor<Interface, Interface>();
vtbl->setDestructor(indices.first, indices.second);
delete (Interface*) vtbl->toPointerToInterface();
TS_ASSERT_THROWS(vtbl->expectsKeepAlive(), Exception);
}
void shouldThrowAnExceptionWhenTryingToAccessMethodsOfADeleteObject()
{
std::pair<unsigned int, unsigned int> indices = \
getIndexOfDestructor<Interface, Interface>();
vtbl->setDestructor(indices.first, indices.second);
unsigned int indexOfVtbl = getIndexOfMethod(&Interface::base11);
unsigned int indexOfVptr = getDeltaOfMethod(&Interface::base11);
void* methodAddr = MOCKCPP_NS::getAddrOfMethod(&TestMethodHolder::base11);
vtbl->addMethod(methodAddr, indexOfVtbl, indexOfVptr);
Interface* p = (Interface*) vtbl->toPointerToInterface();
delete p;
TS_ASSERT_THROWS(VirtualTable::getInvokableGetter(vtbl->toPointerToInterface(), 0), Exception);
}
};