Skip to content
Merged
4 changes: 1 addition & 3 deletions examples/mock-injection/platformio.ini
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
[env:native]
platform = native
test_build_src = yes
build_flags = -std=gnu++17

build_flags = -std=c++11 -D USBCON
lib_deps = file://../../

# Use this instead of local file
#lib_deps = FabioBatSilva/ArduinoFake
2 changes: 1 addition & 1 deletion examples/mock-injection/test/test_my_service.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ void test_connect(void)
When(OverloadedMethod(ArduinoFake(Client), println, size_t(const char *))).AlwaysReturn();
When(OverloadedMethod(ArduinoFake(Client), connect, int(const char*, uint16_t))).Return(1);

Client * clientMock(ArduinoFakeInstance0(Client));
Client * clientMock(ArduinoFakeInstance(Client));

MyService service(clientMock);

Expand Down
4 changes: 1 addition & 3 deletions examples/wiring-blink/platformio.ini
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
[env:native]
platform = native
test_build_src = yes
build_flags = -std=gnu++17

build_flags = -std=c++11 -D USBCON
lib_deps = file://../../

#lib_deps = FabioBatSilva/ArduinoFake
2 changes: 1 addition & 1 deletion library.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ArduinoFake",
"version": "1.0.0",
"version": "2.0.0",
"keywords": "test, mock, fake, arduino",
"description": "Arduino mocking made easy.",
"repository": {
Expand Down
1 change: 0 additions & 1 deletion src/Arduino.h

This file was deleted.

177 changes: 20 additions & 157 deletions src/ArduinoFake.h
Original file line number Diff line number Diff line change
@@ -1,19 +1,13 @@
#pragma once
// clang-format off

#if !defined(UBRRH) && !defined(UBRR0H) && !defined(USBCON)
#define USBCON
#if !defined(USBCON)
#error Add "-D USBCON" to the compiler command line
#endif

#include <unordered_map>
#include <cstring>
#include <cstdint>
#include <stdexcept>
#if defined(abs)
#undef abs
#endif
#include <single_header/standalone/fakeit.hpp>

#include <Arduino.h>
#include <Stream.h>
#include <USBAPI.h>
Expand All @@ -23,38 +17,11 @@
#include <SPI.h>
#include <EEPROM.h>

#include "ArduinoFakeOverride.h"
#include "FunctionFake.h"

using PrintFake = Print;
using ClientFake = Client;
using StreamFake = Stream;
using SerialFake = Serial_;
using WireFake = TwoWire;
using SPIFake = SPIClass;
using EEPROMFake = EEPROMClass;

#define CONCAT2(x,y) x##y
#define CONCAT(x,y) CONCAT2(x,y)

// These map a Arduino class name (E.g. "SPIClass") to
// the corresponding fake member name (E.g. "SPI")
#define _ClazzToFakePrint() Print
#define _ClazzToFakeClient() Client
#define _ClazzToFakeStream() Stream
#define _ClazzToFakeSerial_() Serial
#define _ClazzToFakeTwoWire() Wire
#define _ClazzToFakeSPIClass() SPI
#define _ClazzToFakeEEPROMClass() EEPROM
#define _ClazzToFake(clazz) _ClazzToFake##clazz()

#define ArduinoFakeReset() \
getArduinoFakeContext()->Reset()

#define ArduinoFakeInstance0(mock) \
getArduinoFakeContext()->mock()

#define ArduinoFakeInstance(clazz) \
getArduinoFakeContext()->getFake(clazz)
/// @cond
// Implementation details

#define _ArduinoFakeGetMock(mock) \
getArduinoFakeContext()->_##mock
Expand All @@ -69,132 +36,28 @@ using EEPROMFake = EEPROMClass;
#define _ArduinoFakeGetPrint() _ArduinoFakeGetMock(Print)
#define _ArduinoFakeGet() _ArduinoFakeGetMock(Function)

#define ArduinoFake(mock) _ArduinoFakeGet##mock()

// Access fakeit::Mock<T>.get()
// There is no equivalent in fakeit since Mock is a template
struct IFake
{
// Ideally this have a templated return type, but then it can't be
// virtual :-(
// We need this to be virtual to store instances in a map.
virtual void* toFake(void) = 0;
};

template <class FakeT, typename BaseT = fakeit::Mock<FakeT>>
struct ArduinoFake_t : public BaseT, IFake
{
// Proxy to fake
template <class ArduinoT>
FakeT* getFake(ArduinoT *instance)
{
return static_cast<FakeT*>(toFake());
}

// Get the 'real' fake - the one that is actually mocked.
virtual void* toFake(void) override
{
return &fakeit::Mock<FakeT>::get();
}
};

// Maps from global instances to the equivalent IFake.
// E.g. Serial => SerialFake
//
// Required to respect inherited classes.
// E.g. Stream has 2 derived classes, Serial_ & TwoWire. Each has a global instance, Serial & Wire.
// We want to allow different mock implementations *of the same Stream method* for Serial_ & TwoWire
// and have the global instances use those different mocks.
class FakeOverride_t
{
public:
void Reset(void)
{
_mapping.clear();
}

IFake *getOverride(void *instance)
{
auto iter = _mapping.find(instance);
return iter==_mapping.end() ? nullptr : iter->second;
}

void setOverride(void *instance, IFake *override)
{
_mapping[instance] = override;
}
/// @endcond

private:
std::unordered_map<void*, IFake*> _mapping;
};
#define ArduinoFakeReset() \
getArduinoFakeContext()->Reset()

template <class FakeT, typename BaseT = ArduinoFake_t<FakeT>>
struct OverrideableArduinoFake_t : public BaseT
{
FakeOverride_t &_overrides;
#define ArduinoFakeInstance(mock, ...) \
_ArduinoFakeGetMock(mock).getFake(__VA_ARGS__)

OverrideableArduinoFake_t(FakeOverride_t &overrides)
: BaseT()
, _overrides(overrides)
{
}

template <class ArduinoT>
FakeT* getFake(ArduinoT *instance)
{
auto *pOverride = _overrides.getOverride(instance);
if (pOverride!=nullptr) {
return (FakeT*)pOverride->toFake();
}
return BaseT::getFake(instance);
}
};
#define ArduinoFake(mock) _ArduinoFakeGet##mock()

class ArduinoFakeContext
{
public:
FakeOverride_t _fakeOverrides;
fakeit::Mock<FunctionFake> _Function;
OverrideableArduinoFake_t<SerialFake> _Serial;
OverrideableArduinoFake_t<WireFake> _Wire;
OverrideableArduinoFake_t<StreamFake> _Stream;
OverrideableArduinoFake_t<ClientFake> _Client;
OverrideableArduinoFake_t<PrintFake> _Print;
OverrideableArduinoFake_t<SPIFake> _SPI;
OverrideableArduinoFake_t<EEPROMFake> _EEPROM;

#define _ArduinoFakeInstanceGetter1(mock) \
mock##Fake* mock() \
{ \
return &this->_##mock.get(); \
}

_ArduinoFakeInstanceGetter1(Print)
_ArduinoFakeInstanceGetter1(Stream)
_ArduinoFakeInstanceGetter1(Serial)
_ArduinoFakeInstanceGetter1(Wire)
_ArduinoFakeInstanceGetter1(Client)
_ArduinoFakeInstanceGetter1(Function)
_ArduinoFakeInstanceGetter1(SPI)
_ArduinoFakeInstanceGetter1(EEPROM)

#undef _ArduinoFakeInstanceGetter1

#define _ArduinoFakeInstanceGetter2(clazz) \
CONCAT(_ClazzToFake(clazz), Fake)* getFake(class clazz* instance) \
{ \
return this->CONCAT(_, _ClazzToFake(clazz)).getFake(instance); \
}

_ArduinoFakeInstanceGetter2(Print)
_ArduinoFakeInstanceGetter2(Client)
_ArduinoFakeInstanceGetter2(Stream)
_ArduinoFakeInstanceGetter2(Serial_)
_ArduinoFakeInstanceGetter2(TwoWire)
_ArduinoFakeInstanceGetter2(SPIClass)
_ArduinoFakeInstanceGetter2(EEPROMClass)

#undef _ArduinoFakeInstanceGetter2
ArduinoFake::details::FakeOverride_t _fakeOverrides;
ArduinoFake::details::ArduinoFake_t<ArduinoFake::details::FunctionFake> _Function;
ArduinoFake::details::OverrideableArduinoFake_t<Serial_> _Serial;
ArduinoFake::details::OverrideableArduinoFake_t<TwoWire> _Wire;
ArduinoFake::details::OverrideableArduinoFake_t<Stream> _Stream;
ArduinoFake::details::OverrideableArduinoFake_t<Client> _Client;
ArduinoFake::details::OverrideableArduinoFake_t<Print> _Print;
ArduinoFake::details::OverrideableArduinoFake_t<SPIClass> _SPI;
ArduinoFake::details::OverrideableArduinoFake_t<EEPROMClass> _EEPROM;

ArduinoFakeContext()
: _fakeOverrides()
Expand Down
103 changes: 103 additions & 0 deletions src/ArduinoFakeOverride.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/**
* @file
* @brief Support code for mocking global Arduino instances that form part of an inheritance hierarchy
*
* Required to respect inherited classes.
* E.g. Stream has 2 derived classes, Serial_ & TwoWire. Each has a *global* instance, Serial & Wire.
* We want to allow different mock implementations *of the same Stream method* for Serial_ & TwoWire
* and have the global instances use those different mocks.
*
*/
#pragma once

#include <unordered_map>
#if defined(abs)
#undef abs
#endif
#include <single_header/standalone/fakeit.hpp>

namespace ArduinoFake {

namespace details {

// Access fakeit::Mock<T>.get()
// There is no equivalent in fakeit since Mock is a template
struct IFake
{
// Ideally this have a templated return type, but then it can't be
// virtual :-(
// We need this to be virtual to store instances in a map.
virtual void* toFake(void) = 0;
};

template <class FakeT, typename BaseT = fakeit::Mock<FakeT>>
struct ArduinoFake_t : public BaseT, IFake
{
// Typed access to the mocked object
FakeT* getFake(void)
{
return &fakeit::Mock<FakeT>::get();
}

// Untyped access to the mocked object
virtual void* toFake(void) override
{
return getFake();
}
};

// Maps from global instances to the equivalent IFake.
//
// Required to respect inherited classes.
// E.g. Stream has 2 derived classes, Serial_ & TwoWire. Each has a global instance, Serial & Wire.
// We want to allow different mock implementations *of the same Stream method* for Serial_ & TwoWire
// and have the global instances use those different mocks.
class FakeOverride_t
{
public:
void Reset(void)
{
_mapping.clear();
}

IFake *getOverride(void *instance)
{
auto iter = _mapping.find(instance);
return iter==_mapping.end() ? nullptr : iter->second;
}

void setOverride(void *instance, IFake *override)
{
_mapping[instance] = override;
}

private:
std::unordered_map<void*, IFake*> _mapping;
};

template <class FakeT, typename BaseT = ArduinoFake_t<FakeT>>
struct OverrideableArduinoFake_t : public BaseT
{
FakeOverride_t &_overrides;

OverrideableArduinoFake_t(FakeOverride_t &overrides)
: BaseT()
, _overrides(overrides)
{
}

template <class ArduinoT>
FakeT* getFake(ArduinoT *instance)
{
auto *pOverride = _overrides.getOverride(instance);
if (pOverride!=nullptr) {
return (FakeT*)pOverride->toFake();
}
return getFake();
}

using BaseT::getFake;
};

}
}
Loading
Loading