From dd178016b2764cb310d88a60f6ac4ffb33bd29c0 Mon Sep 17 00:00:00 2001 From: David Cermak Date: Fri, 13 Sep 2024 18:09:19 +0200 Subject: [PATCH] feat(mqtt): Add publish stress test with local broker --- .../mqtt/publish_connect_test/CMakeLists.txt | 4 -- .../main/Kconfig.projbuild | 23 ++++++++++- .../main/idf_component.yml | 8 ++++ .../main/publish_connect_test.c | 17 +++++++- .../pytest_mqtt_publish_app.py | 40 ++++++++++++++++--- .../sdkconfig.ci.local_broker | 13 ++++++ 6 files changed, 92 insertions(+), 13 deletions(-) create mode 100644 tools/test_apps/protocols/mqtt/publish_connect_test/main/idf_component.yml create mode 100644 tools/test_apps/protocols/mqtt/publish_connect_test/sdkconfig.ci.local_broker diff --git a/tools/test_apps/protocols/mqtt/publish_connect_test/CMakeLists.txt b/tools/test_apps/protocols/mqtt/publish_connect_test/CMakeLists.txt index ee3e421091..4776e7424d 100644 --- a/tools/test_apps/protocols/mqtt/publish_connect_test/CMakeLists.txt +++ b/tools/test_apps/protocols/mqtt/publish_connect_test/CMakeLists.txt @@ -2,10 +2,6 @@ # in this exact order for cmake to work correctly cmake_minimum_required(VERSION 3.16) -# (Not part of the boilerplate) -# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection. -set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common) - include($ENV{IDF_PATH}/tools/cmake/project.cmake) project(mqtt_publish_connect_test) diff --git a/tools/test_apps/protocols/mqtt/publish_connect_test/main/Kconfig.projbuild b/tools/test_apps/protocols/mqtt/publish_connect_test/main/Kconfig.projbuild index 3bea21ce03..8e4cfe56e6 100644 --- a/tools/test_apps/protocols/mqtt/publish_connect_test/main/Kconfig.projbuild +++ b/tools/test_apps/protocols/mqtt/publish_connect_test/main/Kconfig.projbuild @@ -34,7 +34,7 @@ menu "Example Configuration" string "subscribe topic" default "/topic/subscribe/py2esp" help - topic to which esp32 client subsribes (and expects data) + topic to which esp32 client subscribes (and expects data) config EXAMPLE_BROKER_CERTIFICATE_OVERRIDE string "Broker certificate override" @@ -47,6 +47,27 @@ menu "Example Configuration" bool default y if EXAMPLE_BROKER_CERTIFICATE_OVERRIDE != "" + config EXAMPLE_RUN_LOCAL_BROKER + bool "Run local mosquitto" + default n + help + If enabled, this tests uses local mosquitto broker + running on the same endpoint as the client + + config EXAMPLE_BROKER_HOST + string "Broker host address" + default "0.0.0.0" + depends on EXAMPLE_RUN_LOCAL_BROKER + help + Host name of the endpoint to bind the mosquitto listener. + + config EXAMPLE_BROKER_PORT + int "Broker port" + default 1234 + depends on EXAMPLE_RUN_LOCAL_BROKER + help + Port of the endpoint to bind the mosquitto listener + config EXAMPLE_CONNECT_CASE_NO_CERT # Note: All the below config values (EXAMPLE_CONNECT_CASE...) are hidden and # used to give symbolic names to test cases, which are then referenced from both diff --git a/tools/test_apps/protocols/mqtt/publish_connect_test/main/idf_component.yml b/tools/test_apps/protocols/mqtt/publish_connect_test/main/idf_component.yml new file mode 100644 index 0000000000..9eba86b13b --- /dev/null +++ b/tools/test_apps/protocols/mqtt/publish_connect_test/main/idf_component.yml @@ -0,0 +1,8 @@ +## IDF Component Manager Manifest File +dependencies: + espressif/mosquitto: + version: "*" + protocol_examples_common: + path: ${IDF_PATH}/examples/common_components/protocol_examples_common + idf: + version: ">=4.1.0" diff --git a/tools/test_apps/protocols/mqtt/publish_connect_test/main/publish_connect_test.c b/tools/test_apps/protocols/mqtt/publish_connect_test/main/publish_connect_test.c index d92a1fa01b..23a58a6750 100644 --- a/tools/test_apps/protocols/mqtt/publish_connect_test/main/publish_connect_test.c +++ b/tools/test_apps/protocols/mqtt/publish_connect_test/main/publish_connect_test.c @@ -20,6 +20,7 @@ #include "argtable3/argtable3.h" #include "esp_log.h" #include "publish_connect_test.h" +#include "mosq_broker.h" static const char *TAG = "publish_connect_test"; @@ -47,7 +48,7 @@ static int do_init(int argc, char **argv) { (void)argc; (void)argv; const esp_mqtt_client_config_t mqtt_cfg = { - .broker.address.uri = "mqtts://127.0.0.1:1234", + .broker.address.uri = "mqtt://127.0.0.1:1234", .network.disable_auto_reconnect = true }; command_context.mqtt_client = esp_mqtt_client_init(&mqtt_cfg); @@ -78,7 +79,7 @@ static int do_stop(int argc, char **argv) { ESP_LOGE(TAG, "Failed to stop mqtt client task"); return 1; } - ESP_LOGI(TAG, "Mqtt client stoped"); + ESP_LOGI(TAG, "Mqtt client stopped"); return 0; } @@ -286,6 +287,15 @@ void register_connect_commands(void){ ESP_ERROR_CHECK(esp_console_cmd_register(&connection_teardown)); } +#ifdef CONFIG_EXAMPLE_RUN_LOCAL_BROKER +static void broker_task(void* ctx) +{ + // broker continues to run in this task + struct mosq_broker_config config = { .host = CONFIG_EXAMPLE_BROKER_HOST, .port = CONFIG_EXAMPLE_BROKER_PORT }; + mosq_broker_run(&config); +} +#endif // CONFIG_EXAMPLE_RUN_LOCAL_BROKER + void app_main(void) { static const size_t max_line = 256; @@ -301,6 +311,9 @@ void app_main(void) ESP_ERROR_CHECK(nvs_flash_init()); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); +#ifdef CONFIG_EXAMPLE_RUN_LOCAL_BROKER + xTaskCreate(broker_task, "broker", 4096, NULL, 4, NULL); +#endif ESP_ERROR_CHECK(example_connect()); esp_console_repl_t *repl = NULL; esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT(); diff --git a/tools/test_apps/protocols/mqtt/publish_connect_test/pytest_mqtt_publish_app.py b/tools/test_apps/protocols/mqtt/publish_connect_test/pytest_mqtt_publish_app.py index 544c0b7027..763574c13e 100644 --- a/tools/test_apps/protocols/mqtt/publish_connect_test/pytest_mqtt_publish_app.py +++ b/tools/test_apps/protocols/mqtt/publish_connect_test/pytest_mqtt_publish_app.py @@ -1,6 +1,5 @@ -# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: Unlicense OR CC0-1.0 - import contextlib import difflib import logging @@ -9,9 +8,15 @@ import random import re import ssl import string -from itertools import count, product -from threading import Event, Lock -from typing import Any, Dict, List, Tuple, no_type_check +from itertools import count +from itertools import product +from threading import Event +from threading import Lock +from typing import Any +from typing import Dict +from typing import List +from typing import no_type_check +from typing import Tuple import paho.mqtt.client as mqtt import pexpect @@ -152,7 +157,7 @@ def get_scenarios() -> List[Dict[str, int]]: def get_timeout(test_case: Any) -> int: transport, qos, enqueue, scenario = test_case if transport in ['ws', 'wss'] or qos == 2: - return 90 + return 120 return 60 @@ -198,6 +203,11 @@ stress_scenarios = [{'len':20, 'repeat':50}] # many medium sized transport_cases = ['tcp', 'ws', 'wss', 'ssl'] qos_cases = [0, 1, 2] enqueue_cases = [0, 1] +local_broker_supported_transports = ['tcp'] +local_broker_scenarios = [{'len':0, 'repeat':5}, # zero-sized messages + {'len':5, 'repeat':20}, # short messages + {'len':500, 'repeat':10}, # long messages + {'len':20, 'repeat':20}] # many medium sized def make_cases(scenarios: List[Dict[str, int]]) -> List[Tuple[str, int, int, Dict[str, int]]]: @@ -212,6 +222,7 @@ stress_test_cases = make_cases(stress_scenarios) @pytest.mark.ethernet @pytest.mark.nightly_run @pytest.mark.parametrize('test_case', test_cases) +@pytest.mark.parametrize('config', ['default'], indirect=True) def test_mqtt_publish(dut: Dut, test_case: Any) -> None: publish_cfg = get_configurations(dut) dut.expect(re.compile(rb'mqtt>'), timeout=30) @@ -223,8 +234,25 @@ def test_mqtt_publish(dut: Dut, test_case: Any) -> None: @pytest.mark.ethernet @pytest.mark.nightly_run @pytest.mark.parametrize('test_case', stress_test_cases) +@pytest.mark.parametrize('config', ['default'], indirect=True) def test_mqtt_publish_stress(dut: Dut, test_case: Any) -> None: publish_cfg = get_configurations(dut) dut.expect(re.compile(rb'mqtt>'), timeout=30) dut.write('init') run_publish_test_case(dut, test_case, publish_cfg) + + +@pytest.mark.esp32 +@pytest.mark.ethernet +@pytest.mark.parametrize('test_case', make_cases(local_broker_scenarios)) +@pytest.mark.parametrize('config', ['local_broker'], indirect=True) +def test_mqtt_publish_lcoal(dut: Dut, test_case: Any) -> None: + if test_case[0] not in local_broker_supported_transports: + pytest.skip(f'Skipping transport: {test_case[0]}...') + dut_ip = dut.expect(r'esp_netif_handlers: .+ ip: (\d+\.\d+\.\d+\.\d+),').group(1) + publish_cfg = get_configurations(dut) + publish_cfg['broker_host_tcp'] = dut_ip + publish_cfg['broker_port_tcp'] = 1234 + dut.expect(re.compile(rb'mqtt>'), timeout=30) + dut.confirm_write('init', expect_pattern='init', timeout=30) + run_publish_test_case(dut, test_case, publish_cfg) diff --git a/tools/test_apps/protocols/mqtt/publish_connect_test/sdkconfig.ci.local_broker b/tools/test_apps/protocols/mqtt/publish_connect_test/sdkconfig.ci.local_broker new file mode 100644 index 0000000000..0cf44fb785 --- /dev/null +++ b/tools/test_apps/protocols/mqtt/publish_connect_test/sdkconfig.ci.local_broker @@ -0,0 +1,13 @@ +CONFIG_EXAMPLE_BROKER_SSL_URI="" +CONFIG_EXAMPLE_BROKER_TCP_URI="mqtt://127.0.0.1:1234" +CONFIG_EXAMPLE_BROKER_WS_URI="" +CONFIG_EXAMPLE_BROKER_WSS_URI="" +CONFIG_EXAMPLE_BROKER_CERTIFICATE_OVERRIDE="" +CONFIG_ESP_TLS_INSECURE=y +CONFIG_ESP_TLS_SKIP_SERVER_CERT_VERIFY=y +CONFIG_EXAMPLE_CONNECT_ETHERNET=y +CONFIG_EXAMPLE_CONNECT_WIFI=n +CONFIG_MQTT_USE_CUSTOM_CONFIG=y +CONFIG_MQTT_POLL_READ_TIMEOUT_MS=50 +CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE=y +CONFIG_EXAMPLE_RUN_LOCAL_BROKER=y