Added MQTT client, better speaker handling, fixed bug in FTP server, ...
This commit is contained in:
4
lib/pubsubclient/tests/.gitignore
vendored
Normal file
4
lib/pubsubclient/tests/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
.build
|
||||
tmpbin
|
||||
logs
|
||||
*.pyc
|
25
lib/pubsubclient/tests/Makefile
Normal file
25
lib/pubsubclient/tests/Makefile
Normal file
@ -0,0 +1,25 @@
|
||||
SRC_PATH=./src
|
||||
OUT_PATH=./bin
|
||||
TEST_SRC=$(wildcard ${SRC_PATH}/*_spec.cpp)
|
||||
TEST_BIN= $(TEST_SRC:${SRC_PATH}/%.cpp=${OUT_PATH}/%)
|
||||
VPATH=${SRC_PATH}
|
||||
SHIM_FILES=${SRC_PATH}/lib/*.cpp
|
||||
PSC_FILE=../src/PubSubClient.cpp
|
||||
CC=g++
|
||||
CFLAGS=-I${SRC_PATH}/lib -I../src
|
||||
|
||||
all: $(TEST_BIN)
|
||||
|
||||
${OUT_PATH}/%: ${SRC_PATH}/%.cpp ${PSC_FILE} ${SHIM_FILES}
|
||||
mkdir -p ${OUT_PATH}
|
||||
${CC} ${CFLAGS} $^ -o $@
|
||||
|
||||
clean:
|
||||
@rm -rf ${OUT_PATH}
|
||||
|
||||
test:
|
||||
@bin/connect_spec
|
||||
@bin/publish_spec
|
||||
@bin/receive_spec
|
||||
@bin/subscribe_spec
|
||||
@bin/keepalive_spec
|
93
lib/pubsubclient/tests/README.md
Normal file
93
lib/pubsubclient/tests/README.md
Normal file
@ -0,0 +1,93 @@
|
||||
# Arduino Client for MQTT Test Suite
|
||||
|
||||
This is a regression test suite for the `PubSubClient` library.
|
||||
|
||||
There are two parts:
|
||||
|
||||
- Tests that can be compiled and run on any machine
|
||||
- Tests that build the example sketches using the Arduino IDE
|
||||
|
||||
|
||||
It is a work-in-progress and is subject to complete refactoring as the whim takes
|
||||
me.
|
||||
|
||||
|
||||
## Local tests
|
||||
|
||||
These are a set of executables that can be run to test specific areas of functionality.
|
||||
They do not require a real Arduino to be attached, nor the use of the Arduino IDE.
|
||||
|
||||
The tests include a set of mock files to stub out the parts of the Arduino environment the library
|
||||
depends on.
|
||||
|
||||
### Dependencies
|
||||
|
||||
- g++
|
||||
|
||||
### Running
|
||||
|
||||
Build the tests using the provided `Makefile`:
|
||||
|
||||
$ make
|
||||
|
||||
This will create a set of executables in `./bin/`. Run each of these executables to test the corresponding functionality.
|
||||
|
||||
*Note:* the `connect_spec` and `keepalive_spec` tests involve testing keepalive timers so naturally take a few minutes to run through.
|
||||
|
||||
## Arduino tests
|
||||
|
||||
*Note:* INO Tool doesn't currently play nicely with Arduino 1.5. This has broken this test suite.
|
||||
|
||||
Without a suitable arduino plugged in, the test suite will only check the
|
||||
example sketches compile cleanly against the library.
|
||||
|
||||
With an arduino plugged in, each sketch that has a corresponding python
|
||||
test case is built, uploaded and then the tests run.
|
||||
|
||||
### Dependencies
|
||||
|
||||
- Python 2.7+
|
||||
- [INO Tool](http://inotool.org/) - this provides command-line build/upload of Arduino sketches
|
||||
|
||||
### Running
|
||||
|
||||
The test suite _does not_ run an MQTT server - it is assumed to be running already.
|
||||
|
||||
$ python testsuite.py
|
||||
|
||||
A summary of activity is printed to the console. More comprehensive logs are written
|
||||
to the `logs` directory.
|
||||
|
||||
### What it does
|
||||
|
||||
For each sketch in the library's `examples` directory, e.g. `mqtt_basic.ino`, the suite looks for a matching test case
|
||||
`testcases/mqtt_basic.py`.
|
||||
|
||||
The test case must follow these conventions:
|
||||
- sub-class `unittest.TestCase`
|
||||
- provide the class methods `setUpClass` and `tearDownClass` (TODO: make this optional)
|
||||
- all test method names begin with `test_`
|
||||
|
||||
The suite will call the `setUpClass` method _before_ uploading the sketch. This
|
||||
allows any test setup to be performed before the sketch runs - such as connecting
|
||||
a client and subscribing to topics.
|
||||
|
||||
|
||||
### Settings
|
||||
|
||||
The file `testcases/settings.py` is used to config the test environment.
|
||||
|
||||
- `server_ip` - the IP address of the broker the client should connect to (the broker port is assumed to be 1883).
|
||||
- `arduino_ip` - the IP address the arduino should use (when not testing DHCP).
|
||||
|
||||
Before each sketch is compiled, these values are automatically substituted in. To
|
||||
do this, the suite looks for lines that _start_ with the following:
|
||||
|
||||
byte server[] = {
|
||||
byte ip[] = {
|
||||
|
||||
and replaces them with the appropriate values.
|
||||
|
||||
|
||||
|
||||
|
302
lib/pubsubclient/tests/src/connect_spec.cpp
Normal file
302
lib/pubsubclient/tests/src/connect_spec.cpp
Normal file
@ -0,0 +1,302 @@
|
||||
#include "PubSubClient.h"
|
||||
#include "ShimClient.h"
|
||||
#include "Buffer.h"
|
||||
#include "BDDTest.h"
|
||||
#include "trace.h"
|
||||
|
||||
|
||||
byte server[] = { 172, 16, 0, 2 };
|
||||
|
||||
void callback(char* topic, byte* payload, unsigned int length) {
|
||||
// handle message arrived
|
||||
}
|
||||
|
||||
|
||||
int test_connect_fails_no_network() {
|
||||
IT("fails to connect if underlying client doesn't connect");
|
||||
ShimClient shimClient;
|
||||
shimClient.setAllowConnect(false);
|
||||
PubSubClient client(server, 1883, callback, shimClient);
|
||||
int rc = client.connect((char*)"client_test1");
|
||||
IS_FALSE(rc);
|
||||
int state = client.state();
|
||||
IS_TRUE(state == MQTT_CONNECT_FAILED);
|
||||
END_IT
|
||||
}
|
||||
|
||||
int test_connect_fails_on_no_response() {
|
||||
IT("fails to connect if no response received after 15 seconds");
|
||||
ShimClient shimClient;
|
||||
shimClient.setAllowConnect(true);
|
||||
PubSubClient client(server, 1883, callback, shimClient);
|
||||
int rc = client.connect((char*)"client_test1");
|
||||
IS_FALSE(rc);
|
||||
int state = client.state();
|
||||
IS_TRUE(state == MQTT_CONNECTION_TIMEOUT);
|
||||
END_IT
|
||||
}
|
||||
|
||||
int test_connect_properly_formatted() {
|
||||
IT("sends a properly formatted connect packet and succeeds");
|
||||
ShimClient shimClient;
|
||||
|
||||
shimClient.setAllowConnect(true);
|
||||
byte expectServer[] = { 172, 16, 0, 2 };
|
||||
shimClient.expectConnect(expectServer,1883);
|
||||
byte connect[] = {0x10,0x18,0x0,0x4,0x4d,0x51,0x54,0x54,0x4,0x2,0x0,0xf,0x0,0xc,0x63,0x6c,0x69,0x65,0x6e,0x74,0x5f,0x74,0x65,0x73,0x74,0x31};
|
||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
||||
|
||||
shimClient.expect(connect,26);
|
||||
shimClient.respond(connack,4);
|
||||
|
||||
PubSubClient client(server, 1883, callback, shimClient);
|
||||
int state = client.state();
|
||||
IS_TRUE(state == MQTT_DISCONNECTED);
|
||||
|
||||
int rc = client.connect((char*)"client_test1");
|
||||
IS_TRUE(rc);
|
||||
IS_FALSE(shimClient.error());
|
||||
|
||||
state = client.state();
|
||||
IS_TRUE(state == MQTT_CONNECTED);
|
||||
|
||||
END_IT
|
||||
}
|
||||
|
||||
int test_connect_properly_formatted_hostname() {
|
||||
IT("accepts a hostname");
|
||||
ShimClient shimClient;
|
||||
|
||||
shimClient.setAllowConnect(true);
|
||||
shimClient.expectConnect((char* const)"localhost",1883);
|
||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
||||
shimClient.respond(connack,4);
|
||||
|
||||
PubSubClient client((char* const)"localhost", 1883, callback, shimClient);
|
||||
int rc = client.connect((char*)"client_test1");
|
||||
IS_TRUE(rc);
|
||||
IS_FALSE(shimClient.error());
|
||||
|
||||
END_IT
|
||||
}
|
||||
|
||||
|
||||
int test_connect_fails_on_bad_rc() {
|
||||
IT("fails to connect if a bad return code is received");
|
||||
ShimClient shimClient;
|
||||
shimClient.setAllowConnect(true);
|
||||
byte connack[] = { 0x20, 0x02, 0x00, 0x01 };
|
||||
shimClient.respond(connack,4);
|
||||
|
||||
PubSubClient client(server, 1883, callback, shimClient);
|
||||
int rc = client.connect((char*)"client_test1");
|
||||
IS_FALSE(rc);
|
||||
|
||||
int state = client.state();
|
||||
IS_TRUE(state == 0x01);
|
||||
|
||||
END_IT
|
||||
}
|
||||
|
||||
int test_connect_non_clean_session() {
|
||||
IT("sends a properly formatted non-clean session connect packet and succeeds");
|
||||
ShimClient shimClient;
|
||||
|
||||
shimClient.setAllowConnect(true);
|
||||
byte expectServer[] = { 172, 16, 0, 2 };
|
||||
shimClient.expectConnect(expectServer,1883);
|
||||
byte connect[] = {0x10,0x18,0x0,0x4,0x4d,0x51,0x54,0x54,0x4,0x0,0x0,0xf,0x0,0xc,0x63,0x6c,0x69,0x65,0x6e,0x74,0x5f,0x74,0x65,0x73,0x74,0x31};
|
||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
||||
|
||||
shimClient.expect(connect,26);
|
||||
shimClient.respond(connack,4);
|
||||
|
||||
PubSubClient client(server, 1883, callback, shimClient);
|
||||
int state = client.state();
|
||||
IS_TRUE(state == MQTT_DISCONNECTED);
|
||||
|
||||
int rc = client.connect((char*)"client_test1",0,0,0,0,0,0,0);
|
||||
IS_TRUE(rc);
|
||||
IS_FALSE(shimClient.error());
|
||||
|
||||
state = client.state();
|
||||
IS_TRUE(state == MQTT_CONNECTED);
|
||||
|
||||
END_IT
|
||||
}
|
||||
|
||||
int test_connect_accepts_username_password() {
|
||||
IT("accepts a username and password");
|
||||
ShimClient shimClient;
|
||||
shimClient.setAllowConnect(true);
|
||||
|
||||
byte connect[] = { 0x10,0x24,0x0,0x4,0x4d,0x51,0x54,0x54,0x4,0xc2,0x0,0xf,0x0,0xc,0x63,0x6c,0x69,0x65,0x6e,0x74,0x5f,0x74,0x65,0x73,0x74,0x31,0x0,0x4,0x75,0x73,0x65,0x72,0x0,0x4,0x70,0x61,0x73,0x73};
|
||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
||||
shimClient.expect(connect,0x26);
|
||||
shimClient.respond(connack,4);
|
||||
|
||||
PubSubClient client(server, 1883, callback, shimClient);
|
||||
int rc = client.connect((char*)"client_test1",(char*)"user",(char*)"pass");
|
||||
IS_TRUE(rc);
|
||||
IS_FALSE(shimClient.error());
|
||||
|
||||
END_IT
|
||||
}
|
||||
|
||||
int test_connect_accepts_username_no_password() {
|
||||
IT("accepts a username but no password");
|
||||
ShimClient shimClient;
|
||||
shimClient.setAllowConnect(true);
|
||||
|
||||
byte connect[] = { 0x10,0x1e,0x0,0x4,0x4d,0x51,0x54,0x54,0x4,0x82,0x0,0xf,0x0,0xc,0x63,0x6c,0x69,0x65,0x6e,0x74,0x5f,0x74,0x65,0x73,0x74,0x31,0x0,0x4,0x75,0x73,0x65,0x72};
|
||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
||||
shimClient.expect(connect,0x20);
|
||||
shimClient.respond(connack,4);
|
||||
|
||||
PubSubClient client(server, 1883, callback, shimClient);
|
||||
int rc = client.connect((char*)"client_test1",(char*)"user",0);
|
||||
IS_TRUE(rc);
|
||||
IS_FALSE(shimClient.error());
|
||||
|
||||
END_IT
|
||||
}
|
||||
int test_connect_accepts_username_blank_password() {
|
||||
IT("accepts a username and blank password");
|
||||
ShimClient shimClient;
|
||||
shimClient.setAllowConnect(true);
|
||||
|
||||
byte connect[] = { 0x10,0x20,0x0,0x4,0x4d,0x51,0x54,0x54,0x4,0xc2,0x0,0xf,0x0,0xc,0x63,0x6c,0x69,0x65,0x6e,0x74,0x5f,0x74,0x65,0x73,0x74,0x31,0x0,0x4,0x75,0x73,0x65,0x72,0x0,0x0};
|
||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
||||
shimClient.expect(connect,0x26);
|
||||
shimClient.respond(connack,4);
|
||||
|
||||
PubSubClient client(server, 1883, callback, shimClient);
|
||||
int rc = client.connect((char*)"client_test1",(char*)"user",(char*)"pass");
|
||||
IS_TRUE(rc);
|
||||
IS_FALSE(shimClient.error());
|
||||
|
||||
END_IT
|
||||
}
|
||||
|
||||
int test_connect_ignores_password_no_username() {
|
||||
IT("ignores a password but no username");
|
||||
ShimClient shimClient;
|
||||
shimClient.setAllowConnect(true);
|
||||
|
||||
byte connect[] = {0x10,0x18,0x0,0x4,0x4d,0x51,0x54,0x54,0x4,0x2,0x0,0xf,0x0,0xc,0x63,0x6c,0x69,0x65,0x6e,0x74,0x5f,0x74,0x65,0x73,0x74,0x31};
|
||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
||||
shimClient.expect(connect,26);
|
||||
shimClient.respond(connack,4);
|
||||
|
||||
PubSubClient client(server, 1883, callback, shimClient);
|
||||
int rc = client.connect((char*)"client_test1",0,(char*)"pass");
|
||||
IS_TRUE(rc);
|
||||
IS_FALSE(shimClient.error());
|
||||
|
||||
END_IT
|
||||
}
|
||||
|
||||
int test_connect_with_will() {
|
||||
IT("accepts a will");
|
||||
ShimClient shimClient;
|
||||
shimClient.setAllowConnect(true);
|
||||
|
||||
byte connect[] = {0x10,0x30,0x0,0x4,0x4d,0x51,0x54,0x54,0x4,0xe,0x0,0xf,0x0,0xc,0x63,0x6c,0x69,0x65,0x6e,0x74,0x5f,0x74,0x65,0x73,0x74,0x31,0x0,0x9,0x77,0x69,0x6c,0x6c,0x54,0x6f,0x70,0x69,0x63,0x0,0xb,0x77,0x69,0x6c,0x6c,0x4d,0x65,0x73,0x73,0x61,0x67,0x65};
|
||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
||||
shimClient.expect(connect,0x32);
|
||||
shimClient.respond(connack,4);
|
||||
|
||||
PubSubClient client(server, 1883, callback, shimClient);
|
||||
int rc = client.connect((char*)"client_test1",(char*)"willTopic",1,0,(char*)"willMessage");
|
||||
IS_TRUE(rc);
|
||||
IS_FALSE(shimClient.error());
|
||||
|
||||
END_IT
|
||||
}
|
||||
|
||||
int test_connect_with_will_username_password() {
|
||||
IT("accepts a will, username and password");
|
||||
ShimClient shimClient;
|
||||
shimClient.setAllowConnect(true);
|
||||
|
||||
byte connect[] = {0x10,0x40,0x0,0x4,0x4d,0x51,0x54,0x54,0x4,0xce,0x0,0xf,0x0,0xc,0x63,0x6c,0x69,0x65,0x6e,0x74,0x5f,0x74,0x65,0x73,0x74,0x31,0x0,0x9,0x77,0x69,0x6c,0x6c,0x54,0x6f,0x70,0x69,0x63,0x0,0xb,0x77,0x69,0x6c,0x6c,0x4d,0x65,0x73,0x73,0x61,0x67,0x65,0x0,0x4,0x75,0x73,0x65,0x72,0x0,0x8,0x70,0x61,0x73,0x73,0x77,0x6f,0x72,0x64};
|
||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
||||
shimClient.expect(connect,0x42);
|
||||
shimClient.respond(connack,4);
|
||||
|
||||
PubSubClient client(server, 1883, callback, shimClient);
|
||||
int rc = client.connect((char*)"client_test1",(char*)"user",(char*)"password",(char*)"willTopic",1,0,(char*)"willMessage");
|
||||
IS_TRUE(rc);
|
||||
IS_FALSE(shimClient.error());
|
||||
|
||||
END_IT
|
||||
}
|
||||
|
||||
int test_connect_disconnect_connect() {
|
||||
IT("connects, disconnects and connects again");
|
||||
ShimClient shimClient;
|
||||
|
||||
shimClient.setAllowConnect(true);
|
||||
byte expectServer[] = { 172, 16, 0, 2 };
|
||||
shimClient.expectConnect(expectServer,1883);
|
||||
byte connect[] = {0x10,0x18,0x0,0x4,0x4d,0x51,0x54,0x54,0x4,0x2,0x0,0xf,0x0,0xc,0x63,0x6c,0x69,0x65,0x6e,0x74,0x5f,0x74,0x65,0x73,0x74,0x31};
|
||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
||||
|
||||
shimClient.expect(connect,26);
|
||||
shimClient.respond(connack,4);
|
||||
|
||||
PubSubClient client(server, 1883, callback, shimClient);
|
||||
|
||||
int state = client.state();
|
||||
IS_TRUE(state == MQTT_DISCONNECTED);
|
||||
|
||||
int rc = client.connect((char*)"client_test1");
|
||||
IS_TRUE(rc);
|
||||
IS_FALSE(shimClient.error());
|
||||
|
||||
state = client.state();
|
||||
IS_TRUE(state == MQTT_CONNECTED);
|
||||
|
||||
byte disconnect[] = {0xE0,0x00};
|
||||
shimClient.expect(disconnect,2);
|
||||
|
||||
client.disconnect();
|
||||
|
||||
IS_FALSE(client.connected());
|
||||
IS_FALSE(shimClient.connected());
|
||||
IS_FALSE(shimClient.error());
|
||||
|
||||
state = client.state();
|
||||
IS_TRUE(state == MQTT_DISCONNECTED);
|
||||
|
||||
shimClient.expect(connect,28);
|
||||
shimClient.respond(connack,4);
|
||||
rc = client.connect((char*)"client_test1");
|
||||
IS_TRUE(rc);
|
||||
IS_FALSE(shimClient.error());
|
||||
state = client.state();
|
||||
IS_TRUE(state == MQTT_CONNECTED);
|
||||
|
||||
END_IT
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
SUITE("Connect");
|
||||
|
||||
test_connect_fails_no_network();
|
||||
test_connect_fails_on_no_response();
|
||||
|
||||
test_connect_properly_formatted();
|
||||
test_connect_non_clean_session();
|
||||
test_connect_accepts_username_password();
|
||||
test_connect_fails_on_bad_rc();
|
||||
test_connect_properly_formatted_hostname();
|
||||
|
||||
test_connect_accepts_username_no_password();
|
||||
test_connect_ignores_password_no_username();
|
||||
test_connect_with_will();
|
||||
test_connect_with_will_username_password();
|
||||
test_connect_disconnect_connect();
|
||||
FINISH
|
||||
}
|
185
lib/pubsubclient/tests/src/keepalive_spec.cpp
Normal file
185
lib/pubsubclient/tests/src/keepalive_spec.cpp
Normal file
@ -0,0 +1,185 @@
|
||||
#include "PubSubClient.h"
|
||||
#include "ShimClient.h"
|
||||
#include "Buffer.h"
|
||||
#include "BDDTest.h"
|
||||
#include "trace.h"
|
||||
#include <unistd.h>
|
||||
|
||||
byte server[] = { 172, 16, 0, 2 };
|
||||
|
||||
void callback(char* topic, byte* payload, unsigned int length) {
|
||||
// handle message arrived
|
||||
}
|
||||
|
||||
|
||||
int test_keepalive_pings_idle() {
|
||||
IT("keeps an idle connection alive (takes 1 minute)");
|
||||
|
||||
ShimClient shimClient;
|
||||
shimClient.setAllowConnect(true);
|
||||
|
||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
||||
shimClient.respond(connack,4);
|
||||
|
||||
PubSubClient client(server, 1883, callback, shimClient);
|
||||
int rc = client.connect((char*)"client_test1");
|
||||
IS_TRUE(rc);
|
||||
|
||||
byte pingreq[] = { 0xC0,0x0 };
|
||||
shimClient.expect(pingreq,2);
|
||||
byte pingresp[] = { 0xD0,0x0 };
|
||||
shimClient.respond(pingresp,2);
|
||||
|
||||
for (int i = 0; i < 50; i++) {
|
||||
sleep(1);
|
||||
if ( i == 15 || i == 31 || i == 47) {
|
||||
shimClient.expect(pingreq,2);
|
||||
shimClient.respond(pingresp,2);
|
||||
}
|
||||
rc = client.loop();
|
||||
IS_TRUE(rc);
|
||||
}
|
||||
|
||||
IS_FALSE(shimClient.error());
|
||||
|
||||
END_IT
|
||||
}
|
||||
|
||||
int test_keepalive_pings_with_outbound_qos0() {
|
||||
IT("keeps a connection alive that only sends qos0 (takes 1 minute)");
|
||||
|
||||
ShimClient shimClient;
|
||||
shimClient.setAllowConnect(true);
|
||||
|
||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
||||
shimClient.respond(connack,4);
|
||||
|
||||
PubSubClient client(server, 1883, callback, shimClient);
|
||||
int rc = client.connect((char*)"client_test1");
|
||||
IS_TRUE(rc);
|
||||
|
||||
byte publish[] = {0x30,0xe,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
|
||||
|
||||
for (int i = 0; i < 50; i++) {
|
||||
TRACE(i<<":");
|
||||
shimClient.expect(publish,16);
|
||||
rc = client.publish((char*)"topic",(char*)"payload");
|
||||
IS_TRUE(rc);
|
||||
IS_FALSE(shimClient.error());
|
||||
sleep(1);
|
||||
if ( i == 15 || i == 31 || i == 47) {
|
||||
byte pingreq[] = { 0xC0,0x0 };
|
||||
shimClient.expect(pingreq,2);
|
||||
byte pingresp[] = { 0xD0,0x0 };
|
||||
shimClient.respond(pingresp,2);
|
||||
}
|
||||
rc = client.loop();
|
||||
IS_TRUE(rc);
|
||||
IS_FALSE(shimClient.error());
|
||||
}
|
||||
|
||||
END_IT
|
||||
}
|
||||
|
||||
int test_keepalive_pings_with_inbound_qos0() {
|
||||
IT("keeps a connection alive that only receives qos0 (takes 1 minute)");
|
||||
|
||||
ShimClient shimClient;
|
||||
shimClient.setAllowConnect(true);
|
||||
|
||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
||||
shimClient.respond(connack,4);
|
||||
|
||||
PubSubClient client(server, 1883, callback, shimClient);
|
||||
int rc = client.connect((char*)"client_test1");
|
||||
IS_TRUE(rc);
|
||||
|
||||
byte publish[] = {0x30,0xe,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
|
||||
|
||||
for (int i = 0; i < 50; i++) {
|
||||
TRACE(i<<":");
|
||||
sleep(1);
|
||||
if ( i == 15 || i == 31 || i == 47) {
|
||||
byte pingreq[] = { 0xC0,0x0 };
|
||||
shimClient.expect(pingreq,2);
|
||||
byte pingresp[] = { 0xD0,0x0 };
|
||||
shimClient.respond(pingresp,2);
|
||||
}
|
||||
shimClient.respond(publish,16);
|
||||
rc = client.loop();
|
||||
IS_TRUE(rc);
|
||||
IS_FALSE(shimClient.error());
|
||||
}
|
||||
|
||||
END_IT
|
||||
}
|
||||
|
||||
int test_keepalive_no_pings_inbound_qos1() {
|
||||
IT("does not send pings for connections with inbound qos1 (takes 1 minute)");
|
||||
|
||||
ShimClient shimClient;
|
||||
shimClient.setAllowConnect(true);
|
||||
|
||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
||||
shimClient.respond(connack,4);
|
||||
|
||||
PubSubClient client(server, 1883, callback, shimClient);
|
||||
int rc = client.connect((char*)"client_test1");
|
||||
IS_TRUE(rc);
|
||||
|
||||
byte publish[] = {0x32,0x10,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x12,0x34,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
|
||||
byte puback[] = {0x40,0x2,0x12,0x34};
|
||||
|
||||
for (int i = 0; i < 50; i++) {
|
||||
shimClient.respond(publish,18);
|
||||
shimClient.expect(puback,4);
|
||||
sleep(1);
|
||||
rc = client.loop();
|
||||
IS_TRUE(rc);
|
||||
IS_FALSE(shimClient.error());
|
||||
}
|
||||
|
||||
END_IT
|
||||
}
|
||||
|
||||
int test_keepalive_disconnects_hung() {
|
||||
IT("disconnects a hung connection (takes 30 seconds)");
|
||||
|
||||
ShimClient shimClient;
|
||||
shimClient.setAllowConnect(true);
|
||||
|
||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
||||
shimClient.respond(connack,4);
|
||||
|
||||
PubSubClient client(server, 1883, callback, shimClient);
|
||||
int rc = client.connect((char*)"client_test1");
|
||||
IS_TRUE(rc);
|
||||
|
||||
byte pingreq[] = { 0xC0,0x0 };
|
||||
shimClient.expect(pingreq,2);
|
||||
|
||||
for (int i = 0; i < 32; i++) {
|
||||
sleep(1);
|
||||
rc = client.loop();
|
||||
}
|
||||
IS_FALSE(rc);
|
||||
|
||||
int state = client.state();
|
||||
IS_TRUE(state == MQTT_CONNECTION_TIMEOUT);
|
||||
|
||||
IS_FALSE(shimClient.error());
|
||||
|
||||
END_IT
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
SUITE("Keep-alive");
|
||||
test_keepalive_pings_idle();
|
||||
test_keepalive_pings_with_outbound_qos0();
|
||||
test_keepalive_pings_with_inbound_qos0();
|
||||
test_keepalive_no_pings_inbound_qos1();
|
||||
test_keepalive_disconnects_hung();
|
||||
|
||||
FINISH
|
||||
}
|
26
lib/pubsubclient/tests/src/lib/Arduino.h
Normal file
26
lib/pubsubclient/tests/src/lib/Arduino.h
Normal file
@ -0,0 +1,26 @@
|
||||
#ifndef Arduino_h
|
||||
#define Arduino_h
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include "Print.h"
|
||||
|
||||
|
||||
extern "C"{
|
||||
typedef uint8_t byte ;
|
||||
typedef uint8_t boolean ;
|
||||
|
||||
/* sketch */
|
||||
extern void setup( void ) ;
|
||||
extern void loop( void ) ;
|
||||
uint32_t millis( void );
|
||||
}
|
||||
|
||||
#define PROGMEM
|
||||
#define pgm_read_byte_near(x) *(x)
|
||||
|
||||
#define yield(x) {}
|
||||
|
||||
#endif // Arduino_h
|
50
lib/pubsubclient/tests/src/lib/BDDTest.cpp
Normal file
50
lib/pubsubclient/tests/src/lib/BDDTest.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
#include "BDDTest.h"
|
||||
#include "trace.h"
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <list>
|
||||
|
||||
int testCount = 0;
|
||||
int testPasses = 0;
|
||||
const char* testDescription;
|
||||
|
||||
std::list<std::string> failureList;
|
||||
|
||||
void bddtest_suite(const char* name) {
|
||||
LOG(name << "\n");
|
||||
}
|
||||
|
||||
int bddtest_test(const char* file, int line, const char* assertion, int result) {
|
||||
if (!result) {
|
||||
LOG("✗\n");
|
||||
std::ostringstream os;
|
||||
os << " ! "<<testDescription<<"\n " <<file << ":" <<line<<" : "<<assertion<<" ["<<result<<"]";
|
||||
failureList.push_back(os.str());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void bddtest_start(const char* description) {
|
||||
LOG(" - "<<description<<" ");
|
||||
testDescription = description;
|
||||
testCount ++;
|
||||
}
|
||||
void bddtest_end() {
|
||||
LOG("✓\n");
|
||||
testPasses ++;
|
||||
}
|
||||
|
||||
int bddtest_summary() {
|
||||
for (std::list<std::string>::iterator it = failureList.begin(); it != failureList.end(); it++) {
|
||||
LOG("\n");
|
||||
LOG(*it);
|
||||
LOG("\n");
|
||||
}
|
||||
|
||||
LOG(std::dec << testPasses << "/" << testCount << " tests passed\n\n");
|
||||
if (testPasses == testCount) {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
23
lib/pubsubclient/tests/src/lib/BDDTest.h
Normal file
23
lib/pubsubclient/tests/src/lib/BDDTest.h
Normal file
@ -0,0 +1,23 @@
|
||||
#ifndef bddtest_h
|
||||
#define bddtest_h
|
||||
|
||||
void bddtest_suite(const char* name);
|
||||
int bddtest_test(const char*, int, const char*, int);
|
||||
void bddtest_start(const char*);
|
||||
void bddtest_end();
|
||||
int bddtest_summary();
|
||||
|
||||
#define SUITE(x) { bddtest_suite(x); }
|
||||
#define TEST(x) { if (!bddtest_test(__FILE__, __LINE__, #x, (x))) return false; }
|
||||
|
||||
#define IT(x) { bddtest_start(x); }
|
||||
#define END_IT { bddtest_end();return true;}
|
||||
|
||||
#define FINISH { return bddtest_summary(); }
|
||||
|
||||
#define IS_TRUE(x) TEST(x)
|
||||
#define IS_FALSE(x) TEST(!(x))
|
||||
#define IS_EQUAL(x,y) TEST(x==y)
|
||||
#define IS_NOT_EQUAL(x,y) TEST(x!=y)
|
||||
|
||||
#endif
|
34
lib/pubsubclient/tests/src/lib/Buffer.cpp
Normal file
34
lib/pubsubclient/tests/src/lib/Buffer.cpp
Normal file
@ -0,0 +1,34 @@
|
||||
#include "Buffer.h"
|
||||
#include "Arduino.h"
|
||||
|
||||
Buffer::Buffer() {
|
||||
this->pos = 0;
|
||||
this->length = 0;
|
||||
}
|
||||
|
||||
Buffer::Buffer(uint8_t* buf, size_t size) {
|
||||
this->pos = 0;
|
||||
this->length = 0;
|
||||
this->add(buf,size);
|
||||
}
|
||||
bool Buffer::available() {
|
||||
return this->pos < this->length;
|
||||
}
|
||||
|
||||
uint8_t Buffer::next() {
|
||||
if (this->available()) {
|
||||
return this->buffer[this->pos++];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Buffer::reset() {
|
||||
this->pos = 0;
|
||||
}
|
||||
|
||||
void Buffer::add(uint8_t* buf, size_t size) {
|
||||
uint16_t i = 0;
|
||||
for (;i<size;i++) {
|
||||
this->buffer[this->length++] = buf[i];
|
||||
}
|
||||
}
|
23
lib/pubsubclient/tests/src/lib/Buffer.h
Normal file
23
lib/pubsubclient/tests/src/lib/Buffer.h
Normal file
@ -0,0 +1,23 @@
|
||||
#ifndef buffer_h
|
||||
#define buffer_h
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
class Buffer {
|
||||
private:
|
||||
uint8_t buffer[1024];
|
||||
uint16_t pos;
|
||||
uint16_t length;
|
||||
|
||||
public:
|
||||
Buffer();
|
||||
Buffer(uint8_t* buf, size_t size);
|
||||
|
||||
virtual bool available();
|
||||
virtual uint8_t next();
|
||||
virtual void reset();
|
||||
|
||||
virtual void add(uint8_t* buf, size_t size);
|
||||
};
|
||||
|
||||
#endif
|
21
lib/pubsubclient/tests/src/lib/Client.h
Normal file
21
lib/pubsubclient/tests/src/lib/Client.h
Normal file
@ -0,0 +1,21 @@
|
||||
#ifndef client_h
|
||||
#define client_h
|
||||
#include "IPAddress.h"
|
||||
|
||||
class Client {
|
||||
public:
|
||||
virtual int connect(IPAddress ip, uint16_t port) =0;
|
||||
virtual int connect(const char *host, uint16_t port) =0;
|
||||
virtual size_t write(uint8_t) =0;
|
||||
virtual size_t write(const uint8_t *buf, size_t size) =0;
|
||||
virtual int available() = 0;
|
||||
virtual int read() = 0;
|
||||
virtual int read(uint8_t *buf, size_t size) = 0;
|
||||
virtual int peek() = 0;
|
||||
virtual void flush() = 0;
|
||||
virtual void stop() = 0;
|
||||
virtual uint8_t connected() = 0;
|
||||
virtual operator bool() = 0;
|
||||
};
|
||||
|
||||
#endif
|
44
lib/pubsubclient/tests/src/lib/IPAddress.cpp
Normal file
44
lib/pubsubclient/tests/src/lib/IPAddress.cpp
Normal file
@ -0,0 +1,44 @@
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <IPAddress.h>
|
||||
|
||||
IPAddress::IPAddress()
|
||||
{
|
||||
memset(_address, 0, sizeof(_address));
|
||||
}
|
||||
|
||||
IPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet)
|
||||
{
|
||||
_address[0] = first_octet;
|
||||
_address[1] = second_octet;
|
||||
_address[2] = third_octet;
|
||||
_address[3] = fourth_octet;
|
||||
}
|
||||
|
||||
IPAddress::IPAddress(uint32_t address)
|
||||
{
|
||||
memcpy(_address, &address, sizeof(_address));
|
||||
}
|
||||
|
||||
IPAddress::IPAddress(const uint8_t *address)
|
||||
{
|
||||
memcpy(_address, address, sizeof(_address));
|
||||
}
|
||||
|
||||
IPAddress& IPAddress::operator=(const uint8_t *address)
|
||||
{
|
||||
memcpy(_address, address, sizeof(_address));
|
||||
return *this;
|
||||
}
|
||||
|
||||
IPAddress& IPAddress::operator=(uint32_t address)
|
||||
{
|
||||
memcpy(_address, (const uint8_t *)&address, sizeof(_address));
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool IPAddress::operator==(const uint8_t* addr)
|
||||
{
|
||||
return memcmp(addr, _address, sizeof(_address)) == 0;
|
||||
}
|
||||
|
72
lib/pubsubclient/tests/src/lib/IPAddress.h
Normal file
72
lib/pubsubclient/tests/src/lib/IPAddress.h
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
*
|
||||
* MIT License:
|
||||
* Copyright (c) 2011 Adrian McEwen
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* adrianm@mcqn.com 1/1/2011
|
||||
*/
|
||||
|
||||
#ifndef IPAddress_h
|
||||
#define IPAddress_h
|
||||
|
||||
|
||||
// A class to make it easier to handle and pass around IP addresses
|
||||
|
||||
class IPAddress {
|
||||
private:
|
||||
uint8_t _address[4]; // IPv4 address
|
||||
// Access the raw byte array containing the address. Because this returns a pointer
|
||||
// to the internal structure rather than a copy of the address this function should only
|
||||
// be used when you know that the usage of the returned uint8_t* will be transient and not
|
||||
// stored.
|
||||
uint8_t* raw_address() { return _address; };
|
||||
|
||||
public:
|
||||
// Constructors
|
||||
IPAddress();
|
||||
IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet);
|
||||
IPAddress(uint32_t address);
|
||||
IPAddress(const uint8_t *address);
|
||||
|
||||
// Overloaded cast operator to allow IPAddress objects to be used where a pointer
|
||||
// to a four-byte uint8_t array is expected
|
||||
operator uint32_t() { return *((uint32_t*)_address); };
|
||||
bool operator==(const IPAddress& addr) { return (*((uint32_t*)_address)) == (*((uint32_t*)addr._address)); };
|
||||
bool operator==(const uint8_t* addr);
|
||||
|
||||
// Overloaded index operator to allow getting and setting individual octets of the address
|
||||
uint8_t operator[](int index) const { return _address[index]; };
|
||||
uint8_t& operator[](int index) { return _address[index]; };
|
||||
|
||||
// Overloaded copy operators to allow initialisation of IPAddress objects from other types
|
||||
IPAddress& operator=(const uint8_t *address);
|
||||
IPAddress& operator=(uint32_t address);
|
||||
|
||||
|
||||
friend class EthernetClass;
|
||||
friend class UDP;
|
||||
friend class Client;
|
||||
friend class Server;
|
||||
friend class DhcpClass;
|
||||
friend class DNSClient;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
28
lib/pubsubclient/tests/src/lib/Print.h
Normal file
28
lib/pubsubclient/tests/src/lib/Print.h
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
Print.h - Base class that provides print() and println()
|
||||
Copyright (c) 2008 David A. Mellis. All right reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef Print_h
|
||||
#define Print_h
|
||||
|
||||
class Print {
|
||||
public:
|
||||
virtual size_t write(uint8_t) = 0;
|
||||
};
|
||||
|
||||
#endif
|
153
lib/pubsubclient/tests/src/lib/ShimClient.cpp
Normal file
153
lib/pubsubclient/tests/src/lib/ShimClient.cpp
Normal file
@ -0,0 +1,153 @@
|
||||
#include "ShimClient.h"
|
||||
#include "trace.h"
|
||||
#include <iostream>
|
||||
#include <Arduino.h>
|
||||
#include <ctime>
|
||||
|
||||
extern "C" {
|
||||
uint32_t millis(void) {
|
||||
return time(0)*1000;
|
||||
}
|
||||
}
|
||||
|
||||
ShimClient::ShimClient() {
|
||||
this->responseBuffer = new Buffer();
|
||||
this->expectBuffer = new Buffer();
|
||||
this->_allowConnect = true;
|
||||
this->_connected = false;
|
||||
this->_error = false;
|
||||
this->expectAnything = true;
|
||||
this->_received = 0;
|
||||
this->_expectedPort = 0;
|
||||
}
|
||||
|
||||
int ShimClient::connect(IPAddress ip, uint16_t port) {
|
||||
if (this->_allowConnect) {
|
||||
this->_connected = true;
|
||||
}
|
||||
if (this->_expectedPort !=0) {
|
||||
// if (memcmp(ip,this->_expectedIP,4) != 0) {
|
||||
// TRACE( "ip mismatch\n");
|
||||
// this->_error = true;
|
||||
// }
|
||||
if (port != this->_expectedPort) {
|
||||
TRACE( "port mismatch\n");
|
||||
this->_error = true;
|
||||
}
|
||||
}
|
||||
return this->_connected;
|
||||
}
|
||||
int ShimClient::connect(const char *host, uint16_t port) {
|
||||
if (this->_allowConnect) {
|
||||
this->_connected = true;
|
||||
}
|
||||
if (this->_expectedPort !=0) {
|
||||
if (strcmp(host,this->_expectedHost) != 0) {
|
||||
TRACE( "host mismatch\n");
|
||||
this->_error = true;
|
||||
}
|
||||
if (port != this->_expectedPort) {
|
||||
TRACE( "port mismatch\n");
|
||||
this->_error = true;
|
||||
}
|
||||
|
||||
}
|
||||
return this->_connected;
|
||||
}
|
||||
size_t ShimClient::write(uint8_t b) {
|
||||
this->_received += 1;
|
||||
TRACE(std::hex << (unsigned int)b);
|
||||
if (!this->expectAnything) {
|
||||
if (this->expectBuffer->available()) {
|
||||
uint8_t expected = this->expectBuffer->next();
|
||||
if (expected != b) {
|
||||
this->_error = true;
|
||||
TRACE("!=" << (unsigned int)expected);
|
||||
}
|
||||
} else {
|
||||
this->_error = true;
|
||||
}
|
||||
}
|
||||
TRACE("\n"<< std::dec);
|
||||
return 1;
|
||||
}
|
||||
size_t ShimClient::write(const uint8_t *buf, size_t size) {
|
||||
this->_received += size;
|
||||
TRACE( "[" << std::dec << (unsigned int)(size) << "] ");
|
||||
uint16_t i=0;
|
||||
for (;i<size;i++) {
|
||||
if (i>0) {
|
||||
TRACE(":");
|
||||
}
|
||||
TRACE(std::hex << (unsigned int)(buf[i]));
|
||||
|
||||
if (!this->expectAnything) {
|
||||
if (this->expectBuffer->available()) {
|
||||
uint8_t expected = this->expectBuffer->next();
|
||||
if (expected != buf[i]) {
|
||||
this->_error = true;
|
||||
TRACE("!=" << (unsigned int)expected);
|
||||
}
|
||||
} else {
|
||||
this->_error = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
TRACE("\n"<<std::dec);
|
||||
return size;
|
||||
}
|
||||
int ShimClient::available() {
|
||||
return this->responseBuffer->available();
|
||||
}
|
||||
int ShimClient::read() { return this->responseBuffer->next(); }
|
||||
int ShimClient::read(uint8_t *buf, size_t size) {
|
||||
uint16_t i = 0;
|
||||
for (;i<size;i++) {
|
||||
buf[i] = this->read();
|
||||
}
|
||||
return size;
|
||||
}
|
||||
int ShimClient::peek() { return 0; }
|
||||
void ShimClient::flush() {}
|
||||
void ShimClient::stop() {
|
||||
this->setConnected(false);
|
||||
}
|
||||
uint8_t ShimClient::connected() { return this->_connected; }
|
||||
ShimClient::operator bool() { return true; }
|
||||
|
||||
|
||||
ShimClient* ShimClient::respond(uint8_t *buf, size_t size) {
|
||||
this->responseBuffer->add(buf,size);
|
||||
return this;
|
||||
}
|
||||
|
||||
ShimClient* ShimClient::expect(uint8_t *buf, size_t size) {
|
||||
this->expectAnything = false;
|
||||
this->expectBuffer->add(buf,size);
|
||||
return this;
|
||||
}
|
||||
|
||||
void ShimClient::setConnected(bool b) {
|
||||
this->_connected = b;
|
||||
}
|
||||
void ShimClient::setAllowConnect(bool b) {
|
||||
this->_allowConnect = b;
|
||||
}
|
||||
|
||||
bool ShimClient::error() {
|
||||
return this->_error;
|
||||
}
|
||||
|
||||
uint16_t ShimClient::received() {
|
||||
return this->_received;
|
||||
}
|
||||
|
||||
void ShimClient::expectConnect(IPAddress ip, uint16_t port) {
|
||||
this->_expectedIP = ip;
|
||||
this->_expectedPort = port;
|
||||
}
|
||||
|
||||
void ShimClient::expectConnect(const char *host, uint16_t port) {
|
||||
this->_expectedHost = host;
|
||||
this->_expectedPort = port;
|
||||
}
|
51
lib/pubsubclient/tests/src/lib/ShimClient.h
Normal file
51
lib/pubsubclient/tests/src/lib/ShimClient.h
Normal file
@ -0,0 +1,51 @@
|
||||
#ifndef shimclient_h
|
||||
#define shimclient_h
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "Client.h"
|
||||
#include "IPAddress.h"
|
||||
#include "Buffer.h"
|
||||
|
||||
|
||||
class ShimClient : public Client {
|
||||
private:
|
||||
Buffer* responseBuffer;
|
||||
Buffer* expectBuffer;
|
||||
bool _allowConnect;
|
||||
bool _connected;
|
||||
bool expectAnything;
|
||||
bool _error;
|
||||
uint16_t _received;
|
||||
IPAddress _expectedIP;
|
||||
uint16_t _expectedPort;
|
||||
const char* _expectedHost;
|
||||
|
||||
public:
|
||||
ShimClient();
|
||||
virtual int connect(IPAddress ip, uint16_t port);
|
||||
virtual int connect(const char *host, uint16_t port);
|
||||
virtual size_t write(uint8_t);
|
||||
virtual size_t write(const uint8_t *buf, size_t size);
|
||||
virtual int available();
|
||||
virtual int read();
|
||||
virtual int read(uint8_t *buf, size_t size);
|
||||
virtual int peek();
|
||||
virtual void flush();
|
||||
virtual void stop();
|
||||
virtual uint8_t connected();
|
||||
virtual operator bool();
|
||||
|
||||
virtual ShimClient* respond(uint8_t *buf, size_t size);
|
||||
virtual ShimClient* expect(uint8_t *buf, size_t size);
|
||||
|
||||
virtual void expectConnect(IPAddress ip, uint16_t port);
|
||||
virtual void expectConnect(const char *host, uint16_t port);
|
||||
|
||||
virtual uint16_t received();
|
||||
virtual bool error();
|
||||
|
||||
virtual void setAllowConnect(bool b);
|
||||
virtual void setConnected(bool b);
|
||||
};
|
||||
|
||||
#endif
|
39
lib/pubsubclient/tests/src/lib/Stream.cpp
Normal file
39
lib/pubsubclient/tests/src/lib/Stream.cpp
Normal file
@ -0,0 +1,39 @@
|
||||
#include "Stream.h"
|
||||
#include "trace.h"
|
||||
#include <iostream>
|
||||
#include <Arduino.h>
|
||||
|
||||
Stream::Stream() {
|
||||
this->expectBuffer = new Buffer();
|
||||
this->_error = false;
|
||||
this->_written = 0;
|
||||
}
|
||||
|
||||
size_t Stream::write(uint8_t b) {
|
||||
this->_written++;
|
||||
TRACE(std::hex << (unsigned int)b);
|
||||
if (this->expectBuffer->available()) {
|
||||
uint8_t expected = this->expectBuffer->next();
|
||||
if (expected != b) {
|
||||
this->_error = true;
|
||||
TRACE("!=" << (unsigned int)expected);
|
||||
}
|
||||
} else {
|
||||
this->_error = true;
|
||||
}
|
||||
TRACE("\n"<< std::dec);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
bool Stream::error() {
|
||||
return this->_error;
|
||||
}
|
||||
|
||||
void Stream::expect(uint8_t *buf, size_t size) {
|
||||
this->expectBuffer->add(buf,size);
|
||||
}
|
||||
|
||||
uint16_t Stream::length() {
|
||||
return this->_written;
|
||||
}
|
22
lib/pubsubclient/tests/src/lib/Stream.h
Normal file
22
lib/pubsubclient/tests/src/lib/Stream.h
Normal file
@ -0,0 +1,22 @@
|
||||
#ifndef Stream_h
|
||||
#define Stream_h
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "Buffer.h"
|
||||
|
||||
class Stream {
|
||||
private:
|
||||
Buffer* expectBuffer;
|
||||
bool _error;
|
||||
uint16_t _written;
|
||||
|
||||
public:
|
||||
Stream();
|
||||
virtual size_t write(uint8_t);
|
||||
|
||||
virtual bool error();
|
||||
virtual void expect(uint8_t *buf, size_t size);
|
||||
virtual uint16_t length();
|
||||
};
|
||||
|
||||
#endif
|
10
lib/pubsubclient/tests/src/lib/trace.h
Normal file
10
lib/pubsubclient/tests/src/lib/trace.h
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef trace_h
|
||||
#define trace_h
|
||||
#include <iostream>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#define LOG(x) {std::cout << x << std::flush; }
|
||||
#define TRACE(x) {if (getenv("TRACE")) { std::cout << x << std::flush; }}
|
||||
|
||||
#endif
|
190
lib/pubsubclient/tests/src/publish_spec.cpp
Normal file
190
lib/pubsubclient/tests/src/publish_spec.cpp
Normal file
@ -0,0 +1,190 @@
|
||||
#include "PubSubClient.h"
|
||||
#include "ShimClient.h"
|
||||
#include "Buffer.h"
|
||||
#include "BDDTest.h"
|
||||
#include "trace.h"
|
||||
|
||||
|
||||
byte server[] = { 172, 16, 0, 2 };
|
||||
|
||||
void callback(char* topic, byte* payload, unsigned int length) {
|
||||
// handle message arrived
|
||||
}
|
||||
|
||||
int test_publish() {
|
||||
IT("publishes a null-terminated string");
|
||||
ShimClient shimClient;
|
||||
shimClient.setAllowConnect(true);
|
||||
|
||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
||||
shimClient.respond(connack,4);
|
||||
|
||||
PubSubClient client(server, 1883, callback, shimClient);
|
||||
int rc = client.connect((char*)"client_test1");
|
||||
IS_TRUE(rc);
|
||||
|
||||
byte publish[] = {0x30,0xe,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
|
||||
shimClient.expect(publish,16);
|
||||
|
||||
rc = client.publish((char*)"topic",(char*)"payload");
|
||||
IS_TRUE(rc);
|
||||
|
||||
IS_FALSE(shimClient.error());
|
||||
|
||||
END_IT
|
||||
}
|
||||
|
||||
|
||||
int test_publish_bytes() {
|
||||
IT("publishes a byte array");
|
||||
ShimClient shimClient;
|
||||
shimClient.setAllowConnect(true);
|
||||
|
||||
byte payload[] = { 0x01,0x02,0x03,0x0,0x05 };
|
||||
int length = 5;
|
||||
|
||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
||||
shimClient.respond(connack,4);
|
||||
|
||||
PubSubClient client(server, 1883, callback, shimClient);
|
||||
int rc = client.connect((char*)"client_test1");
|
||||
IS_TRUE(rc);
|
||||
|
||||
byte publish[] = {0x30,0xc,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x1,0x2,0x3,0x0,0x5};
|
||||
shimClient.expect(publish,14);
|
||||
|
||||
rc = client.publish((char*)"topic",payload,length);
|
||||
IS_TRUE(rc);
|
||||
|
||||
IS_FALSE(shimClient.error());
|
||||
|
||||
END_IT
|
||||
}
|
||||
|
||||
|
||||
int test_publish_retained() {
|
||||
IT("publishes retained - 1");
|
||||
ShimClient shimClient;
|
||||
shimClient.setAllowConnect(true);
|
||||
|
||||
byte payload[] = { 0x01,0x02,0x03,0x0,0x05 };
|
||||
int length = 5;
|
||||
|
||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
||||
shimClient.respond(connack,4);
|
||||
|
||||
PubSubClient client(server, 1883, callback, shimClient);
|
||||
int rc = client.connect((char*)"client_test1");
|
||||
IS_TRUE(rc);
|
||||
|
||||
byte publish[] = {0x31,0xc,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x1,0x2,0x3,0x0,0x5};
|
||||
shimClient.expect(publish,14);
|
||||
|
||||
rc = client.publish((char*)"topic",payload,length,true);
|
||||
IS_TRUE(rc);
|
||||
|
||||
IS_FALSE(shimClient.error());
|
||||
|
||||
END_IT
|
||||
}
|
||||
|
||||
int test_publish_retained_2() {
|
||||
IT("publishes retained - 2");
|
||||
ShimClient shimClient;
|
||||
shimClient.setAllowConnect(true);
|
||||
|
||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
||||
shimClient.respond(connack,4);
|
||||
|
||||
PubSubClient client(server, 1883, callback, shimClient);
|
||||
int rc = client.connect((char*)"client_test1");
|
||||
IS_TRUE(rc);
|
||||
|
||||
byte publish[] = {0x31,0xc,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,'A','B','C','D','E'};
|
||||
shimClient.expect(publish,14);
|
||||
|
||||
rc = client.publish((char*)"topic",(char*)"ABCDE",true);
|
||||
IS_TRUE(rc);
|
||||
|
||||
IS_FALSE(shimClient.error());
|
||||
|
||||
END_IT
|
||||
}
|
||||
|
||||
int test_publish_not_connected() {
|
||||
IT("publish fails when not connected");
|
||||
ShimClient shimClient;
|
||||
|
||||
PubSubClient client(server, 1883, callback, shimClient);
|
||||
|
||||
int rc = client.publish((char*)"topic",(char*)"payload");
|
||||
IS_FALSE(rc);
|
||||
|
||||
IS_FALSE(shimClient.error());
|
||||
|
||||
END_IT
|
||||
}
|
||||
|
||||
int test_publish_too_long() {
|
||||
IT("publish fails when topic/payload are too long");
|
||||
ShimClient shimClient;
|
||||
shimClient.setAllowConnect(true);
|
||||
|
||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
||||
shimClient.respond(connack,4);
|
||||
|
||||
PubSubClient client(server, 1883, callback, shimClient);
|
||||
int rc = client.connect((char*)"client_test1");
|
||||
IS_TRUE(rc);
|
||||
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2
|
||||
rc = client.publish((char*)"topic",(char*)"123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
|
||||
IS_FALSE(rc);
|
||||
|
||||
IS_FALSE(shimClient.error());
|
||||
|
||||
END_IT
|
||||
}
|
||||
|
||||
int test_publish_P() {
|
||||
IT("publishes using PROGMEM");
|
||||
ShimClient shimClient;
|
||||
shimClient.setAllowConnect(true);
|
||||
|
||||
byte payload[] = { 0x01,0x02,0x03,0x0,0x05 };
|
||||
int length = 5;
|
||||
|
||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
||||
shimClient.respond(connack,4);
|
||||
|
||||
PubSubClient client(server, 1883, callback, shimClient);
|
||||
int rc = client.connect((char*)"client_test1");
|
||||
IS_TRUE(rc);
|
||||
|
||||
byte publish[] = {0x31,0xc,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x1,0x2,0x3,0x0,0x5};
|
||||
shimClient.expect(publish,14);
|
||||
|
||||
rc = client.publish_P((char*)"topic",payload,length,true);
|
||||
IS_TRUE(rc);
|
||||
|
||||
IS_FALSE(shimClient.error());
|
||||
|
||||
END_IT
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
SUITE("Publish");
|
||||
test_publish();
|
||||
test_publish_bytes();
|
||||
test_publish_retained();
|
||||
test_publish_retained_2();
|
||||
test_publish_not_connected();
|
||||
test_publish_too_long();
|
||||
test_publish_P();
|
||||
|
||||
FINISH
|
||||
}
|
279
lib/pubsubclient/tests/src/receive_spec.cpp
Normal file
279
lib/pubsubclient/tests/src/receive_spec.cpp
Normal file
@ -0,0 +1,279 @@
|
||||
#include "PubSubClient.h"
|
||||
#include "ShimClient.h"
|
||||
#include "Buffer.h"
|
||||
#include "BDDTest.h"
|
||||
#include "trace.h"
|
||||
|
||||
|
||||
byte server[] = { 172, 16, 0, 2 };
|
||||
|
||||
bool callback_called = false;
|
||||
char lastTopic[1024];
|
||||
char lastPayload[1024];
|
||||
unsigned int lastLength;
|
||||
|
||||
void reset_callback() {
|
||||
callback_called = false;
|
||||
lastTopic[0] = '\0';
|
||||
lastPayload[0] = '\0';
|
||||
lastLength = 0;
|
||||
}
|
||||
|
||||
void callback(char* topic, byte* payload, unsigned int length) {
|
||||
callback_called = true;
|
||||
strcpy(lastTopic,topic);
|
||||
memcpy(lastPayload,payload,length);
|
||||
lastLength = length;
|
||||
}
|
||||
|
||||
int test_receive_callback() {
|
||||
IT("receives a callback message");
|
||||
reset_callback();
|
||||
|
||||
ShimClient shimClient;
|
||||
shimClient.setAllowConnect(true);
|
||||
|
||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
||||
shimClient.respond(connack,4);
|
||||
|
||||
PubSubClient client(server, 1883, callback, shimClient);
|
||||
int rc = client.connect((char*)"client_test1");
|
||||
IS_TRUE(rc);
|
||||
|
||||
byte publish[] = {0x30,0xe,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
|
||||
shimClient.respond(publish,16);
|
||||
|
||||
rc = client.loop();
|
||||
|
||||
IS_TRUE(rc);
|
||||
|
||||
IS_TRUE(callback_called);
|
||||
IS_TRUE(strcmp(lastTopic,"topic")==0);
|
||||
IS_TRUE(memcmp(lastPayload,"payload",7)==0);
|
||||
IS_TRUE(lastLength == 7);
|
||||
|
||||
IS_FALSE(shimClient.error());
|
||||
|
||||
END_IT
|
||||
}
|
||||
|
||||
int test_receive_stream() {
|
||||
IT("receives a streamed callback message");
|
||||
reset_callback();
|
||||
|
||||
Stream stream;
|
||||
stream.expect((uint8_t*)"payload",7);
|
||||
|
||||
ShimClient shimClient;
|
||||
shimClient.setAllowConnect(true);
|
||||
|
||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
||||
shimClient.respond(connack,4);
|
||||
|
||||
PubSubClient client(server, 1883, callback, shimClient, stream);
|
||||
int rc = client.connect((char*)"client_test1");
|
||||
IS_TRUE(rc);
|
||||
|
||||
byte publish[] = {0x30,0xe,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
|
||||
shimClient.respond(publish,16);
|
||||
|
||||
rc = client.loop();
|
||||
|
||||
IS_TRUE(rc);
|
||||
|
||||
IS_TRUE(callback_called);
|
||||
IS_TRUE(strcmp(lastTopic,"topic")==0);
|
||||
IS_TRUE(lastLength == 7);
|
||||
|
||||
IS_FALSE(stream.error());
|
||||
IS_FALSE(shimClient.error());
|
||||
|
||||
END_IT
|
||||
}
|
||||
|
||||
int test_receive_max_sized_message() {
|
||||
IT("receives an max-sized message");
|
||||
reset_callback();
|
||||
|
||||
ShimClient shimClient;
|
||||
shimClient.setAllowConnect(true);
|
||||
|
||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
||||
shimClient.respond(connack,4);
|
||||
|
||||
PubSubClient client(server, 1883, callback, shimClient);
|
||||
int rc = client.connect((char*)"client_test1");
|
||||
IS_TRUE(rc);
|
||||
|
||||
int length = MQTT_MAX_PACKET_SIZE;
|
||||
byte publish[] = {0x30,length-2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
|
||||
byte bigPublish[length];
|
||||
memset(bigPublish,'A',length);
|
||||
bigPublish[length] = 'B';
|
||||
memcpy(bigPublish,publish,16);
|
||||
shimClient.respond(bigPublish,length);
|
||||
|
||||
rc = client.loop();
|
||||
|
||||
IS_TRUE(rc);
|
||||
|
||||
IS_TRUE(callback_called);
|
||||
IS_TRUE(strcmp(lastTopic,"topic")==0);
|
||||
IS_TRUE(lastLength == length-9);
|
||||
IS_TRUE(memcmp(lastPayload,bigPublish+9,lastLength)==0);
|
||||
|
||||
IS_FALSE(shimClient.error());
|
||||
|
||||
END_IT
|
||||
}
|
||||
|
||||
int test_receive_oversized_message() {
|
||||
IT("drops an oversized message");
|
||||
reset_callback();
|
||||
|
||||
ShimClient shimClient;
|
||||
shimClient.setAllowConnect(true);
|
||||
|
||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
||||
shimClient.respond(connack,4);
|
||||
|
||||
PubSubClient client(server, 1883, callback, shimClient);
|
||||
int rc = client.connect((char*)"client_test1");
|
||||
IS_TRUE(rc);
|
||||
|
||||
int length = MQTT_MAX_PACKET_SIZE+1;
|
||||
byte publish[] = {0x30,length-2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
|
||||
byte bigPublish[length];
|
||||
memset(bigPublish,'A',length);
|
||||
bigPublish[length] = 'B';
|
||||
memcpy(bigPublish,publish,16);
|
||||
shimClient.respond(bigPublish,length);
|
||||
|
||||
rc = client.loop();
|
||||
|
||||
IS_TRUE(rc);
|
||||
|
||||
IS_FALSE(callback_called);
|
||||
|
||||
IS_FALSE(shimClient.error());
|
||||
|
||||
END_IT
|
||||
}
|
||||
|
||||
int test_drop_invalid_remaining_length_message() {
|
||||
IT("drops invalid remaining length message");
|
||||
reset_callback();
|
||||
|
||||
ShimClient shimClient;
|
||||
shimClient.setAllowConnect(true);
|
||||
|
||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
||||
shimClient.respond(connack,4);
|
||||
|
||||
PubSubClient client(server, 1883, callback, shimClient);
|
||||
int rc = client.connect((char*)"client_test1");
|
||||
IS_TRUE(rc);
|
||||
|
||||
byte publish[] = {0x30,0x92,0x92,0x92,0x92,0x01,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
|
||||
shimClient.respond(publish,20);
|
||||
|
||||
rc = client.loop();
|
||||
|
||||
IS_FALSE(rc);
|
||||
|
||||
IS_FALSE(callback_called);
|
||||
|
||||
IS_FALSE(shimClient.error());
|
||||
|
||||
END_IT
|
||||
}
|
||||
|
||||
|
||||
int test_receive_oversized_stream_message() {
|
||||
IT("drops an oversized message");
|
||||
reset_callback();
|
||||
|
||||
Stream stream;
|
||||
|
||||
ShimClient shimClient;
|
||||
shimClient.setAllowConnect(true);
|
||||
|
||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
||||
shimClient.respond(connack,4);
|
||||
|
||||
PubSubClient client(server, 1883, callback, shimClient, stream);
|
||||
int rc = client.connect((char*)"client_test1");
|
||||
IS_TRUE(rc);
|
||||
|
||||
int length = MQTT_MAX_PACKET_SIZE+1;
|
||||
byte publish[] = {0x30,length-2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
|
||||
|
||||
byte bigPublish[length];
|
||||
memset(bigPublish,'A',length);
|
||||
bigPublish[length] = 'B';
|
||||
memcpy(bigPublish,publish,16);
|
||||
|
||||
shimClient.respond(bigPublish,length);
|
||||
stream.expect(bigPublish+9,length-9);
|
||||
|
||||
rc = client.loop();
|
||||
|
||||
IS_TRUE(rc);
|
||||
|
||||
IS_TRUE(callback_called);
|
||||
IS_TRUE(strcmp(lastTopic,"topic")==0);
|
||||
IS_TRUE(lastLength == length-9);
|
||||
|
||||
IS_FALSE(stream.error());
|
||||
IS_FALSE(shimClient.error());
|
||||
|
||||
END_IT
|
||||
}
|
||||
|
||||
int test_receive_qos1() {
|
||||
IT("receives a qos1 message");
|
||||
reset_callback();
|
||||
|
||||
ShimClient shimClient;
|
||||
shimClient.setAllowConnect(true);
|
||||
|
||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
||||
shimClient.respond(connack,4);
|
||||
|
||||
PubSubClient client(server, 1883, callback, shimClient);
|
||||
int rc = client.connect((char*)"client_test1");
|
||||
IS_TRUE(rc);
|
||||
|
||||
byte publish[] = {0x32,0x10,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x12,0x34,0x70,0x61,0x79,0x6c,0x6f,0x61,0x64};
|
||||
shimClient.respond(publish,18);
|
||||
|
||||
byte puback[] = {0x40,0x2,0x12,0x34};
|
||||
shimClient.expect(puback,4);
|
||||
|
||||
rc = client.loop();
|
||||
|
||||
IS_TRUE(rc);
|
||||
|
||||
IS_TRUE(callback_called);
|
||||
IS_TRUE(strcmp(lastTopic,"topic")==0);
|
||||
IS_TRUE(memcmp(lastPayload,"payload",7)==0);
|
||||
IS_TRUE(lastLength == 7);
|
||||
|
||||
IS_FALSE(shimClient.error());
|
||||
|
||||
END_IT
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
SUITE("Receive");
|
||||
test_receive_callback();
|
||||
test_receive_stream();
|
||||
test_receive_max_sized_message();
|
||||
test_drop_invalid_remaining_length_message();
|
||||
test_receive_oversized_message();
|
||||
test_receive_oversized_stream_message();
|
||||
test_receive_qos1();
|
||||
|
||||
FINISH
|
||||
}
|
177
lib/pubsubclient/tests/src/subscribe_spec.cpp
Normal file
177
lib/pubsubclient/tests/src/subscribe_spec.cpp
Normal file
@ -0,0 +1,177 @@
|
||||
#include "PubSubClient.h"
|
||||
#include "ShimClient.h"
|
||||
#include "Buffer.h"
|
||||
#include "BDDTest.h"
|
||||
#include "trace.h"
|
||||
|
||||
|
||||
byte server[] = { 172, 16, 0, 2 };
|
||||
|
||||
void callback(char* topic, byte* payload, unsigned int length) {
|
||||
// handle message arrived
|
||||
}
|
||||
|
||||
int test_subscribe_no_qos() {
|
||||
IT("subscribe without qos defaults to 0");
|
||||
ShimClient shimClient;
|
||||
shimClient.setAllowConnect(true);
|
||||
|
||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
||||
shimClient.respond(connack,4);
|
||||
|
||||
PubSubClient client(server, 1883, callback, shimClient);
|
||||
int rc = client.connect((char*)"client_test1");
|
||||
IS_TRUE(rc);
|
||||
|
||||
byte subscribe[] = { 0x82,0xa,0x0,0x2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x0 };
|
||||
shimClient.expect(subscribe,12);
|
||||
byte suback[] = { 0x90,0x3,0x0,0x2,0x0 };
|
||||
shimClient.respond(suback,5);
|
||||
|
||||
rc = client.subscribe((char*)"topic");
|
||||
IS_TRUE(rc);
|
||||
|
||||
IS_FALSE(shimClient.error());
|
||||
|
||||
END_IT
|
||||
}
|
||||
|
||||
int test_subscribe_qos_1() {
|
||||
IT("subscribes qos 1");
|
||||
ShimClient shimClient;
|
||||
shimClient.setAllowConnect(true);
|
||||
|
||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
||||
shimClient.respond(connack,4);
|
||||
|
||||
PubSubClient client(server, 1883, callback, shimClient);
|
||||
int rc = client.connect((char*)"client_test1");
|
||||
IS_TRUE(rc);
|
||||
|
||||
byte subscribe[] = { 0x82,0xa,0x0,0x2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63,0x1 };
|
||||
shimClient.expect(subscribe,12);
|
||||
byte suback[] = { 0x90,0x3,0x0,0x2,0x1 };
|
||||
shimClient.respond(suback,5);
|
||||
|
||||
rc = client.subscribe((char*)"topic",1);
|
||||
IS_TRUE(rc);
|
||||
|
||||
IS_FALSE(shimClient.error());
|
||||
|
||||
END_IT
|
||||
}
|
||||
|
||||
int test_subscribe_not_connected() {
|
||||
IT("subscribe fails when not connected");
|
||||
ShimClient shimClient;
|
||||
|
||||
PubSubClient client(server, 1883, callback, shimClient);
|
||||
|
||||
int rc = client.subscribe((char*)"topic");
|
||||
IS_FALSE(rc);
|
||||
|
||||
IS_FALSE(shimClient.error());
|
||||
|
||||
END_IT
|
||||
}
|
||||
|
||||
int test_subscribe_invalid_qos() {
|
||||
IT("subscribe fails with invalid qos values");
|
||||
ShimClient shimClient;
|
||||
shimClient.setAllowConnect(true);
|
||||
|
||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
||||
shimClient.respond(connack,4);
|
||||
|
||||
PubSubClient client(server, 1883, callback, shimClient);
|
||||
int rc = client.connect((char*)"client_test1");
|
||||
IS_TRUE(rc);
|
||||
|
||||
rc = client.subscribe((char*)"topic",2);
|
||||
IS_FALSE(rc);
|
||||
rc = client.subscribe((char*)"topic",254);
|
||||
IS_FALSE(rc);
|
||||
|
||||
IS_FALSE(shimClient.error());
|
||||
|
||||
END_IT
|
||||
}
|
||||
|
||||
int test_subscribe_too_long() {
|
||||
IT("subscribe fails with too long topic");
|
||||
ShimClient shimClient;
|
||||
shimClient.setAllowConnect(true);
|
||||
|
||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
||||
shimClient.respond(connack,4);
|
||||
|
||||
PubSubClient client(server, 1883, callback, shimClient);
|
||||
int rc = client.connect((char*)"client_test1");
|
||||
IS_TRUE(rc);
|
||||
|
||||
// max length should be allowed
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2
|
||||
rc = client.subscribe((char*)"12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789");
|
||||
IS_TRUE(rc);
|
||||
|
||||
// 0 1 2 3 4 5 6 7 8 9 0 1 2
|
||||
rc = client.subscribe((char*)"123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890");
|
||||
IS_FALSE(rc);
|
||||
|
||||
IS_FALSE(shimClient.error());
|
||||
|
||||
END_IT
|
||||
}
|
||||
|
||||
|
||||
int test_unsubscribe() {
|
||||
IT("unsubscribes");
|
||||
ShimClient shimClient;
|
||||
shimClient.setAllowConnect(true);
|
||||
|
||||
byte connack[] = { 0x20, 0x02, 0x00, 0x00 };
|
||||
shimClient.respond(connack,4);
|
||||
|
||||
PubSubClient client(server, 1883, callback, shimClient);
|
||||
int rc = client.connect((char*)"client_test1");
|
||||
IS_TRUE(rc);
|
||||
|
||||
byte unsubscribe[] = { 0xA2,0x9,0x0,0x2,0x0,0x5,0x74,0x6f,0x70,0x69,0x63 };
|
||||
shimClient.expect(unsubscribe,12);
|
||||
byte unsuback[] = { 0xB0,0x2,0x0,0x2 };
|
||||
shimClient.respond(unsuback,4);
|
||||
|
||||
rc = client.unsubscribe((char*)"topic");
|
||||
IS_TRUE(rc);
|
||||
|
||||
IS_FALSE(shimClient.error());
|
||||
|
||||
END_IT
|
||||
}
|
||||
|
||||
int test_unsubscribe_not_connected() {
|
||||
IT("unsubscribe fails when not connected");
|
||||
ShimClient shimClient;
|
||||
|
||||
PubSubClient client(server, 1883, callback, shimClient);
|
||||
|
||||
int rc = client.unsubscribe((char*)"topic");
|
||||
IS_FALSE(rc);
|
||||
|
||||
IS_FALSE(shimClient.error());
|
||||
|
||||
END_IT
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
SUITE("Subscribe");
|
||||
test_subscribe_no_qos();
|
||||
test_subscribe_qos_1();
|
||||
test_subscribe_not_connected();
|
||||
test_subscribe_invalid_qos();
|
||||
test_subscribe_too_long();
|
||||
test_unsubscribe();
|
||||
test_unsubscribe_not_connected();
|
||||
FINISH
|
||||
}
|
0
lib/pubsubclient/tests/testcases/__init__.py
Normal file
0
lib/pubsubclient/tests/testcases/__init__.py
Normal file
39
lib/pubsubclient/tests/testcases/mqtt_basic.py
Normal file
39
lib/pubsubclient/tests/testcases/mqtt_basic.py
Normal file
@ -0,0 +1,39 @@
|
||||
import unittest
|
||||
import settings
|
||||
import time
|
||||
import mosquitto
|
||||
|
||||
|
||||
def on_message(mosq, obj, msg):
|
||||
obj.message_queue.append(msg)
|
||||
|
||||
|
||||
class mqtt_basic(unittest.TestCase):
|
||||
|
||||
message_queue = []
|
||||
|
||||
@classmethod
|
||||
def setUpClass(self):
|
||||
self.client = mosquitto.Mosquitto("pubsubclient_ut", clean_session=True, obj=self)
|
||||
self.client.connect(settings.server_ip)
|
||||
self.client.on_message = on_message
|
||||
self.client.subscribe("outTopic", 0)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(self):
|
||||
self.client.disconnect()
|
||||
|
||||
def test_one(self):
|
||||
i = 30
|
||||
while len(self.message_queue) == 0 and i > 0:
|
||||
self.client.loop()
|
||||
time.sleep(0.5)
|
||||
i -= 1
|
||||
self.assertTrue(i > 0, "message receive timed-out")
|
||||
self.assertEqual(len(self.message_queue), 1, "unexpected number of messages received")
|
||||
msg = self.message_queue[0]
|
||||
self.assertEqual(msg.mid, 0, "message id not 0")
|
||||
self.assertEqual(msg.topic, "outTopic", "message topic incorrect")
|
||||
self.assertEqual(msg.payload, "hello world")
|
||||
self.assertEqual(msg.qos, 0, "message qos not 0")
|
||||
self.assertEqual(msg.retain, False, "message retain flag incorrect")
|
59
lib/pubsubclient/tests/testcases/mqtt_publish_in_callback.py
Normal file
59
lib/pubsubclient/tests/testcases/mqtt_publish_in_callback.py
Normal file
@ -0,0 +1,59 @@
|
||||
import unittest
|
||||
import settings
|
||||
import time
|
||||
import mosquitto
|
||||
|
||||
|
||||
def on_message(mosq, obj, msg):
|
||||
obj.message_queue.append(msg)
|
||||
|
||||
|
||||
class mqtt_publish_in_callback(unittest.TestCase):
|
||||
|
||||
message_queue = []
|
||||
|
||||
@classmethod
|
||||
def setUpClass(self):
|
||||
self.client = mosquitto.Mosquitto("pubsubclient_ut", clean_session=True, obj=self)
|
||||
self.client.connect(settings.server_ip)
|
||||
self.client.on_message = on_message
|
||||
self.client.subscribe("outTopic", 0)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(self):
|
||||
self.client.disconnect()
|
||||
|
||||
def test_connect(self):
|
||||
i = 30
|
||||
while len(self.message_queue) == 0 and i > 0:
|
||||
self.client.loop()
|
||||
time.sleep(0.5)
|
||||
i -= 1
|
||||
self.assertTrue(i > 0, "message receive timed-out")
|
||||
self.assertEqual(len(self.message_queue), 1, "unexpected number of messages received")
|
||||
msg = self.message_queue.pop(0)
|
||||
self.assertEqual(msg.mid, 0, "message id not 0")
|
||||
self.assertEqual(msg.topic, "outTopic", "message topic incorrect")
|
||||
self.assertEqual(msg.payload, "hello world")
|
||||
self.assertEqual(msg.qos, 0, "message qos not 0")
|
||||
self.assertEqual(msg.retain, False, "message retain flag incorrect")
|
||||
|
||||
def test_publish(self):
|
||||
self.assertEqual(len(self.message_queue), 0, "message queue not empty")
|
||||
payload = "abcdefghij"
|
||||
self.client.publish("inTopic", payload)
|
||||
|
||||
i = 30
|
||||
while len(self.message_queue) == 0 and i > 0:
|
||||
self.client.loop()
|
||||
time.sleep(0.5)
|
||||
i -= 1
|
||||
|
||||
self.assertTrue(i > 0, "message receive timed-out")
|
||||
self.assertEqual(len(self.message_queue), 1, "unexpected number of messages received")
|
||||
msg = self.message_queue.pop(0)
|
||||
self.assertEqual(msg.mid, 0, "message id not 0")
|
||||
self.assertEqual(msg.topic, "outTopic", "message topic incorrect")
|
||||
self.assertEqual(msg.payload, payload)
|
||||
self.assertEqual(msg.qos, 0, "message qos not 0")
|
||||
self.assertEqual(msg.retain, False, "message retain flag incorrect")
|
2
lib/pubsubclient/tests/testcases/settings.py
Normal file
2
lib/pubsubclient/tests/testcases/settings.py
Normal file
@ -0,0 +1,2 @@
|
||||
server_ip = "172.16.0.2"
|
||||
arduino_ip = "172.16.0.100"
|
181
lib/pubsubclient/tests/testsuite.py
Normal file
181
lib/pubsubclient/tests/testsuite.py
Normal file
@ -0,0 +1,181 @@
|
||||
#!/usr/bin/env python
|
||||
import os
|
||||
import os.path
|
||||
import sys
|
||||
import shutil
|
||||
from subprocess import call
|
||||
import importlib
|
||||
import unittest
|
||||
import re
|
||||
|
||||
from testcases import settings
|
||||
|
||||
|
||||
class Workspace(object):
|
||||
|
||||
def __init__(self):
|
||||
self.root_dir = os.getcwd()
|
||||
self.build_dir = os.path.join(self.root_dir, "tmpbin")
|
||||
self.log_dir = os.path.join(self.root_dir, "logs")
|
||||
self.tests_dir = os.path.join(self.root_dir, "testcases")
|
||||
self.examples_dir = os.path.join(self.root_dir, "../PubSubClient/examples")
|
||||
self.examples = []
|
||||
self.tests = []
|
||||
if not os.path.isdir("../PubSubClient"):
|
||||
raise Exception("Cannot find PubSubClient library")
|
||||
try:
|
||||
return __import__('ino')
|
||||
except ImportError:
|
||||
raise Exception("ino tool not installed")
|
||||
|
||||
def init(self):
|
||||
if os.path.isdir(self.build_dir):
|
||||
shutil.rmtree(self.build_dir)
|
||||
os.mkdir(self.build_dir)
|
||||
if os.path.isdir(self.log_dir):
|
||||
shutil.rmtree(self.log_dir)
|
||||
os.mkdir(self.log_dir)
|
||||
|
||||
os.chdir(self.build_dir)
|
||||
call(["ino", "init"])
|
||||
|
||||
shutil.copytree("../../PubSubClient", "lib/PubSubClient")
|
||||
|
||||
filenames = []
|
||||
for root, dirs, files in os.walk(self.examples_dir):
|
||||
filenames += [os.path.join(root, f) for f in files if f.endswith(".ino")]
|
||||
filenames.sort()
|
||||
for e in filenames:
|
||||
self.examples.append(Sketch(self, e))
|
||||
|
||||
filenames = []
|
||||
for root, dirs, files in os.walk(self.tests_dir):
|
||||
filenames += [os.path.join(root, f) for f in files if f.endswith(".ino")]
|
||||
filenames.sort()
|
||||
for e in filenames:
|
||||
self.tests.append(Sketch(self, e))
|
||||
|
||||
def clean(self):
|
||||
shutil.rmtree(self.build_dir)
|
||||
|
||||
|
||||
class Sketch(object):
|
||||
def __init__(self, wksp, fn):
|
||||
self.w = wksp
|
||||
self.filename = fn
|
||||
self.basename = os.path.basename(self.filename)
|
||||
self.build_log = os.path.join(self.w.log_dir, "%s.log" % (os.path.basename(self.filename),))
|
||||
self.build_err_log = os.path.join(self.w.log_dir, "%s.err.log" % (os.path.basename(self.filename),))
|
||||
self.build_upload_log = os.path.join(self.w.log_dir, "%s.upload.log" % (os.path.basename(self.filename),))
|
||||
|
||||
def build(self):
|
||||
sys.stdout.write(" Build: ")
|
||||
sys.stdout.flush()
|
||||
|
||||
# Copy sketch over, replacing IP addresses as necessary
|
||||
fin = open(self.filename, "r")
|
||||
lines = fin.readlines()
|
||||
fin.close()
|
||||
fout = open(os.path.join(self.w.build_dir, "src", "sketch.ino"), "w")
|
||||
for l in lines:
|
||||
if re.match(r"^byte server\[\] = {", l):
|
||||
fout.write("byte server[] = { %s };\n" % (settings.server_ip.replace(".", ", "),))
|
||||
elif re.match(r"^byte ip\[\] = {", l):
|
||||
fout.write("byte ip[] = { %s };\n" % (settings.arduino_ip.replace(".", ", "),))
|
||||
else:
|
||||
fout.write(l)
|
||||
fout.flush()
|
||||
fout.close()
|
||||
|
||||
# Run build
|
||||
fout = open(self.build_log, "w")
|
||||
ferr = open(self.build_err_log, "w")
|
||||
rc = call(["ino", "build"], stdout=fout, stderr=ferr)
|
||||
fout.close()
|
||||
ferr.close()
|
||||
if rc == 0:
|
||||
sys.stdout.write("pass")
|
||||
sys.stdout.write("\n")
|
||||
return True
|
||||
else:
|
||||
sys.stdout.write("fail")
|
||||
sys.stdout.write("\n")
|
||||
with open(self.build_err_log) as f:
|
||||
for line in f:
|
||||
print(" " + line)
|
||||
return False
|
||||
|
||||
def upload(self):
|
||||
sys.stdout.write(" Upload: ")
|
||||
sys.stdout.flush()
|
||||
fout = open(self.build_upload_log, "w")
|
||||
rc = call(["ino", "upload"], stdout=fout, stderr=fout)
|
||||
fout.close()
|
||||
if rc == 0:
|
||||
sys.stdout.write("pass")
|
||||
sys.stdout.write("\n")
|
||||
return True
|
||||
else:
|
||||
sys.stdout.write("fail")
|
||||
sys.stdout.write("\n")
|
||||
with open(self.build_upload_log) as f:
|
||||
for line in f:
|
||||
print(" " + line)
|
||||
return False
|
||||
|
||||
def test(self):
|
||||
# import the matching test case, if it exists
|
||||
try:
|
||||
basename = os.path.basename(self.filename)[:-4]
|
||||
i = importlib.import_module("testcases." + basename)
|
||||
except:
|
||||
sys.stdout.write(" Test: no tests found")
|
||||
sys.stdout.write("\n")
|
||||
return
|
||||
c = getattr(i, basename)
|
||||
|
||||
testmethods = [m for m in dir(c) if m.startswith("test_")]
|
||||
testmethods.sort()
|
||||
tests = []
|
||||
for m in testmethods:
|
||||
tests.append(c(m))
|
||||
|
||||
result = unittest.TestResult()
|
||||
c.setUpClass()
|
||||
if self.upload():
|
||||
sys.stdout.write(" Test: ")
|
||||
sys.stdout.flush()
|
||||
for t in tests:
|
||||
t.run(result)
|
||||
print(str(result.testsRun - len(result.failures) - len(result.errors)) + "/" + str(result.testsRun))
|
||||
if not result.wasSuccessful():
|
||||
if len(result.failures) > 0:
|
||||
for f in result.failures:
|
||||
print("-- " + str(f[0]))
|
||||
print(f[1])
|
||||
if len(result.errors) > 0:
|
||||
print(" Errors:")
|
||||
for f in result.errors:
|
||||
print("-- " + str(f[0]))
|
||||
print(f[1])
|
||||
c.tearDownClass()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
run_tests = True
|
||||
|
||||
w = Workspace()
|
||||
w.init()
|
||||
|
||||
for e in w.examples:
|
||||
print("--------------------------------------")
|
||||
print("[" + e.basename + "]")
|
||||
if e.build() and run_tests:
|
||||
e.test()
|
||||
for e in w.tests:
|
||||
print("--------------------------------------")
|
||||
print("[" + e.basename + "]")
|
||||
if e.build() and run_tests:
|
||||
e.test()
|
||||
|
||||
w.clean()
|
Reference in New Issue
Block a user