diff --git a/.gitignore b/.gitignore index 9465b8b..55bb629 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *~ src/my_credentials.h *.o +*.swp *.out diff --git a/src/StringIndexer.h b/src/StringIndexer.h index 0c45f2a..dc3777c 100644 --- a/src/StringIndexer.h +++ b/src/StringIndexer.h @@ -15,6 +15,16 @@ class StringIndexer std::string str; uint8_t used=0; friend class StringIndexer; + + #if EPOXY_DUINO + public: + // Workaround to avoid coredump in Indexer::release + // when destroying a Topic after the deletion of + // StringIndexer::strings map (which can occurs only with AUnit, + // never in the ESP itself, because ESP never ends) + // (I hate static vars) + ~StringCounter() { used=255; } + #endif }; public: using index_t=uint8_t; diff --git a/src/TinyMqtt.cpp b/src/TinyMqtt.cpp index 9e3f3ce..8f100d3 100644 --- a/src/TinyMqtt.cpp +++ b/src/TinyMqtt.cpp @@ -30,8 +30,8 @@ MqttClient::MqttClient(MqttBroker* parent, WiFiClient& new_client) alive = millis()+5000; // client expires after 5s if no CONNECT msg } -MqttClient::MqttClient(MqttBroker* parent) - : parent(parent) +MqttClient::MqttClient(MqttBroker* parent, const std::string& id) + : parent(parent), clientId(id) { client = nullptr; diff --git a/src/TinyMqtt.h b/src/TinyMqtt.h index a558369..5385e61 100644 --- a/src/TinyMqtt.h +++ b/src/TinyMqtt.h @@ -120,9 +120,8 @@ class MqttClient FlagReserved = 1 }; public: - MqttClient(MqttBroker*); - MqttClient(MqttBroker* brk, const std::string& id) : MqttClient(brk) { clientId=id; } - MqttClient() : MqttClient(nullptr) {}; + MqttClient(MqttBroker* brk = nullptr, const std::string& id=""); + MqttClient(const std::string& id) : MqttClient(nullptr, id){} ~MqttClient(); @@ -158,11 +157,10 @@ class MqttClient void dump() { uint32_t ms=millis(); - Serial << "MqttClient (" << clientId.c_str() << ") p=" << (uint32_t) parent - << " c=" << (uint32_t)client << (connected() ? " ON " : " OFF"); - Serial << ", alive=" << (uint32_t)alive << '/' << ms << ", ka=" << keep_alive; + Serial << "MqttClient (" << clientId.c_str() << ") " << (connected() ? " ON " : " OFF"); + Serial << ", alive=" << alive << '/' << ms << ", ka=" << keep_alive; Serial << (client && client->connected() ? "" : "dis") << "connected"; - message.hexdump("entrant msg"); + message.hexdump("entrant msg"); bool c=false; Serial << " ["; for(auto s: subscriptions) diff --git a/tests/local-tests/Makefile b/tests/local-tests/Makefile new file mode 100644 index 0000000..d6ab7a3 --- /dev/null +++ b/tests/local-tests/Makefile @@ -0,0 +1,6 @@ +# See https://github.com/bxparks/EpoxyDuino for documentation about this +# Makefile to compile and run Arduino programs natively on Linux or MacOS. + +APP_NAME := local-tests +ARDUINO_LIBS := AUnit AceCommon AceTime TinyMqtt EspMock +include ../../../EpoxyDuino/EpoxyDuino.mk diff --git a/tests/local-tests/local-tests.ino b/tests/local-tests/local-tests.ino new file mode 100644 index 0000000..7415f58 --- /dev/null +++ b/tests/local-tests/local-tests.ino @@ -0,0 +1,146 @@ +#include +#include +#include + +/** + * TinyMqtt local unit tests. + * + * Clients are connected to pseudo remote broker + * The remote will be 127.0.0.1:1883 + * We are using 127.0.0.1 because this is simpler to test with a single ESP + * Also, this will allow to mock and thus run Action on github + **/ + +using namespace std; + +MqttBroker broker(1883); + +std::map> published; // map[client_id] => map[topic] = count + +void onPublish(const MqttClient* srce, const Topic& topic, const char* payload, size_t length) +{ + if (srce) + published[srce->id()][topic]++; +} + +test(local_client_should_unregister_when_destroyed) +{ + assertEqual(broker.clientsCount(), (size_t)0); + { + MqttClient client; + assertEqual(broker.clientsCount(), (size_t)0); // Ensure client is not yet connected + client.connect("127.0.0.1", 1883); + assertEqual(broker.clientsCount(), (size_t)1); // Ensure client is now connected + } + assertEqual(broker.clientsCount(), (size_t)0); +} + +#if 0 +test(local_connect) +{ + assertEqual(broker.clientsCount(), (size_t)0); + + MqttClient client; + assertTrue(client.connected()); + assertEqual(broker.clientsCount(), (size_t)1); +} + +test(local_publish_should_be_dispatched) +{ + published.clear(); + assertEqual(broker.clientsCount(), (size_t)0); + + MqttClient subscriber; + subscriber.subscribe("a/b"); + subscriber.subscribe("a/c"); + subscriber.setCallback(onPublish); + + MqttClient publisher; + publisher.publish("a/b"); + publisher.publish("a/c"); + publisher.publish("a/c"); + + assertEqual(published.size(), (size_t)1); // 1 client has received something + assertTrue(published[""]["a/b"] == 1); + assertTrue(published[""]["a/c"] == 2); +} + +test(local_publish_should_be_dispatched_to_local_clients) +{ + published.clear(); + assertEqual(broker.clientsCount(), (size_t)0); + + MqttClient subscriber_a("A"); + subscriber_a.setCallback(onPublish); + subscriber_a.subscribe("a/b"); + subscriber_a.subscribe("a/c"); + + MqttClient subscriber_b("B"); + subscriber_b.setCallback(onPublish); + subscriber_b.subscribe("a/b"); + + MqttClient publisher; + publisher.publish("a/b"); + publisher.publish("a/c"); + + assertEqual(published.size(), (size_t)2); // 2 clients have received something + assertTrue(published["A"]["a/b"] == 1); + assertTrue(published["A"]["a/c"] == 1); + assertTrue(published["B"]["a/b"] == 1); + assertTrue(published["B"]["a/c"] == 0); +} + +test(local_unsubscribe) +{ + published.clear(); + assertEqual(broker.clientsCount(), (size_t)0); + + MqttClient subscriber; + subscriber.setCallback(onPublish); + subscriber.subscribe("a/b"); + + MqttClient publisher; + publisher.publish("a/b"); + + subscriber.unsubscribe("a/b"); + + publisher.publish("a/b"); + publisher.publish("a/b"); + + assertTrue(published[""]["a/b"] == 1); // Only one publish has been received +} + +test(local_nocallback_when_destroyed) +{ + published.clear(); + assertEqual(broker.clientsCount(), (size_t)0); + + MqttClient publisher; + { + MqttClient subscriber; + subscriber.setCallback(onPublish); + subscriber.subscribe("a/b"); + publisher.publish("a/b"); + } + + publisher.publish("a/b"); + + assertEqual(published.size(), (size_t)1); // Only one publish has been received +} +#endif + +//---------------------------------------------------------------------------- +// setup() and loop() +void setup() { + delay(1000); + Serial.begin(115200); + while(!Serial); + + Serial.println("=============[ NO WIFI CONNECTION TinyMqtt TESTS ]========================"); +} + +void loop() { + aunit::TestRunner::run(); + + if (Serial.available()) ESP.reset(); +} diff --git a/tests/nowifi-tests/nowifi-tests.ino b/tests/nowifi-tests/nowifi-tests.ino index 3bce9c4..b4d1cc7 100644 --- a/tests/nowifi-tests/nowifi-tests.ino +++ b/tests/nowifi-tests/nowifi-tests.ino @@ -3,9 +3,10 @@ #include /** - * TinyMqtt local unit tests. + * TinyMqtt nowifi unit tests. * * No wifi connection unit tests. + * Checks with a local broker. Clients must connect to the local client **/ using namespace std; @@ -20,7 +21,7 @@ void onPublish(const MqttClient* srce, const Topic& topic, const char* payload, published[srce->id()][topic]++; } -test(local_client_should_unregister_when_destroyed) +test(nowifi_client_should_unregister_when_destroyed) { assertEqual(broker.clientsCount(), (size_t)0); { @@ -30,7 +31,7 @@ test(local_client_should_unregister_when_destroyed) assertEqual(broker.clientsCount(), (size_t)0); } -test(local_connect) +test(nowifi_connect) { assertEqual(broker.clientsCount(), (size_t)0); @@ -39,7 +40,7 @@ test(local_connect) assertEqual(broker.clientsCount(), (size_t)1); } -test(local_publish_should_be_dispatched) +test(nowifi_publish_should_be_dispatched) { published.clear(); assertEqual(broker.clientsCount(), (size_t)0); @@ -59,7 +60,7 @@ test(local_publish_should_be_dispatched) assertTrue(published[""]["a/c"] == 2); } -test(local_publish_should_be_dispatched_to_local_clients) +test(nowifi_publish_should_be_dispatched_to_nowifi_clients) { published.clear(); assertEqual(broker.clientsCount(), (size_t)0); @@ -84,7 +85,7 @@ test(local_publish_should_be_dispatched_to_local_clients) assertTrue(published["B"]["a/c"] == 0); } -test(local_unsubscribe) +test(nowifi_unsubscribe) { published.clear(); assertEqual(broker.clientsCount(), (size_t)0); @@ -104,21 +105,23 @@ test(local_unsubscribe) assertTrue(published[""]["a/b"] == 1); // Only one publish has been received } -test(local_nocallback_when_destroyed) +test(nowifi_nocallback_when_destroyed) { published.clear(); assertEqual(broker.clientsCount(), (size_t)0); + MqttClient publisher(&broker); + { MqttClient subscriber(&broker); subscriber.setCallback(onPublish); subscriber.subscribe("a/b"); + publisher.publish("a/b"); } - MqttClient publisher(&broker); publisher.publish("a/b"); - assertEqual(published.size(), (size_t)0); // Only one publish has been received + assertEqual(published.size(), (size_t)1); // Only one publish has been received } //---------------------------------------------------------------------------- diff --git a/tests/result.json b/tests/result.json new file mode 100644 index 0000000..fda2614 --- /dev/null +++ b/tests/result.json @@ -0,0 +1,6 @@ +{ + "schemaVersion" : 1, + "label" : "tests", + "message" : "Message content", + "color": "red" +} diff --git a/tests/result.yaml b/tests/result.yaml new file mode 100644 index 0000000..3d352b4 --- /dev/null +++ b/tests/result.yaml @@ -0,0 +1,2 @@ +result: 1 +insert: "passed"