diff --git a/Makefile b/Makefile index a283e22..5cd44e2 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ build: cmake .PHONY: test test: - @cd $(CURDIR)/build && CTEST_OUTPUT_ON_FAILURE=TRUE make test + @cd $(CURDIR)/build && ctest --verbose .PHONY: pio-test pio-test: diff --git a/src/ArduinoFake.cpp b/src/ArduinoFake.cpp index 8678cbd..1b7be17 100644 --- a/src/ArduinoFake.cpp +++ b/src/ArduinoFake.cpp @@ -1,12 +1,7 @@ #include "ArduinoFake.h" -ArduinoFakeContext* arduinoFakeContext; - ArduinoFakeContext* getArduinoFakeContext() { - if (!arduinoFakeContext) { - arduinoFakeContext = new ArduinoFakeContext(); - } - - return arduinoFakeContext; + static ArduinoFakeContext arduinoFakeContext; + return &arduinoFakeContext; } diff --git a/src/ArduinoFake.h b/src/ArduinoFake.h index 7eaa22b..e6bdd05 100644 --- a/src/ArduinoFake.h +++ b/src/ArduinoFake.h @@ -22,10 +22,8 @@ #include "SPI.h" #include "EEPROM.h" -#define ArduinoFake(mock) _ArduinoFakeGet##mock() - #define ArduinoFakeReset() \ - getArduinoFakeContext()->reset() + getArduinoFakeContext()->Reset() #define ArduinoFakeInstance(mock, ...) \ getArduinoFakeContext()->mock(__VA_ARGS__) @@ -34,7 +32,7 @@ new mock##FakeProxy(ArduinoFakeInstance(mock)) #define _ArduinoFakeGetMock(mock) \ - getArduinoFakeContext()->Mocks->mock + getArduinoFakeContext()->_##mock #define _ArduinoFakeGetFunction() _ArduinoFakeGetMock(Function) #define _ArduinoFakeGetSerial() _ArduinoFakeGetMock(Serial) @@ -46,101 +44,144 @@ #define _ArduinoFakeGetPrint() _ArduinoFakeGetMock(Print) #define _ArduinoFakeGet() _ArduinoFakeGetMock(Function) -#define _ArduinoFakeInstanceGetter1(mock) \ - mock##Fake* mock() \ - { \ - if (!this->Instances->mock){ \ - this->Instances->mock = &this->Mocks->mock.get(); \ - } \ - return this->Instances->mock; \ - } +#define ArduinoFake(mock) _ArduinoFakeGet##mock() -#define _ArduinoFakeInstanceGetter2(name, clazz) \ - name##Fake* name(class clazz* instance) \ - { \ - if (Mapping[instance]) { \ - return (name##Fake*) Mapping[instance]; \ - } \ - if (dynamic_cast(instance)) { \ - return dynamic_cast(instance)->get##name##Fake(); \ - } \ - throw std::runtime_error("Unknown instance"); \ +template > +struct ProxiedArduinoFake_t : public BaseT +{ + template + FakeT* getFake(ArduinoT *instance) + { + if (dynamic_cast(instance)) { + return dynamic_cast(instance)->getFake(); + } + throw std::runtime_error("Unknown instance"); } +}; -struct ArduinoFakeMocks +class FakeOverride_t { - fakeit::Mock Function; - fakeit::Mock Serial; - fakeit::Mock Wire; - fakeit::Mock Stream; - fakeit::Mock Client; - fakeit::Mock Print; - fakeit::Mock SPI; - fakeit::Mock EEPROM; +public: + void Reset(void) + { + _mapping.clear(); + } + + void *getOverride(void *instance) + { + auto iter = _mapping.find(instance); + return iter==_mapping.end() ? nullptr : iter->second; + } + + void setOverride(void *instance, void *override) + { + _mapping[instance] = override; + } + +private: + std::unordered_map _mapping; }; -struct ArduinoFakeInstances +template > +struct OverrideableProxiedArduinoFake_t : public BaseT { - FunctionFake* Function; - SerialFake* Serial; - WireFake* Wire; - StreamFake* Stream; - ClientFake* Client; - PrintFake* Print; - SPIFake* SPI; - EEPROMFake* EEPROM; + FakeOverride_t &_overrides; + + OverrideableProxiedArduinoFake_t(FakeOverride_t &overrides) + : BaseT() + , _overrides(overrides) + { + } + + template + FakeT* getFake(ArduinoT *instance) + { + fakeit::Mock *pOverride = static_cast *>(_overrides.getOverride(instance)); + if (pOverride!=nullptr) { + return &pOverride->get(); + } + return BaseT::getFake(instance); + } }; class ArduinoFakeContext { - public: - ArduinoFakeInstances* Instances = new ArduinoFakeInstances(); - ArduinoFakeMocks* Mocks = new ArduinoFakeMocks(); - std::unordered_map Mapping; - - _ArduinoFakeInstanceGetter1(Print) - _ArduinoFakeInstanceGetter1(Stream) - _ArduinoFakeInstanceGetter1(Serial) - _ArduinoFakeInstanceGetter1(Wire) - _ArduinoFakeInstanceGetter1(Client) - _ArduinoFakeInstanceGetter1(Function) - _ArduinoFakeInstanceGetter1(SPI) - _ArduinoFakeInstanceGetter1(EEPROM) - - _ArduinoFakeInstanceGetter2(Print, Print) - _ArduinoFakeInstanceGetter2(Client, Client) - _ArduinoFakeInstanceGetter2(Stream, Stream) - _ArduinoFakeInstanceGetter2(Serial, Serial_) - _ArduinoFakeInstanceGetter2(Wire, TwoWire) - _ArduinoFakeInstanceGetter2(SPI, SPIClass) - _ArduinoFakeInstanceGetter2(EEPROM, EEPROMClass) - - ArduinoFakeContext() - { - this->reset(); - } +public: + FakeOverride_t _fakeOverrides; + fakeit::Mock _Function; + OverrideableProxiedArduinoFake_t _Serial; + OverrideableProxiedArduinoFake_t _Wire; + OverrideableProxiedArduinoFake_t _Stream; + OverrideableProxiedArduinoFake_t _Client; + OverrideableProxiedArduinoFake_t _Print; + OverrideableProxiedArduinoFake_t _SPI; + OverrideableProxiedArduinoFake_t _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(name, clazz) \ + name##Fake* name(class clazz* instance) \ + { \ + return this->_##name.getFake(instance); \ + } + + _ArduinoFakeInstanceGetter2(Print, Print) + _ArduinoFakeInstanceGetter2(Client, Client) + _ArduinoFakeInstanceGetter2(Stream, Stream) + _ArduinoFakeInstanceGetter2(Serial, Serial_) + _ArduinoFakeInstanceGetter2(Wire, TwoWire) + _ArduinoFakeInstanceGetter2(SPI, SPIClass) + _ArduinoFakeInstanceGetter2(EEPROM, EEPROMClass) + +#undef _ArduinoFakeInstanceGetter2 + + ArduinoFakeContext() + : _fakeOverrides() + , _Function() + , _Serial(_fakeOverrides) + , _Wire(_fakeOverrides) + , _Stream(_fakeOverrides) + , _Client(_fakeOverrides) + , _Print(_fakeOverrides) + , _SPI(_fakeOverrides) + , _EEPROM(_fakeOverrides) + { + this->Reset(); + } + + void Reset(void) + { + _Function.Reset(); + _Serial.Reset(); + _Wire.Reset(); + _Stream.Reset(); + _Client.Reset(); + _Print.Reset(); + _SPI.Reset(); + _EEPROM.Reset(); + + _fakeOverrides.Reset(); + _fakeOverrides.setOverride(&::Serial, &_Serial); + _fakeOverrides.setOverride(&::Wire, &_Wire); + _fakeOverrides.setOverride(&::SPI, &_SPI); + _fakeOverrides.setOverride(&::EEPROM, &_EEPROM); + } - void reset(void) - { - if (this->Instances) { - delete this->Instances; - } - this->Instances = new ArduinoFakeInstances(); - - this->Mocks->Function.Reset(); - this->Mocks->Stream.Reset(); - this->Mocks->Serial.Reset(); - this->Mocks->Wire.Reset(); - this->Mocks->Client.Reset(); - this->Mocks->Print.Reset(); - this->Mocks->SPI.Reset(); - this->Mocks->EEPROM.Reset(); - - Mapping[&::Serial] = this->Serial(); - Mapping[&::Wire] = this->Wire(); - Mapping[&::SPI] = this->SPI(); - Mapping[&::EEPROM] = this->EEPROM(); - } }; ArduinoFakeContext* getArduinoFakeContext(); diff --git a/src/ClientFake.h b/src/ClientFake.h index 804e54f..dbfbce1 100644 --- a/src/ClientFake.h +++ b/src/ClientFake.h @@ -97,7 +97,7 @@ class ClientFakeProxy : public StreamFakeProxy, public Client virtual operator bool(); - ClientFake* getClientFake() + ClientFake* getFake() { return clientFake; } diff --git a/src/EEPROMFake.h b/src/EEPROMFake.h index bcb1b7a..3f58038 100644 --- a/src/EEPROMFake.h +++ b/src/EEPROMFake.h @@ -17,5 +17,5 @@ class EEPROMFakeProxy : public EEPROMClass { public: EEPROMFakeProxy(EEPROMFake *fake) { eepromFake = fake; } - EEPROMFake *getEEPROMFake() { return eepromFake; } + EEPROMFake *getFake() { return eepromFake; } }; diff --git a/src/PrintFake.h b/src/PrintFake.h index 6173f82..d0b7c0e 100644 --- a/src/PrintFake.h +++ b/src/PrintFake.h @@ -55,7 +55,7 @@ class PrintFakeProxy : public Print return printFake->write(value); } - PrintFake* getPrintFake() + PrintFake* getFake() { return printFake; } diff --git a/src/SPIFake.h b/src/SPIFake.h index 9e90f9a..4b1699e 100644 --- a/src/SPIFake.h +++ b/src/SPIFake.h @@ -22,5 +22,5 @@ class SPIFakeProxy : public SPIClass { public: SPIFakeProxy(SPIFake *fake) { spiFake = fake; } - SPIFake *getSPIFake() { return spiFake; } + SPIFake *getFake() { return spiFake; } }; diff --git a/src/SerialFake.h b/src/SerialFake.h index a4cc725..9941fe4 100644 --- a/src/SerialFake.h +++ b/src/SerialFake.h @@ -43,7 +43,7 @@ class SerialFakeProxy : public StreamFakeProxy, public Serial_ serialFake = fake; } - SerialFake* getSerialFake() + SerialFake* getFake() { return serialFake; } diff --git a/src/StreamFake.h b/src/StreamFake.h index 106bb54..65113ac 100644 --- a/src/StreamFake.h +++ b/src/StreamFake.h @@ -74,7 +74,7 @@ class StreamFakeProxy : public Stream, public PrintFakeProxy streamFake->flush(); } - StreamFake* getStreamFake() + StreamFake* getFake() { return streamFake; } diff --git a/src/WireFake.h b/src/WireFake.h index b076bf4..1b5b07f 100644 --- a/src/WireFake.h +++ b/src/WireFake.h @@ -37,5 +37,5 @@ class WireFakeProxy : public StreamFakeProxy, public TwoWire { public: WireFakeProxy(WireFake *fake) : StreamFakeProxy(fake) { wireFake = fake; } - WireFake *getWireFake() { return wireFake; } + WireFake *getFake() { return wireFake; } }; \ No newline at end of file diff --git a/test/main.cpp b/test/main.cpp index a308066..4ef2cb4 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -16,6 +16,8 @@ using namespace fakeit; #include "test_client.h" #include "test_arduino_string.h" #include "test_include.h" +#include "test_ProxiedArduinoFake_t.h" +#include "test_OverrideableProxiedArduinoFake_t.h" #ifdef UNIT_TEST @@ -45,10 +47,10 @@ int main(int argc, char **argv) RUN_TEST_GROUP(EEPROMTest); RUN_TEST_GROUP(ClientTest); RUN_TEST_GROUP(IncludeTest); + RUN_TEST_GROUP(ProxiedArduinoFakeTTest); + RUN_TEST_GROUP(OverrideableProxiedArduinoFakeTTest); - UNITY_END(); - - return 0; + return UNITY_END(); } #endif diff --git a/test/test_OverrideableProxiedArduinoFake_t.h b/test/test_OverrideableProxiedArduinoFake_t.h new file mode 100644 index 0000000..017386a --- /dev/null +++ b/test/test_OverrideableProxiedArduinoFake_t.h @@ -0,0 +1,44 @@ +#ifdef UNIT_TEST + +namespace OverrideableProxiedArduinoFakeTTest +{ + struct IDummy + { + virtual void foo(void) { } + }; + + struct IArduino + { + virtual void bar(void) {} + }; + + struct IDummyProxy : public IArduino + { + IDummy *_dummy; + IDummy* getFake(void) { return _dummy; } + + virtual void bar(void) override {} + }; + + void test_getFake(void) + { + FakeOverride_t overrides; + OverrideableProxiedArduinoFake_t subject(overrides); + + // No override, should get the proxy fake + IDummyProxy proxy; + TEST_ASSERT_EQUAL_PTR(proxy.getFake(), subject.getFake(&proxy)); + + // Should return the alternate, since it's now overriden + fakeit::Mock alternateFake; + overrides.setOverride(&proxy, &alternateFake); + TEST_ASSERT_EQUAL_PTR(&alternateFake.get(), subject.getFake(&proxy)); + } + + void run_tests(void) + { + RUN_TEST(test_getFake); + } +} + +#endif \ No newline at end of file diff --git a/test/test_ProxiedArduinoFake_t.h b/test/test_ProxiedArduinoFake_t.h new file mode 100644 index 0000000..3c49e1d --- /dev/null +++ b/test/test_ProxiedArduinoFake_t.h @@ -0,0 +1,54 @@ +#ifdef UNIT_TEST + +namespace ProxiedArduinoFakeTTest +{ + struct IDummy + { + virtual void foo(void) { } + }; + + struct IArduino + { + virtual void bar(void) {} + }; + + struct IDummyProxy : public IArduino + { + IDummy *_dummy; + IDummy* getFake(void) { return _dummy; } + + virtual void bar(void) override {} + }; + + void test_getFake(void) + { + ProxiedArduinoFake_t subject; + + IDummyProxy proxy; + + proxy._dummy = nullptr; + TEST_ASSERT_EQUAL_PTR(nullptr, subject.getFake(&proxy)); + + IDummy dummy; + proxy._dummy = &dummy; + TEST_ASSERT_EQUAL_PTR(&dummy, subject.getFake(&proxy)); + + // Following should throw exception + try + { + IArduino arduino; + TEST_ASSERT_EQUAL_PTR(&dummy, subject.getFake(&arduino)); + TEST_FAIL(); + } + catch(const std::runtime_error& e) + { + } + } + + void run_tests(void) + { + RUN_TEST(test_getFake); + } +} + +#endif \ No newline at end of file diff --git a/test/test_context.h b/test/test_context.h index 1ae3b02..3bf3450 100644 --- a/test/test_context.h +++ b/test/test_context.h @@ -13,14 +13,102 @@ namespace ArduinoContextTest TEST_ASSERT_EQUAL(context1, context2); } - void test_reset(void) + template + void assert_test_reset(FakeMethod fakeMethod, std::function realMethod) { - ArduinoFakeContext* context = getArduinoFakeContext(); - ArduinoFakeInstances* instances = context->Instances; + Verify(fakeMethod).Never(); + + // Call the "real" method... + realMethod(); + // ...which should call the faked method + Verify(fakeMethod).Once(); + // Reset all fakes ArduinoFakeReset(); + + try { + // This should throw an exception... + realMethod(); + // ...fail the test if not. + TEST_FAIL(); + } + catch (fakeit::FakeitException &e) { + Verify(fakeMethod).Never(); + } + } - TEST_ASSERT_NOT_EQUAL(context->Instances, instances); + void test_reset_function(void) + { + auto method = Method(ArduinoFake(Function), millis); + When(method).AlwaysReturn(101L); + assert_test_reset(method, []() { (void)millis(); }); + } + + void test_reset_print(void) + { + auto method = OverloadedMethod(ArduinoFake(Print), print, size_t(const char[])); + When(method).AlwaysDo([](const char *str) { + std::cout << str; + return strlen(str); + }); + assert_test_reset(method, []() { (ArduinoFakeMock(Print))->print("abc"); }); + } + + void test_reset_serial(void) + { + auto method = Method(ArduinoFake(Serial), end); + When(method).AlwaysReturn(); + assert_test_reset(method, []() { (ArduinoFakeMock(Serial))->end(); }); + } + + void test_reset_wire(void) + { + auto method = Method(ArduinoFake(Wire), end); + When(method).AlwaysReturn(); + assert_test_reset(method, []() { (ArduinoFakeMock(Wire))->end(); }); + } + + void test_reset_stream(void) + { + auto method = OverloadedMethod(ArduinoFake(Stream), find, bool(char *)); + When(method).AlwaysReturn(false); + assert_test_reset(method, []() { + char toFind[] = "abc"; + (ArduinoFakeMock(Stream))->find(toFind); + }); + } + + void test_reset_client(void) + { + auto method = Method(ArduinoFake(Client), available); + When(method).AlwaysReturn(); + assert_test_reset(method, []() { (ArduinoFakeMock(Client))->available(); }); + } + + void test_reset_spi(void) + { + auto method = Method(ArduinoFake(SPI), end); + When(method).AlwaysReturn(); + assert_test_reset(method, []() { (ArduinoFakeMock(SPI))->end(); }); + } + + void test_reset_eeprom(void) + { + auto method = Method(ArduinoFake(EEPROM), length); + When(method).AlwaysReturn(0xffff); + assert_test_reset(method, []() { (ArduinoFakeMock(EEPROM))->length(); }); + } + + void test_reset(void) + { + RUN_TEST(test_reset_function); + RUN_TEST(test_reset_serial); + RUN_TEST(test_reset_wire); + RUN_TEST(test_reset_stream); + RUN_TEST(test_reset_client); + RUN_TEST(test_reset_print); + RUN_TEST(test_reset_spi); + RUN_TEST(test_reset_eeprom); } void test_function_mock(void) @@ -125,7 +213,6 @@ namespace ArduinoContextTest void run_tests(void) { RUN_TEST(ArduinoContextTest::test_single_instance); - RUN_TEST(ArduinoContextTest::test_reset); RUN_TEST(ArduinoContextTest::test_function_mock); RUN_TEST(ArduinoContextTest::test_print_mock); RUN_TEST(ArduinoContextTest::test_stream_mock); @@ -133,6 +220,8 @@ namespace ArduinoContextTest RUN_TEST(ArduinoContextTest::test_getter_overload_with_proxy); RUN_TEST(ArduinoContextTest::test_getter_overload_with_mapping); RUN_TEST(ArduinoContextTest::test_unknown_instance_exception); + + ArduinoContextTest::test_reset(); } }