Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow mocks to be moved. #334

Merged
merged 1 commit into from
May 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions include/fakeit/Mock.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@ namespace fakeit {
class Mock : public ActualInvocationsSource {
MockImpl<C, baseclasses...> impl;
public:
~Mock() override = default;

static_assert(std::is_polymorphic<C>::value, "Can only mock a polymorphic type");

Mock() : impl(Fakeit) {
Expand Down
16 changes: 14 additions & 2 deletions include/fakeit/MockImpl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,22 @@ namespace fakeit {

MockImpl(FakeitContext &fakeit)
: MockImpl<C, baseclasses...>(fakeit, *(createFakeInstance()), false){
FakeObject<C, baseclasses...> *fake = asFakeObject(_instanceOwner.get());
fake->getVirtualTable().setCookie(1, this);
_instanceOwner.get()->getVirtualTable().setCookie(1, this);
}

MockImpl(const MockImpl&) = delete;
MockImpl(MockImpl&& other) FAKEIT_NO_THROWS
: _instanceOwner(std::move(other._instanceOwner))
, _proxy(std::move(other._proxy))
, _fakeit(other._fakeit) {
if (isOwner()) {
_instanceOwner.get()->getVirtualTable().setCookie(1, this);
}
}

MockImpl& operator=(const MockImpl&) = delete;
MockImpl& operator=(MockImpl&&) = delete;

~MockImpl() FAKEIT_NO_THROWS override {
_proxy.detach();
}
Expand Down
57 changes: 31 additions & 26 deletions include/mockutils/DynamicProxy.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,8 @@ namespace fakeit {
}

public:
InvocationHandlers(
std::vector<std::shared_ptr<Destructible>> &methodMocks,
std::vector<unsigned int> &offsets) :
_methodMocks(methodMocks), _offsets(offsets) {
for (std::vector<unsigned int>::iterator it = _offsets.begin(); it != _offsets.end(); ++it)
{
*it = std::numeric_limits<int>::max();
}
InvocationHandlers(std::vector<std::shared_ptr<Destructible>> &methodMocks, std::vector<unsigned int> &offsets)
: _methodMocks(methodMocks), _offsets(offsets) {
}

Destructible *getInvocatoinHandlerPtrById(unsigned int id) override {
Expand All @@ -66,26 +60,39 @@ namespace fakeit {
static_assert(std::is_polymorphic<C>::value, "DynamicProxy requires a polymorphic type");

DynamicProxy(C &inst) :
instance(inst),
originalVtHandle(VirtualTable<C, baseclasses...>::getVTable(instance).createHandle()),
_instancePtr(&inst),
_methodMocks(VTUtils::getVTSize<C>()),
_offsets(VTUtils::getVTSize<C>()),
_offsets(VTUtils::getVTSize<C>(), std::numeric_limits<int>::max()),
_invocationHandlers(_methodMocks, _offsets) {
_cloneVt.copyFrom(originalVtHandle.restore());
_cloneVt.setCookie(InvocationHandlerCollection::VtCookieIndex, &_invocationHandlers);
getFake().setVirtualTable(_cloneVt);
_originalVt.copyFrom(VirtualTable<C, baseclasses...>::getVTable(*_instancePtr));
_originalVt.setCookie(InvocationHandlerCollection::VtCookieIndex, &_invocationHandlers);
getFake().swapVirtualTable(_originalVt);
}

void detach() {
getFake().setVirtualTable(originalVtHandle.restore());
DynamicProxy(const DynamicProxy&) = delete;
DynamicProxy(DynamicProxy&& other) FAKEIT_NO_THROWS
: _originalVt(std::move(other._originalVt))
, _methodMocks(std::move(other._methodMocks))
, _members(std::move(other._members))
, _offsets(std::move(other._offsets))
, _invocationHandlers(_methodMocks, _offsets) {
std::swap(_instancePtr, other._instancePtr);
VirtualTable<C, baseclasses...>::getVTable(*_instancePtr).setCookie(InvocationHandlerCollection::VtCookieIndex, &_invocationHandlers);
}

~DynamicProxy() {
_cloneVt.dispose();
DynamicProxy& operator=(const DynamicProxy&) = delete;
DynamicProxy& operator=(DynamicProxy&&) = delete;

~DynamicProxy() = default;

void detach() {
if (_instancePtr != nullptr) {
getFake().swapVirtualTable(_originalVt);
}
}

C &get() {
return instance;
return *_instancePtr;
}

void Reset() {
Expand All @@ -94,7 +101,7 @@ namespace fakeit {
_members = {};
_offsets = {};
_offsets.resize(VTUtils::getVTSize<C>());
_cloneVt.copyFrom(originalVtHandle.restore());
VirtualTable<C, baseclasses...>::getVTable(*_instancePtr).copyFrom(_originalVt);
}

void Clear()
Expand Down Expand Up @@ -170,8 +177,7 @@ namespace fakeit {
}

VirtualTable<C, baseclasses...> &getOriginalVT() {
VirtualTable<C, baseclasses...> &vt = originalVtHandle.restore();
return vt;
return _originalVt;
}

template<typename R, typename ... arglist>
Expand Down Expand Up @@ -206,17 +212,16 @@ namespace fakeit {

static_assert(sizeof(C) == sizeof(FakeObject<C, baseclasses...>), "This is a problem");

C &instance;
typename VirtualTable<C, baseclasses...>::Handle originalVtHandle; // avoid delete!! this is the original!
VirtualTable<C, baseclasses...> _cloneVt;
C* _instancePtr = nullptr;
VirtualTable<C, baseclasses...> _originalVt; // avoid delete!! this is the original!
//
std::vector<std::shared_ptr<Destructible>> _methodMocks;
std::vector<std::shared_ptr<Destructible>> _members;
std::vector<unsigned int> _offsets;
InvocationHandlers _invocationHandlers;

FakeObject<C, baseclasses...> &getFake() {
return reinterpret_cast<FakeObject<C, baseclasses...> &>(instance);
return reinterpret_cast<FakeObject<C, baseclasses...> &>(*_instancePtr);
}

void bind(const MethodProxy &methodProxy, Destructible *invocationHandler) {
Expand Down
9 changes: 2 additions & 7 deletions include/mockutils/FakeObject.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,6 @@ namespace fakeit
this->initializeDataMembersArea();
}

~FakeObject()
{
this->vtable.dispose();
}

void setMethod(unsigned int index, void* method)
{
this->vtable.setMethod(index, method);
Expand All @@ -73,9 +68,9 @@ namespace fakeit
return this->vtable;
}

void setVirtualTable(VirtualTable<C, BaseClasses...>& t)
void swapVirtualTable(VirtualTable<C, BaseClasses...>& t)
{
this->vtable = t;
std::swap(this->vtable, t);
}

void setDtor(void* dtor)
Expand Down
62 changes: 27 additions & 35 deletions include/mockutils/gcc/VirtualTable.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,16 @@
*/
#pragma once

#include <utility>

#ifndef __clang__

#include "mockutils/gcc/is_simple_inheritance_layout.hpp"

#endif

#include "mockutils/Macros.hpp"

namespace fakeit {

struct VirtualTableBase {
Expand All @@ -24,6 +28,26 @@ namespace fakeit {

VirtualTableBase(void **firstMethod) : _firstMethod(firstMethod) { }

VirtualTableBase(const VirtualTableBase&) = delete;
VirtualTableBase(VirtualTableBase&& other) FAKEIT_NO_THROWS {
std::swap(_firstMethod, other._firstMethod);
}

VirtualTableBase& operator=(const VirtualTableBase&) = delete;
VirtualTableBase& operator=(VirtualTableBase&& other) FAKEIT_NO_THROWS {
std::swap(_firstMethod, other._firstMethod);
return *this;
}

~VirtualTableBase() {
if (_firstMethod != nullptr) {
_firstMethod--; // type_info
_firstMethod--; // top_offset
_firstMethod -= numOfCookies; // skip cookies
delete[] _firstMethod;
}
}

void *getCookie(int index) {
return _firstMethod[-3 - index];
}
Expand All @@ -41,7 +65,8 @@ namespace fakeit {
}

protected:
void **_firstMethod;
static const unsigned int numOfCookies = 2;
void **_firstMethod = nullptr;
};

template<class C, class ... baseclasses>
Expand All @@ -51,23 +76,6 @@ namespace fakeit {
static_assert(is_simple_inheritance_layout<C>::value, "Can't mock a type with multiple inheritance");
#endif

class Handle {

friend struct VirtualTable<C, baseclasses...>;
void **firstMethod;

Handle(void **method) :
firstMethod(method) {
}

public:

VirtualTable<C, baseclasses...> &restore() {
VirtualTable<C, baseclasses...> *vt = (VirtualTable<C, baseclasses...> *) this;
return *vt;
}
};

static VirtualTable<C, baseclasses...> &getVTable(C &instance) {
fakeit::VirtualTable<C, baseclasses...> *vt = (fakeit::VirtualTable<C, baseclasses...> *) (&instance);
return *vt;
Expand All @@ -85,25 +93,17 @@ namespace fakeit {
VirtualTable(buildVTArray()) {
}

void dispose() {
_firstMethod--; // type_info
_firstMethod--; // top_offset
_firstMethod -= numOfCookies; // skip cookies
delete[] _firstMethod;
}

unsigned int dtor(int) {
C *c = (C *) this;
C &cRef = *c;
auto vt = VirtualTable<C, baseclasses...>::getVTable(cRef);
auto& vt = VirtualTable<C, baseclasses...>::getVTable(cRef);
unsigned int index = VTUtils::getDestructorOffset<C>();
void *dtorPtr = vt.getMethod(index);
void(*method)(C *) = union_cast<void (*)(C *)>(dtorPtr);
method(c);
return 0;
}


void setDtor(void *method) {
unsigned int index = VTUtils::getDestructorOffset<C>();
void *dtorPtr = union_cast<void *>(&VirtualTable<C, baseclasses...>::dtor);
Expand All @@ -114,7 +114,6 @@ namespace fakeit {
_firstMethod[index + 1] = dtorPtr;
}


unsigned int getSize() {
return VTUtils::getVTSize<C>();
}
Expand All @@ -130,14 +129,7 @@ namespace fakeit {
return (const std::type_info *) (_firstMethod[-1]);
}

Handle createHandle() {
Handle h(_firstMethod);
return h;
}

private:
static const unsigned int numOfCookies = 2;

static void **buildVTArray() {
int size = VTUtils::getVTSize<C>();
auto array = new void *[size + 2 + numOfCookies]{};
Expand Down
54 changes: 24 additions & 30 deletions include/mockutils/mscpp/VirtualTable.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
*/
#pragma once

#include <utility>
#include <typeinfo>

#include "mockutils/Macros.hpp"

namespace fakeit {

typedef unsigned long dword_;
Expand Down Expand Up @@ -167,22 +172,6 @@ namespace fakeit {
template<class C, class... baseclasses>
struct VirtualTable : public VirtualTableBase {

class Handle {

friend struct VirtualTable<C, baseclasses...>;

void **firstMethod;

Handle(void **method) : firstMethod(method) { }

public:

VirtualTable<C, baseclasses...> &restore() {
VirtualTable<C, baseclasses...> *vt = (VirtualTable<C, baseclasses...> *) this;
return *vt;
}
};

static VirtualTable<C, baseclasses...> &getVTable(C &instance) {
fakeit::VirtualTable<C, baseclasses...> *vt = (fakeit::VirtualTable<C, baseclasses...> *) (&instance);
return *vt;
Expand All @@ -200,23 +189,33 @@ namespace fakeit {
VirtualTable() : VirtualTable(buildVTArray()) {
}

~VirtualTable() {
VirtualTable(const VirtualTable&) = delete;
VirtualTable(VirtualTable&& other) FAKEIT_NO_THROWS
: VirtualTableBase(nullptr) {
std::swap(_firstMethod, other._firstMethod);
}

VirtualTable& operator=(const VirtualTable&) = delete;
VirtualTable& operator=(VirtualTable&& other) FAKEIT_NO_THROWS {
std::swap(_firstMethod, other._firstMethod);
return *this;
}

void dispose() {
_firstMethod--; // skip objectLocator
RTTICompleteObjectLocator<C, baseclasses...> *locator = (RTTICompleteObjectLocator<C, baseclasses...> *) _firstMethod[0];
delete locator;
_firstMethod -= numOfCookies; // skip cookies
delete[] _firstMethod;
~VirtualTable() {
if (_firstMethod != nullptr) {
_firstMethod--; // skip objectLocator
RTTICompleteObjectLocator<C, baseclasses...> *locator = (RTTICompleteObjectLocator<C, baseclasses...> *) _firstMethod[0];
delete locator;
_firstMethod -= numOfCookies; // skip cookies
delete[] _firstMethod;
}
}

// the dtor VC++ must of the format: int dtor(int)
unsigned int dtor(int) {
C *c = (C *) this;
C &cRef = *c;
auto vt = VirtualTable<C, baseclasses...>::getVTable(cRef);
auto& vt = VirtualTable<C, baseclasses...>::getVTable(cRef);
void *dtorPtr = vt.getCookie(dtorCookieIndex);
void(*method)(C *) = reinterpret_cast<void (*)(C *)>(dtorPtr);
method(c);
Expand Down Expand Up @@ -246,18 +245,13 @@ namespace fakeit {
}
}

Handle createHandle() {
Handle h(_firstMethod);
return h;
}

private:

class SimpleType {
};

static_assert(sizeof(unsigned int (SimpleType::*)()) == sizeof(unsigned int (C::*)()),
"Can't mock a type with multiple inheritance or with non-polymorphic base class");

static const unsigned int numOfCookies = 3;
static const unsigned int dtorCookieIndex = numOfCookies - 1; // use the last cookie

Expand Down
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ add_executable(FakeIt_tests
gcc_type_info_tests.cpp
miscellaneous_tests.cpp
move_only_return_tests.cpp
moving_mocks_around.cpp
msc_stubbing_multiple_values_tests.cpp
msc_type_info_tests.cpp
overloadded_methods_tests.cpp
Expand Down
Loading
Loading