From acf0caa04a80063ad009e0d9ba45397c2a1319f5 Mon Sep 17 00:00:00 2001 From: David Cermak Date: Mon, 4 Oct 2021 10:24:31 +0200 Subject: [PATCH 1/3] MQTT: Add more unit tests with actual broker --- components/mqtt/test/Kconfig | 9 ++ components/mqtt/test/connection.c | 138 +++++++++++++++++++++++++ components/mqtt/test/test_mqtt.c | 54 +++++++++- tools/unit-test-app/sdkconfig.defaults | 1 + 4 files changed, 200 insertions(+), 2 deletions(-) create mode 100644 components/mqtt/test/Kconfig create mode 100644 components/mqtt/test/connection.c diff --git a/components/mqtt/test/Kconfig b/components/mqtt/test/Kconfig new file mode 100644 index 0000000000..e006f92f25 --- /dev/null +++ b/components/mqtt/test/Kconfig @@ -0,0 +1,9 @@ +menu "ESP-MQTT Unit Test Config" + + config MQTT_TEST_BROKER_URI + string "URI of the test broker" + default "mqtt://mqtt.eclipseprojects.io" + help + URL of an mqtt broker which this test connects to. + +endmenu diff --git a/components/mqtt/test/connection.c b/components/mqtt/test/connection.c new file mode 100644 index 0000000000..213d6b3528 --- /dev/null +++ b/components/mqtt/test/connection.c @@ -0,0 +1,138 @@ +/* + * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "freertos/FreeRTOS.h" +#include "freertos/event_groups.h" +#include "unity.h" +#include "esp_event.h" +#include "esp_eth.h" +#include "esp_log.h" + +#if SOC_EMAC_SUPPORTED +#define ETH_START_BIT BIT(0) +#define ETH_STOP_BIT BIT(1) +#define ETH_CONNECT_BIT BIT(2) +#define ETH_GOT_IP_BIT BIT(3) +#define ETH_STOP_TIMEOUT_MS (10000) +#define ETH_GET_IP_TIMEOUT_MS (60000) + +static const char *TAG = "esp32_eth_test_fixture"; + +/** Event handler for Ethernet events */ +static void eth_event_handler(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + EventGroupHandle_t eth_event_group = (EventGroupHandle_t)arg; + switch (event_id) { + case ETHERNET_EVENT_CONNECTED: + xEventGroupSetBits(eth_event_group, ETH_CONNECT_BIT); + ESP_LOGI(TAG, "Ethernet Link Up"); + break; + case ETHERNET_EVENT_DISCONNECTED: + ESP_LOGI(TAG, "Ethernet Link Down"); + break; + case ETHERNET_EVENT_START: + xEventGroupSetBits(eth_event_group, ETH_START_BIT); + ESP_LOGI(TAG, "Ethernet Started"); + break; + case ETHERNET_EVENT_STOP: + xEventGroupSetBits(eth_event_group, ETH_STOP_BIT); + ESP_LOGI(TAG, "Ethernet Stopped"); + break; + default: + break; + } +} + +/** Event handler for IP_EVENT_ETH_GOT_IP */ +static void got_ip_event_handler(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + EventGroupHandle_t eth_event_group = (EventGroupHandle_t)arg; + ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data; + const esp_netif_ip_info_t *ip_info = &event->ip_info; + ESP_LOGI(TAG, "Ethernet Got IP Address"); + ESP_LOGI(TAG, "~~~~~~~~~~~"); + ESP_LOGI(TAG, "ETHIP:" IPSTR, IP2STR(&ip_info->ip)); + ESP_LOGI(TAG, "ETHMASK:" IPSTR, IP2STR(&ip_info->netmask)); + ESP_LOGI(TAG, "ETHGW:" IPSTR, IP2STR(&ip_info->gw)); + ESP_LOGI(TAG, "~~~~~~~~~~~"); + xEventGroupSetBits(eth_event_group, ETH_GOT_IP_BIT); +} + +static esp_err_t test_uninstall_driver(esp_eth_handle_t eth_hdl, uint32_t ms_to_wait) +{ + int i = 0; + ms_to_wait += 100; + for (i = 0; i < ms_to_wait / 100; i++) { + vTaskDelay(pdMS_TO_TICKS(100)); + if (esp_eth_driver_uninstall(eth_hdl) == ESP_OK) { + break; + } + } + if (i < ms_to_wait / 10) { + return ESP_OK; + } else { + return ESP_FAIL; + } +} +static EventGroupHandle_t eth_event_group; +static esp_netif_t *eth_netif; +static esp_eth_mac_t *mac; +static esp_eth_phy_t *phy; +static esp_eth_handle_t eth_handle = NULL; +static esp_eth_netif_glue_handle_t glue; + +void eth_test_fixture_connect(void) +{ + EventBits_t bits; + eth_event_group = xEventGroupCreate(); + TEST_ASSERT(eth_event_group != NULL); + TEST_ESP_OK(esp_event_loop_create_default()); + // create TCP/IP netif + esp_netif_config_t netif_cfg = ESP_NETIF_DEFAULT_ETH(); + eth_netif = esp_netif_new(&netif_cfg); + + eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG(); + mac = esp_eth_mac_new_esp32(&mac_config); + eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); + phy = esp_eth_phy_new_ip101(&phy_config); + esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, phy); + + // install Ethernet driver + TEST_ESP_OK(esp_eth_driver_install(ð_config, ð_handle)); + // combine driver with netif + glue = esp_eth_new_netif_glue(eth_handle); + TEST_ESP_OK(esp_netif_attach(eth_netif, glue)); + // register user defined event handers + TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, ð_event_handler, eth_event_group)); + TEST_ESP_OK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &got_ip_event_handler, eth_event_group)); + // start Ethernet driver + TEST_ESP_OK(esp_eth_start(eth_handle)); + /* wait for IP lease */ + bits = xEventGroupWaitBits(eth_event_group, ETH_GOT_IP_BIT, true, true, pdMS_TO_TICKS(ETH_GET_IP_TIMEOUT_MS)); + TEST_ASSERT((bits & ETH_GOT_IP_BIT) == ETH_GOT_IP_BIT); +} + +void eth_test_fixture_deinit(void) +{ + EventBits_t bits; + // stop Ethernet driver + TEST_ESP_OK(esp_eth_stop(eth_handle)); + /* wait for connection stop */ + bits = xEventGroupWaitBits(eth_event_group, ETH_STOP_BIT, true, true, pdMS_TO_TICKS(ETH_STOP_TIMEOUT_MS)); + TEST_ASSERT((bits & ETH_STOP_BIT) == ETH_STOP_BIT); + TEST_ESP_OK(esp_eth_del_netif_glue(glue)); + /* driver should be uninstalled within 2 seconds */ + TEST_ESP_OK(test_uninstall_driver(eth_handle, 2000)); + TEST_ESP_OK(phy->del(phy)); + TEST_ESP_OK(mac->del(mac)); + TEST_ESP_OK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_ETH_GOT_IP, got_ip_event_handler)); + TEST_ESP_OK(esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler)); + esp_netif_destroy(eth_netif); + TEST_ESP_OK(esp_event_loop_delete_default()); + vEventGroupDelete(eth_event_group); +} +#endif // SOC_EMAC_SUPPORTED diff --git a/components/mqtt/test/test_mqtt.c b/components/mqtt/test/test_mqtt.c index 1cf06188a2..5cc0e47cf6 100644 --- a/components/mqtt/test/test_mqtt.c +++ b/components/mqtt/test/test_mqtt.c @@ -1,9 +1,13 @@ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/event_groups.h" +#include "unity.h" #include "test_utils.h" #include "mqtt_client.h" -#include "unity.h" -#include #include "nvs_flash.h" #include "esp_ota_ops.h" +#include "sdkconfig.h" +#include "soc/soc_caps.h" static void test_leak_setup(const char * file, long line) { @@ -68,3 +72,49 @@ TEST_CASE("mqtt enqueue and destroy outbox", "[mqtt][leaks=0]") esp_mqtt_client_destroy(client); } + +#if SOC_EMAC_SUPPORTED + +void eth_test_fixture_connect(void); +void eth_test_fixture_deinit(void); + +static const int CONNECT_BIT = BIT0; +static const int DISCONNECT_BIT = BIT1; + +static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) +{ + EventGroupHandle_t *event_group = handler_args; + switch ((esp_mqtt_event_id_t)event_id) { + case MQTT_EVENT_CONNECTED: + xEventGroupSetBits(*event_group, CONNECT_BIT); + break; + case MQTT_EVENT_DISCONNECTED: + xEventGroupSetBits(*event_group, DISCONNECT_BIT); + break; + default: + break; + } +} + +TEST_CASE("connect disconnect", "[mqtt][test_env=UT_T2_Ethernet]") +{ + test_leak_setup(__FILE__, __LINE__); + test_case_uses_tcpip(); + eth_test_fixture_connect(); + const int TEST_CONNECT_TIMEOUT = 10000; + const esp_mqtt_client_config_t mqtt_cfg = { + // no connection takes place, but the uri has to be valid for init() to succeed + .uri = CONFIG_MQTT_TEST_BROKER_URI, + }; + EventGroupHandle_t event_group = xEventGroupCreate(); + esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg); + TEST_ASSERT_NOT_EQUAL(NULL, client ); + esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, &event_group); + TEST_ASSERT_EQUAL(ESP_OK, esp_mqtt_client_start(client)); + TEST_ASSERT_TRUE(xEventGroupWaitBits(event_group, CONNECT_BIT, pdTRUE, pdTRUE, pdMS_TO_TICKS(TEST_CONNECT_TIMEOUT)) & CONNECT_BIT); + esp_mqtt_client_disconnect(client); + TEST_ASSERT_TRUE(xEventGroupWaitBits(event_group, DISCONNECT_BIT, pdTRUE, pdTRUE, pdMS_TO_TICKS(TEST_CONNECT_TIMEOUT)) & DISCONNECT_BIT); + esp_mqtt_client_destroy(client); + eth_test_fixture_deinit(); +} +#endif // SOC_EMAC_SUPPORTED diff --git a/tools/unit-test-app/sdkconfig.defaults b/tools/unit-test-app/sdkconfig.defaults index 42b3b3effa..fac8729fd5 100644 --- a/tools/unit-test-app/sdkconfig.defaults +++ b/tools/unit-test-app/sdkconfig.defaults @@ -26,3 +26,4 @@ CONFIG_FATFS_ALLOC_PREFER_EXTRAM=y CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL=y CONFIG_ESP_NETIF_TCPIP_ADAPTER_COMPATIBLE_LAYER=n CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=3000 +CONFIG_MQTT_TEST_BROKER_URI="mqtt://${EXAMPLE_MQTT_BROKER_TCP}" From 3b0dcf890158cd41708ac9c13e7ee9214cff87db Mon Sep 17 00:00:00 2001 From: David Cermak Date: Wed, 6 Oct 2021 17:52:22 +0200 Subject: [PATCH 2/3] MQTT: Add more tests --- components/mqtt/test/test_mqtt.c | 55 ++--- .../mqtt/test/test_mqtt_client_broker.c | 226 ++++++++++++++++++ .../mqtt/test/test_mqtt_client_broker.h | 50 ++++ .../{connection.c => test_mqtt_connection.c} | 62 ++--- components/mqtt/test/test_mqtt_connection.h | 18 ++ 5 files changed, 341 insertions(+), 70 deletions(-) create mode 100644 components/mqtt/test/test_mqtt_client_broker.c create mode 100644 components/mqtt/test/test_mqtt_client_broker.h rename components/mqtt/test/{connection.c => test_mqtt_connection.c} (70%) create mode 100644 components/mqtt/test/test_mqtt_connection.h diff --git a/components/mqtt/test/test_mqtt.c b/components/mqtt/test/test_mqtt.c index 5cc0e47cf6..ede3b31a79 100644 --- a/components/mqtt/test/test_mqtt.c +++ b/components/mqtt/test/test_mqtt.c @@ -7,7 +7,8 @@ #include "nvs_flash.h" #include "esp_ota_ops.h" #include "sdkconfig.h" -#include "soc/soc_caps.h" +#include "test_mqtt_client_broker.h" +#include "test_mqtt_connection.h" static void test_leak_setup(const char * file, long line) { @@ -74,47 +75,19 @@ TEST_CASE("mqtt enqueue and destroy outbox", "[mqtt][leaks=0]") } #if SOC_EMAC_SUPPORTED - -void eth_test_fixture_connect(void); -void eth_test_fixture_deinit(void); - -static const int CONNECT_BIT = BIT0; -static const int DISCONNECT_BIT = BIT1; - -static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) +/** + * This test cases uses ethernet kit, so build and use it only if EMAC supported + */ +TEST_CASE("mqtt broker tests", "[mqtt][test_env=UT_T2_Ethernet]") { - EventGroupHandle_t *event_group = handler_args; - switch ((esp_mqtt_event_id_t)event_id) { - case MQTT_EVENT_CONNECTED: - xEventGroupSetBits(*event_group, CONNECT_BIT); - break; - case MQTT_EVENT_DISCONNECTED: - xEventGroupSetBits(*event_group, DISCONNECT_BIT); - break; - default: - break; - } -} - -TEST_CASE("connect disconnect", "[mqtt][test_env=UT_T2_Ethernet]") -{ - test_leak_setup(__FILE__, __LINE__); test_case_uses_tcpip(); - eth_test_fixture_connect(); - const int TEST_CONNECT_TIMEOUT = 10000; - const esp_mqtt_client_config_t mqtt_cfg = { - // no connection takes place, but the uri has to be valid for init() to succeed - .uri = CONFIG_MQTT_TEST_BROKER_URI, - }; - EventGroupHandle_t event_group = xEventGroupCreate(); - esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg); - TEST_ASSERT_NOT_EQUAL(NULL, client ); - esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, &event_group); - TEST_ASSERT_EQUAL(ESP_OK, esp_mqtt_client_start(client)); - TEST_ASSERT_TRUE(xEventGroupWaitBits(event_group, CONNECT_BIT, pdTRUE, pdTRUE, pdMS_TO_TICKS(TEST_CONNECT_TIMEOUT)) & CONNECT_BIT); - esp_mqtt_client_disconnect(client); - TEST_ASSERT_TRUE(xEventGroupWaitBits(event_group, DISCONNECT_BIT, pdTRUE, pdTRUE, pdMS_TO_TICKS(TEST_CONNECT_TIMEOUT)) & DISCONNECT_BIT); - esp_mqtt_client_destroy(client); - eth_test_fixture_deinit(); + connect_test_fixture_setup(); + + RUN_MQTT_BROKER_TEST(mqtt_connect_disconnect); + RUN_MQTT_BROKER_TEST(mqtt_subscribe_publish); + RUN_MQTT_BROKER_TEST(mqtt_lwt_clean_disconnect); + RUN_MQTT_BROKER_TEST(mqtt_subscribe_payload); + + connect_test_fixture_teardown(); } #endif // SOC_EMAC_SUPPORTED diff --git a/components/mqtt/test/test_mqtt_client_broker.c b/components/mqtt/test/test_mqtt_client_broker.c new file mode 100644 index 0000000000..167a00752b --- /dev/null +++ b/components/mqtt/test/test_mqtt_client_broker.c @@ -0,0 +1,226 @@ +/* + * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "freertos/FreeRTOS.h" +#include "freertos/event_groups.h" +#include "mqtt_client.h" +#include "esp_log.h" + +#define WAIT_FOR_EVENT(event) \ + TEST_ASSERT_TRUE(xEventGroupWaitBits(s_event_group, event, pdTRUE, pdTRUE, pdMS_TO_TICKS(COMMON_OPERATION_TIMEOUT)) & event); + +#define TEST_ASSERT_TRUE(condition) TEST_ASSERT_TRUE_LINE(condition, __LINE__) +#define TEST_ASSERT_TRUE_LINE(condition, line) \ + do { \ + if (!(condition)) { \ + ESP_LOGE("test_mqtt_client_broker.c", \ + "Assertion failed in line %d", line); \ + return false; \ + } \ + } while(0) + + +static const int COMMON_OPERATION_TIMEOUT = 10000; +static const int CONNECT_BIT = BIT0; +static const int DISCONNECT_BIT = BIT1; +static const int DATA_BIT = BIT2; + +static EventGroupHandle_t s_event_group; + +static char* append_mac(const char* string) +{ + uint8_t mac[6]; + char *id_string = NULL; + esp_read_mac(mac, ESP_MAC_WIFI_STA); + asprintf(&id_string, "%s_%02x%02X%02X", string, mac[3], mac[4], mac[5]); + return id_string; +} + +static void mqtt_data_handler_qos(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) +{ + if (event_id == MQTT_EVENT_DATA) { + esp_mqtt_event_handle_t event = event_data; + int * qos = handler_args; + *qos = event->qos; + xEventGroupSetBits(s_event_group, DATA_BIT); + } +} + +static void mqtt_data_handler_lwt(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) +{ + if (event_id == MQTT_EVENT_DATA) { + esp_mqtt_event_handle_t event = event_data; + ESP_LOGI("mqtt-lwt", "MQTT_EVENT_DATA"); + ESP_LOGI("mqtt-lwt", "TOPIC=%.*s", event->topic_len, event->topic); + ESP_LOGI("mqtt-lwt", "DATA=%.*s", event->data_len, event->data); + if (strncmp(event->data, "no-lwt", event->data_len) == 0) { + // no lwt, just to indicate the test has finished + xEventGroupSetBits(s_event_group, DATA_BIT); + } else { + // count up any potential lwt message + int * count = handler_args; + *count = *count + 1; + ESP_LOGE("mqtt-lwt", "count=%d", *count); + } + } +} + +static void mqtt_data_handler_subscribe(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) +{ + if (event_id == MQTT_EVENT_SUBSCRIBED) { + esp_mqtt_event_handle_t event = event_data; + ESP_LOGI("mqtt-subscribe", "MQTT_EVENT_SUBSCRIBED, data size=%d", event->data_len); + int * sub_payload = handler_args; + if (event->data_len == 1) { + ESP_LOGI("mqtt-subscribe", "DATA=%d", *(uint8_t*)event->data); + *sub_payload = *(uint8_t*)event->data; + } + xEventGroupSetBits(s_event_group, DATA_BIT); + } +} + + +static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) +{ + switch ((esp_mqtt_event_id_t)event_id) { + case MQTT_EVENT_CONNECTED: + xEventGroupSetBits(s_event_group, CONNECT_BIT); + break; + + case MQTT_EVENT_DISCONNECTED: + xEventGroupSetBits(s_event_group, DISCONNECT_BIT); + break; + default: + break; + } +} + +bool mqtt_connect_disconnect(void) +{ + const esp_mqtt_client_config_t mqtt_cfg = { + .uri = CONFIG_MQTT_TEST_BROKER_URI, + .disable_auto_reconnect = true, + }; + s_event_group = xEventGroupCreate(); + esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg); + TEST_ASSERT_TRUE(NULL != client ); + esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL); + TEST_ASSERT_TRUE(ESP_OK == esp_mqtt_client_start(client)); + WAIT_FOR_EVENT(CONNECT_BIT); + esp_mqtt_client_disconnect(client); + WAIT_FOR_EVENT(DISCONNECT_BIT); + esp_mqtt_client_reconnect(client); + WAIT_FOR_EVENT(CONNECT_BIT); + esp_mqtt_client_destroy(client); + vEventGroupDelete(s_event_group); + return true; +} + +bool mqtt_subscribe_publish(void) +{ + const esp_mqtt_client_config_t mqtt_cfg = { + .uri = CONFIG_MQTT_TEST_BROKER_URI, + }; + char* topic = append_mac("topic"); + TEST_ASSERT_TRUE(NULL != topic); + s_event_group = xEventGroupCreate(); + esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg); + TEST_ASSERT_TRUE(NULL != client ); + esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL); + TEST_ASSERT_TRUE(ESP_OK == esp_mqtt_client_start(client)); + WAIT_FOR_EVENT(CONNECT_BIT); + int qos = -1; + esp_mqtt_client_register_event(client, MQTT_EVENT_DATA, mqtt_data_handler_qos, &qos); + TEST_ASSERT_TRUE(esp_mqtt_client_subscribe(client, topic, 2) != -1); + TEST_ASSERT_TRUE(esp_mqtt_client_publish(client, topic, "message", 0, 2, 0) != -1); + WAIT_FOR_EVENT(DATA_BIT); + TEST_ASSERT_TRUE(qos == 2); + TEST_ASSERT_TRUE(esp_mqtt_client_publish(client, topic, "message", 0, 1, 0) != -1); + WAIT_FOR_EVENT(DATA_BIT); + TEST_ASSERT_TRUE(qos == 1); + esp_mqtt_client_destroy(client); + vEventGroupDelete(s_event_group); + free(topic); + return true; +} + +bool mqtt_lwt_clean_disconnect(void) +{ + char* lwt = append_mac("lwt"); + TEST_ASSERT_TRUE(lwt); + const esp_mqtt_client_config_t mqtt_cfg1 = { + .uri = CONFIG_MQTT_TEST_BROKER_URI, + .set_null_client_id = true, + .lwt_topic = lwt, + .lwt_msg = "lwt_msg" + }; + const esp_mqtt_client_config_t mqtt_cfg2 = { + .uri = CONFIG_MQTT_TEST_BROKER_URI, + .set_null_client_id = true, + .lwt_topic = lwt, + .lwt_msg = "lwt_msg" + }; + s_event_group = xEventGroupCreate(); + + esp_mqtt_client_handle_t client1 = esp_mqtt_client_init(&mqtt_cfg1); + esp_mqtt_client_handle_t client2 = esp_mqtt_client_init(&mqtt_cfg2); + TEST_ASSERT_TRUE(NULL != client1 && NULL != client2 ); + esp_mqtt_client_register_event(client1, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL); + esp_mqtt_client_register_event(client2, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL); + TEST_ASSERT_TRUE(esp_mqtt_client_start(client1) == ESP_OK); + WAIT_FOR_EVENT(CONNECT_BIT); + TEST_ASSERT_TRUE(esp_mqtt_client_start(client2) == ESP_OK); + WAIT_FOR_EVENT(CONNECT_BIT); + int counter = 0; + esp_mqtt_client_register_event(client1, MQTT_EVENT_DATA, mqtt_data_handler_lwt, &counter); + esp_mqtt_client_register_event(client2, MQTT_EVENT_DATA, mqtt_data_handler_lwt, &counter); + TEST_ASSERT_TRUE(esp_mqtt_client_subscribe(client1, lwt, 0) != -1); + TEST_ASSERT_TRUE(esp_mqtt_client_subscribe(client2, lwt, 0) != -1); + esp_mqtt_client_disconnect(client1); + WAIT_FOR_EVENT(DISCONNECT_BIT); + esp_mqtt_client_reconnect(client1); + WAIT_FOR_EVENT(CONNECT_BIT); + TEST_ASSERT_TRUE(esp_mqtt_client_subscribe(client1, lwt, 0) != -1); + esp_mqtt_client_stop(client2); + esp_mqtt_client_start(client2); + WAIT_FOR_EVENT(CONNECT_BIT); + TEST_ASSERT_TRUE(esp_mqtt_client_subscribe(client2, lwt, 0) != -1); + TEST_ASSERT_TRUE(esp_mqtt_client_publish(client1, lwt, "no-lwt", 0, 0, 0) != -1); + WAIT_FOR_EVENT(DATA_BIT); + TEST_ASSERT_TRUE(counter == 0); + esp_mqtt_client_destroy(client1); + esp_mqtt_client_destroy(client2); + vEventGroupDelete(s_event_group); + free(lwt); + return true; +} + +bool mqtt_subscribe_payload(void) +{ + const esp_mqtt_client_config_t mqtt_cfg = { + .uri = CONFIG_MQTT_TEST_BROKER_URI, + .disable_auto_reconnect = true, + }; + char* topic = append_mac("topic"); + TEST_ASSERT_TRUE(NULL != topic); + s_event_group = xEventGroupCreate(); + esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg); + TEST_ASSERT_TRUE(NULL != client ); + esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL); + TEST_ASSERT_TRUE(ESP_OK == esp_mqtt_client_start(client)); + WAIT_FOR_EVENT(CONNECT_BIT); + int qos_payload = -1; + esp_mqtt_client_register_event(client, MQTT_EVENT_SUBSCRIBED, mqtt_data_handler_subscribe, &qos_payload); + TEST_ASSERT_TRUE(esp_mqtt_client_subscribe(client, topic, 2) != -1); + WAIT_FOR_EVENT(DATA_BIT); + TEST_ASSERT_TRUE(qos_payload == 2); + TEST_ASSERT_TRUE(esp_mqtt_client_subscribe(client, topic, 0) != -1); + WAIT_FOR_EVENT(DATA_BIT); + TEST_ASSERT_TRUE(qos_payload == 0); + esp_mqtt_client_destroy(client); + vEventGroupDelete(s_event_group); + free(topic); + return true; +} diff --git a/components/mqtt/test/test_mqtt_client_broker.h b/components/mqtt/test/test_mqtt_client_broker.h new file mode 100644 index 0000000000..6bfd5d8a25 --- /dev/null +++ b/components/mqtt/test/test_mqtt_client_broker.h @@ -0,0 +1,50 @@ +/* + * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once +#include "esp_log.h" + +/** + * @brief MQTT client-broker tests are not implemented as separate test cases + * due to time consuming connection setup/teardown. + * This utility macro is used to run functional cases as MQTT tests + * and evaluate as separate assertions in one "mqtt broker tests" test case. + */ +#define RUN_MQTT_BROKER_TEST(test_name) \ + do { \ + ESP_LOGI("mqtt_test", "Running test:" #test_name "()"); \ + TEST_ASSERT_TRUE_MESSAGE(test_name(), "Mqtt test failed: " #test_name "() "); \ + ESP_LOGI("mqtt_test", "Test:" #test_name "() passed "); \ + } while(0) + + +/** + * @brief This module contains mqtt test cases interacting the client with a (real) broker + */ + +/** + * @brief The client subscribes and publishes on the same topic + * and verifies the received published qos in the event + */ +bool mqtt_subscribe_publish(void); + +/** + * @brief The client connects, disconnects and reconnects. + * Tests basic client state transitions + */ +bool mqtt_connect_disconnect(void); + +/** + * @brief Two clients with defined lwt connect and subscribe to lwt topic. + * This test verifies that no lwt is send when each of the client disconnects. + * (we expect a clean disconnection, so no last-will being sent) + */ +bool mqtt_lwt_clean_disconnect(void); + +/** + * @brief The client subscribes to a topic with certain qos + * and verifies the qos in SUBACK message from the broker. + */ +bool mqtt_subscribe_payload(void); diff --git a/components/mqtt/test/connection.c b/components/mqtt/test/test_mqtt_connection.c similarity index 70% rename from components/mqtt/test/connection.c rename to components/mqtt/test/test_mqtt_connection.c index 213d6b3528..bf3f09ab4a 100644 --- a/components/mqtt/test/connection.c +++ b/components/mqtt/test/test_mqtt_connection.c @@ -10,6 +10,7 @@ #include "esp_eth.h" #include "esp_log.h" + #if SOC_EMAC_SUPPORTED #define ETH_START_BIT BIT(0) #define ETH_STOP_BIT BIT(1) @@ -18,7 +19,15 @@ #define ETH_STOP_TIMEOUT_MS (10000) #define ETH_GET_IP_TIMEOUT_MS (60000) + static const char *TAG = "esp32_eth_test_fixture"; +static EventGroupHandle_t s_eth_event_group = NULL; +static esp_netif_t *s_eth_netif = NULL; +static esp_eth_mac_t *s_mac = NULL; +static esp_eth_phy_t *s_phy = NULL; +static esp_eth_handle_t s_eth_handle = NULL; +static esp_eth_netif_glue_handle_t s_eth_glue = NULL; + /** Event handler for Ethernet events */ static void eth_event_handler(void *arg, esp_event_base_t event_base, @@ -78,61 +87,56 @@ static esp_err_t test_uninstall_driver(esp_eth_handle_t eth_hdl, uint32_t ms_to_ return ESP_FAIL; } } -static EventGroupHandle_t eth_event_group; -static esp_netif_t *eth_netif; -static esp_eth_mac_t *mac; -static esp_eth_phy_t *phy; -static esp_eth_handle_t eth_handle = NULL; -static esp_eth_netif_glue_handle_t glue; -void eth_test_fixture_connect(void) + +void connect_test_fixture_setup(void) { EventBits_t bits; - eth_event_group = xEventGroupCreate(); - TEST_ASSERT(eth_event_group != NULL); + s_eth_event_group = xEventGroupCreate(); + TEST_ASSERT(s_eth_event_group != NULL); TEST_ESP_OK(esp_event_loop_create_default()); // create TCP/IP netif esp_netif_config_t netif_cfg = ESP_NETIF_DEFAULT_ETH(); - eth_netif = esp_netif_new(&netif_cfg); + s_eth_netif = esp_netif_new(&netif_cfg); eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG(); - mac = esp_eth_mac_new_esp32(&mac_config); + s_mac = esp_eth_mac_new_esp32(&mac_config); eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); - phy = esp_eth_phy_new_ip101(&phy_config); - esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, phy); + s_phy = esp_eth_phy_new_ip101(&phy_config); + esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(s_mac, s_phy); // install Ethernet driver - TEST_ESP_OK(esp_eth_driver_install(ð_config, ð_handle)); + TEST_ESP_OK(esp_eth_driver_install(ð_config, &s_eth_handle)); // combine driver with netif - glue = esp_eth_new_netif_glue(eth_handle); - TEST_ESP_OK(esp_netif_attach(eth_netif, glue)); + s_eth_glue = esp_eth_new_netif_glue(s_eth_handle); + TEST_ESP_OK(esp_netif_attach(s_eth_netif, s_eth_glue)); // register user defined event handers - TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, ð_event_handler, eth_event_group)); - TEST_ESP_OK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &got_ip_event_handler, eth_event_group)); + TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, ð_event_handler, s_eth_event_group)); + TEST_ESP_OK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &got_ip_event_handler, s_eth_event_group)); // start Ethernet driver - TEST_ESP_OK(esp_eth_start(eth_handle)); + TEST_ESP_OK(esp_eth_start(s_eth_handle)); /* wait for IP lease */ - bits = xEventGroupWaitBits(eth_event_group, ETH_GOT_IP_BIT, true, true, pdMS_TO_TICKS(ETH_GET_IP_TIMEOUT_MS)); + bits = xEventGroupWaitBits(s_eth_event_group, ETH_GOT_IP_BIT, true, true, pdMS_TO_TICKS(ETH_GET_IP_TIMEOUT_MS)); TEST_ASSERT((bits & ETH_GOT_IP_BIT) == ETH_GOT_IP_BIT); } -void eth_test_fixture_deinit(void) +void connect_test_fixture_teardown(void) { EventBits_t bits; // stop Ethernet driver - TEST_ESP_OK(esp_eth_stop(eth_handle)); + TEST_ESP_OK(esp_eth_stop(s_eth_handle)); /* wait for connection stop */ - bits = xEventGroupWaitBits(eth_event_group, ETH_STOP_BIT, true, true, pdMS_TO_TICKS(ETH_STOP_TIMEOUT_MS)); + bits = xEventGroupWaitBits(s_eth_event_group, ETH_STOP_BIT, true, true, pdMS_TO_TICKS(ETH_STOP_TIMEOUT_MS)); TEST_ASSERT((bits & ETH_STOP_BIT) == ETH_STOP_BIT); - TEST_ESP_OK(esp_eth_del_netif_glue(glue)); + TEST_ESP_OK(esp_eth_del_netif_glue(s_eth_glue)); /* driver should be uninstalled within 2 seconds */ - TEST_ESP_OK(test_uninstall_driver(eth_handle, 2000)); - TEST_ESP_OK(phy->del(phy)); - TEST_ESP_OK(mac->del(mac)); + TEST_ESP_OK(test_uninstall_driver(s_eth_handle, 2000)); + TEST_ESP_OK(s_phy->del(s_phy)); + TEST_ESP_OK(s_mac->del(s_mac)); TEST_ESP_OK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_ETH_GOT_IP, got_ip_event_handler)); TEST_ESP_OK(esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler)); - esp_netif_destroy(eth_netif); + esp_netif_destroy(s_eth_netif); TEST_ESP_OK(esp_event_loop_delete_default()); - vEventGroupDelete(eth_event_group); + vEventGroupDelete(s_eth_event_group); } #endif // SOC_EMAC_SUPPORTED diff --git a/components/mqtt/test/test_mqtt_connection.h b/components/mqtt/test/test_mqtt_connection.h new file mode 100644 index 0000000000..5322ddf579 --- /dev/null +++ b/components/mqtt/test/test_mqtt_connection.h @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once +#include "soc/soc_caps.h" + +/** + * Connection test fixture setup, so we expect the broker is available + * on network + */ +void connect_test_fixture_setup(void); + +/** + * Cleans up the connection + */ +void connect_test_fixture_teardown(void); From cd085992c663cb15bce282fe3bd54044d6c2ee9f Mon Sep 17 00:00:00 2001 From: David Cermak Date: Fri, 26 Nov 2021 13:35:35 +0100 Subject: [PATCH 3/3] MQTT: Fix disconnect/reconnect, Adds empty client id, ... Updated MQTT submodule: git log --oneline 89894bd0c611b1392967fe90bb49682eba858383...b86d42c130ac64a916ce6cf299d99f9756692394 * Added support for client with empty id * Fixed user requested disconnect to correctly send MQTT disconnection message * Fixed reconnection request with disabled autoreconnect * Added qos and dup flags to data events * Added Support for suback massage payload in mqtt events Detailed description of the changes (https://github.com/espressif/esp-mqtt/compare/89894bd0c611b1392967fe90bb49682eba858383...b86d42c130ac64a916ce6cf299d99f9756692394): * Adds the possibility of client with empty id - See merge request esp-mqtt!114 - esp_mqtt commit https://github.com/espressif/esp-mqtt/commit/09287a11565fdd26f5c0deb2960566dff676509b - esp_mqtt commit https://github.com/espressif/esp-mqtt/commit/1fd50dd2cbbe9a7847d8e99d2d802c17122a0be9 - Related IDF-4124 * Client: Disconnect/Reconnect improvements - See merge request esp-mqtt!113 - esp_mqtt commit https://github.com/espressif/esp-mqtt/commit/3f05b1aedc4da048d3a41657339e77dfc0bfe0a8 - esp_mqtt commit https://github.com/espressif/esp-mqtt/commit/86e40f86152a522b179b5dcc1dbbd0b288ee8321 - Related https://github.com/espressif/esp-mqtt/issues/206 - Related https://github.com/espressif/esp-mqtt/issues/208 * Events: Support qos/dup flags and suback payload in mqtt events (GitHub PR) - See merge request esp-mqtt!112 - esp_mqtt commit https://github.com/espressif/esp-mqtt/commit/de47f1c3415d38509d9fe3436dc6d35886556954 - esp_mqtt commit https://github.com/espressif/esp-mqtt/commit/e1d5a9402f08a21e3ca755afbbcd8d99be681902 - Related https://github.com/espressif/esp-mqtt/issues/200 - Related https://github.com/espressif/esp-mqtt/pull/203 --- components/mqtt/esp-mqtt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/mqtt/esp-mqtt b/components/mqtt/esp-mqtt index 89894bd0c6..b86d42c130 160000 --- a/components/mqtt/esp-mqtt +++ b/components/mqtt/esp-mqtt @@ -1 +1 @@ -Subproject commit 89894bd0c611b1392967fe90bb49682eba858383 +Subproject commit b86d42c130ac64a916ce6cf299d99f9756692394