From 08615177e334827a6e09d3a0c5baf6153249d127 Mon Sep 17 00:00:00 2001 From: Euripedes Rocha Filho Date: Tue, 26 May 2026 15:33:07 +0200 Subject: [PATCH] refactor: Moves outbox to an internal library to improve testing --- .gitlab/ci/test.yml | 2 + CMakeLists.txt | 5 +- lib/mqtt_outbox/CMakeLists.txt | 9 + test/mqtt_outbox_host_test/CMakeLists.txt | 11 + test/mqtt_outbox_host_test/README.md | 14 + .../mqtt_outbox_host_test/main/CMakeLists.txt | 22 ++ test/mqtt_outbox_host_test/main/Kconfig | 9 + .../main/idf_component.yml | 3 + test/mqtt_outbox_host_test/main/test_main.cpp | 23 ++ .../main/test_outbox.cpp | 356 ++++++++++++++++++ .../sdkconfig.ci.coverage | 1 + test/mqtt_outbox_host_test/sdkconfig.defaults | 8 + 12 files changed, 462 insertions(+), 1 deletion(-) create mode 100644 lib/mqtt_outbox/CMakeLists.txt create mode 100644 test/mqtt_outbox_host_test/CMakeLists.txt create mode 100644 test/mqtt_outbox_host_test/README.md create mode 100644 test/mqtt_outbox_host_test/main/CMakeLists.txt create mode 100644 test/mqtt_outbox_host_test/main/Kconfig create mode 100644 test/mqtt_outbox_host_test/main/idf_component.yml create mode 100644 test/mqtt_outbox_host_test/main/test_main.cpp create mode 100644 test/mqtt_outbox_host_test/main/test_outbox.cpp create mode 100644 test/mqtt_outbox_host_test/sdkconfig.ci.coverage create mode 100644 test/mqtt_outbox_host_test/sdkconfig.defaults diff --git a/.gitlab/ci/test.yml b/.gitlab/ci/test.yml index a577b36..9a4f5a6 100644 --- a/.gitlab/ci/test.yml +++ b/.gitlab/ci/test.yml @@ -34,5 +34,7 @@ host_tests: ./build_linux_coverage/host_mqtt_client_test.elf -r junit -o junit.xml cd ../mqtt_utils_host_test ./build_linux_default/mqtt_utils_host_test.elf + cd ../mqtt_outbox_host_test + ./build_linux_coverage/mqtt_outbox_host_test.elf cd ../.. gcovr --gcov-ignore-parse-errors -g -k -r . --html coverage.html -x coverage.xml diff --git a/CMakeLists.txt b/CMakeLists.txt index 7f4b8ce..c0a4b86 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -set(srcs mqtt_client.c lib/mqtt_msg.c lib/mqtt_outbox.c lib/platform_esp32_idf.c) +set(srcs mqtt_client.c lib/mqtt_msg.c lib/platform_esp32_idf.c) if(CONFIG_MQTT_PROTOCOL_5) list(APPEND srcs lib/mqtt5_msg.c mqtt5_client.c) @@ -15,3 +15,6 @@ idf_component_register(SRCS "${srcs}" add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/lib/mqtt_utils) target_link_libraries(${COMPONENT_LIB} PUBLIC idf::mqtt::utils) + +add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/lib/mqtt_outbox) +target_link_libraries(${COMPONENT_LIB} PRIVATE idf::mqtt::outbox) diff --git a/lib/mqtt_outbox/CMakeLists.txt b/lib/mqtt_outbox/CMakeLists.txt new file mode 100644 index 0000000..126fd99 --- /dev/null +++ b/lib/mqtt_outbox/CMakeLists.txt @@ -0,0 +1,9 @@ +set(srcs "${CMAKE_CURRENT_LIST_DIR}/../mqtt_outbox.c") + +add_library(mqtt_outbox_lib ${srcs}) +target_include_directories(mqtt_outbox_lib PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/../include) +idf_component_get_property(heap heap COMPONENT_LIB) +idf_component_get_property(log log COMPONENT_LIB) +target_link_libraries(mqtt_outbox_lib PRIVATE ${heap} ${log}) +add_library(idf::mqtt::outbox ALIAS mqtt_outbox_lib) diff --git a/test/mqtt_outbox_host_test/CMakeLists.txt b/test/mqtt_outbox_host_test/CMakeLists.txt new file mode 100644 index 0000000..ca32ff1 --- /dev/null +++ b/test/mqtt_outbox_host_test/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.16) + +include(${CMAKE_CURRENT_LIST_DIR}/../../cmake/CPM.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/../../cmake/RapidCheck.cmake) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +idf_build_set_property(MINIMAL_BUILD ON) +list(APPEND EXTRA_COMPONENT_DIRS + "$ENV{IDF_PATH}/tools/mocks/freertos/") + +project(mqtt_outbox_host_test) diff --git a/test/mqtt_outbox_host_test/README.md b/test/mqtt_outbox_host_test/README.md new file mode 100644 index 0000000..76902e9 --- /dev/null +++ b/test/mqtt_outbox_host_test/README.md @@ -0,0 +1,14 @@ +# mqtt_outbox host tests + +Isolated host tests for `lib/mqtt_outbox.c`. Tests call the outbox API directly — +no MQTT client, no transport, no FreeRTOS scheduler required. + +## Build and run + +```bash +cd test/mqtt_outbox_host_test +idf.py --preview set-target linux +idf.py build +./build/mqtt_outbox_host_test.elf +``` + diff --git a/test/mqtt_outbox_host_test/main/CMakeLists.txt b/test/mqtt_outbox_host_test/main/CMakeLists.txt new file mode 100644 index 0000000..bd3e3ec --- /dev/null +++ b/test/mqtt_outbox_host_test/main/CMakeLists.txt @@ -0,0 +1,22 @@ +idf_component_register(SRCS "test_main.cpp" "test_outbox.cpp" + INCLUDE_DIRS "." + REQUIRES freertos + WHOLE_ARCHIVE) + +set(SANITIZER_FLAGS -fsanitize=address,undefined -fno-sanitize-recover=undefined) +target_compile_options(${COMPONENT_LIB} PRIVATE ${SANITIZER_FLAGS} -Wno-missing-field-initializers) +target_link_options(${COMPONENT_LIB} INTERFACE ${SANITIZER_FLAGS}) + +add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/../../../lib/mqtt_outbox + ${CMAKE_CURRENT_BINARY_DIR}/mqtt_outbox) + +target_link_libraries(${COMPONENT_LIB} PUBLIC + idf::mqtt::outbox + Catch2::Catch2WithMain + rapidcheck + rapidcheck_catch) + +if(CONFIG_GCOV_ENABLED) + target_compile_options(mqtt_outbox_lib PUBLIC --coverage -fprofile-arcs -ftest-coverage) + target_link_options(mqtt_outbox_lib PUBLIC --coverage -fprofile-arcs -ftest-coverage) +endif() diff --git a/test/mqtt_outbox_host_test/main/Kconfig b/test/mqtt_outbox_host_test/main/Kconfig new file mode 100644 index 0000000..6ef29cd --- /dev/null +++ b/test/mqtt_outbox_host_test/main/Kconfig @@ -0,0 +1,9 @@ +menu "Host-test config" + + config GCOV_ENABLED + bool "Coverage analyzer" + default n + help + Enables coverage analyzing for host tests. + +endmenu diff --git a/test/mqtt_outbox_host_test/main/idf_component.yml b/test/mqtt_outbox_host_test/main/idf_component.yml new file mode 100644 index 0000000..6898760 --- /dev/null +++ b/test/mqtt_outbox_host_test/main/idf_component.yml @@ -0,0 +1,3 @@ +dependencies: + espressif/catch2: + version: "*" diff --git a/test/mqtt_outbox_host_test/main/test_main.cpp b/test/mqtt_outbox_host_test/main/test_main.cpp new file mode 100644 index 0000000..5272683 --- /dev/null +++ b/test/mqtt_outbox_host_test/main/test_main.cpp @@ -0,0 +1,23 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +#include +#include +#include + +extern "C" void app_main(void) +{ + int argc = 1; + const char *argv[2] = {"mqtt_outbox_host_test", nullptr}; + int result = Catch::Session().run(argc, argv); + + if (result != 0) { + printf("Test failed with result %d\n", result); + exit(1); + } else { + printf("Test passed.\n"); + exit(0); + } +} diff --git a/test/mqtt_outbox_host_test/main/test_outbox.cpp b/test/mqtt_outbox_host_test/main/test_outbox.cpp new file mode 100644 index 0000000..f1bc1ff --- /dev/null +++ b/test/mqtt_outbox_host_test/main/test_outbox.cpp @@ -0,0 +1,356 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + * + * Isolated host tests for lib/mqtt_outbox.c. + * All outbox API functions are called directly — no MQTT client, no transport, + * no network stack required. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern "C" { +#include "mqtt_outbox.h" +} + +struct OutboxGuard { + explicit OutboxGuard() : handle(outbox_init()) + { + REQUIRE(handle != nullptr); + } + ~OutboxGuard() + { + outbox_destroy(handle); + } + OutboxGuard(const OutboxGuard &) = delete; + OutboxGuard &operator=(const OutboxGuard &) = delete; + + outbox_handle_t handle; +}; + +static outbox_message_t make_msg(int msg_id, int qos, int msg_type, + const char *payload, int len) +{ + outbox_message_t message{}; + message.msg_id = msg_id; + message.msg_qos = qos; + message.msg_type = msg_type; + message.data = reinterpret_cast(const_cast(payload)); + message.len = len; + message.remaining_data = nullptr; + message.remaining_len = 0; + return message; +} + +TEST_CASE("Outbox lifecycle") +{ + SECTION("init returns a non-null handle") { + outbox_handle_t outbox = outbox_init(); + REQUIRE(outbox != nullptr); + outbox_destroy(outbox); + } + SECTION("destroy on a non-empty outbox reclaims all items") { + OutboxGuard outbox; + auto message = make_msg(1, 1, 3, "hello", 5); + REQUIRE(outbox_enqueue(outbox.handle, &message, 0) != nullptr); + // destructor calls outbox_destroy — LSan catches leaks if it is omitted + } +} + +TEST_CASE("Outbox enqueue") +{ + OutboxGuard outbox; + SECTION("enqueued item starts in QUEUED state") { + auto message = make_msg(42, 1, 3, "data", 4); + outbox_item_handle_t item = outbox_enqueue(outbox.handle, &message, 0); + REQUIRE(item != nullptr); + REQUIRE(outbox_item_get_pending(item) == QUEUED); + } + SECTION("size increases by item length after enqueue") { + REQUIRE(outbox_get_size(outbox.handle) == 0); + const char payload[] = "hello"; + auto message = make_msg(1, 1, 3, payload, static_cast(sizeof(payload) - 1)); + outbox_enqueue(outbox.handle, &message, 0); + REQUIRE(outbox_get_size(outbox.handle) == sizeof(payload) - 1); + } + SECTION("multiple enqueues accumulate size") { + auto message1 = make_msg(1, 1, 3, "abc", 3); + auto message2 = make_msg(2, 1, 3, "de", 2); + outbox_enqueue(outbox.handle, &message1, 0); + outbox_enqueue(outbox.handle, &message2, 0); + REQUIRE(outbox_get_size(outbox.handle) == 5); + } +} + +TEST_CASE("Outbox FIFO dequeue") +{ + OutboxGuard outbox; + SECTION("dequeue returns items in enqueue order") { + auto message1 = make_msg(10, 1, 3, "first", 5); + auto message2 = make_msg(20, 1, 3, "second", 6); + auto message3 = make_msg(30, 1, 3, "third", 5); + outbox_enqueue(outbox.handle, &message1, 0); + outbox_enqueue(outbox.handle, &message2, 0); + outbox_enqueue(outbox.handle, &message3, 0); + uint16_t id; + int type, qos; + size_t len; + outbox_item_handle_t item1 = outbox_dequeue(outbox.handle, QUEUED, nullptr); + REQUIRE(item1 != nullptr); + outbox_item_get_data(item1, &len, &id, &type, &qos); + REQUIRE(id == 10); + // promote first item so next dequeue skips it + outbox_set_pending(outbox.handle, 10, TRANSMITTED); + outbox_item_handle_t item2 = outbox_dequeue(outbox.handle, QUEUED, nullptr); + REQUIRE(item2 != nullptr); + outbox_item_get_data(item2, &len, &id, &type, &qos); + REQUIRE(id == 20); + } + SECTION("dequeue returns nullptr when no item matches state") { + auto message = make_msg(1, 1, 3, "x", 1); + outbox_enqueue(outbox.handle, &message, 0); + REQUIRE(outbox_dequeue(outbox.handle, TRANSMITTED, nullptr) == nullptr); + REQUIRE(outbox_dequeue(outbox.handle, ACKNOWLEDGED, nullptr) == nullptr); + } + SECTION("dequeue on empty outbox returns nullptr") { + REQUIRE(outbox_dequeue(outbox.handle, QUEUED, nullptr) == nullptr); + } +} + +TEST_CASE("Outbox state transitions") +{ + OutboxGuard outbox; + SECTION("set_pending changes the state of an item") { + auto message = make_msg(5, 1, 3, "msg", 3); + outbox_enqueue(outbox.handle, &message, 0); + REQUIRE(outbox_set_pending(outbox.handle, 5, TRANSMITTED) == ESP_OK); + outbox_item_handle_t item = outbox_dequeue(outbox.handle, TRANSMITTED, nullptr); + REQUIRE(item != nullptr); + uint16_t id; + int type, qos; + size_t len; + outbox_item_get_data(item, &len, &id, &type, &qos); + REQUIRE(id == 5); + } + SECTION("full state cycle: QUEUED -> TRANSMITTED -> ACKNOWLEDGED -> CONFIRMED") { + auto message = make_msg(7, 2, 3, "qos2", 4); + outbox_enqueue(outbox.handle, &message, 0); + REQUIRE(outbox_set_pending(outbox.handle, 7, TRANSMITTED) == ESP_OK); + REQUIRE(outbox_set_pending(outbox.handle, 7, ACKNOWLEDGED) == ESP_OK); + REQUIRE(outbox_set_pending(outbox.handle, 7, CONFIRMED) == ESP_OK); + REQUIRE(outbox_dequeue(outbox.handle, CONFIRMED, nullptr) != nullptr); + } + SECTION("set_pending on unknown msg_id returns ESP_FAIL") { + REQUIRE(outbox_set_pending(outbox.handle, 999, TRANSMITTED) == ESP_FAIL); + } +} + +TEST_CASE("Outbox concurrent states") +{ + OutboxGuard outbox; + SECTION("QUEUED head does not shadow a later TRANSMITTED item") { + auto message1 = make_msg(1, 1, 3, "first", 5); + auto message2 = make_msg(2, 1, 3, "second", 6); + outbox_enqueue(outbox.handle, &message1, 0); + outbox_enqueue(outbox.handle, &message2, 0); + // Promote the second item only + outbox_set_pending(outbox.handle, 2, TRANSMITTED); + // dequeue(QUEUED) must still return msg 1 + outbox_item_handle_t queued_item = outbox_dequeue(outbox.handle, QUEUED, nullptr); + REQUIRE(queued_item != nullptr); + uint16_t id; int type, qos; size_t len; + outbox_item_get_data(queued_item, &len, &id, &type, &qos); + REQUIRE(id == 1); + // dequeue(TRANSMITTED) must return msg 2 + outbox_item_handle_t transmitted_item = outbox_dequeue(outbox.handle, TRANSMITTED, nullptr); + REQUIRE(transmitted_item != nullptr); + outbox_item_get_data(transmitted_item, &len, &id, &type, &qos); + REQUIRE(id == 2); + } + SECTION("TRANSMITTED item is not visible to dequeue(QUEUED)") { + auto message = make_msg(3, 1, 3, "only", 4); + outbox_enqueue(outbox.handle, &message, 0); + outbox_set_pending(outbox.handle, 3, TRANSMITTED); + REQUIRE(outbox_dequeue(outbox.handle, QUEUED, nullptr) == nullptr); + } +} + +TEST_CASE("Outbox lookup by msg_id") +{ + OutboxGuard outbox; + SECTION("outbox_get returns the correct item") { + auto message1 = make_msg(100, 1, 3, "a", 1); + auto message2 = make_msg(200, 1, 3, "b", 1); + outbox_enqueue(outbox.handle, &message1, 0); + outbox_enqueue(outbox.handle, &message2, 0); + outbox_item_handle_t item = outbox_get(outbox.handle, 200); + REQUIRE(item != nullptr); + uint16_t id; int type, qos; size_t len; + outbox_item_get_data(item, &len, &id, &type, &qos); + REQUIRE(id == 200); + } + SECTION("outbox_get returns nullptr for unknown msg_id") { + auto message = make_msg(1, 1, 3, "x", 1); + outbox_enqueue(outbox.handle, &message, 0); + REQUIRE(outbox_get(outbox.handle, 999) == nullptr); + } +} + +TEST_CASE("Outbox delete by msg_id and type") +{ + OutboxGuard outbox; + SECTION("deleted item is no longer found") { + auto message = make_msg(55, 1, 3, "del", 3); + outbox_enqueue(outbox.handle, &message, 0); + REQUIRE(outbox_get_size(outbox.handle) == 3); + REQUIRE(outbox_delete(outbox.handle, 55, 3) == ESP_OK); + REQUIRE(outbox_get(outbox.handle, 55) == nullptr); + REQUIRE(outbox_get_size(outbox.handle) == 0); + } + SECTION("delete with wrong type returns ESP_FAIL") { + auto message = make_msg(56, 1, 3, "x", 1); + outbox_enqueue(outbox.handle, &message, 0); + REQUIRE(outbox_delete(outbox.handle, 56, 99) == ESP_FAIL); + REQUIRE(outbox_get(outbox.handle, 56) != nullptr); + } + SECTION("delete unknown msg_id returns ESP_FAIL") { + REQUIRE(outbox_delete(outbox.handle, 9999, 3) == ESP_FAIL); + } +} + +TEST_CASE("Outbox delete by item handle") +{ + OutboxGuard outbox; + SECTION("item is removed and size decreases") { + auto message = make_msg(77, 1, 3, "item", 4); + outbox_item_handle_t item = outbox_enqueue(outbox.handle, &message, 0); + REQUIRE(item != nullptr); + REQUIRE(outbox_get_size(outbox.handle) == 4); + REQUIRE(outbox_delete_item(outbox.handle, item) == ESP_OK); + REQUIRE(outbox_get(outbox.handle, 77) == nullptr); + REQUIRE(outbox_get_size(outbox.handle) == 0); + } +} + +TEST_CASE("Outbox expiry") +{ + OutboxGuard outbox; + SECTION("delete_expired removes items older than timeout") { + // enqueue with tick=0; current_tick=200, timeout=100 → age=200 > 100 + auto message1 = make_msg(1, 1, 3, "old", 3); + auto message2 = make_msg(2, 1, 3, "fresh", 5); + outbox_enqueue(outbox.handle, &message1, 0); + outbox_enqueue(outbox.handle, &message2, 150); // tick=150, age=50 at current_tick=200 + int deleted = outbox_delete_expired(outbox.handle, 200, 100); + REQUIRE(deleted == 1); + REQUIRE(outbox_get(outbox.handle, 1) == nullptr); + REQUIRE(outbox_get(outbox.handle, 2) != nullptr); + } + SECTION("delete_single_expired removes exactly one item") { + auto message1 = make_msg(10, 1, 3, "old1", 4); + auto message2 = make_msg(20, 1, 3, "old2", 4); + outbox_enqueue(outbox.handle, &message1, 0); + outbox_enqueue(outbox.handle, &message2, 0); + // Both are expired at current_tick=500, timeout=100; only one is removed + int id = outbox_delete_single_expired(outbox.handle, 500, 100); + REQUIRE(id >= 0); + // Exactly one item was removed — the other is still present + int remaining = (outbox_get(outbox.handle, 10) != nullptr ? 1 : 0) + + (outbox_get(outbox.handle, 20) != nullptr ? 1 : 0); + REQUIRE(remaining == 1); + } + SECTION("delete_expired returns 0 when no items are expired") { + auto message = make_msg(1, 1, 3, "fresh", 5); + outbox_enqueue(outbox.handle, &message, 1000); + int deleted = outbox_delete_expired(outbox.handle, 1050, 100); + REQUIRE(deleted == 0); + } +} + +// --------------------------------------------------------------------------- +// Property-based tests +// --------------------------------------------------------------------------- + +TEST_CASE("Outbox size invariant (RapidCheck)") +{ + rc::prop("size equals sum of surviving item lengths after arbitrary enqueue/delete", + []() { + OutboxGuard outbox; + // Generate between 1 and 10 items + int count = *rc::gen::inRange(1, 11); + uint64_t expected_size = 0; + std::vector> items; // {msg_id, len} + + for (int i = 0; i < count; ++i) { + int len = *rc::gen::inRange(1, 32); + std::string payload(len, 'x'); + auto message = make_msg(i + 1, 1, 3, payload.c_str(), len); + const bool enqueued = outbox_enqueue(outbox.handle, &message, 0) != nullptr; + RC_ASSERT(enqueued); + items.push_back({i + 1, len}); + expected_size += len; + } + + RC_ASSERT(outbox_get_size(outbox.handle) == expected_size); + + // Randomly delete some items + for (auto &[id, len] : items) { + bool del = *rc::gen::arbitrary(); + + if (del) { + outbox_delete(outbox.handle, id, 3); + expected_size -= len; + } + } + + RC_ASSERT(outbox_get_size(outbox.handle) == expected_size); + }); +} + +TEST_CASE("Outbox FIFO ordering property (RapidCheck)") +{ + rc::prop("dequeue(QUEUED) returns items in exact enqueue order", + []() { + OutboxGuard outbox; + int count = *rc::gen::inRange(1, 16); + std::vector msg_ids; + msg_ids.reserve(count); + + for (int i = 0; i < count; ++i) { + int id = i + 1; + msg_ids.push_back(id); + std::string payload = "p" + std::to_string(id); + auto message = make_msg(id, 1, 3, payload.c_str(), + static_cast(payload.size())); + const bool enqueued = outbox_enqueue(outbox.handle, &message, 0) != nullptr; + RC_ASSERT(enqueued); + } + + // Drain in order: promote each head item to TRANSMITTED after inspecting it + // so the next dequeue(QUEUED) advances to the next item. + for (int i = 0; i < count; ++i) { + outbox_item_handle_t item = outbox_dequeue(outbox.handle, QUEUED, nullptr); + const bool item_valid = item != nullptr; + RC_ASSERT(item_valid); + uint16_t id; int type, qos; size_t len; + outbox_item_get_data(item, &len, &id, &type, &qos); + RC_ASSERT(static_cast(id) == msg_ids[i]); + // Advance past this item for the next iteration + outbox_set_pending(outbox.handle, id, TRANSMITTED); + } + + // No more QUEUED items + const bool no_queued_items = outbox_dequeue(outbox.handle, QUEUED, nullptr) == nullptr; + RC_ASSERT(no_queued_items); + }); +} diff --git a/test/mqtt_outbox_host_test/sdkconfig.ci.coverage b/test/mqtt_outbox_host_test/sdkconfig.ci.coverage new file mode 100644 index 0000000..3c490c0 --- /dev/null +++ b/test/mqtt_outbox_host_test/sdkconfig.ci.coverage @@ -0,0 +1 @@ +CONFIG_GCOV_ENABLED=y diff --git a/test/mqtt_outbox_host_test/sdkconfig.defaults b/test/mqtt_outbox_host_test/sdkconfig.defaults new file mode 100644 index 0000000..d695075 --- /dev/null +++ b/test/mqtt_outbox_host_test/sdkconfig.defaults @@ -0,0 +1,8 @@ +CONFIG_IDF_TARGET="linux" +CONFIG_COMPILER_CXX_EXCEPTIONS=y +CONFIG_COMPILER_CXX_RTTI=y +CONFIG_COMPILER_CXX_EXCEPTIONS_EMG_POOL_SIZE=0 +CONFIG_COMPILER_DISABLE_DEFAULT_ERRORS=y +CONFIG_LOG_COLORS=n +CONFIG_LOG_DEFAULT_LEVEL_DEBUG=y +CONFIG_ESP_MAIN_TASK_STACK_SIZE=10000